ADDED build/nano.txt Index: build/nano.txt ================================================================== --- /dev/null +++ build/nano.txt @@ -0,0 +1,50 @@ +Address +::nano::address::toPublicKey address ?-hex|-binary? ?-verify|-no-verify? -> publicKey +::nano::address::fromPublicKey pubKey ?-xrb|-nano? -> address +::nano::address::fromPrivateKey privateKey ?-xrb|-nano? -> address + +Key and Seed +::nano::key::newSeed ?-hex|-binary? -> seed +::nano::key::newKey ?-hex|-binary? -> privateKey +::nano::key::fromSeed seed ?index? ?-hex|-binary? -> privateKey +::nano::key::publicKeyFromPrivateKey privateKey ?-hex|-binary? -> publicKey + +Low-level Block +::nano::block::json::toBlock blockJSON -> blockData +::nano::block::json::fromDict blockDict -> blockJSON +::nano::block::json::fromBlock blockData ?-xrb|-nano? ?-type=? ?-signKey=? -> blockJSON +::nano::block::json::sign blockJSON privateKey ?-update|-signature ?-hex|binary?? -> signature|blockJSON +::nano::block::json::verifySignature blockJSON args -> boolean +::nano::block::json::work blockJSON ?-update|-work ?-hex|-binary?? -> work|blockJSON +::nano::block::json::validateWork blockJSON -> boolean + +::nano::block::dict::toBlock blockDict -> blockData +::nano::block::dict::fromJSON blockJSON -> blockDict +::nano::block::dict::fromBlock blockData ?-xrb|-nano? ?-type=? ?-signKey=? -> blockDict +::nano::block::dict::sign blockDict privateKey ?-update|-signature ?-hex|-binary?? -> signature|blockDict +::nano::block::dict::verifySignature blockDict args -> boolean +::nano::block::dict::work blockDict ?-update|-work ?-hex|-binary?? -> work|blockDict +::nano::block::dict::validateWork blockDict -> boolean + +::nano::block::hash blockData ?-hex|-binary? -> blockHash + +::nano::block::signBlockHash blockHash privateKey ?-hex|-binary? -> signature +::nano::block::sign blockData privateKey ?-hex|-binary? -> signature + +::nano::block::verifyBlockHash blockHash signature publicKey -> boolean +::nano::block::verify blockData signature publicKey -> boolean + +::nano::block::create::send args -> blockJSON|blockDict +::nano::block::create::receive args -> blockJSON|blockDict +::nano::block::create::setRepresentative args -> blockJSON|blockDict + +::nano::work::fromBlockHash blockHash -> work +::nano::work::fromBlock blockData -> work +::nano::work::validate blockHash work -> boolean + +:nano::account::setFrontier account frontierHash balance representative -> nil +:nano::account::getFrontier account args -> frontierInfo +:nano::account::addPending account blockHash amount -> nil +:nano::account::receive account blockHash privateKey -> blockJSON|blockDict +:nano::account::send fromAccount toAccount amount privateKey -> blockJSON|blockDict +:nano::account::receiveAllPending account privateKey -> ListOfBlockJSON|ListOfBlockDict Index: build/test/test.tcl ================================================================== --- build/test/test.tcl +++ build/test/test.tcl @@ -85,11 +85,11 @@ return true } proc test_keygeneration {} { # Generate a new key pair - set key [::nano::key::generateNewKey] + set key [::nano::key::newKey] if {[string length $key] != 32} { puts "\[1.FAIL\] Got: [string length $key]" puts "\[1.FAIL\] Exp: 32" return false @@ -107,11 +107,11 @@ return false } # Create a key pair from a seed and index set seed [binary decode hex C4D214F19E706E9C7487CEF00DE8059200C32414F0ED82E5E33B523AEDF719BA] - set key [::nano::key::computeKey $seed 0] + set key [::nano::key::fromSeed $seed 0] set pubKey [string toupper [binary encode hex [::nano::internal::publicKey $key]]] set pubKey_expected "B63EC7A797F2A5858C754EC9C0537920C4F9DEA58F9F411F0C2161F6D303AA7A" if {$pubKey ne $pubKey_expected} { puts "\[3.FAIL\] Got: $pubKey" puts "\[3.FAIL\] Exp: $pubKey_expected" @@ -145,20 +145,20 @@ return true } proc test_blocks {} { set seed [binary decode hex C4D214F19E706E9C7487CEF00DE8059200C32414F0ED82E5E33B523AEDF719BA] - set key [::nano::key::computeKey $seed 0 -hex] + set key [::nano::key::fromSeed $seed 0 -hex] set address [::nano::address::fromPrivateKey $key -xrb] # High-level primitives ## Receive/Open set block [::nano::block::create::receive \ to $address \ amount 1000000000000000000000000000000 \ sourceBlock "207D3043D77B84E892AD4949D147386DE4C2FE4B2C8DC13F9469BC4A764681A7" \ - signKey $key + signKey $key -json true ] set blockDict [::json::json2dict $block] set blockSignature [string toupper [dict get $blockDict signature]] set blockSignature_expected "B574DE37F5FFF3DCFB5D0E505FC36B402444777CAA99BA86F89E9B82B6EB901B809554287F0B67D8C2A8306B4F69FE77FD0C9B3D0D10422A02CFEBB3810C7D02" @@ -174,11 +174,11 @@ from $address \ to "xrb_1unc5hriitrdjq5dnyhr3zmd8t5hm7rhm9a1u3uun5ycbaacpu649yh5c4b5" \ previous "D46BFC2E35B5A3CA4230839D67676F4A8498C2567F571D2B66A7F7B72214DEEE" \ previousBalance 1000000000000000000000000000000 \ amount 1000000000000000000000000000000 \ - signKey $key + signKey $key -json true ] set blockDict [::json::json2dict $block] set blockSignature [string toupper [dict get $blockDict signature]] set blockSignature_expected "BFE238A27FFBFBCF722EDC3700CA8E2405F5AE18E353E591917A2CBE393F0759C948E710DD723B3BFB21B491D9D0856EEFCAC0E25C7E5FF06185FE5D633B5204" @@ -188,15 +188,15 @@ return false } # JSON Parsing a block - set blockDict [::nano::block::toDict [::nano::block::fromJSON $block]] + set blockDict [::nano::block::dict::fromBlock [::nano::block::json::toBlock $block]] dict unset blockDict _blockData - dict set blockDict signKey $key - set block [::nano::block::jsonFromDict $blockDict] + set block [::nano::block::json::fromDict $blockDict] + set block [::nano::block::json::sign $block $key -update] set blockDict [::json::json2dict $block] set blockSignature [string toupper [dict get $blockDict signature]] if {$blockSignature ne $blockSignature_expected} { puts "\[3.FAIL\] Got: $blockSignature" puts "\[3.FAIL\] Exp: $blockSignature_expected" @@ -203,12 +203,12 @@ return false } # Verifying a block - set signature [::nano::block::signBlockJSON $block $key -hex] - set verify [::nano::block::verifyBlockJSON $block $signature [::nano::key::publicKeyFromPrivateKey $key]] + set signature [::nano::block::json::sign $block $key -hex] + set verify [::nano::block::json::verifySignature $block] if {!$verify} { puts "\[4.FAIL\] Got: $verify" puts "\[4.FAIL\] Exp: true" return false @@ -240,11 +240,11 @@ return false } # Generation set blockhash "1C840FED01000000D8CBCF440CB1E4DF386761E6E66609563BD62A649DF6D0BE" - set work [::nano::work::fromBlockhash $blockhash] + set work [::nano::work::fromBlockHash $blockhash] set verify [::nano::work::validate $blockhash $work] if {!$verify} { puts "\[3.FAIL\] Got: $verify" puts "\[3.FAIL\] Exp: true" Index: examples/generate-work/generate-work ================================================================== --- examples/generate-work/generate-work +++ examples/generate-work/generate-work @@ -8,11 +8,11 @@ exit 1 } foreach arg $argv { if {[catch { - set work [::nano::work::fromBlockhash $arg] + set work [::nano::work::fromBlockHash $arg] } err]} { puts stderr "Error generating work for \"$arg\": $err" } else { puts "WORK($arg) = $work" } Index: examples/offline-batch-sign/sign ================================================================== --- examples/offline-batch-sign/sign +++ examples/offline-batch-sign/sign @@ -41,18 +41,18 @@ break } if {[catch { # Verify the seed - ::nano::internal::generateKey [binary decode hex $seed] 0 + ::nano::key::fromSeed $seed 0 lappend seeds $seed } err]} { puts "Error: $err" } } -set penultimateDestinationKey [::nano::internal::generateKey [binary decode hex [lindex $seeds 0]] 0] +set penultimateDestinationKey [::nano::key::fromSeed [lindex $seeds 0] 0] set penultimateDestination [::nano::address::fromPrivateKey $penultimateDestinationKey] puts ">> Output File" while true { prompt "Filename" outputFileName Index: nano.c ================================================================== --- nano.c +++ nano.c @@ -437,12 +437,15 @@ return; } static int nano_tcl_validate_work(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { unsigned char *blockhash, *work; + Tcl_WideUInt tclWorkMin; uint64_t workMin = NANO_WORK_DEFAULT_MIN; int blockhash_length, work_length; + int valid, result; + int tgwifo_ret; if (objc < 3 || objc > 4) { Tcl_WrongNumArgs(interp, 1, objv, "workBlockhash work ?workMin?"); return(TCL_ERROR); @@ -461,18 +464,18 @@ return(TCL_ERROR); } if (objc == 4) { - /* XXX:TODO: Implement getting a uint64_t from Tcl */ - Tcl_SetResult(interp, "User-supplied workMin is not implemented", NULL); + tgwifo_ret = Tcl_GetWideIntFromObj(interp, objv[3], (Tcl_WideInt *) &tclWorkMin); + if (tgwifo_ret != TCL_OK) { + return(tgwifo_ret); + } - return(TCL_ERROR); + workMin = tclWorkMin; } - int valid, result; - valid = nano_validate_work(blockhash, work, workMin); if (valid) { result = 1; } else { result = 0; @@ -485,14 +488,16 @@ /* NOTREACH */ clientData = clientData; } static int nano_tcl_generate_work(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + Tcl_WideUInt tclWorkMin; unsigned char *blockhash; unsigned char work[NANO_WORK_VALUE_LENGTH]; uint64_t workMin = NANO_WORK_DEFAULT_MIN; int blockhash_length; + int tgwifo_ret; if (objc < 2 || objc > 3) { Tcl_WrongNumArgs(interp, 1, objv, "workBlockhash ?workMin?"); return(TCL_ERROR); @@ -504,14 +509,16 @@ return(TCL_ERROR); } if (objc == 3) { - /* XXX:TODO: Implement getting a uint64_t from Tcl */ - Tcl_SetResult(interp, "User-supplied workMin is not implemented", NULL); + tgwifo_ret = Tcl_GetWideIntFromObj(interp, objv[2], (Tcl_WideInt *) &tclWorkMin); + if (tgwifo_ret != TCL_OK) { + return(tgwifo_ret); + } - return(TCL_ERROR); + workMin = tclWorkMin; } nano_generate_work(blockhash, work, workMin); Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(work, sizeof(work))); Index: nano.tcl ================================================================== --- nano.tcl +++ nano.tcl @@ -5,15 +5,21 @@ 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 { @@ -82,81 +88,106 @@ } return $result } -proc ::nano::address::fromPublicKey {pubKey args} { +proc ::nano::address::fromPublicKey {publicKey args} { set addressPrefix "nano_" foreach arg $args { switch -exact -- $arg { "-xrb" { set addressPrefix "xrb_" } "-nano" { set addressPrefix "nano_" } - "-hex" { - set inputFormat "hex" - } - "-binary" { - set inputFormat "bytes" - } default { return -code error "Invalid option: $arg" } } } - if {![info exists inputFormat]} { - if {[string length $pubKey] == $::nano::key::publicKeyLength} { - set inputFormat "bytes" - } else { - set inputFormat "hex" - } - } - - if {$inputFormat eq "hex"} { - set pubKey [binary decode hex $pubKey] - } - - if {[string length $pubKey] != $::nano::key::publicKeyLength} { - return -code error "Invalid key (length)" - } - - set checksum [string reverse [::nano::internal::hashData $pubKey 5]] - append pubKey $checksum - - set pubKey [binary encode hex $pubKey] - set pubKey [expr "0x$pubKey"] + 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 {$pubKey & 0x1F}] - set pubKey [expr {$pubKey >> 5}] + 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 {key args} { - set pubKey [::nano::key::publicKeyFromPrivateKey $key] +proc ::nano::address::fromPrivateKey {privateKey args} { + set pubKey [::nano::key::publicKeyFromPrivateKey $privateKey] tailcall ::nano::address::fromPublicKey $pubKey {*}$args } -proc ::nano::key::generateNewSeed {} { - tailcall ::nano::internal::generateSeed +# 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::generateNewKey {} { - tailcall ::nano::internal::generateKey +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::computeKey {seed args} { +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] @@ -187,11 +218,11 @@ } return $key } -proc ::nano::key::publicKeyFromPrivateKey {key args} { +proc ::nano::key::publicKeyFromPrivateKey {privateKey args} { set outputFormat "bytes" foreach arg $args { switch -- $arg { "-hex" { set outputFormat "hex" @@ -203,29 +234,29 @@ return -code error "Invalid option: $arg" } } } - if {[string length $key] != $::nano::key::privateKeyLength} { - set key [binary decode hex $key] + if {[string length $privateKey] != $::nano::key::privateKeyLength} { + set privateKey [binary decode hex $privateKey] } - set pubKey [::nano::internal::publicKey $key] + set pubKey [::nano::internal::publicKey $privateKey] if {$outputFormat eq "hex"} { set pubKey [string toupper [binary encode hex $pubKey]] } return $pubKey } -proc ::nano::block::fromJSON {json} { - array set block [json::json2dict $json] +# Low-level block management +proc ::nano::block::dict::toBlock {blockDict} { + array set block $blockDict switch -- $block(type) { "state" { - # XXX:TODO: Find the source of this - append blockData [binary decode hex "0000000000000000000000000000000000000000000000000000000000000006"] + 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)]} { @@ -258,97 +289,27 @@ } return $blockData } -proc ::nano::block::signBlockHash {blockHash key args} { - set outputFormat "bytes" - foreach arg $args { - switch -exact -- $arg { - "-hex" { - set outputFormat "hex" - } - "-binary" { - set outputFormat "bytes" - } - } - } - - if {[string length $blockHash] != $::nano::block::hashLength} { - set blockHash [binary decode hex $blockHash] - } - - if {[string length $key] != $::nano::key::privateKeyLength} { - set key [binary decode hex $key] - } - - set signature [::nano::internal::signDetached $blockHash $key] - - if {$outputFormat eq "hex"} { - set signature [string toupper [binary encode hex $signature]] - } - - return $signature -} - -proc ::nano::block::signBlock {blockData args} { - set blockHash [::nano::block::hash $blockData] - - tailcall ::nano::block::signBlockHash $blockHash {*}$args -} - -proc ::nano::block::signBlockJSON {blockJSON args} { - set blockData [::nano::block::fromJSON $blockJSON] - - tailcall ::nano::block::signBlock $blockData {*}$args -} - -proc ::nano::block::verifyBlockHash {blockHash signature pubKey} { - 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 $pubKey] != $::nano::key::publicKeyLength} { - set key [binary decode hex $pubKey] - } - - set valid [::nano::internal::verifyDetached $blockHash $signature $pubKey] - - return $valid -} - -proc ::nano::block::verifyBlock {blockData args} { - set blockHash [::nano::block::hash $blockData] - - tailcall ::nano::block::verifyBlockHash $blockHash {*}$args -} - -proc ::nano::block::verifyBlockJSON {blockJSON args} { - set blockData [::nano::block::fromJSON $blockJSON] - - tailcall ::nano::block::verifyBlock $blockData {*}$args -} - -proc ::nano::block::_dictToJSON {blockDict} { - array set block $blockDict - - if {[info exists block(signKey)] && ([info exists block(_blockData)] || [info exists block(_blockHash)])} { - if {![info exists block(_blockHash)]} { - set block(_blockHash) [binary encode hex [::nano::block::hash $block(_blockData)]] - } - - set block(signature) [::nano::block::signBlockHash $block(_blockHash) $block(signKey) -hex] - } +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} { + tailcall ::json::json2dict $blockJSON +} + +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 @@ -376,23 +337,42 @@ continue } switch -exact -- $field { "source" - "previous" - "link" - "_blockHash" - "_workHash" { + 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)] + } } + 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::toDict {blockData args} { +proc ::nano::block::dict::fromBlock {blockData args} { set block(type) "" set addressPrefix "nano_" foreach arg $args { switch -glob -- $arg { "-type=*" { @@ -428,19 +408,19 @@ lappend addArgs_fromPublicKey "-xrb" } switch -- $block(type) { "state" { - binary scan $blockData H64a32H64a32H32H64 \ + binary scan $blockData a32a32H64a32H32H64 \ block(header) \ block(account) \ block(previous) \ block(representative) \ block(balance) \ block(link) - if {$block(header) ne "0000000000000000000000000000000000000000000000000000000000000006"} { + if {$block(header) ne $::nano::block::stateBlockPreamble} { return -code error "Invalid block" } } "open" { binary scan $blockData H64a32a32 \ @@ -487,14 +467,14 @@ set block(_blockData) $blockData return [array get block] } -proc ::nano::block::toJSON {blockData args} { - set blockDict [::nano::block::toDict $blockData {*}$args] +proc ::nano::block::json::fromBlock {blockData args} { + set blockDict [::nano::block::dict::fromBlock $blockData {*}$args] - set blockJSON [_dictToJSON $blockDict] + set blockJSON [::nano::block::json::fromDict $blockDict] return $blockJSON } proc ::nano::block::hash {blockData args} { @@ -520,20 +500,273 @@ } return $hash } -proc ::nano::block::jsonFromDict {blockDict} { - set blockJSON [::nano::block::_dictToJSON $blockDict] - set block [::nano::block::fromJSON $blockJSON] - set blockHash [::nano::block::hash $block] - - dict set blockDict "_blockHash" [binary encode hex $blockHash] - - set blockJSON [::nano::block::_dictToJSON $blockDict] - - return $blockJSON +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 _blockHash] + + set work [::nano::work::fromBlockHash $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 _blockHash] + 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::validate $blockDict } # send from to previousBalance # amount sourceBlock # previous ?representative ? @@ -555,22 +788,27 @@ "_workHash" $block(previous) \ "_comment" "Send $block(amount) raw from $block(from) to $block(to)" \ ] if {[info exists block(signKey)]} { - dict set blockDict signKey $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] } - tailcall ::nano::block::jsonFromDict $blockDict + return $blockDict } # Usage: # receive to previousBalance amount # sourceBlock ?previous ? # ?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)]} { @@ -593,14 +831,18 @@ "_workHash" $block(_workHash) \ "_comment" "Receive $block(amount) raw on $block(to) from hash $block(sourceBlock)" \ ] if {[info exists block(signKey)]} { - dict set blockDict signKey $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] } - tailcall ::nano::block::jsonFromDict $blockDict + return $blockDict } # Usage: # setRepresentative account previous # representative balance @@ -621,50 +863,52 @@ if {[info exists block(signKey)]} { dict set blockDict signKey $block(signKey) } - tailcall ::nano::block::jsonFromDict $blockDict + 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 } -proc ::nano::work::fromBlockhash {blockhash} { - if {[string length $blockhash] != 32} { - set blockhash [binary decode hex $blockhash] +# Work generation functions +proc ::nano::work::fromBlockHash {blockHash} { + if {[string length $blockHash] != $::nano::block::hashLength} { + set blockHash [binary decode hex $blockHash] } - set work [binary encode hex [::nano::internal::generateWork $blockhash]] + set work [binary encode hex [::nano::internal::generateWork $blockHash]] set work [string tolower $work] + return $work } -proc ::nano::work::fromBlock {blockJSON} { - set blockDict [::json::json2dict $blockJSON] - set workhash [dict get $blockDict _workHash] - tailcall ::nano::work::fromBlockhash $workhash -} - -proc ::nano::work::updateBlock {blockJSON} { - set blockDict [::json::json2dict $blockJSON] - set workhash [dict get $blockDict _workHash] - set work [::nano::work::fromBlockhash $workhash] - dict set blockDict work $work - tailcall ::nano::block::_dictToJSON $blockDict -} - -proc ::nano::work::validate {blockhash work} { - if {[string length $blockhash] != 32} { - set blockhash [binary decode hex $blockhash] - } - - if {[string length $work] != 8} { +proc ::nano::work::fromBlock {blockData} { + set blockHash [::nano::block::hash $blockData -binary] + + tailcall ::nano::work::fromBlockhash $blockHash +} + +proc ::nano::work::validate {blockHash work} { + if {[string length $blockHash] != $::nano::block::hashLength} { + set blockHash [binary decode hex $blockHash] + } + + if {[string length $work] != $::nano::work::workValueLength} { set work [binary decode hex $work] } - tailcall ::nano::internal::validateWork $blockhash $work + tailcall ::nano::internal::validateWork $blockHash $work } -# -- Tracked accounts -- +# 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 \ ] @@ -692,27 +936,29 @@ set frontierInfo [getFrontier $account] dict with frontierInfo {} set blockInfo [dict get $::nano::account::pending $accountPubKey $blockHash] - dict unset ::nano::account::pending $accountPubKey $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 + dict unset ::nano::account::pending $accountPubKey $blockHash return $block } proc ::nano::account::send {fromAccount toAccount amount signKey} { @@ -732,11 +978,12 @@ from $fromAccount \ to $toAccount \ previous $fromFrontierHash \ previousBalance $fromBalance \ amount $amount \ - signKey $signKey + signKey $signKey \ + -json true ] set newBalance [expr {$fromBalance - $amount}] set newFrontierHash [dict get [json::json2dict $block] "_blockHash"]