#! /usr/bin/env tclsh
package require json
package require json::write
namespace eval ::nano {}
namespace eval ::nano::address {}
namespace eval ::nano::key {}
namespace eval ::nano::block {}
namespace eval ::nano::block::json {}
namespace eval ::nano::block::dict {}
namespace eval ::nano::block::create {}
namespace eval ::nano::work {}
namespace eval ::nano::account {}
# Constants
set ::nano::block::stateBlockPreamble [binary decode hex "0000000000000000000000000000000000000000000000000000000000000006"]
set ::nano::address::base32alphabet {13456789abcdefghijkmnopqrstuwxyz}
# Address management functions
proc ::nano::address::toPublicKey {address args} {
set performChecksumCheck false
set outputFormat "bytes"
foreach arg $args {
switch -exact -- $arg {
"-verify" {
set performChecksumCheck true
}
"-no-verify" {
set performChecksumCheck false
}
"-hex" {
set outputFormat "hex"
}
"-binary" {
set outputFormat "bytes"
}
default {
return -code error "Invalid option: $arg"
}
}
}
switch -glob -- $address {
"xrb_*" - "nano_*" {}
default {
return -code error "Invalid address (format, header)"
}
}
set address [join [lrange [split $address _] 1 end] _]
set address [split $address ""]
if {[llength $address] != 60} {
return -code error "Invalid address (length)"
}
set alphabet [split $::nano::address::base32alphabet ""]
set result 0
foreach byte $address {
set fiveBits [lsearch -exact $alphabet $byte]
if {$fiveBits < 0} {
return -code error "Invalid address (format, alphabet)"
}
set result [expr {($result << 5) | $fiveBits}]
}
set checksum [expr {$result & 0xffffffffff}]
set result [expr {$result >> 40}]
set result [format %064llX $result]
if {$performChecksumCheck} {
set resultBinary [binary decode hex $result]
set checksumVerify [binary encode hex [string reverse [::nano::internal::hashData $resultBinary 5]]]
set checksumVerify [expr "0x$checksumVerify"]
if {$checksum != $checksumVerify} {
return -code error "Invalid address (checksum)"
}
}
if {$outputFormat eq "bytes"} {
if {![info exists resultBinary]} {
set resultBinary [binary decode hex $result]
}
set result $resultBinary
}
return $result
}
proc ::nano::address::fromPublicKey {publicKey args} {
set addressPrefix "nano_"
foreach arg $args {
switch -exact -- $arg {
"-xrb" {
set addressPrefix "xrb_"
}
"-nano" {
set addressPrefix "nano_"
}
default {
return -code error "Invalid option: $arg"
}
}
}
if {[string length $publicKey] != $::nano::key::publicKeyLength} {
set publicKey [binary decode hex $publicKey]
}
set checksum [string reverse [::nano::internal::hashData $publicKey 5]]
append publicKey $checksum
set publicKey [binary encode hex $publicKey]
set publicKey [expr "0x$publicKey"]
set alphabet [split $::nano::address::base32alphabet ""]
set address ""
for {set index 0} {$index < 60} {incr index} {
set fiveBits [expr {$publicKey & 0x1F}]
set publicKey [expr {$publicKey >> 5}]
set byte [lindex $alphabet $fiveBits]
append address $byte
}
set address [string reverse $address]
set address "${addressPrefix}${address}"
return $address
}
proc ::nano::address::fromPrivateKey {privateKey args} {
set pubKey [::nano::key::publicKeyFromPrivateKey $privateKey]
tailcall ::nano::address::fromPublicKey $pubKey {*}$args
}
# Key management functions
proc ::nano::key::newSeed {args} {
set outputFormat "bytes"
foreach arg $args {
switch -exact -- $arg {
"-hex" {
set outputFormat "hex"
}
"-binary" {
set outputFormat "bytes"
}
default {
return -code error "Invalid option: $arg"
}
}
}
set retval [::nano::internal::generateSeed]
if {$outputFormat eq "hex"} {
set retval [binary encode hex $retval]
}
return $retval
}
proc ::nano::key::newKey {args} {
set outputFormat "bytes"
foreach arg $args {
switch -exact -- $arg {
"-hex" {
set outputFormat "hex"
}
"-binary" {
set outputFormat "bytes"
}
default {
return -code error "Invalid option: $arg"
}
}
}
set retval [::nano::internal::generateKey]
if {$outputFormat eq "hex"} {
set retval [binary encode hex $retval]
}
return $retval
}
proc ::nano::key::fromSeed {seed args} {
set index 0
set outputFormat "bytes"
if {[llength $args] > 0} {
if {[string index [lindex $args 0] 0] ne "-"} {
set index [lindex $args 0]
set args [lrange $args 1 end]
}
foreach arg $args {
switch -exact -- $arg {
"-hex" {
set outputFormat "hex"
}
"-binary" {
set outputFormat "bytes"
}
default {
return -code error "Invalid option: $arg"
}
}
}
}
if {[string length $seed] != $::nano::key::seedLength} {
set seed [binary decode hex $seed]
}
set key [::nano::internal::generateKey $seed $index]
if {$outputFormat eq "hex"} {
set key [binary encode hex $key]
}
return $key
}
proc ::nano::key::publicKeyFromPrivateKey {privateKey args} {
set outputFormat "bytes"
foreach arg $args {
switch -- $arg {
"-hex" {
set outputFormat "hex"
}
"-binary" {
set outputFormat "bytes"
}
default {
return -code error "Invalid option: $arg"
}
}
}
if {[string length $privateKey] != $::nano::key::privateKeyLength} {
set privateKey [binary decode hex $privateKey]
}
set pubKey [::nano::internal::publicKey $privateKey]
if {$outputFormat eq "hex"} {
set pubKey [string toupper [binary encode hex $pubKey]]
}
return $pubKey
}
# Low-level block management
proc ::nano::block::dict::toBlock {blockDict} {
array set block $blockDict
switch -- $block(type) {
"state" {
append blockData $::nano::block::stateBlockPreamble
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)]]
if {![info exists block(link)] && [info exists block(link_as_account)]} {
append blockData [::nano::address::toPublicKey $block(link_as_account)]
} else {
append blockData [binary decode hex $block(link)]
}
}
"open" {
append blockData [binary decode hex $block(source)]
append blockData [::nano::address::toPublicKey $block(representative)]
append blockData [::nano::address::toPublicKey $block(account)]
}
"send" {
append blockData [binary decode hex $block(previous)]
append blockData [::nano::address::toPublicKey $block(destination)]
append blockData [binary decode hex [format %032llX $block(balance)]]
}
"receive" {
append blockData [binary decode hex $block(previous)]
append blockData [binary decode hex $block(source)]
}
"change" {
append blockData [binary decode hex $block(previous)]
append blockData [::nano::address::toPublicKey $block(representative)]
}
default {
return -code error "Invalid block type $block(type)"
}
}
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::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"
}
if {[dict get $retval "type"] eq "state" && [dict get $retval "previous"] eq "0000000000000000000000000000000000000000000000000000000000000000" && [dict get $retval "link"] eq "0000000000000000000000000000000000000000000000000000000000000000"} {
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
if {$block(type) eq "state"} {
if {![info exists block(link)]} {
set block(link) [::nano::address::toPublicKey $block(link_as_account) -hex]
}
if {![info exists block(link_as_address)]} {
set addressFormatFlag "-nano"
foreach field {account destination representative} {
if {![info exists block($field)]} {
continue
}
if {[string match "nano_*" $block($field)]} {
set addressFormatFlag "-nano"
} else {
set addressFormatFlag "-xrb"
}
break
}
set block(link_as_account) [::nano::address::fromPublicKey $block(link) $addressFormatFlag]
}
}
set blockJSONFields {
type account source destination previous representative balance
link link_as_account _blockHash _workData work signature _comment
}
set blockJSONEntries [lmap field $blockJSONFields {
if {![info exists block($field)]} {
continue
}
switch -exact -- $field {
"source" - "previous" - "link" - "_blockHash" - "_workData" {
if {[string length $block($field)] == $::nano::block::hashLength} {
set block($field) [binary encode hex $block($field)]
}
set block($field) [string toupper $block($field)]
}
"signature" {
if {[string length $block($field)] == $::nano::block::signatureLength} {
set block($field) [binary encode hex $block($field)]
}
set block($field) [string toupper $block($field)]
}
"work" {
if {[string length $block($field)] == $::nano::work::workValueLength} {
set block($field) [binary encode hex $block($field)]
}
set block($field) [string tolower $block($field)]
}
"balance" {
if {$block(type) in {send receive change open}} {
set balanceFormatStr %032llx
} else {
set balanceFormatStr %lli
}
set block($field) [string toupper [format $balanceFormatStr "$block($field)"]]
}
}
return -level 0 [list $field [json::write string $block($field)]]
}]
set blockJSONEntries [join $blockJSONEntries]
set blockJSON [json::write object {*}$blockJSONEntries]
return $blockJSON
}
proc ::nano::block::dict::fromBlock {blockData args} {
set block(type) ""
set addressPrefix "nano_"
foreach arg $args {
switch -glob -- $arg {
"-type=*" {
set block(type) [lindex [split $arg =] 1]
}
"-signKey=*" {
set block(signKey) [string range $arg 9 end]
}
"-xrb" {
set addressPrefix "xrb_"
}
"-nano" {
set addressPrefix "nano_"
}
default {
}
}
}
if {$block(type) eq ""} {
switch -- [string length $blockData] {
176 { set block(type) state }
96 { set block(type) open }
80 { set block(type) send }
default {
return -code error "Unable to parse block, must specify type"
}
}
}
set addArgs_fromPublicKey [list]
if {$addressPrefix eq "xrb_"} {
lappend addArgs_fromPublicKey "-xrb"
}
switch -- $block(type) {
"state" {
binary scan $blockData a32a32H64a32H32H64 \
block(header) \
block(account) \
block(previous) \
block(representative) \
block(balance) \
block(link)
if {$block(header) ne $::nano::block::stateBlockPreamble} {
return -code error "Invalid block"
}
}
"open" {
binary scan $blockData H64a32a32 \
block(source) \
block(representative) \
block(account)
set block(_workData) $block(account)
}
"send" {
binary scan $blockData H64a32H32 \
block(previous) \
block(destination) \
block(balance)
set block(_workData) $block(previous)
}
"receive" {
binary scan $blockData H64H64 \
block(previous) \
block(source)
set block(_workData) $block(previous)
}
"change" {
binary scan $blockData H64a32 \
block(previous) \
block(representative)
set block(_workData) $block(previous)
}
default {
return -code error "Invalid block type: $block(type)"
}
}
foreach field {account representative link_as_account destination balance} {
if {![info exists block($field)]} {
continue
}
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)"]
}
}
}
set block(_blockData) $blockData
return [array get block]
}
proc ::nano::block::json::fromBlock {blockData args} {
set blockDict [::nano::block::dict::fromBlock $blockData {*}$args]
set blockJSON [::nano::block::json::fromDict $blockDict]
return $blockJSON
}
proc ::nano::block::hash {blockData args} {
set outputFormat "bytes"
foreach arg $args {
switch -exact -- $arg {
"-hex" {
set outputFormat "hex"
}
"-binary" {
set outputFormat "bytes"
}
default {
return -code error "Invalid option: $arg"
}
}
}
set hash [::nano::internal::hashData $blockData $::nano::block::hashLength]
if {$outputFormat eq "hex"} {
set hash [string toupper [binary encode hex $hash]]
}
return $hash
}
proc ::nano::block::signBlockHash {blockHash privateKey args} {
set outputFormat "bytes"
foreach arg $args {
switch -exact -- $arg {
"-hex" {
set outputFormat "hex"
}
"-binary" {
set outputFormat "bytes"
}
default {
return -code error "Invalid option: $arg"
}
}
}
if {[string length $blockHash] != $::nano::block::hashLength} {
set blockHash [binary decode hex $blockHash]
}
if {[string length $privateKey] != $::nano::key::privateKeyLength} {
set privateKey [binary decode hex $privateKey]
}
set signature [::nano::internal::signDetached $blockHash $privateKey]
if {$outputFormat eq "hex"} {
set signature [string toupper [binary encode hex $signature]]
}
return $signature
}
proc ::nano::block::sign {blockData args} {
set blockHash [::nano::block::hash $blockData]
tailcall ::nano::block::signBlockHash $blockHash {*}$args
}
proc ::nano::block::verifyBlockHash {blockHash signature publicKey} {
if {[string length $blockHash] != $::nano::block::hashLength} {
set blockHash [binary decode hex $blockHash]
}
if {[string length $signature] != $::nano::block::signatureLength} {
set signature [binary decode hex $signature]
}
if {[string length $publicKey] != $::nano::key::publicKeyLength} {
set publicKey [binary decode hex $publicKey]
}
set valid [::nano::internal::verifyDetached $blockHash $signature $publicKey]
return $valid
}
proc ::nano::block::verifyBlock {blockData args} {
set blockHash [::nano::block::hash $blockData]
tailcall ::nano::block::verifyBlockHash $blockHash {*}$args
}
proc ::nano::block::dict::_addBlockData {blockDict} {
if {[dict exists $blockDict _blockData]} {
return $blockDict
}
set blockData [::nano::block::dict::toBlock $blockDict]
dict set blockDict _blockData $blockData
return $blockDict
}
proc ::nano::block::dict::_addBlockHash {blockDict} {
if {[dict exists $blockDict _blockHash]} {
return $blockDict
}
set blockDict [_addBlockData $blockDict]
set blockData [dict get $blockDict _blockData]
set blockHash [::nano::block::hash $blockData -binary]
dict set blockDict _blockHash $blockHash
return $blockDict
}
proc ::nano::block::dict::sign {blockDict privateKey args} {
set outputMode "signature"
set outputFormat "bytes"
foreach arg $args {
switch -- $arg {
"-update" {
set outputMode "update"
}
"-signature" {
set outputMode "signature"
}
"-hex" {
set outputFormat "hex"
}
"-binary" {
set outputFormat "bytes"
}
default {
return -code error "Invalid option: $arg"
}
}
}
set blockDict [_addBlockHash $blockDict]
set blockHash [dict get $blockDict _blockHash]
set signature [::nano::block::signBlockHash $blockHash $privateKey -binary]
if {$outputMode eq "signature"} {
if {$outputFormat eq "hex"} {
set signature [binary encode hex $signature]
}
return $signature
}
dict set blockDict signature $signature
return $blockDict
}
proc ::nano::block::json::sign {blockJSON privateKey args} {
set outputMode "signature"
foreach arg $args {
switch -- $arg {
"-update" {
set outputMode "update"
}
"-signature" {
set outputMode "signature"
}
"-hex" - "-binary" {}
default {
return -code error "Invalid option: $arg"
}
}
}
set blockDict [::nano::block::dict::fromJSON $blockJSON]
set retval [::nano::block::dict::sign $blockDict $privateKey {*}$args]
if {$outputMode eq "signature"} {
return $retval
}
set retval [::nano::block::json::fromDict $retval]
return $retval
}
proc ::nano::block::dict::verifySignature {blockDict} {
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
}
proc ::nano::block::json::verifySignature {blockJSON} {
set blockDict [::nano::block::dict::fromJSON $blockJSON]
tailcall ::nano::block::dict::verifySignature $blockDict
}
proc ::nano::block::dict::work {blockDict args} {
set outputMode "work"
foreach arg $args {
switch -- $arg {
"-update" {
set outputMode "update"
}
"-work" {
set outputMode "work"
}
"-hex" {
set outputFormat "hex"
}
"-binary" {
set outputFormat "bytes"
}
default {
return -code error "Invalid option: $arg"
}
}
}
set blockDict [_addBlockHash $blockDict]
set blockHash [dict get $blockDict _workData]
set work [::nano::work::fromWorkData $blockHash -binary]
if {$outputMode eq "work"} {
if {$outputFormat eq "hex"} {
set work [binary encode hex $work]
}
return $work
}
dict set blockDict work $work
return $blockDict
}
proc ::nano::block::json::work {blockJSON args} {
set outputMode "work"
foreach arg $args {
switch -- $arg {
"-update" {
set outputMode "update"
}
"-work" {
set outputMode "work"
}
"-hex" - "-binary" {}
default {
return -code error "Invalid option: $arg"
}
}
}
set blockDict [::nano::block::dict::fromJSON $blockJSON]
set retval [::nano::block::dict::work $blockDict {*}$args]
if {$outputMode eq "work"} {
return $retval
}
set retval [::nano::block::json::fromDict $retval]
return $retval
}
proc ::nano::block::dict::validateWork {blockDict} {
set blockDict [_addBlockHash $blockDict]
set blockHash [dict get $blockDict _workData]
set work [dict get $blockDict work]
tailcall ::nano::work::validate $blockHash $work
}
proc ::nano::block::json::validateWork {blockJSON} {
set blockDict [::nano::block::dict::fromJSON $blockJSON]
tailcall ::nano::block::dict::validateWork $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)}]
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]
}
return $blockDict
}
# Usage:
# receive to <account> previousBalance <balance> amount <amount>
# sourceBlock <sourceBlockHash> ?previous <previousBlockHash>?
# ?representative <representative>?
proc ::nano::block::create::receive {args} {
array set block $args
if {![info exists block(representative)]} {
set block(representative) $block(to)
}
if {![info exists block(previous)]} {
set block(previous) "0000000000000000000000000000000000000000000000000000000000000000"
set block(previousBalance) 0
set block(_workData) [::nano::address::toPublicKey $block(to) -hex]
} else {
set block(_workData) $block(previous)
}
set block(balance) [expr {$block(previousBalance) + $block(amount)}]
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]
}
return $blockDict
}
# Usage:
# setRepresentative account <account> previous <previousBlockHash>
# representative <newRepresentative> balance <balance>
proc ::nano::block::create::setRepresentative {args} {
array set block $args
set block(link) "0000000000000000000000000000000000000000000000000000000000000000"
set blockDict [dict create \
"type" state \
"account" $block(account) \
"previous" $block(previous) \
"representative" $block(representative) \
"balance" $block(balance) \
"link" $block(link) \
"_workData" $block(previous) \
]
if {[info exists block(signKey)]} {
dict set blockDict signKey $block(signKey)
}
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]
}
return $blockDict
}
# Work generation functions
proc ::nano::work::fromWorkData {blockHashOrPublicKey} {
if {[string length $blockHashOrPublicKey] != $::nano::block::hashLength} {
set blockHashOrPublicKey [binary decode hex $blockHashOrPublicKey]
}
set work [binary encode hex [::nano::internal::generateWork $blockHashOrPublicKey]]
set work [string tolower $work]
return $work
}
proc ::nano::work::fromBlock {blockData} {
set blockDict [::nano::block::dict::fromBlock $blockData]
set workData [dict get $blockDict _workData]
tailcall ::nano::work::fromBlockhash $workData
}
proc ::nano::work::validate {workData work} {
if {[string length $workData] != $::nano::block::hashLength} {
set workData [binary decode hex $workData]
}
if {[string length $work] != $::nano::work::workValueLength} {
set work [binary decode hex $work]
}
tailcall ::nano::internal::validateWork $workData $work
}
# High level account management
proc ::nano::account::setFrontier {account frontierHash balance representative} {
set accountPubKey [::nano::address::toPublicKey $account -hex]
set ::nano::account::frontiers($accountPubKey) [dict create \
frontierHash $frontierHash balance $balance representative $representative \
]
}
proc ::nano::account::getFrontier {account args} {
set accountPubKey [::nano::address::toPublicKey $account -hex]
if {![info exists ::nano::account::frontiers($accountPubKey)]} {
set frontier [dict create balance 0]
} else {
set frontier $::nano::account::frontiers($accountPubKey)
}
return [dict get $frontier {*}$args]
}
proc ::nano::account::addPending {account blockHash amount} {
set accountPubKey [::nano::address::toPublicKey $account -hex]
dict set ::nano::account::pending $accountPubKey $blockHash amount $amount
}
proc ::nano::account::getPending {account args} {
set accountPubKey [::nano::address::toPublicKey $account -hex]
set retval [dict create]
catch {
set retval [dict get $::nano::account::pending $accountPubKey {*}$args]
}
return $retval
}
proc ::nano::account::clearPending {account args} {
set accountPubKey [::nano::address::toPublicKey $account -hex]
catch {
dict unset ::nano::account::pending $accountPubKey {*}$args
}
return
}
proc ::nano::account::receive {account blockHash signKey} {
set accountPubKey [::nano::address::toPublicKey $account -hex]
set frontierInfo [getFrontier $account]
dict with frontierInfo {}
set blockInfo [getPending $account $blockHash]
set amount [dict get $blockInfo amount]
set blockArgs [list to $account previousBalance $balance \
amount $amount sourceBlock $blockHash \
signKey $signKey representative $representative]
if {[info exists frontierHash]} {
lappend blockArgs previous $frontierHash
}
dict set blockArgs -json true
set block [::nano::block::create::receive {*}$blockArgs]
set newFrontierHash [dict get [json::json2dict $block] "_blockHash"]
set balance [expr {$balance + $amount}]
setFrontier $account $newFrontierHash $balance $representative
clearPending $account $blockHash
return $block
}
proc ::nano::account::send {fromAccount toAccount amount signKey} {
set fromAccountPubKey [::nano::address::toPublicKey $fromAccount -hex]
set toAccountPubKey [::nano::address::toPublicKey $fromAccount -hex]
set fromFrontierInfo [getFrontier $fromAccount]
set toFrontierInfo [getFrontier $toAccount]
set fromBalance [dict get $fromFrontierInfo balance]
set fromFrontierHash [dict get $fromFrontierInfo frontierHash]
set fromRepresentative [dict get $fromFrontierInfo representative]
set signKey [binary encode hex $signKey]
set block [::nano::block::create::send \
from $fromAccount \
to $toAccount \
previous $fromFrontierHash \
previousBalance $fromBalance \
amount $amount \
signKey $signKey \
-json true
]
set newBalance [expr {$fromBalance - $amount}]
set newFrontierHash [dict get [json::json2dict $block] "_blockHash"]
setFrontier $fromAccount $newFrontierHash $newBalance $fromRepresentative
addPending $toAccount $newFrontierHash $amount
return $block
}
proc ::nano::account::receiveAllPending {key {accountPubKey ""}} {
set outBlocks [list]
if {$accountPubKey eq ""} {
set accountPubKey [::nano::key::publicKeyFromPrivateKey $key -hex]
}
set account [::nano::address::fromPublicKey $accountPubKey]
set pendingBlocks [getPending $account]
if {[llength $pendingBlocks] == 0} {
return $outBlocks
}
set signKey [binary encode hex $key]
foreach blockHash [dict keys $pendingBlocks] {
lappend outBlocks [receive $account $blockHash $signKey]
}
return $outBlocks
}
proc ::nano::account::setRepresentative {account representative signKey} {
set accountPubKey [::nano::address::toPublicKey $account -hex]
set frontierInfo [getFrontier $account]
dict with frontierInfo {}
set blockArgs [list account $account \
representative $representative \
signKey $signKey \
previous $frontierHash \
]
dict set blockArgs -json true
set block [::nano::block::create::setRepresentative {*}$blockArgs]
set newFrontierHash [dict get [json::json2dict $block] "_blockHash"]
setFrontier $account $newFrontierHash $balance $representative
return $block
}