Check-in [e9fd1f6094]
Overview
Comment:Added an RPC client REPL
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: e9fd1f6094151578975cbb75412634373c0dd9a1dc663ee9d8b934910b2a35f9
User & Date: rkeene on 2018-12-09 22:10:47
Other Links: manifest | tags
Context
2018-12-09
22:11
Removed extra auto_paths check-in: 542443e166 user: rkeene tags: trunk
22:10
Added an RPC client REPL check-in: e9fd1f6094 user: rkeene tags: trunk
02:24
Include information on what is required to build check-in: 5980af203a user: rkeene tags: trunk
Changes

Modified nano.tcl from [0b1342a368] to [58efd19329].

15
16
17
18
19
20
21

22
23
24
25
26
27
28
29
30
31

32
33
34
35
36
37
38
namespace eval ::nano::work {}
namespace eval ::nano::account {}
namespace eval ::nano::node {}
namespace eval ::nano::ledger {}
namespace eval ::nano::ledger::lmdb {}
namespace eval ::nano::rpc {}
namespace eval ::nano::rpc::client {}

namespace eval ::nano::balance {}
namespace eval ::nano::node::bootstrap {}
namespace eval ::nano::node::realtime {}
namespace eval ::nano::node::cli {}
namespace eval ::nano::network::client {}
namespace eval ::nano::network::server {}
namespace eval ::nano::protocol::create {}
namespace eval ::nano::protocol::parse {}
namespace eval ::nano::protocol::extensions {}
namespace eval ::nano::network::_dns {}


# Constants
set ::nano::block::genesis(main) {{
	"type": "open",
	"source": "E89208DD038FBB269987689621D52292AE9C35941A7484756ECCED92A65093BA",
	"representative": "xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3",
	"account": "xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3",







>










>







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
namespace eval ::nano::work {}
namespace eval ::nano::account {}
namespace eval ::nano::node {}
namespace eval ::nano::ledger {}
namespace eval ::nano::ledger::lmdb {}
namespace eval ::nano::rpc {}
namespace eval ::nano::rpc::client {}
namespace eval ::nano::rpc::cli {}
namespace eval ::nano::balance {}
namespace eval ::nano::node::bootstrap {}
namespace eval ::nano::node::realtime {}
namespace eval ::nano::node::cli {}
namespace eval ::nano::network::client {}
namespace eval ::nano::network::server {}
namespace eval ::nano::protocol::create {}
namespace eval ::nano::protocol::parse {}
namespace eval ::nano::protocol::extensions {}
namespace eval ::nano::network::_dns {}
namespace eval ::nano::_cli {}

# Constants
set ::nano::block::genesis(main) {{
	"type": "open",
	"source": "E89208DD038FBB269987689621D52292AE9C35941A7484756ECCED92A65093BA",
	"representative": "xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3",
	"account": "xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3",
61
62
63
64
65
66
67









68
69
70
71
72
73
74
	"confirm_ack"
	"bulk_pull"
	"bulk_push"
	"frontier_req"
	"bulk_pull_blocks"
	"node_id_handshake"
	"bulk_pull_account"









}
set ::nano::balance::_conversion {
	GNano 1000000000000000000000000000000000000000
	MNano 1000000000000000000000000000000000000
	Gnano 1000000000000000000000000000000000
	Gxrb  1000000000000000000000000000000000
	KNano 1000000000000000000000000000000000







>
>
>
>
>
>
>
>
>







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
	"confirm_ack"
	"bulk_pull"
	"bulk_push"
	"frontier_req"
	"bulk_pull_blocks"
	"node_id_handshake"
	"bulk_pull_account"
}
set ::nano::block::blockTypes {
	"invalid"
	"not_a_block"
	"send"
	"receive"
	"open"
	"change"
	"state"
}
set ::nano::balance::_conversion {
	GNano 1000000000000000000000000000000000000000
	MNano 1000000000000000000000000000000000000
	Gnano 1000000000000000000000000000000000
	Gxrb  1000000000000000000000000000000000
	KNano 1000000000000000000000000000000000
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
			}
			default {
				return -code error "Invalid option: $arg"
			}
		}
	}

	switch -- $type {
		"invalid"     { set typeId 0 }
		"not_a_block" { set typeId 1 }
		"send"        { set typeId 2 }
		"receive"     { set typeId 3 }
		"open"        { set typeId 4 }
		"change"      { set typeId 5 }
		"state"       { set typeId 6 }
		default       { error "Invalid type: $type" }
	}

	switch -- $outputFormat {
		"decimal" {
			set result $typeId
		}
		"hex" {







<
|
|
|
|
<
<
<
|







519
520
521
522
523
524
525

526
527
528
529



530
531
532
533
534
535
536
537
			}
			default {
				return -code error "Invalid option: $arg"
			}
		}
	}


	set type [string tolower $type]
	set typeId [lsearch -exact $::nano::block::blockTypes $type]

	if {$typeId == -1} {



		error "Invalid type: $type"
	}

	switch -- $outputFormat {
		"decimal" {
			set result $typeId
		}
		"hex" {
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
		}
		"bytes" {
			set typeId [binary encode hex $typeId]
			set typeId "0x${typeId}"
		}
	}

	switch -- [format %i $typeId] {
		0 { set type "invalid" }
		1 { set type "not_a_block" }
		2 { set type "send" }
		3 { set type "receive" }
		4 { set type "open" }
		5 { set type "change" }
		6 { set type "state" }
		default {
			error "Invalid type ID: $typeId"
		}
	}

	return $type
}

proc ::nano::block::lengthFromType {type args} {
	set includeType false







|
<
<
|
<
|
<
<
<
|
<







573
574
575
576
577
578
579
580


581

582



583

584
585
586
587
588
589
590
		}
		"bytes" {
			set typeId [binary encode hex $typeId]
			set typeId "0x${typeId}"
		}
	}

	set typeId [format %i $typeId]


	set type [lindex $::nano::block::blockTypes $typeId]

	if {$type eq ""} {



		error "Invalid type ID: $typeId"

	}

	return $type
}

proc ::nano::block::lengthFromType {type args} {
	set includeType false
3107
3108
3109
3110
3111
3112
3113















3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124


3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
	if {[info coroutine] eq ""} {
		after $ms
	} else {
		after $ms [info coroutine]
		yield
	}
}
















proc ::nano::node::getPeers {} {
	if {[info exists ::nano::node::configuration]} {
		set peers [dict get $::nano::node::configuration node preconfigured_peers]
		set defaultPeerPort [dict get $::nano::node::configuration node peering_port]
	} else {
		return [list]
	}

	set completePeers [list]
	foreach peer $peers {


		catch {
			foreach peer [::nano::network::_dns::toIPList $peer] {
				lappend completePeers [dict create address $peer port $defaultPeerPort]
			}
		}
	}

	set now [clock seconds]
	# Cleanup nonces while we are here
	foreach {peerKey peerInfo} [array get ::nano::node::_node_id_nonces] {







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>











>
>


|







3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
	if {[info coroutine] eq ""} {
		after $ms
	} else {
		after $ms [info coroutine]
		yield
	}
}

proc ::nano::internal::parseAddress {address defaultPort} {
	set addressPort $defaultPort
	if {[string index $address 0] eq "\["} {
		regexp {\]:(.*)$} $address -> addressPort
		regexp {^\[(.*)\].*$} $address -> address
		regexp {^::ffff:([0-9\.]*$)} $address -> address
	} elseif {[string match "*:*" $address]} {
		set address [split $address :]
		set addressPort [lindex $address 1]
		set address [lindex $address 0]
	}

	return [list $address $addressPort]
}

proc ::nano::node::getPeers {} {
	if {[info exists ::nano::node::configuration]} {
		set peers [dict get $::nano::node::configuration node preconfigured_peers]
		set defaultPeerPort [dict get $::nano::node::configuration node peering_port]
	} else {
		return [list]
	}

	set completePeers [list]
	foreach peer $peers {
		lassign [::nano::internal::parseAddress $peer $defaultPeerPort] peer peerPort

		catch {
			foreach peer [::nano::network::_dns::toIPList $peer] {
				lappend completePeers [dict create address $peer port $peerPort]
			}
		}
	}

	set now [clock seconds]
	# Cleanup nonces while we are here
	foreach {peerKey peerInfo} [array get ::nano::node::_node_id_nonces] {
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319

	set votedOn [llength $hashes]

	incr ::nano::node::stats([list confirm_ack votedOnCount]) $votedOn
	incr ::nano::node::stats([list confirm_ack rep $voteAccount votedOnCount]) $votedOn

	foreach hash $hashes {
		set ::nano::node::_stats_seen_hashes($hashes) 1
		set ::nano::node::_stats_seen_hashes_by_rep([list $voteAccount $hashes]) 1
	}

	set ::nano::node::stats([list confirm_ack votedOnUniqueCount]) [llength [array names ::nano::node::_stats_seen_hashes]]
	set ::nano::node::stats([list confirm_ack rep $voteAccount votedOnUniqueCount]) [llength [array names ::nano::node::_stats_seen_hashes_by_rep [list $voteAccount *]]]

	return ""
}

proc ::nano::protocol::parse::publish {extensions messageData} {
	set blockTypeID [expr {($extensions >> 8) & 0x0f}]







|
|


|







3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336

	set votedOn [llength $hashes]

	incr ::nano::node::stats([list confirm_ack votedOnCount]) $votedOn
	incr ::nano::node::stats([list confirm_ack rep $voteAccount votedOnCount]) $votedOn

	foreach hash $hashes {
		set ::nano::node::_stats_seen_hashes([list confirm_ack $hash]) 1
		set ::nano::node::_stats_seen_hashes_by_rep([list $voteAccount $hash]) 1
	}

	set ::nano::node::stats([list confirm_ack votedOnUniqueCount]) [llength [array names ::nano::node::_stats_seen_hashes [list confirm_ack *]]]
	set ::nano::node::stats([list confirm_ack rep $voteAccount votedOnUniqueCount]) [llength [array names ::nano::node::_stats_seen_hashes_by_rep [list $voteAccount *]]]

	return ""
}

proc ::nano::protocol::parse::publish {extensions messageData} {
	set blockTypeID [expr {($extensions >> 8) & 0x0f}]
3332
3333
3334
3335
3336
3337
3338









3339
3340
3341
3342
3343
3344
3345
	return [array get retval]
}

proc ::nano::network::server::publish {messageDict} {
	#puts "block: [binary encode hex $blockData]"
#9e1272edade3c247c738a4bd303eb0cfc3da298444bb9d13b8ffbced34ff036f4e1ff833324efc81c237776242928ef76a2cdfaa53f4c4530ee39bfff1977e26e382dd09ec8cafc2427cf817e9afe1f372ce81085ab4feb1f3de1f25ee818e5d000000008fc492fd20e57d048e000000204e7a62f25df739eaa224d403cb107b3f9caa0280113b0328fad3b402c465169006f988549a8b1e20e0a09b4b4dcae5397f6fcc4d507675f58c2b29ae02341b0a4fe562201a61bf27481aa4567c287136b4fd26b4840c93c42c7d1f5c518503d68ec561af4b8cf8
#9e1272edade3c247c738a4bd303eb0cfc3da298444bb9d13b8ffbced34ff036fa5e3647d3d296ec72baea013ba7fa1bf5c3357c33c90196f078ba091295e6e03e382dd09ec8cafc2427cf817e9afe1f372ce81085ab4feb1f3de1f25ee818e5d000000008fb2604ebd1fe098b8000000204e7a62f25df739eaa224d403cb107b3f9caa0280113b0328fad3b402c465165287cd9c61752dc9d011f666534dbdc10461e927503f9599791d73b1cca7fdc032d76db3f91e5b5c3d6206fa48b01bd08da4a89f2e880242e9917cfc3db80d0b9bfe8e6d1dd183d5









	return ""
}

# Namespace ::nano::protocol::parse deals with the network level protocol (outside the node)
# Namespace ::nano::network::server deals with the node's actual interaction with the network
proc ::nano::protocol::parse {message} {
	set messageParsed [binary scan $message a2ccccsa* \







>
>
>
>
>
>
>
>
>







3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
	return [array get retval]
}

proc ::nano::network::server::publish {messageDict} {
	#puts "block: [binary encode hex $blockData]"
#9e1272edade3c247c738a4bd303eb0cfc3da298444bb9d13b8ffbced34ff036f4e1ff833324efc81c237776242928ef76a2cdfaa53f4c4530ee39bfff1977e26e382dd09ec8cafc2427cf817e9afe1f372ce81085ab4feb1f3de1f25ee818e5d000000008fc492fd20e57d048e000000204e7a62f25df739eaa224d403cb107b3f9caa0280113b0328fad3b402c465169006f988549a8b1e20e0a09b4b4dcae5397f6fcc4d507675f58c2b29ae02341b0a4fe562201a61bf27481aa4567c287136b4fd26b4840c93c42c7d1f5c518503d68ec561af4b8cf8
#9e1272edade3c247c738a4bd303eb0cfc3da298444bb9d13b8ffbced34ff036fa5e3647d3d296ec72baea013ba7fa1bf5c3357c33c90196f078ba091295e6e03e382dd09ec8cafc2427cf817e9afe1f372ce81085ab4feb1f3de1f25ee818e5d000000008fb2604ebd1fe098b8000000204e7a62f25df739eaa224d403cb107b3f9caa0280113b0328fad3b402c465165287cd9c61752dc9d011f666534dbdc10461e927503f9599791d73b1cca7fdc032d76db3f91e5b5c3d6206fa48b01bd08da4a89f2e880242e9917cfc3db80d0b9bfe8e6d1dd183d5
	# XXX:TODO: Validate
	set valid true
	incr ::nano::node::stats([list publish valid $valid])

	set hash [dict get $messageDict hash]
	set ::nano::node::_stats_seen_hashes([list publish $hash]) 1

	set ::nano::node::stats([list publish unique]) [llength [array names ::nano::node::_stats_seen_hashes [list publish *]]]

	return ""
}

# Namespace ::nano::protocol::parse deals with the network level protocol (outside the node)
# Namespace ::nano::network::server deals with the node's actual interaction with the network
proc ::nano::protocol::parse {message} {
	set messageParsed [binary scan $message a2ccccsa* \
3568
3569
3570
3571
3572
3573
3574


3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
		vwait ::nano::node::_FOREVER_
	}
}

# RPC Client
## Side-effect: Sets ::nano::rpc::client::config
proc ::nano::rpc::client::init args {


	if {![info exists ::nano::rpc::client::config]} {
		set ::nano::rpc::client::config [dict create \
		    url "http://localhost:7076/" \
		]
	}

	if {[llength $args] > 0} {
		set ::nano::rpc::client::config [dict merge $::nano::rpc::client::config $args]
	}

	return true
}

proc ::nano::rpc::client {action args} {
	::nano::rpc::client::init

	set rpcURL [dict get $::nano::rpc::client::config "url"]

	set jsonArgs [list]
	foreach {key value} $args {
		switch -exact -- $key {
			"-count" {}
			"-accounts" {
				set valueAsStrings [lmap valueItem $value { json::write string $valueItem }]







>
>


|













|







3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
		vwait ::nano::node::_FOREVER_
	}
}

# RPC Client
## Side-effect: Sets ::nano::rpc::client::config
proc ::nano::rpc::client::init args {
	package require http 2

	if {![info exists ::nano::rpc::client::config]} {
		set ::nano::rpc::client::config [dict create \
		    -url "http://localhost:7076/" \
		]
	}

	if {[llength $args] > 0} {
		set ::nano::rpc::client::config [dict merge $::nano::rpc::client::config $args]
	}

	return true
}

proc ::nano::rpc::client {action args} {
	::nano::rpc::client::init

	set rpcURL [dict get $::nano::rpc::client::config "-url"]

	set jsonArgs [list]
	foreach {key value} $args {
		switch -exact -- $key {
			"-count" {}
			"-accounts" {
				set valueAsStrings [lmap valueItem $value { json::write string $valueItem }]
3630
3631
3632
3633
3634
3635
3636








3637
3638
3639
3640
3641
3642
3643
	set data [json::json2dict $data]

	return $data
}

# Account balance manipulation
proc ::nano::balance::toUnit {raw toUnit {decimals 0}} {








	set divisor [dict get $::nano::balance::_conversion $toUnit]

	if {$decimals == 0} {
		set balance [expr {entier(($raw / ($divisor * 1.0)) + 0.5)}]
	} else {
		set balance [expr {$raw / ($divisor * 1.0)}]
		set balance [format "%.${decimals}f" $balance]







>
>
>
>
>
>
>
>







3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
	set data [json::json2dict $data]

	return $data
}

# Account balance manipulation
proc ::nano::balance::toUnit {raw toUnit {decimals 0}} {
	if {![string is entier -strict $raw]} {
		error "Raw values must not be fractional"
	}

	if {$toUnit eq "raw"} {
		return $raw
	}

	set divisor [dict get $::nano::balance::_conversion $toUnit]

	if {$decimals == 0} {
		set balance [expr {entier(($raw / ($divisor * 1.0)) + 0.5)}]
	} else {
		set balance [expr {$raw / ($divisor * 1.0)}]
		set balance [format "%.${decimals}f" $balance]
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708

3709


3710




3711

















3712
3713
3714
3715
3716
3717
3718
3719
3720
3721




3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
	}
}

proc ::nano::balance::toHuman {raw {decimals 3}} {
	set humanUnit [normalizeUnitName _USER]
	set humanUnitMultiplier [dict get $::nano::balance::_conversion $humanUnit]

	if {$raw > [expr {$humanUnitMultiplier / 10000000}]} {
		set baseUnit $humanUnit
	} else {
		set baseUnit "raw"
	}

	set balance [toUnit $raw $baseUnit $decimals]
	set work [split $balance "."]
	set leading  [lindex $work 0]
	set trailing [string trimright [lindex $work 1] "0"]
	set balance [join [list $leading $trailing] "."]
	set balance [string trimright $balance "."]

	set result [list $balance $baseUnit]

	return $result
}


proc ::nano::node::cli args {


	switch -exact -- [lindex $args 0] {




		"-interactive" {

















			set ::nano::node::cli::_using_repl true

			::nano::node::cli -import

			set use_tclreadline false
			catch {
				package require tclreadline
				set use_tclreadline true
			}





			if {$use_tclreadline} {
				proc ::tclreadline::prompt1 {} {
					return "\[[dict get $::nano::node::configuration network]\] nano-node [package present nano]> "
				}
				::tclreadline::Loop
			} else {
				fconfigure stdout -blocking false
				puts -nonewline "> "
				flush stdout
				fileevent stdin readable [list apply {{} {
					uplevel #0 {







|

















>
|
>
>
|
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

|







>
>
>
>

|
<
<







3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787


3788
3789
3790
3791
3792
3793
3794
	}
}

proc ::nano::balance::toHuman {raw {decimals 3}} {
	set humanUnit [normalizeUnitName _USER]
	set humanUnitMultiplier [dict get $::nano::balance::_conversion $humanUnit]

	if {$raw > [expr {$humanUnitMultiplier / (10 ** $decimals)}]} {
		set baseUnit $humanUnit
	} else {
		set baseUnit "raw"
	}

	set balance [toUnit $raw $baseUnit $decimals]
	set work [split $balance "."]
	set leading  [lindex $work 0]
	set trailing [string trimright [lindex $work 1] "0"]
	set balance [join [list $leading $trailing] "."]
	set balance [string trimright $balance "."]

	set result [list $balance $baseUnit]

	return $result
}

# Generic CLI helpers
proc ::nano::_cli {namespace args} {
	for {set argIndex 0} {$argIndex < [llength $args]} {incr argIndex} {
		set arg [lindex $args $argIndex]
		switch -exact -- $arg {
			"-prompt" {
				incr argIndex
				set prompt [lindex $args $argIndex]
			}
			"-interactive" {
				set mode "interactive"
			}
			"-import" {
				set mode "import"
			}
			default {
				error "Unknown argument: $arg"
			}
		}
	}

	if {![info exists mode]} {
		error "Must specify either -interactive or -import"
	}

	switch -exact -- $mode {
		"interactive" {
			set ::nano::${namespace}::cli::_using_repl true

			::nano::_cli $namespace -import

			set use_tclreadline false
			catch {
				package require tclreadline
				set use_tclreadline true
			}

			if {![info exists prompt]} {
				set prompt { return "> " }
			}

			if {$use_tclreadline} {
				proc ::tclreadline::prompt1 {} $prompt


				::tclreadline::Loop
			} else {
				fconfigure stdout -blocking false
				puts -nonewline "> "
				flush stdout
				fileevent stdin readable [list apply {{} {
					uplevel #0 {
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
















































































3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
					puts -nonewline "> "
					flush stdout
				}}]

				vwait forever
			}
		}
		"-import" {
			uplevel #0 {
				namespace import ::nano::node::cli::*
			}
		}
		default {
			error "Not implemented"
		}
	}

}

proc ::nano::node::cli::_interval {interval} {
	set response [list]
	foreach {divisor unit} {60 seconds 60 minutes 24 hours 36527 days 1 century} {
		set amount [expr {$interval % $divisor}]
		if {$amount > 0} {
			if {$amount == 1} {
				set unit [string range $unit 0 end-1]
			}

			lappend response $unit $amount
		}
		set interval [expr {$interval / $divisor}]
	}

	return "[lreverse $response]"
}

















































































proc {::nano::node::cli::show uptime} {} {
	set now [clock seconds]
	set start $::nano::node::startTime
	set statsStart $::nano::node::statsStartTime

	set uptime [expr {$now - $start}]
	set uptimeStats [expr {$now - $statsStart}]

	set format {%-19s: %s}
	lappend response [format $format Uptime [_interval $uptime]]
	lappend response [format $format "Stats last cleared" "[_interval $uptimeStats] ago"]

	return [join $response "\n"]
}

proc {::nano::node::cli::show node-id} args {
	set nodeIDPrivate [dict get $::nano::node::configuration node client_id_private_key]
	set nodeIDPublic [::nano::key::publicKeyFromPrivateKey $nodeIDPrivate -hex]







|
<
|
|
<

|


|
|
<
|
















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>









|
|







3809
3810
3811
3812
3813
3814
3815
3816

3817
3818

3819
3820
3821
3822
3823
3824

3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
					puts -nonewline "> "
					flush stdout
				}}]

				vwait forever
			}
		}
		"import" {

			uplevel #0 [list namespace import ::nano::${namespace}::cli::*]
		}

		default {
			error "Not implemented: $mode"
		}
	}
}


proc ::nano::_cli::interval {interval} {
	set response [list]
	foreach {divisor unit} {60 seconds 60 minutes 24 hours 36527 days 1 century} {
		set amount [expr {$interval % $divisor}]
		if {$amount > 0} {
			if {$amount == 1} {
				set unit [string range $unit 0 end-1]
			}

			lappend response $unit $amount
		}
		set interval [expr {$interval / $divisor}]
	}

	return "[lreverse $response]"
}

proc ::nano::_cli::help {namespace commandRoot args} {
	set response [list]
	if {[llength $args] == 0} {
		lappend response "Commands:"

		set pattern [string trimleft "${commandRoot} *"]

		foreach command [lsort -dictionary [info command ::nano::${namespace}::cli::${pattern}]] {
			set command [namespace tail $command]
			if {[string match "_*" $command]} {
				continue
			}

			set extra [string trim [string range $command [string length $commandRoot] end]]
			if {[string first " " $extra] != -1} {
				continue
			}

			set description ""
			lappend response [format "   %-12s - %s" $command $description]
		}
	}

	return [join $response "\n"]
}

proc ::nano::_cli::multiword {namespace baseCommand args} {
	if {[llength $args] == 0} {
		set args [list "help"]
	}

	set matched false
	set includeBaseCommand false
	foreach base [list $baseCommand multiword] {
		for {set included [expr {[llength $args] - 1}]} {$included >= 0} {incr included -1} {
			if {$base eq "multiword"} {
				set proc "::nano::_cli::${base} [join [lrange $args 0 $included] { }]"
			} else {
				set proc "::nano::${namespace}::cli::${base} [join [lrange $args 0 $included] { }]"
			}
			if {[info command $proc] ne ""} {
				set matched true

				break
			}
		}

		if {$matched} {
			if {$base eq "multiword"} {
				set includeBaseCommand true
			}

			break
		}
	}

	if {!$matched} {
		return -code error "No matching commands in $baseCommand [join $args { }]"
	}

	set args [lrange $args $included+1 end]

	if {$includeBaseCommand} {
		set args [concat [list $namespace $baseCommand] $args]
	}

	tailcall $proc {*}$args
}

proc {::nano::_cli::multiword help} {namespace base args} {
	tailcall help $namespace $base {*}$args
}

# Node CLI
proc ::nano::node::cli {args} {
	tailcall ::nano::_cli node -prompt {
		return "\[[dict get $::nano::node::configuration network]\] nano-node [package present nano]> "
	} {*}$args
}

proc {::nano::node::cli::show uptime} {} {
	set now [clock seconds]
	set start $::nano::node::startTime
	set statsStart $::nano::node::statsStartTime

	set uptime [expr {$now - $start}]
	set uptimeStats [expr {$now - $statsStart}]

	set format {%-19s: %s}
	lappend response [format $format Uptime [::nano::_cli::interval $uptime]]
	lappend response [format $format "Stats last cleared" "[::nano::_cli::interval $uptimeStats] ago"]

	return [join $response "\n"]
}

proc {::nano::node::cli::show node-id} args {
	set nodeIDPrivate [dict get $::nano::node::configuration node client_id_private_key]
	set nodeIDPublic [::nano::key::publicKeyFromPrivateKey $nodeIDPrivate -hex]
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
	return
}

proc {::nano::node::cli::show version} {} {
	return [package present nano]
}

proc ::nano::node::cli::_help {commandRoot args} {
	set response [list]
	if {[llength $args] == 0} {
		lappend response "Commands:"

		set pattern [string trimleft "${commandRoot} *"]

		foreach command [lsort -dictionary [info command ::nano::node::cli::${pattern}]] {
			set command [namespace tail $command]
			if {[string match "_*" $command]} {
				continue
			}

			set extra [string trim [string range $command [string length $commandRoot] end]]
			if {[string first " " $extra] != -1} {
				continue
			}

			set description ""
			lappend response [format "   %-12s - %s" $command $description]
		}
	}

	return [join $response "\n"]
}

proc ::nano::node::cli::help args {
	tailcall _help "" {*}$args
}

proc {::nano::node::cli::show network} {} {
	return [dict get $::nano::node::configuration network]
}

proc {::nano::node::cli::show peers} args {







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|







3996
3997
3998
3999
4000
4001
4002


























4003
4004
4005
4006
4007
4008
4009
4010
4011
	return
}

proc {::nano::node::cli::show version} {} {
	return [package present nano]
}



























proc ::nano::node::cli::help args {
	tailcall ::nano::_cli::help node "" {*}$args
}

proc {::nano::node::cli::show network} {} {
	return [dict get $::nano::node::configuration network]
}

proc {::nano::node::cli::show peers} args {
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
	}
	vwait ::nano::node::bootstrap::lazyRunning
	set endTime [clock seconds]

	set blocksPulled [::nano::node::bootstrap::TMP_LEDGER_BLOCKHASHCOUNT]
	set delta [expr {$endTime - $startTime}]

	puts "Pulled $blocksPulled blocks in [::nano::node::cli::_interval $delta]"
}

proc ::nano::node::cli::_multiword {baseCommand args} {
	if {[llength $args] == 0} {
		set args [list "help"]
	}

	set matched false
	set includeBaseCommand false
	foreach base [list $baseCommand _multiword] {
		for {set included [expr {[llength $args] - 1}]} {$included >= 0} {incr included -1} {
			set proc "::nano::node::cli::${base} [join [lrange $args 0 $included] { }]"
			if {[info command $proc] ne ""} {
				set matched true

				break
			}
		}

		if {$matched} {
			if {$base eq "_multiword"} {
				set includeBaseCommand true
			}

			break
		}
	}

	if {!$matched} {
		return -code error "No matching commands in $baseCommand [join $args { }]"
	}

	set args [lrange $args $included+1 end]

	if {$includeBaseCommand} {
		set args [concat [list $baseCommand] $args]
	}

	tailcall $proc {*}$args
}

proc {::nano::node::cli::_multiword help} {base args} {
	tailcall _help ${base} {*}$args
}

proc ::nano::node::cli::show {args} {
	tailcall ::nano::node::cli::_multiword show {*}$args
}

proc ::nano::node::cli::config {args} {
	tailcall ::nano::node::cli::_multiword config {*}$args
}

proc {::nano::node::cli::config get} {args} {
	set config $::nano::node::configuration
	dict set config node client_id_private_key [binary encode hex [dict get $config node client_id_private_key]]
	set subtreePrefix [lrange $args 0 end-1]
	set subtreeKey [lindex $args end]







|


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|



|







4079
4080
4081
4082
4083
4084
4085
4086
4087
4088











































4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
	}
	vwait ::nano::node::bootstrap::lazyRunning
	set endTime [clock seconds]

	set blocksPulled [::nano::node::bootstrap::TMP_LEDGER_BLOCKHASHCOUNT]
	set delta [expr {$endTime - $startTime}]

	puts "Pulled $blocksPulled blocks in [::nano::_cli::interval $delta]"
}












































proc ::nano::node::cli::show {args} {
	tailcall ::nano::_cli::multiword node show {*}$args
}

proc ::nano::node::cli::config {args} {
	tailcall ::nano::_cli::multiword node config {*}$args
}

proc {::nano::node::cli::config get} {args} {
	set config $::nano::node::configuration
	dict set config node client_id_private_key [binary encode hex [dict get $config node client_id_private_key]]
	set subtreePrefix [lrange $args 0 end-1]
	set subtreeKey [lindex $args end]
4052
4053
4054
4055
4056
4057
4058




















































































































































4059
4060
4061




4062
4063
4064
4065




	lappend response ""
	lappend response "New:"
	lappend response [{::nano::node::cli::config get} {*}$key]

	return [join $response "\n"]
}





















































































































































namespace eval ::nano::node::cli {
	namespace export -clear *
}





if {[info exists ::nano::node::cli::_using_repl]} {
	::nano::node::cli -import
}











>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



>
>
>
>




>
>
>
>
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
	lappend response ""
	lappend response "New:"
	lappend response [{::nano::node::cli::config get} {*}$key]

	return [join $response "\n"]
}

# RPC CLI
proc ::nano::rpc::cli {args} {
	tailcall ::nano::_cli rpc -prompt {
		if {![info exists ::nano::rpc::cli::_cached_network]} {
			set ::nano::rpc::cli::_cached_network [{::nano::rpc::cli::show network}]
		}

		if {![info exists ::nano::rpc::cli::_cached_version]} {
			set ::nano::rpc::cli::_cached_version [{::nano::rpc::cli::show version} -vendor]
		}

		set network $::nano::rpc::cli::_cached_network
		set version $::nano::rpc::cli::_cached_version

		return "\[$network\] nano-rpc $version> "
	} {*}$args
}

proc ::nano::rpc::cli::help args {
	tailcall ::nano::_cli::help rpc "" {*}$args
}

proc ::nano::rpc::cli::show args {
	tailcall ::nano::_cli::multiword rpc show {*}$args
}

proc {::nano::rpc::cli::show uptime} args {
	set stats [::nano::rpc::client stats -type counters]
	set started [dict get $stats created]
	set started [clock scan $started -format {%Y.%m.%d %H:%M:%S}]
	set now [clock seconds]
	set delta [expr {$now - $started}]

	puts "$now, $started"

	return [::nano::_cli::interval $delta]
}

proc {::nano::rpc::cli::show stats} args {
	set response [list]
	if {[llength $args] == 0} {
		set args [list -counters -samples -bootstrap -blocks]
	}

	if {[lsearch -exact $args "-counters"] != -1} {
		lappend countersResponse "Counters:"
		set stats [::nano::rpc::client stats -type counters]
		foreach stat [dict get $stats entries] {
			set value [dict get $stat value]
			dict unset stat value
			lappend countersResponse "  $stat = $value"
		}
		lappend response [join $countersResponse "\n"]
	}

	if {[lsearch -exact $args "-samples"] != -1} {
		unset -nocomplain stats
		set stats [::nano::rpc::client stats -type samples]
		set entries [dict get $stats entries]
		if {[llength $entries] > 0} {
			lappend samplesResponse "Samples:"
			foreach stat $entries {
				lappend samplesResponse "  $stat"
			}
			lappend response [join $samplesResponse "\n"]
		}
	}

	if {[lsearch -exact $args "-bootstrap"] != -1} {
		catch {
			unset -nocomplain stats
			set stats [::nano::rpc::client bootstrap_status]
		}
		if {[info exists stats]} {
			lappend bootstrapResponse "Bootstrap:"
			set maxKeyLength 1
			foreach {statName _} $stats {
				set keyLength [string length $statName]
				if {$keyLength > $maxKeyLength} {
					set maxKeyLength $keyLength
				}
			}
			foreach {statName statVal} $stats {
				lappend bootstrapResponse [format "  %-${maxKeyLength}s = %s" $statName $statVal]
			}
			lappend response [join $bootstrapResponse "\n"]
		}
	}

	if {[lsearch -exact $args "-blocks"] != -1} {
		set blockCount [::nano::rpc::client block_count]
		lappend blocksResponse "Blocks:"
		lappend blocksResponse "  Count     = [dict get $blockCount count]"
		lappend blocksResponse "  Unchecked = [dict get $blockCount unchecked]"
		lappend response [join $blocksResponse "\n"]
	}

	return [join $response "\n\n"]
}

proc {::nano::rpc::cli::show network} args {
	foreach network {main beta} {
		set genesisBlock [::nano::block::dict::genesis $network]
		set genesisBlockHash [::nano::block::dict::toHash $genesisBlock -hex]
		catch {
			set check [dict create]
			set check [::nano::rpc::client block -hash $genesisBlockHash]
		}
		if {[dict exists $check contents]} {
			return $network
		}
	}
	return "<unknown>"
}

proc {::nano::rpc::cli::show version} args {
	set versions [::nano::rpc::client version]

	set vendor [dict get $versions node_vendor]
	set vendorVersion [lindex [split $vendor] end]

	if {[lsearch -exact $args "-vendor"] != -1} {
		return $vendorVersion
	}

	return $versions
}

proc {::nano::rpc::cli::show peers} args {
	set peers [dict get [::nano::rpc::client peers] "peers"]

	if {[lindex $args 0] eq "-count"} {
		return [expr {[llength $peers] / 2}]
	}


	set result [list]
	foreach {peer peerVersion} $peers {
		lassign [::nano::internal::parseAddress $peer ""] peer peerPort
		set peer [list address $peer port $peerPort]
		lappend result "  $peer: version $peerVersion"
	}

	return [join $result "\n"]
}


# Export namespaces
namespace eval ::nano::node::cli {
	namespace export -clear *
}

namespace eval ::nano::rpc::cli {
	namespace export -clear *
}

if {[info exists ::nano::node::cli::_using_repl]} {
	::nano::node::cli -import
}

if {[info exists ::nano::rpc::cli::_using_repl]} {
	::nano::rpc::cli -import
}

Added rpc-client.tcl version [42f34ebffa].





























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#! /usr/bin/env tclsh

lappend auto_path [pwd]
lappend auto_path /opt/appfs/rkeene.org/tcllib/platform/latest/lib
lappend auto_path /opt/appfs/rkeene.org/tclreadline/platform/latest/lib
lappend auto_path [file join [pwd] lib]

package require nano

## Set defaults
set url {http://[::1]:55000/}

## Process
if {[llength $argv] % 2 != 0} {
	lappend argv ""
}
foreach {opt optval} $argv {
	switch -- $opt {
		"--url" {
			set url $optval
		}
		"-h" - "--help" {
			puts "Usage: rpc-client \[--help\] \[--url <rpc-url>\]"
			exit 0
		}
		default {
			puts stderr "Invalid option: $opt"
			exit 1
		}
	}
}

# Override logging, to file
proc ::nano::node::user_log {line} {
	if {![info exists ::logfd]} {
		set ::logfd [open "node.log" a+]
		fconfigure $::logfd -blocking false
	}

	puts $::logfd $line
	flush $::logfd
}


nano::rpc::client::init -url $url
::nano::rpc::cli -interactive