Diff

Differences From Artifact [58efd19329]:

To Artifact [1cbf28674e]:


343
344
345
346
347
348
349
350

351








352
353
354
355
356
357
358
343
344
345
346
347
348
349

350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366







-
+

+
+
+
+
+
+
+
+







		set pubKey [string toupper [binary encode hex $pubKey]]
	}

	return $pubKey
}

# Low-level block management
proc ::nano::block::dict::toBlock {blockDict} {
proc ::nano::block::dict::toBlock {blockDict args} {
	array set block $blockDict

	set blockData ""

	# Include type if requested
	set field "-type"
	if {[lsearch -exact $args $field] != -1} {
		append blockData [binary format c [lsearch -exact $::nano::block::blockTypes $block(type)]]
	}

	switch -- $block(type) {
		"state" {
			append blockData [::nano::address::toPublicKey $block(account)]
			append blockData [binary decode hex $block(previous)]
			append blockData [::nano::address::toPublicKey $block(representative)]
			append blockData [binary decode hex [format %032llX $block(balance)]]
380
381
382
383
384
385
386






























387
388
389
390
391
392
393
394






























395
396
397
398
399
400
401
402
403
404
405
406
407
408
409

410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472





473















474
475
476
477
478
479
480







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







			append blockData [binary decode hex $block(previous)]
			append blockData [::nano::address::toPublicKey $block(representative)]
		}
		default {
			return -code error "Invalid block type $block(type)"
		}
	}

	foreach field {-work -signature} {
		if {[lsearch -exact $args $field] == -1} {
			# It makes no sense to miss an intermediate field, so if
			# one of them is excluded, stop processing further fields
			break
		}

		set field [string trim $field "-"]
		set value $block($field)

		switch -exact -- $field {
			"work" {
				if {[string length $value] != $::nano::work::workValueLength} {
					if {$block(type) eq "state"} {
						set value [binary format W $value]
					} else {
						set value [binary format w $value]
					}
				}
			}
			"signature" {
				if {[string length $value] != $::nano::block::signatureLength} {
					set value [binary decode hex $value]
				}
			}
		}

		append blockData $value
	}

	return $blockData
}

proc ::nano::block::json::toBlock {blockJSON} {
	set blockDict [::nano::block::dict::fromJSON $blockJSON]
	tailcall ::nano::block::dict::toBlock $blockDict
}

proc ::nano::block::dict::_addWorkData {blockDict} {
	# Do nothing if this is already set
	if {[dict exists $blockDict _workData]} {
		return $blockDict
	}

	# Parse out the work data
	if {[dict get $blockDict "type"] in {send receive change state}} {
		set workDataBasedOn "previous"
	}

	if {[dict get $blockDict "type"] eq "state" && [dict get $blockDict "previous"] eq $::nano::block::zero && [dict get $blockDict "link"] ne $::nano::block::zero} {
		set workDataBasedOn "account"
	}

	if {[dict get $blockDict "type"] eq "open"} {
		set workDataBasedOn "account"
	}

	if {[info exists workDataBasedOn]} {
		if {$workDataBasedOn eq "previous"} {
			dict set blockDict "_workData" [dict get $blockDict "previous"]
		} else {
			dict set blockDict "_workData" [::nano::address::toPublicKey [dict get $blockDict "account"]]
		}
	}

	return $blockDict
}

proc ::nano::block::dict::fromJSON {blockJSON} {
	set retval [::json::json2dict $blockJSON]

	if {[dict get $retval "type"] eq "send"} {
		set balance [dict get $retval "balance"]
		set balance [format %lli "0x$balance"]
		dict set retval "balance" $balance
	}

	# Parse out the work data
	if {[dict get $retval "type"] in {send receive change state}} {
		set workDataBasedOn "previous"
	}

	set retval [::nano::block::dict::_addWorkData $retval]
	if {[dict get $retval "type"] eq "state" && [dict get $retval "previous"] eq $::nano::block::zero && [dict get $retval "link"] eq $::nano::block::zero} {
		set workDataBasedOn "account"
	}

	if {[dict get $retval "type"] eq "open"} {
		set workDataBasedOn "account"
	}

	if {[info exists workDataBasedOn]} {
		if {$workDataBasedOn eq "previous"} {
			dict set retval "_workData" [dict get $retval "previous"]
		} else {
			dict set retval "_workData" [::nano::address::toPublicKey [dict get $retval "account"]]
		}
	}

	return $retval
}

proc ::nano::block::json::fromDict {blockDict} {
	array set block $blockDict

690
691
692
693
694
695
696
697

698
699
700
701
702
703
704
705
706
707

708
709
710
711
712
713
714
715
716
717

718
719
720
721
722
723
724
725
726
727

728
729
730
731
732
733
734
735
736

737
738
739
740
741
742
743
739
740
741
742
743
744
745

746
747
748
749
750
751
752
753
754
755

756
757
758
759
760
761
762
763
764
765

766
767
768
769
770
771
772
773
774
775

776
777
778
779
780
781
782
783
784

785
786
787
788
789
790
791
792







-
+









-
+









-
+









-
+








-
+







	set addArgs_fromPublicKey [list]
	if {$addressPrefix eq "xrb_"} {
		lappend addArgs_fromPublicKey "-xrb"
	}

	switch -- $block(type) {
		"state" {
			binary scan $blockData a32H64a32H32H64H128H16 \
			binary scan $blockData a32H64a32H32H64H128Wu \
				block(account) \
				block(previous) \
				block(representative) \
				block(balance) \
				block(link) \
				block(signature) \
				block(work)
		}
		"open" {
			binary scan $blockData H64a32a32H128H16 \
			binary scan $blockData H64a32a32H128wu \
				block(source) \
				block(representative) \
				block(account) \
				block(signature) \
				block(work)

			set block(_workData) $block(account)
		}
		"send" {
			binary scan $blockData H64a32H32H128H16 \
			binary scan $blockData H64a32H32H128wu \
				block(previous) \
				block(destination) \
				block(balance) \
				block(signature) \
				block(work)

			set block(_workData) $block(previous)
		}
		"receive" {
			binary scan $blockData H64H64H128H16 \
			binary scan $blockData H64H64H128wu \
				block(previous) \
				block(source) \
				block(signature) \
				block(work)

			set block(_workData) $block(previous)
		}
		"change" {
			binary scan $blockData H64a32H128H16 \
			binary scan $blockData H64a32H128wu \
				block(previous) \
				block(representative) \
				block(signature) \
				block(work)

			set block(_workData) $block(previous)
		}
754
755
756
757
758
759
760



761
762
763
764
765
766
767
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819







+
+
+







		switch -exact -- $field {
			"account" - "representative" - "link_as_account" - "destination" {
				set block($field) [::nano::address::fromPublicKey $block($field) {*}$addArgs_fromPublicKey]
			}
			"balance" {
				set block($field) [format %lli "0x$block($field)"]
			}
			"work" {
				set block($field) [format %016llx $block($field)]
			}
		}
	}


	return [array get block]
}

976
977
978
979
980
981
982
983
984









985
986
987
988
989
990
991
1028
1029
1030
1031
1032
1033
1034


1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050







-
-
+
+
+
+
+
+
+
+
+







	}

	set retval [::nano::block::json::fromDict $retval]

	return $retval
}

proc ::nano::block::dict::verifySignature {blockDict} {
	set publicKey [::nano::address::toPublicKey [dict get $blockDict account]]
proc ::nano::block::dict::verifySignature {blockDict {publicKey ""}} {
	if {$publicKey eq ""} {
		if {![dict exists $blockDict "account"]} {
			error "Account or public key must be specified"
		} else {
			set publicKey [::nano::address::toPublicKey [dict get $blockDict account]]
		}
	}

	set signature [dict get $blockDict signature]

	set blockDict [_addBlockHash $blockDict]

	set blockHash [dict get $blockDict _blockHash]

	tailcall ::nano::block::verifyBlockHash $blockHash $signature $publicKey
1017
1018
1019
1020
1021
1022
1023

1024
1025
1026
1027
1028
1029
1030
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090







+







			default {
				return -code error "Invalid option: $arg"
			}
		}
	}

	set blockDict [_addBlockHash $blockDict]
	set blockDict [_addWorkData $blockDict]

	set blockHash [dict get $blockDict _workData]

	set work [::nano::work::fromWorkData $blockHash -binary]

	if {$outputMode eq "work"} {
		if {$outputFormat eq "hex"} {
1067
1068
1069
1070
1071
1072
1073

1074
1075
1076
1077
1078
1079
1080
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141







+







	set retval [::nano::block::json::fromDict $retval]

	return $retval
}

proc ::nano::block::dict::validateWork {blockDict} {
	set blockDict [_addBlockHash $blockDict]
	set blockDict [_addWorkData $blockDict]

	set blockHash [dict get $blockDict _workData]
	set work      [dict get $blockDict work]

	tailcall ::nano::work::validate $blockHash $work
}

1104
1105
1106
1107
1108
1109
1110

















1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122

1123
1124
1125
1126
1127
1128
1129
1130
1131
1132



















1133
1134
1135
1136
1137
1138
1139
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201










1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+












+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







proc ::nano::block::dict::genesis {network} {
	if {![info exists ::nano::block::dict::genesis($network)]} {
		set ::nano::block::dict::genesis($network) [::nano::block::dict::fromJSON [::nano::block::json::genesis $network]]
	}

	return $::nano::block::dict::genesis($network)
}

proc ::nano::block::dict::printable {blockDict} {
	dict for {key value} $blockDict {
		switch -exact -- $key {
			"_workData" {
				if {[string length $value] == $::nano::block::hashLength} {
					set value [binary encode hex $value]
					dict set blockDict $key $value
				}
			}
		}
	}

	dict unset blockDict _blockData

	return $blockDict
}

#   send from <account> to <account> previousBalance <balance>
#        amount <amount> sourceBlock <sourceBlockHash>
#        previous <previousBlockHash> ?representative <representative>?
proc ::nano::block::create::send {args} {
	array set block $args
	if {![info exists block(representative)]} {
		set block(representative) $block(from)
	}

	set block(balance) [expr {$block(previousBalance) - $block(amount)}]

	if {[info exists block(-legacy)] && $block(-legacy)} {
	set blockDict [dict create \
		"type" state \
		"account" $block(from) \
		"previous" $block(previous) \
		"representative" $block(representative) \
		"balance" $block(balance) \
		"link_as_account" $block(to) \
		"_workData" $block(previous) \
		"_comment" "Send $block(amount) raw from $block(from) to $block(to)" \
	]
		set blockDict [dict create \
			"type" send \
			"previous" $block(previous) \
			"balance" $block(balance) \
			"_workData" $block(previous) \
			"_comment" "Legacy send $block(amount) raw from $block(from) to $block(to)" \
		]
	} else {
		set blockDict [dict create \
			"type" state \
			"account" $block(from) \
			"previous" $block(previous) \
			"representative" $block(representative) \
			"balance" $block(balance) \
			"link_as_account" $block(to) \
			"_workData" $block(previous) \
			"_comment" "Send $block(amount) raw from $block(from) to $block(to)" \
		]
	}

	if {[info exists block(signKey)]} {
		set blockDict [::nano::block::dict::sign $blockDict $block(signKey) -update]
	}

	if {[info exists block(-json)] && $block(-json)} {
		return [::nano::block::json::fromDict $blockDict]
1150
1151
1152
1153
1154
1155
1156

1157
1158
1159
1160

1161
1162
1163
1164
1165


1166
1167
1168
1169
1170
1171
1172
1173
1174
1175





























1176
1177
1178
1179
1180
1181
1182
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257










1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293







+




+





+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	array set block $args

	if {![info exists block(representative)]} {
		set block(representative) $block(to)
	}

	if {![info exists block(previous)]} {
		set isOpen true
		set block(previous) $::nano::block::zero
		set block(previousBalance) 0
		set block(_workData) [::nano::address::toPublicKey $block(to) -hex]
	} else {
		set isOpen false
		set block(_workData) $block(previous)
	}

	set block(balance) [expr {$block(previousBalance) + $block(amount)}]

	if {[info exists block(-legacy)] && $block(-legacy)} {
		if {$isOpen} {
	set blockDict [dict create \
		"type" state \
		"account" $block(to) \
		"previous" $block(previous) \
		"representative" $block(representative) \
		"balance" $block(balance) \
		"link" $block(sourceBlock) \
		"_workData" $block(_workData) \
		"_comment" "Receive $block(amount) raw on $block(to) from hash $block(sourceBlock)" \
	]
			set blockDict [dict create \
				"type" open \
				"account" $block(to) \
				"representative" $block(representative) \
				"source" $block(sourceBlock) \
				"_workData" $block(_workData) \
				"_comment" "Legacy open + receive of $block(amount) raw on $block(to) from hash $block(sourceBlock)" \
			]
		} else {
			set blockDict [dict create \
				"type" receive \
				"source" $block(sourceBlock) \
				"previous" $block(previous) \
				"_workData" $block(_workData) \
				"_comment" "Legacy receive $block(amount) raw on $block(to) from hash $block(sourceBlock)" \
			]
		}
	} else {
		set blockDict [dict create \
			"type" state \
			"account" $block(to) \
			"previous" $block(previous) \
			"representative" $block(representative) \
			"balance" $block(balance) \
			"link" $block(sourceBlock) \
			"_workData" $block(_workData) \
			"_comment" "Receive $block(amount) raw on $block(to) from hash $block(sourceBlock)" \
		]
	}

	if {[info exists block(signKey)]} {
		set blockDict [::nano::block::dict::sign $blockDict $block(signKey) -update]
	}

	if {[info exists block(-json)] && $block(-json)} {
		return [::nano::block::json::fromDict $blockDict]
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434

2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445

2446
2447
2448
2449
2450
2451
2452
2533
2534
2535
2536
2537
2538
2539




2540

2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551

2552
2553
2554
2555
2556
2557
2558
2559







-
-
-
-

-
+










-
+








proc ::nano::protocol::parse::confirm_req {extensions blockData} {
	set blockTypeID [expr {($extensions >> 8) & 0x0f}]
	set blockType [::nano::block::typeFromTypeID $blockTypeID]
	set blockDict [::nano::block::dict::fromBlock $blockData -type=$blockType]
	set blockHash [::nano::block::dict::toHash $blockDict -hex]

	# XXX:TEMPORARY
	dict unset blockDict _blockData
	dict unset blockDict _workData

	set retval(blockType) $blockType
	set retval(block) $blockDict
	set retval(block) [::nano::block::dict::printable $blockDict]
	set retval(hash) $blockHash

	return [array get retval]
}

proc ::nano::protocol::parse::confirm_ack {extensions blockData} {
	set blockTypeID [expr {($extensions >> 8) & 0x0f}]
	set blockType [::nano::block::typeFromTypeID $blockTypeID]

	# Header
	binary scan $blockData H64H128wa* \
	binary scan $blockData H64H128wua* \
		retval(voteAccount) \
		retval(voteSignature) \
		retval(voteSequence) \
		newBlockData

	set blockData $newBlockData

2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487

2488
2489
2490
2491
2492
2493
2494
2581
2582
2583
2584
2585
2586
2587




2588
2589

2590
2591
2592
2593
2594
2595
2596
2597







-
-
-
-


-
+







			append signedData [binary decode hex $hash]
		}
	} else {
		set blockDict [::nano::block::dict::fromBlock $blockData -type=$blockType]
		set blockHash [::nano::block::dict::toHash $blockDict -hex]
		append signedData [binary decode hex $blockHash]

		# XXX:TEMPORARY
		dict unset blockDict _blockData
		dict unset blockDict _workData

		set retval(blockType) $blockType
		set retval(hashes) [list $blockHash]
		set retval(block) $blockDict
		set retval(block) [::nano::block::dict::printable $blockDict]
	}

	# Add the sequence number to the signed data
	append signedData [binary format w $retval(voteSequence)]

	# Verify signature of the hash
	set valid false
2520
2521
2522
2523
2524
2525
2526
2527
2528


2529
2530
2531
2532
2533
2534
2535
2623
2624
2625
2626
2627
2628
2629


2630
2631
2632
2633
2634
2635
2636
2637
2638







-
-
+
+







		set retval(valid) true
	}

	return [array get retval]
}

proc ::nano::protocol::create {network messageType args} {
	set versionUsing 14
	set versionMin 13
	set versionUsing 15
	set versionMin 14
	set versionMax $versionUsing
	set messageInfo(extensions) 0
	set messageInfo(blockType) 0

	set messageType [string tolower $messageType]
	set messageTypeID [lsearch -exact $::nano::network::messageTypes $messageType]
	if {$messageTypeID == -1} {
3232
3233
3234
3235
3236
3237
3238








3239
3240
3241
3242
3243
3244
3245
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356







+
+
+
+
+
+
+
+







				set peerSock [::nano::node::createSocket realtime $address $port]
#puts "Querying $peerSock with node_id_handshake (1)"
				::nano::network::client $peerSock "node_id_handshake" query -query $node_id_nonce
			}
		}
	}

	# Stats
	incr ::nano::node::stats([list keepalive count])
	incr ::nano::node::stats([list keepalive peers]) [llength $peers]
	foreach peer $peers {
		set ::nano::node::_stats_seen_hashes([list keepalive $peer]) 1
	}
	set ::nano::node::stats([list keepalive peersUnique]) [llength [array names ::nano::node::_stats_seen_hashes [list keepalive *]]]

	return ""
}

proc ::nano::protocol::parse::node_id_handshake {extensions messageData} {
	array set result [list]

	set flags [nano::protocol::extensions::get "node_id_handshake" $extensions]
3259
3260
3261
3262
3263
3264
3265



3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283





3284
3285
3286
3287





3288
3289
3290
3291
3292
3293
3294
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418







+
+
+


















+
+
+
+
+




+
+
+
+
+







proc ::nano::network::server::node_id_handshake {messageDict} {
	set retval ""

	if {"query" in [dict get $messageDict flags]} {
		set query [dict get $messageDict query]
		set clientID [dict get $::nano::node::configuration node client_id_private_key]
		set retval [dict create "invoke_client" [list node_id_handshake response -privateKey $clientID -query [binary decode hex $query]]]

		# Stats
		incr ::nano::node::stats([list node_id_handshake query count])
	}

	if {"response" in [dict get $messageDict flags]} {
		set peerInfo [dict get $messageDict socket remote]
		set peerAddress [lindex $peerInfo 0]
		set peerPort [lindex $peerInfo 1]
		set peer [dict create address $peerAddress port $peerPort]

		# XXX:TODO: Verify the nonce
		if {![info exists ::nano::node::_node_id_nonces($peer)]} {
			return ""
		}
		set sentNonce $::nano::node::_node_id_nonces($peer)
		unset ::nano::node::_node_id_nonces($peer)

		# Add the peer to our list of peers
#puts "Got node_id_handshake response from $peer"
		set ::nano::node::peers($peer) [dict create lastSeen [clock seconds]]

		# Stats
		incr ::nano::node::stats([list node_id_handshake response count])
		set ::nano::node::_stats_seen_hashes([list node_id_handshake [dict get $messageDict key]]) 1
		set ::nano::node::stats([list node_id_handshake response uniqueKeys]) [llength [array names ::nano::node::_stats_seen_hashes [list node_id_handshake *]]]
	}

	return $retval
}

proc ::nano::network::server::confirm_req {messageDict} {
	incr ::nano::node::stats([list confirm_req])
	return ""
}

proc ::nano::network::server::confirm_ack {messageDict} {
	# keep statistics
	dict with messageDict {}

	incr ::nano::node::stats([list confirm_ack valid $valid])
	if {!$valid} {
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346

3347
3348
3349
3350








3351
3352
3353
3354
3355





3356
3357












3358


3359
3360
3361
3362
3363

3364
3365
3366
3367
3368
3369
3370
3458
3459
3460
3461
3462
3463
3464




3465

3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488


3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504

3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515







-
-
-
-

-
+




+
+
+
+
+
+
+
+





+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+

+
+

-



+








proc ::nano::protocol::parse::publish {extensions messageData} {
	set blockTypeID [expr {($extensions >> 8) & 0x0f}]
	set blockType [::nano::block::typeFromTypeID $blockTypeID]
	set blockDict [::nano::block::dict::fromBlock $messageData -type=$blockType]
	set blockHash [::nano::block::dict::toHash $blockDict -hex]

	# XXX:TEMPORARY
	dict unset blockDict _blockData
	dict unset blockDict _workData

	set retval(blockType) $blockType
	set retval(block) $blockDict
	set retval(block) [::nano::block::dict::printable $blockDict]
	set retval(hash) $blockHash

	return [array get retval]
}

proc ::nano::internal::boolean {value} {
	if {$value} {
		return true
	} else {
		return false
	}
}

proc ::nano::network::server::publish {messageDict} {
	#puts "block: [binary encode hex $blockData]"
#9e1272edade3c247c738a4bd303eb0cfc3da298444bb9d13b8ffbced34ff036f4e1ff833324efc81c237776242928ef76a2cdfaa53f4c4530ee39bfff1977e26e382dd09ec8cafc2427cf817e9afe1f372ce81085ab4feb1f3de1f25ee818e5d000000008fc492fd20e57d048e000000204e7a62f25df739eaa224d403cb107b3f9caa0280113b0328fad3b402c465169006f988549a8b1e20e0a09b4b4dcae5397f6fcc4d507675f58c2b29ae02341b0a4fe562201a61bf27481aa4567c287136b4fd26b4840c93c42c7d1f5c518503d68ec561af4b8cf8
#9e1272edade3c247c738a4bd303eb0cfc3da298444bb9d13b8ffbced34ff036fa5e3647d3d296ec72baea013ba7fa1bf5c3357c33c90196f078ba091295e6e03e382dd09ec8cafc2427cf817e9afe1f372ce81085ab4feb1f3de1f25ee818e5d000000008fb2604ebd1fe098b8000000204e7a62f25df739eaa224d403cb107b3f9caa0280113b0328fad3b402c465165287cd9c61752dc9d011f666534dbdc10461e927503f9599791d73b1cca7fdc032d76db3f91e5b5c3d6206fa48b01bd08da4a89f2e880242e9917cfc3db80d0b9bfe8e6d1dd183d5
if {[catch {
	set hash [dict get $messageDict hash]
	set block [dict get $messageDict block]
	switch -exact -- [dict get $block type] {
		"send" - "receive" - "change" {
	# XXX:TODO: Validate
	set valid true
			set valid maybe
		}
		"open" - "state" {
			set valid false
		}
	}
	catch {
		set valid [::nano::internal::boolean [::nano::block::dict::verifySignature $block]]
	}
	set validWork [::nano::internal::boolean [::nano::block::dict::validateWork $block]]


	incr ::nano::node::stats([list publish valid $valid])
	incr ::nano::node::stats([list publish validWork $validWork])
	incr ::nano::node::stats([list publish type [dict get $block type]])

	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 *]]]
}]} { puts $::errorInfo }

	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} {
3613
3614
3615
3616
3617
3618
3619
3620











3621

3622
3623











3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641

3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657

3658


3659
3660
3661
3662
3663
3664
3665
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
3801
3802
3803
3804
3805
3806
3807
3808

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








+
+
+
+
+
+
+
+
+
+
+

+


+
+
+
+
+
+
+
+
+
+
+

















-
+
















+
-
+
+







	return true
}

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

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

	switch -exact -- $action {
		"version" - "block" {
			set timeout 1000
		}
	}

	set timeoutArgs [list]
	if {[info exists timeout]} {
		lappend timeoutArgs -timeout $timeout
	}

	set jsonArgs [list]
	set outputFormat "dict"
	foreach {key value} $args {
		switch -exact -- $key {
			"-outputformat" {
				switch -exact -- $value {
					"dict" - "json" {
						set outputFormat $value
					}
					default {
						error "Invalid output format: $value"
					}
				}
				continue
			}
			"-count" {}
			"-accounts" {
				set valueAsStrings [lmap valueItem $value { json::write string $valueItem }]
				set value [json::write array {*}$valueAsStrings]
			}
			default {
				set value [json::write string $value]
			}
		}
		set key [string range $key 1 end]

		lappend jsonArgs $key $value
	}

	set query [json::write object action [json::write string $action] {*}$jsonArgs]

	catch {
		set token [http::geturl $rpcURL -query $query]
		set token [http::geturl $rpcURL -query $query {*}$timeoutArgs]
		set ncode [http::ncode $token]
		set data [http::data $token]
	} error
	if {![info exists data]} {
		set ncode -1
		set data $error
	}

	if {[info exists token]} {
		http::cleanup $token
	}

	if {$ncode ne "200"} {
		return -code error "$ncode: $data"
	}

	if {$outputFormat eq "dict"} {
	set data [json::json2dict $data]
		set data [json::json2dict $data]
	}

	return $data
}

# Account balance manipulation
proc ::nano::balance::toUnit {raw toUnit {decimals 0}} {
	if {![string is entier -strict $raw]} {
3939
3940
3941
3942
3943
3944
3945
3946
3947

3948
3949

3950
3951
3952
3953



3954
3955






















3956
3957
3958
3959
3960
3961
3962
4109
4110
4111
4112
4113
4114
4115
4116

4117


4118
4119



4120
4121
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








-
+
-
-
+

-
-
-
+
+
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	set nodeIDPublic [::nano::key::publicKeyFromPrivateKey $nodeIDPrivate -hex]
	return $nodeIDPublic
}

proc {::nano::node::cli::show logs} args {
	return [join [lrange $::nano::node::log end-19 end] "\n"]
}

proc {::nano::node::cli::show stats} args {
proc {::nano::node::cli::clear stats} args {
	if {[lindex $args 0] eq "-clear"} {
		set ::nano::node::statsStartTime [clock seconds]
	set ::nano::node::statsStartTime [clock seconds]

		unset -nocomplain ::nano::node::stats
		unset -nocomplain ::nano::node::_stats_seen_hashes
		unset -nocomplain ::nano::node::_stats_seen_hashes_by_rep
	unset -nocomplain ::nano::node::stats
	unset -nocomplain ::nano::node::_stats_seen_hashes
	unset -nocomplain ::nano::node::_stats_seen_hashes_by_rep

		return
	return
}

proc {::nano::node::cli::clear peers} args {
	set ::nano::node::statsStartTime [clock seconds]

	unset -nocomplain ::nano::node::peers
	array set ::nano::node::peers [list]

	return
}

proc {::nano::node::cli::show stats} args {
	set now [clock seconds]
	set statsStart $::nano::node::statsStartTime
	set uptimeStats [expr {$now - $statsStart}]

	set quiet false
	if {!$quiet} {
		set format {%-19s: %s}
		puts [format $format "Stats last cleared" "[::nano::_cli::interval $uptimeStats] ago"]
		puts ""
	}

	set globalOnly false
	if {[lindex $args 0] eq "-global"} {
		set globalOnly true
	}

3986
3987
3988
3989
3990
3991
3992









3993

3994
3995
3996
3997
3998
3999
4000
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191

4192
4193
4194
4195
4196
4197
4198
4199







+
+
+
+
+
+
+
+
+
-
+







			set maxKeyLen $keyLen
		}

		set localStats($key) $val
	}

	foreach {key val} [lsort -stride 2 -dictionary [array get localStats]] {
		set extra ""
		if {![regexp { (min|max)[A-Z]} $key]} {
			if {[string is entier -strict $val]} {
				set valAvg [expr {($val * 1.0) / $uptimeStats}]
				set valAvg [format %.4f $valAvg]
				set extra " (avg: $valAvg per second)"
			}
		}

		puts [format "%-${maxKeyLen}s = $val" $key $val]
		puts [format "%-${maxKeyLen}s = %s%s" $key $val $extra]
	}

	return
}

proc {::nano::node::cli::show version} {} {
	return [package present nano]
4081
4082
4083
4084
4085
4086
4087




4088
4089
4090
4091
4092
4093
4094
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297







+
+
+
+







	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::clear {args} {
	tailcall ::nano::_cli::multiword node clear {*}$args
}

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
4126
4127
4128
4129
4130
4131
4132

4133


4134
4135
4136

4137
4138
4139
4140
4141















4142
4143
4144
4145
4146
4147
4148
4329
4330
4331
4332
4333
4334
4335
4336

4337
4338
4339
4340
4341
4342





4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364







+
-
+
+



+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	return [join $response "\n"]
}

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

		if {![info exists ::nano::rpc::cli::_cached_version]} {
			catch {
			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
				set ::nano::rpc::cli::_cached_version [{::nano::rpc::cli::show version} -vendor]
			}
		}

		if {[info exists ::nano::rpc::cli::_cached_network]} {
			set network $::nano::rpc::cli::_cached_network
		} else {
			set network "<unknown>"
		}

		if {[info exists ::nano::rpc::cli::_cached_version]} {
			set version $::nano::rpc::cli::_cached_version
		} else {
			set version "<unknown>"
		}

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

proc ::nano::rpc::cli::help args {
	tailcall ::nano::_cli::help rpc "" {*}$args
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
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439


4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460

4461
4462
4463
4464
4465
4466
4467
4468







+
+
+

-
-
+
+
+


















-
+







			}
			lappend response [join $bootstrapResponse "\n"]
		}
	}

	if {[lsearch -exact $args "-blocks"] != -1} {
		set blockCount [::nano::rpc::client block_count]
		set blockCountInfoCount [dict get $blockCount count]
		set blockCountInfoUnchecked [dict get $blockCount unchecked]
		set blockCountInfoTotal [expr {$blockCountInfoCount + $blockCountInfoUnchecked}]
		lappend blocksResponse "Blocks:"
		lappend blocksResponse "  Count     = [dict get $blockCount count]"
		lappend blocksResponse "  Unchecked = [dict get $blockCount unchecked]"
		lappend blocksResponse "  Count     = $blockCountInfoCount"
		lappend blocksResponse "  Unchecked = $blockCountInfoUnchecked"
		lappend blocksResponse "  Total     = $blockCountInfoTotal"
		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>"
	error "Unable to locate genesis block"
}

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]
4268
4269
4270
4271
4272
4273
4274


























4275
4276
4277
4278
4279
4280
4281
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







		set peer [list address $peer port $peerPort]
		lappend result "  $peer: version $peerVersion"
	}

	return [join $result "\n"]
}

proc {::nano::rpc::cli::show node-id} args {
	set id [dict get [::nano::rpc::client node_id] "public"]
	return $id
}

proc ::nano::rpc::cli::rpc {action args} {
	set format "json"
	set endmarker [lsearch -exact $args "--"]
	if {$endmarker != -1} {
		set field [lrange $args $endmarker+1 end]
		set args [lrange $args 0 $endmarker-1]
		set format "dict"
	}

	set result [::nano::rpc::client $action -outputformat $format {*}$args]

	if {$format eq "json"} {
		set result [string trim $result "\n\r"]
	}

	if {[info exists field]} {
		set result [dict get $result {*}$field]
	}

	return $result
}

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

namespace eval ::nano::rpc::cli {