Key Management¶
Seeds¶
Hex Seed¶
Nano's private key(s) have been traditionally derived from a 64 character, uppercase hexadecimal string (0-9A-F). This is currently the more popular form of seed supported by a variety of services and wallets. Additional details available in The Basics guide.
Mnemonic Seed¶
Wallets that provide mnemonic seeds should use the BIP39 standard word list and methods for generating the seed. When BIP32 master keys are generated from the seed, the HMAC hash key variation "ed25519 seed" should be used due to nano using ed25519 for the signing algorithm (see SLIP-0010).
A non-standard derivation path is used for nano in the Ledger Nano implementation, as well as other popular wallets. With a coin-type of 165'
(0x800000a5
), this is a partial BIP44 path of m/44'/165'/[address index]
. Only hardened paths are defined.
m/44'/165'/0'
derives the first private key, m/44'/165'/1'
derives the second private key, and so on.
The partial derivation path is hard-coded in the common verification tool by Ian Coleman: https://iancoleman.io/bip39/ . If using this tool to verify, note that regardless of custom settings on the BIP32 or BIP44 tabs for address generation, the resulting addresses in the table at the bottom will follow m/44'/165'/[address index]
, even if the path in that table indicates otherwise. To see accurate paths in the Derived Addresses table, on the BIP32 tab set the BIP32 Derivation Path to m/44'/165'
.
Demo Examples¶
External libraries, review before using
The linked resources below contain code dealing with private key management and/or execution of transactions. The Nano Foundation does not control this code, does not endorse it and is not responsible for its use. Use of this code requires review and is at your own discretion.
https://github.com/roosmaa/nano-bip39-demo
https://github.com/joltwallet/bip-mnemonic
Implementations¶
https://github.com/numsu/nanocurrency-web-js
Test Vectors¶
24-Word Mnemonic
edge defense waste choose enrich upon flee junk siren film clown finish luggage leader kid quick brick print evidence swap drill paddle truly occur
some password
0dc285fde768f7ff29b66ce7252d56ed92fe003b605907f7a4f683c3dc8586d34a914d3c71fc099bb38ee4a59e5b081a3497b7a323e90cc68f67b5837690310c
Index 0 (44'/165'/0'
)
Derived Private Key:
3be4fc2ef3f3b7374e6fc4fb6e7bb153f8a2998b3b3dab50853eabe128024143
5b65b0e8173ee0802c2c3e6c9080d1a16b06de1176c938a924f58670904e82c4
nano_1pu7p5n3ghq1i1p4rhmek41f5add1uh34xpb94nkbxe8g4a6x1p69emk8y1d
Index 1 (44'/165'/1'
)
Derived Private Key:
ce7e429e683d652446261c17a96da9ed1897aea96c8046f2b8036f6b05cb1a83
d9f7762e9cd4e7ed632481308cdb8f54abf0241332c0a8641f61e92e2fb03c12
nano_3phqgrqbso99xojkb1bijmfryo7dy1k38ep1o3k3yrhb7rqu1h1k47yu78gz
Index 2 (44'/165'/2'
)
Derived Private Key:
1257df74609b9c6461a3f4e7fd6e3278f2ddcf2562694f2c3aa0515af4f09e38
a46da51986e25a14d82e32d765dcee69b9eeccd4405411430d91ddb61b717566
nano_3b5fnnerfrkt4me4wepqeqggwtfsxu8fai4n473iu6gxprfq4xd8pk9gh1dg
12-Word Mnemonic
company public remove bread fashion tortoise ahead shrimp onion prefer waste blade
No Passphrase
Derived BIP39 Seed:
924a962cae64448812be28a514093ebfeeed537d61a44318eb35f902961d21b2fccd30008d33c8d1d5327a34b9b73281c4b27a0a3d004c1c2e85e8dbb234cba8
Index 0 (44'/165'/0'
)
Derived Private Key:
6f73d61ca0b56fcdb79d69d437f102348ad75ca971433eb92b2b003f8c99b48d
134d938215f68bcaa3a0e574fde325fc4b1abad9bd3d698bfef95633b54ffb57
nano_16tfkg33dxndscjt3sdnzqjkdz4d5cxfmhbxf87zxycp8gtnzytqmcosi3zr
Index 1 (44'/165'/1'
)
Derived Private Key:
7e104389811a0967ef574af1f3f423f23cbf7b614be17844f67fb6fd315f9a7e
71e6caac915affe836c3e822be6a5b3464f40c74bd2e5459d4e74205c6a7c0df
nano_1wh8scpb4pqzx1ue9t34qso7pf56yi89bhbgcjexbst41q5chi8zqtwb74ih
Index 2 (44'/165'/2'
)
Derived Private Key:
8b7250869207a277ac37068dbe32782c2ab9fc6a5342f0deabbfdfae1285196a
fcebc6554853ed01c242817abf1b5050b887002f8de8f55d00c7c6b5fe01075d
nano_3z9drscninzf193671dtqwfo1n7riw14z5hayogi3jy8pqz143txaghe4gbk
External Management¶
For larger, more robust systems, external private key management is recommended. In this setup, the node operator generates and stores private keys in an external database and only queries the nano_node to:
- Find receivable blocks for an account
- Sign transactions given a private key. More advanced systems may choose to implement signing themselves.
- Broadcast the signed transaction to the network.
Note
WALLET_IDs are not used for External Private Key Management since private keys are not stored in the nano_node. Much of this section builds off of the Blocks Specifications documentation.
External accounting systems¶
In order to properly implement accounting systems external to the Nano node the following best practices should be put into place, which ensure only fully confirmed blocks are used for external tracking of credits, debits, etc.
Confirmation and idempotency
The details below expand on this, but the two most important pieces of any integration are:
- Always confirm blocks - make sure to follow the block confirmation tracking recommendations so you are always taking action from confirmed blocks
- Guarantee idempotency - whenever you take action from a block confirmation, it must be idempotent so you don't take the action again if the same block hash is seen through confirmation tracking
Block confirmation procedures¶
Before crediting funds to an account internally based on a deposit on the network, the block sending the funds must be confirmed. This is done by verifying the network has reached quorum on the block. Details of the recommended verification process can be found in the block confirmation tracking guide.
Tracking confirmed balances¶
External accounting systems that track balances arriving to the node must track hashes of blocks that have been received in order to guarantee idempotency. Once confirmation of a block has been validated, the block hash should be recorded for the account along with any credits, debits or other related information. Any attempts to credit or debit accounts external to the node should check that no previous conflicting or duplicate activity was already recorded for that same block hash.
Transaction order and correctness¶
If you are creating a batch of transactions for a single account, which can be a mix of sending and receiving funds, there is no need to wait for the confirmation of blocks in that account to create the next transaction. As long as a transaction is valid, it will be confirmed by the network. The transactions that follow it can only be confirmed if the previous transactions are valid.
However, you must always wait for the confirmation of receivable blocks before creating the corresponding receive transaction, to ensure it will be confirmed. Always wait for confirmation of transactions that you did not create yourself.
Expanding Private Keys¶
A Nano private key is a 256-bit piece of data produced from a cryptographically secure random number generator.
Secure Private Keys
- Generating private keys from an insecure source may result in loss of funds.
- Be sure to backup any generated private key; if lost the funds in the account will become inaccessible.
Step 1: Generate secure private key
The bash command below generates a valid private key from a cryptographically secure random number generator. Always use a cryptographically secure processes for generating any private keys.
Command Example¶
cat /dev/urandom | tr -dc '0-9A-F' | head -c${1:-64}
Success Result¶
781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3
Step 2: Expand private key
From the private key, a public key can be derived, and the public key can be translated into a Nano Address using the key_expand
RPC command.
Request Example¶
curl -d '{
"action": "key_expand",
"key": "781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3"
}' http://127.0.0.1:7076
Success Response¶
{
"private": "781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3",
"public": "3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039",
"account": "nano_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx"
}
Creating Transactions¶
Using external keys, transactions are generated in two steps: creation and broadcast. This section will be more heavy on example rather than precise specifications.
Send Transaction¶
Step 1: Get Account Info
To send funds to an account, first call the account_info
RPC command to gather necessary account information to craft your transaction. Setting "representative": "true"
makes the nano_node also return the account's representative address, a necessary piece of data for creating a transaction.
Request Example¶
curl -d '{
"action": "account_info",
"representative": "true",
"account": "nano_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx"
}' http://127.0.0.1:7076
Success Response¶
{
"frontier": "92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D",
"open_block": "B292BFFAAE9013BE630B31144EF15205E986940080687C0441CCFE6EAB67FE53",
"representative_block": "B292BFFAAE9013BE630B31144EF15205E986940080687C0441CCFE6EAB67FE53",
"balance": "4618869000000000000000000000000",
"modified_timestamp": "1524626644",
"block_count": "4",
"representative": "nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou"
}
Step 2: Build block_create
request
Using details from the account_info
call response, along with other information, we can create the block_create
RPC request.
For more details on values, see the Blocks Specifications documentation.
Field | Value |
---|---|
"json_block" |
always "true" , so that the output is JSON-formatted |
"type" |
always the constant "state" |
"previous" |
"frontier" from account_info response |
"account" |
"account" address used in the account_info call above that the block will be created for |
"representative" |
"representative" address returned in the account_info call |
"balance" |
balance of the account in raw after this transaction is completed (decreased if sending, increased if receiving). In this example, we will send 1 nano (10^{30} raw) to address nano_1q3hqecaw15cjt7thbtxu3pbzr1eihtzzpzxguoc37bj1wc5ffoh7w74gi6p . |
"link" |
destination address the funds will move between |
"key" |
account's private key |
Request Example¶
curl -d '{
"action": "block_create",
"json_block": "true",
"type": "state",
"previous": "92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D",
"account": "nano_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx",
"representative": "nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou",
"balance": "3618869000000000000000000000000",
"link": "nano_1q3hqecaw15cjt7thbtxu3pbzr1eihtzzpzxguoc37bj1wc5ffoh7w74gi6p",
"key": "781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3"
}' http://127.0.0.1:7076
Success Response¶
{
"hash": "8DB5C07E0E62E9DFE8558CB9BD654A115B02245B38CD369753CECE36DAD13C05",
"block": {
"type": "state",
"account": "nano_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx",
"previous": "92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D",
"representative": "nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou",
"balance": "3618869000000000000000000000000",
"link": "5C2FBB148E006A8E8BA7A75DD86C9FE00C83F5FFDBFD76EAA09531071436B6AF",
"link_as_account": "nano_1q3hqecaw15cjt7thbtxu3pbzr1eihtzzpzxguoc37bj1wc5ffoh7w74gi6p",
"signature": "79240D56231EF1885F354473733AF158DC6DA50E53836179565A20C0BE89D473ED3FF8CD11545FF0ED162A0B2C4626FD6BF84518568F8BB965A4884C7C32C205",
"work": "fbffed7c73b61367"
}
}
Additional details
- The option
json_block
, available since V19.0, makes the RPC call return a non-stringified version of the block, which is easier to parse and always recommended. block_create
RPC commands generally take longer than other RPC commands because the nano_node has to generate the Proof-of-Work for the transaction. The response block data is already properly formatted to include in theprocess
RPC command.- The nano_node creating and signing this transaction has no concept of what the transaction amount is, nor network state; all the nano_node knows is that it is creating a block whose previous block on the account chain has hash
92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D
results in the account having a balance of3618869000000000000000000000000
. - If the account's balance at block hash
92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D
was actually5618869000000000000000000000000
, then 2 nano would have been sent tonano_1q3hqecaw15cjt7thbtxu3pbzr1eihtzzpzxguoc37bj1wc5ffoh7w74gi6p
.
What if I receive funds on my account and then broadcast the above crafted send? Would this result in me sending excess funds to the recipient?
If you followed this guide, then the answer is "no". When you issued the account_info
RPC command, you received the account's balance at a specific blockhash on its account-chain. In your crafted transaction, you specify that hash in the "previous"
field. If funds were signed into your account, the headblock on your account-chain would change. Since your send no longer refers to the headblock on your account-chain when broadcasted, the network would reject your transaction.
Warning
Since only the resulting balance is recorded, the transaction amount is interpreted as the difference in balance from the previous block on the account-chain and the newly created block. For this reason, it is crucial that you obtain the current account balance and headblock in the same atomic account_info
RPC command.
When not following this guide closely, the following inappropriate sequence of events could lead to erroneous amounts sent to a recipient.
- An account's balance, say 5 nano, was obtained using the
account_balance
. This balance is valid as of hypothetical BLOCK_A. - By another process you control, a receive (BLOCK_B) was signed and broadcasted into your account-chain (race-condition).
- Lets say this
receive
increased the funds on the account chain by 10 nano, resulting in a final balance 15 nano. - The account's frontier block is obtained by the
accounts_frontiers
RPC command, returning the hash of BLOCK_B. Other transaction metadata is obtained by other RPC commands. - With the collected data, if a send transaction was created for 3 nano, the final balance would be computed as 5 - 3, or 2 nano.
- When this is broadcasted, since it is referring to the current head block on the account, BLOCK_B, the network would accept it. But, because the balance as of BLOCK_B was actually 15 nano, this would result in 12 nano being sent to the recipient.
For this reason, only populate transaction data source from a single account_info
RPC call.
Step 3: Broadcast the transaction
As a result of the command above, the nano_node will return a signed, but not yet broadcasted transaction. Broadcasting of the signed transaction is covered in the Broadcasting Transactions section.
Receive Transaction¶
Manually receiving first block
The very first transaction on an account-chain, which is always a receive, is slightly special and deserves its own section First Receive Transaction.
Step 1: Get Account Info
Receiving funds is very similar to sending funds outlined in the previous section, starting with calling account_info
to get block details for the account frontier. The scenario below pretends that our previous example of a send transaction was not broadcast and confirmed on the network because the starting account_info
details are identical.
Request Example¶
curl -d '{
"action": "account_info",
"representative": "true",
"account": "nano_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx"
}' http://127.0.0.1:7076
Success Response¶
{
"frontier": "92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D",
"open_block": "B292BFFAAE9013BE630B31144EF15205E986940080687C0441CCFE6EAB67FE53",
"representative_block": "B292BFFAAE9013BE630B31144EF15205E986940080687C0441CCFE6EAB67FE53",
"balance": "4618869000000000000000000000000",
"modified_timestamp": "1524626644",
"block_count": "4",
"representative": "nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou"
}
Step 2: Build block_create
request
Using details from the account_info
call response, along with other information, we can create the block_create
RPC request. The two differences between the send transaction are the "link"
and "balance"
fields.
For more details on values, see the Blocks Specifications documentation.
Field | Value |
---|---|
"json_block" |
always "true" , so that the output is JSON-formatted |
"type" |
always the constant "state" |
"previous" |
"frontier" from account_info response, or 0 if first block on new account |
"account" |
"account" address used in the account_info call above that the block will be created for |
"representative" |
"representative" address returned in the account_info call |
"balance" |
balance of the account in raw after this transaction is completed (decreased if sending, increased if receiving). In this example, we will receive 7 nano (7 \times 10^{30} raw) based on the assumed details of the block the "link" hash refers to (block contents not shown in this example). |
"link" |
block hash of its paired send transaction, assumed to be a 7 nano send from block hash CBC911F57B6827649423C92C88C0C56637A4274FF019E77E24D61D12B5338783 |
"key" |
account's private key |
Request Example¶
curl -d '{
"action": "block_create",
"json_block": "true",
"type": "state",
"previous": "92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D",
"account": "nano_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx",
"representative": "nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou",
"balance": "11618869000000000000000000000000",
"link": "CBC911F57B6827649423C92C88C0C56637A4274FF019E77E24D61D12B5338783",
"key": "781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3"
}' http://127.0.0.1:7076
Success Response¶
{
"hash": "350D145570578A36D3D5ADE58DC7465F4CAAF257DD55BD93055FF826057E2CDD",
"block": {
"type": "state",
"account": "nano_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx",
"previous": "92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D",
"representative": "nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou",
"balance": "11618869000000000000000000000000",
"link": "CBC911F57B6827649423C92C88C0C56637A4274FF019E77E24D61D12B5338783",
"link_as_account": "nano_3kyb49tqpt39ekc49kbej51ecsjqnimnzw1swxz4boix4ctm93w517umuiw8",
"signature": "EEFFE1EFCCC8F2F6F2F1B79B80ABE855939DD9D6341323186494ADEE775DAADB3B6A6A07A85511F2185F6E739C4A54F1454436E22255A542ED879FD04FEED001",
"work": "c5cf86de24b24419"
}
}
Additional details
Here the follow scenario occurs:
- Previous balance was 4618869000000000000000000000000 raw
- Increased our balance by 7000000000000000000000000000000 raw
- Final balance becomes 11618869000000000000000000000000 raw
Step 3: Broadcast the transaction
As a result of the command above, the nano_node will return a signed, but not yet broadcasted transaction. Broadcasting of the signed transaction is covered in the Broadcasting Transactions section.
First Receive Transaction¶
The first transaction of an account is crafted in a slightly different way. To open an account, you must have sent some funds to it with a Send Transaction from another account. The funds will be receivable on the receiving account. If you already know the hash of the receivable transaction, you can skip Step 1.
Step 1: Obtain the receivable transaction block hash
Start with obtaining a list of receivable transactions in your unopened account. Limit the response to the highest value transaction by using a combination of sorting
and count
.
Request Example¶
curl -d '{
"action": "receivable",
"account": "nano_1rawdji18mmcu9psd6h87qath4ta7iqfy8i4rqi89sfdwtbcxn57jm9k3q11",
"count": "1",
"sorting": "true"
}' http://127.0.0.1:7076
Success Response¶
{
"blocks": {
"5B2DA492506339C0459867AA1DA1E7EDAAC4344342FAB0848F43B46D248C8E99": "100"
}
}
Step 2: Build block_create
request
Using the block hash and raw transaction amount from the receivable
call response, along with other information, we can create the block_create
RPC request. The only difference between the normal receive transactions is the "previous"
field.
For more details on values, see the Blocks Specifications documentation.
Field | Value |
---|---|
"json_block" |
always "true" , so that the output is JSON-formatted |
"type" |
always the constant "state" |
"previous" |
always the constant "0" as this request is for the first block of the account |
"account" |
"account" address used in the account_info call above that the block will be created for |
"representative" |
"representative" the account address to use as representative for your account. Choose a reliable, trustworthy representative. |
"balance" |
balance of the account in raw after this transaction is completed. In this example, we will receive 100\ raw, based on the assumed details from the "receivable" response above. |
"link" |
block hash of its paired send transaction, in this case assumed to be the block 5B2DA492506339C0459867AA1DA1E7EDAAC4344342FAB0848F43B46D248C8E99 |
"key" |
account's private key |
Request Example¶
curl -d '{
"action": "block_create",
"json_block": "true",
"type": "state",
"previous": "0",
"account": "nano_1rawdji18mmcu9psd6h87qath4ta7iqfy8i4rqi89sfdwtbcxn57jm9k3q11",
"representative": "nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou",
"balance": "100",
"link": "5B2DA492506339C0459867AA1DA1E7EDAAC4344342FAB0848F43B46D248C8E99",
"key": "0ED82E6990A16E7AD2375AB5D54BEAABF6C676D09BEC74D9295FCAE35439F694"
}' http://127.0.0.1:7076
Success Response¶
{
"hash": "ED3BE5340CC9D62964B5A5F84375A06078CBEDC45FB5FA2926985D6E27D803BB",
"block": {
"type": "state",
"account": "nano_1rawdji18mmcu9psd6h87qath4ta7iqfy8i4rqi89sfdwtbcxn57jm9k3q11",
"previous": "0000000000000000000000000000000000000000000000000000000000000000",
"representative": "nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou",
"balance": "100",
"link": "5B2DA492506339C0459867AA1DA1E7EDAAC4344342FAB0848F43B46D248C8E99",
"link_as_account": "nano_1psfnkb71rssr34sisxc5piyhufcrit68iqtp44ayixnfnkas5nsiuy58za7",
"signature": "903991714A55954D15C91DB75CAE2FBF1DD1A2D6DA5524AA2870F76B50A8FE8B4E3FBB53E46B9E82638104AAB3CFA71CFC36B7D676B3D6CAE84725D04E4C360F",
"work": "08d09dc3405d9441"
}
}
Step 3: Broadcast the transaction
As a result of the command above, the nano_node will return a signed, but not yet broadcasted transaction. Broadcasting of the signed transaction is covered in the Broadcasting Transactions section.
Broadcasting Transactions¶
Broadcast using process
RPC command
Common to all of these transactions is the need to broadcast the completed block to the network. This is achieved by the process
RPC command which accepts the block as stringified JSON data. If you followed the previous examples, you used the option json_block
for RPC block_create
, which allows you use the non-stringified version, as long as you include the same option in this RPC call.
A successful broadcast will return the broadcasted block's hash.
Including subtype
in process
RPC calls highly recommended
In order to avoid potential incorrect sends including the optional subtype
parameter on all process
RPC calls is highly recommended. In the next version of the RPC this parameter will be required.
Request Example¶
curl -d '{
"action": "process",
"json_block": "true",
"subtype": "open",
"block": {
"type": "state",
"account": "nano_1rawdji18mmcu9psd6h87qath4ta7iqfy8i4rqi89sfdwtbcxn57jm9k3q11",
"previous": "0000000000000000000000000000000000000000000000000000000000000000",
"representative": "nano_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou",
"balance": "100",
"link": "5B2DA492506339C0459867AA1DA1E7EDAAC4344342FAB0848F43B46D248C8E99",
"link_as_account": "nano_1psfnkb71rssr34sisxc5piyhufcrit68iqtp44ayixnfnkas5nsiuy58za7",
"signature": "903991714A55954D15C91DB75CAE2FBF1DD1A2D6DA5524AA2870F76B50A8FE8B4E3FBB53E46B9E82638104AAB3CFA71CFC36B7D676B3D6CAE84725D04E4C360F",
"work": "08d09dc3405d9441"
}
}' http://127.0.0.1:7076
Success Response¶
{
"hash": "42A723D2B60462BF7C9A003FE9A70057D3A6355CA5F1D0A57581000000000000"
}
Block watching and re-work
Since V20.0, blocks processed using process
are placed under observation by the node for re-broadcasting and re-generation of work under certain conditions. If you wish to disable this feature, add "watch_work": "false"
to the process RPC command.
- If a block is not confirmed within a certain amount of time (configuration option
work_watcher_period
, default 5 seconds), an automatic re-generation of a higher difficulty proof-of-work may take place. - Re-generation only takes place when the network is unable to confirm transactions quickly (commonly referred as the network being saturated) and the higher difficulty proof-of-work is used to help prioritize the block higher in the processing queue of other nodes.
- Configuration option
max_work_generate_multiplier
can be used to limit how much effort should be spent in re-generating the proof-of-work. - The target proof-of-work difficulty threshold is obtained internally as the minimum between
active_difficulty
andmax_work_generate_multiplier
(converted to difficulty). - With a new, higher difficulty proof-of-work, the block will get higher confirmation priority across the network.
When a transaction does not confirm
- If a transaction is taking too long to confirm, you may call the
process
RPC command with the same block data with no risk. - If for some reason a transaction fails to properly broadcast, subsequent transactions on the account-chain after that transaction will not be accepted by the network since the
"previous"
field in the transaction data refers to a non-existant block. - If this situation occurs, rebroadcasting the missing transaction(s) will make the subsequent blocks valid in the network's ledger.
Rebroadcasting blocks for an account-chain
The following command rebroadcasts all hashes on an account-chain starting at block hash provided:
Request Example¶
curl -d '{
"action": "republish",
"hash": "48006BF3146C18CAD3A53A957BF64EF7C57820B21FCCE373FA637559DA260358"
}' http://127.0.0.1:7076
Internal Management¶
The nano_node software has a built-in private-key manager that is suitable for smaller operations (<1000 accounts). External Key Management allows more powerful and robust systems at the cost of additional complexity. External Key Management is recommended for larger operations.
Creating a Wallet¶
To create an account, you first must create a wallet to hold the seed that will subsequently create the account.
Request Example¶
curl -d '{
"action": "wallet_create"
}' http://127.0.0.1:7076
Success Response¶
{
"wallet": "E3E67B1B3FFA46F606240F1D0B964873D42E9C6D0B7A0BF376A2E128541CC446"
}
The nano_node responds with the WALLET_ID. If you lose your WALLET_ID, it can only be recovered via a CLI command. To reiterate, the WALLET_ID is not a seed. The seed can be extracted for backup in Backing Up Seed. Many of the RPC commands in this guide require the WALLET_ID.
Recovering WALLET_ID¶
If you lose your WALLET_ID, you can print out all your WALLET_IDs and public addresses in those wallets with the --wallet_list
CLI command as follows:
Command Format¶
docker exec ${NANO_NAME} nano_node --wallet_list
Success Response¶
Wallet_ID: E3E67B1B3FFA46F606240F1D0B964873D42E9C6D0B7A0BF376A2E128541CC446
nano_16odwi933gpzmkgdcy9tt5zef5ka3jcfubc97fwypsokg7sji4mb9n6qtbme
Wallet_ID: DB0711484E35A4C75230D898853A86BFAFE9F87FCE99C83A4C2668C39607DD4B
In this example, the nano_node's internal private-key management system contains two wallets, each with a different 256-bit seed. The first wallet has a single account and the second wallet has zero accounts. Account creation will be covered later.
Backing Up Seed¶
The following command will print the seed for a given wallet to stdout. Replace ${WALLET_ID}
with the WALLET_ID that you would like to display the seed of.
Command Format¶
docker exec ${NANO_NAME:-nano_node_1} nano_node --wallet_decrypt_unsafe --wallet ${WALLET_ID}
Success Response¶
Seed: D56143E7561D71C1AF4D563C6AF79EECE93E82479818AD8ED88BED1AAE8BE4E5
Pub: nano_16odwi933gpzmkgdcy9tt5zef5ka3jcfubc97fwypsokg7sji4mb9n6qtbme
Prv: 1F6FEB5D1E05C10B904E1112F430C3FA93ACC7067206B63AD155199501794E3E
Info
The nano_node responds with three pieces of information:
- The seed of the wallet (back this up).
- The pairing public address
- The private key (deterministically derived from seed) of accounts within the wallet.
Additional notes:
- If you change the seed in a wallet, future created accounts will be derived from that seed.
- Changing seeds on a wallet that already has accounts can cause accidental loss of funds from improper seed or private key backup.
- It is recommended to always create a new wallet when restoring a seed.
Error Response¶
Wallet doesn't exist
Warning
If anyone has access to the seed, they can freely access funds, so keep this very secure. Since the above command prints to stdout, it is recommended to wipe stdout afterwards using:
clear && printf '\e[3J'
Restoring/Changing Seed¶
Warning
Only change the seed of a wallet that contains no accounts. Changing the seed of a wallet that already has accounts may lead to a false sense of security: accounts are generated by the seed that is currently in the wallet. Generating accounts, then switching the seed and backing up the new seed does not backup the previously generated accounts.
Request Format¶
curl -d '{
"action": "wallet_change_seed",
"wallet": "<WALLET_ID>",
"seed": "<SEED>"
}' http://127.0.0.1:7076
Request Example¶
curl -d '{
"action":"wallet_change_seed",
"wallet":"DB0711484E35A4C75230D898853A86BFAFE9F87FCE99C83A4C2668C39607DD4B",
"seed":"D56143E7561D71C1AF4D563C6AF79EECE93E82479818AD8ED88BED1AAE8BE4E5"
}' http://127.0.0.1:7076
Success Response¶
{
"success": ""
}
Error Response¶
Response if the wallet_id isn't found in nano_node:
{
"error": "Wallet not found"
}
Response if the seed field contains non-hexidecimal values or is too long:
{
"error": "Bad seed"
}
Warning
If the hexidecimal seed represents less than 256 bits, the seed will be 0-padded on the left to become 256 bits.
Account Create¶
After creating a wallet, it's corresponding WALLET_ID, and backing up the seed (not the wallet_id), the wallet can be populated with accounts. To create a new account in a wallet use the account_create
RPC command:
Request Format¶
curl -d '{
"action": "account_create",
"wallet": "<WALLET_ID>"
}' http://127.0.0.1:7076
Success Response¶
{
"account": "nano_16odwi933gpzmkgdcy9tt5zef5ka3jcfubc97fwypsokg7sji4mb9n6qtbme"
}
Error Response¶
{
"error": "Wallet not found"
}
Bulk Account Create¶
To generate many accounts in bulk, it is more efficient to create them all at once using the accounts_create
RPC command:
Request Format¶
curl -d '{
"action": "accounts_create",
"wallet": "<WALLET_ID>",
"count": "<NUM_ACCOUNTS>"
}' http://127.0.0.1:7076
Request Example¶
curl -d '{
"action": "accounts_create",
"wallet": "DB0711484E35A4C75230D898853A86BFAFE9F87FCE99C83A4C2668C39607DD4B",
"count":"5"
}' http://127.0.0.1:7076
Success Response¶
{
"accounts": [
"nano_35kgi43t5hgi64715qnppmz1yb6re1igfcrkfx4ppirkqpfmecnpd1mdmafu",
"nano_3t13y6b7h93yn9hehn8p6yqx1yqzrxxs33drhzep8huhymwxamn15pba75oj",
"nano_11exxzfoosai96w7gnrjrn7m6i8bodch37ib8jgxsm5k96na6e1wda8np881",
"nano_3xbsso8pkemwatwdnkcyn1bfcmrb8dpcg3pit9zqxj9mkxa6ifiankff6m9x",
"nano_1q5gpy46moe1csj8md8oq3x57sxqmwskk8mmr7c63q1yebnjcsxg1yib19kn"
]
}
Receiving Funds¶
As long as the nano_node is synced and the node wallet is unlocked (node wallet locking is not covered in this guide), nano_node automatically creates and signs receive transactions for all accounts in the wallet's internal private-key management system.
Tip
In the event that a receive is not automatically generated, it can be manually generated using the receive
RPC command.
Semi-Manual Receiving Funds¶
If the nano_node does not automatically sign in a receivable transaction, transactions can be manually signed in. The easiest way is to explicitly command the nano_node to check all of the accounts in all of its wallets for receivable blocks.
Request Example¶
curl -d '{
"action": "search_receivable_all"
}' http://127.0.0.1:7076
Success Response¶
{
"success": ""
}
Note
As the number of accounts in a nano_node grows, this command becomes increasingly computationally expensive.
Sending funds¶
The send
RPC command sends funds from an account in the specified wallet to a destination address.
Request Format¶
curl -d '{
"action": "send",
"wallet": "<WALLET_ID>",
"source": "<SOURCE_ADDRESS>",
"destination": "<DESTINATION_ADDRESS>",
"amount": "1000000",
"id": "7081e2b8fec9146e"
}' http://127.0.0.1:7076
Field | Description |
---|---|
wallet | WALLET_ID containing the source address |
source | Address you control starting with "nano_" |
destination | Destination address starting with "nano_" |
amount | Amount to send in raw |
id | Any string |
Important
The "id"
field is a safety mechanism that prevents issuing a transaction multiple times by repeating the RPC command.
- If a transaction is successful, any subsequent
send
RPC commands with the same identifier will be ignored by the nano_node. - If the request times out, then the send may or may not have gone through.
- Most exchange "double withdraw" issues are caused by naive error-handling routines which re-issue the send request without the
"id"
parameter. - The
"id"
field is local to your nano_node instance and does not offer protection when sent to different instances of nano_node that manage the same seed. - As previously mentioned, having a seed loaded in multiple online nano_node is strongly discouraged.
- If managing more than 1000 accounts, building a separate system for managing keys and accounts externally is recommended
Below is a sample command to send 1 nano from nano_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000
to nano_16odwi933gpzmkgdcy9tt5zef5ka3jcfubc97fwypsokg7sji4mb9n6qtbme
.
Request Example¶
curl -d '{
"action": "send",
"wallet": "000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
"source": "nano_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"destination": "nano_16odwi933gpzmkgdcy9tt5zef5ka3jcfubc97fwypsokg7sji4mb9n6qtbme",
"amount": "1000000000000000000000000000000",
"id": "7081e2b8fec9146e"
}' http://127.0.0.1:7076
Success Response¶
{
"block": "000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
}
On success, the nano_node returns the hash of the transaction's block.
Republishing Transactions¶
It may take a few seconds for the transaction to appear on the Nano Network. If the transaction fails to appear, you may call the republish
RPC command with the oldest missing transaction's hash. Account-chains must be continuous and unbroken. If for some reason a transaction fails to properly broadcast, subsequent transactions on the account-chain will not be accepted by the network since the "previous"
field in the transaction data refers to a block unknown to to other nodes on the network.
Tip
Republishing the missing transaction(s) will make all the subsequent blocks valid in the network's ledger. Republishing does not create new transactions.
The following command rebroadcasts all hashes on an acccount-chain starting at block with hash ${BLOCK_HASH}
:
Request Example¶
curl -d '{
"action": "republish",
"hash": "AF9C1D46AAE66CC8F827904ED02D4B3D95AA98B1FF058352BA6B670BEFD40231"
}' http://127.0.0.1:7076
Success Response¶
{
"success": "",
"blocks": [
"AF9C1D46AAE66CC8F827904ED02D4B3D95AA98B1FF058352BA6B670BEFD40231",
"C9A111580A21F3E63F2283DAF6450D5178BFAC2A6C38E09B76EEA9CE37EC9CE0"
]
}