package module
Version: v1.4.1 Latest Latest

This package is not in the latest version of its module.

Go to latest
Published: Oct 28, 2021 License: MIT Imports: 10 Imported by: 0



atto is a tiny Nano wallet, which focuses on ease of use through simplicity. Included is a rudimentary Go library to interact with Nano nodes.

Disclaimer: I am no cryptographer and atto has not been audited. I cannot guarantee that atto is free of security compromising bugs.


You can download precompiled binaries from the releases page or build atto yourself like this; go 1.15 or higher is required:

git clone ''
cd atto
go build ./cmd/atto
# The atto binary is now available at ./atto. You could also install
# to ~/go/bin/ by executing "go install ./cmd/atto".

For Arch Linux @kseistrup also made atto available in the AUR.


$ # The new command generates a new seed.
$ atto new
$ # Store it in your password manager:
$ pass insert nano
Enter password for nano: D420296F5FEF486175FAA8F649DED00A5B0A096DB8D03972937542C51A7F296C
Retype password for nano: D420296F5FEF486175FAA8F649DED00A5B0A096DB8D03972937542C51A7F296C

$ # The address command shows the address for an account.
$ pass nano | atto address

$ # With address and all following commands you can also provide an
$ # alternative account index (default is 0):
$ pass nano | atto -a 1 address

$ # The balance command will receive pending funds automatically.
$ pass nano | atto balance
Creating receive block for 1.025 from nano_34ymtnmhwseiex4eqf7nnf5wcyg44kknuuen5wwurm18ma91msf6e1pqo8hx... done
Creating receive block for 0.1 from nano_39nd8eksw1ia6aokn96z4uthocke47hfsx9gr31othm1nrfwnzmmaeehiccq... done
1.337 NANO

$ # Choosing a representative is important for keeping the network
$ # decentralized.
$ pass nano | atto representative nano_1jr699mk1fi6mxy1y76fmuyf3dgms8s5pzcsge5cyt1az93x4n18uxjenx93
Creating change block... done

$ # To avoid accidental loss of funds, the send command requires
$ # confirmation, unless the -y flag is given:
$ pass nano | atto send 0.1 nano_11zdqnjpisos53uighoaw95satm4ptdruck7xujbjcs44pbkkbw1h3zomns5
Send 0.1 NANO to nano_11zdqnjpisos53uighoaw95satm4ptdruck7xujbjcs44pbkkbw1h3zomns5? [y/N]: y
Creating send block... done

$ atto -h
	atto -v
	atto n[ew]
	atto [-a ACCOUNT_INDEX] a[ddress]
	atto [-a ACCOUNT_INDEX] b[alance]
	atto [-a ACCOUNT_INDEX] r[epresentative] REPRESENTATIVE
	atto [-a ACCOUNT_INDEX] [-y] s[end] AMOUNT RECEIVER

If the -v flag is provided, atto will print its version number.

The new subcommand generates a new seed, which can later be used with
the other subcommands.

The address, balance, representative and send subcommands expect a seed
as the first line of their standard input. Showing the first address of
a newly generated key could work like this:
atto new | tee seed.txt | atto address

The send subcommand also expects manual confirmation of the transaction,
unless the -y flag is given.

The address subcommand displays addresses for a seed, the balance
subcommand receives pending sends and shows the balance of an account,
the representative subcommand changes the account's representative and
the send subcommand sends funds to an address.

ACCOUNT_INDEX is an optional parameter, which must be a number between 0
and 4,294,967,295. It allows you to use multiple accounts derived from
the same seed. By default the account with index 0 is chosen.

Technical details

atto is written with ca. 1000 lines of code and uses minimal external dependencies. This makes it easy to audit the code yourself and ensure, that it does nothing you wouldn't want it to do.

To change some defaults, like the node to use, take a look at cmd/atto/config.go.

Signatures are created without the help of a node, to avoid your seed or private keys being stolen by a node operator. The received account info is always validated using block signatures to ensure the node operator cannot manipulate atto by, for example, reporting wrong balances.

atto does not have any persistance and writes nothing to your file system. This makes atto very portable, but also means, that no history is stored locally. I recommend using a service like to investigate transaction history.

Offline signing

See atto-safesign.


If you want to show your appreciation for atto, you can donate to me at nano_1i7wsbehgwhxct91wpojr1j588ydikd64uc7p3kj54nofqioc6ydjopezf13.




This section is empty.


View Source
var ErrAccountManipulated = fmt.Errorf("the received account info has been manipulated")

ErrAccountManipulated is used when it seems like an account has been manipulated. This probably means someone is trying to steal funds.

View Source
var ErrAccountNotFound = fmt.Errorf("account has not yet been opened")

ErrAccountNotFound is used when an account could not be found by the queried node.

View Source
var ErrSignatureMissing = fmt.Errorf("signature is missing")

ErrSignatureMissing is used when the Signature of a Block is missing but required for the attempted operation.

View Source
var ErrWorkMissing = fmt.Errorf("work is missing")

ErrWorkMissing is used when the Work of a Block is missing but required for the attempted operation.


func GenerateSeed

func GenerateSeed() (string, error)

GenerateSeed generates a new random seed.

func NewPrivateKey

func NewPrivateKey(seed string, index uint32) (*big.Int, error)

NewPrivateKey creates a private key from the given seed and index.


type Account

type Account struct {
	PublicKey *big.Int
	Address   string

Account holds the public key and address of a Nano account.

func NewAccount

func NewAccount(privateKey *big.Int) (a Account, err error)

NewAccount creates a new Account and populates both its fields.

func NewAccountFromAddress

func NewAccountFromAddress(address string) (a Account, err error)

NewAccountFromAddress creates a new Account and populates both its fields.

func (Account) FetchAccountInfo

func (a Account) FetchAccountInfo(node string) (i AccountInfo, err error)

FetchAccountInfo fetches the AccountInfo of Account from the given node.

It is also verified, that the retreived AccountInfo is valid by doing a block_info RPC for the frontier, verifying the signature and ensuring that no fields have been changed in the account_info response.

May return ErrAccountNotFound or ErrAccountManipulated.

If ErrAccountNotFound is returned, FirstReceive can be used to create a first Block and AccountInfo and create the account by then submitting this Block.

func (Account) FetchPending

func (a Account) FetchPending(node string) ([]Pending, error)

FetchPending fetches all unreceived blocks of Account from node.

func (Account) FirstReceive added in v1.4.0

func (a Account) FirstReceive(pending Pending, representative string) (AccountInfo, Block, error)

FirstReceive creates the first receive block of an account. The block will still be missing its signature and work. FirstReceive will also return AccountInfo, which can be used to create further blocks.

type AccountInfo

type AccountInfo struct {
	// Ignore this field. It only exists because of
	Error string `json:"error"`

	Frontier       string `json:"frontier"`
	Representative string `json:"representative"`
	Balance        string `json:"balance"`

	PublicKey *big.Int `json:"-"`
	Address   string   `json:"-"`

AccountInfo holds the basic data needed for Block creation.

func (*AccountInfo) Change

func (i *AccountInfo) Change(representative string) (Block, error)

Change creates a change block, which will still be missing its signature and work. The Frontier and Representative of the AccountInfo will be updated.

func (*AccountInfo) Receive

func (i *AccountInfo) Receive(pending Pending) (Block, error)

Receive creates a receive block, which will still be missing its signature and work. The Frontier and Balance of the AccountInfo will be updated.

func (*AccountInfo) Send

func (i *AccountInfo) Send(amount, toAddr string) (Block, error)

Send creates a send block, which will still be missing its signature and work. The Frontier and Balance of the AccountInfo will be updated. The amount is interpreted as Nano, not raw!

type Block

type Block struct {
	Type           string `json:"type"`
	Account        string `json:"account"`
	Previous       string `json:"previous"`
	Representative string `json:"representative"`
	Balance        string `json:"balance"`
	Link           string `json:"link"`
	Signature      string `json:"signature"`
	Work           string `json:"work"`

	// This field is not part of the JSON but needed to improve the
	// performance of FetchWork and the security of Submit.
	SubType BlockSubType `json:"-"`

Block represents a block in the block chain of an account.

func (*Block) FetchWork

func (b *Block) FetchWork(node string) error

FetchWork uses the generate_work RPC on node to fetch and then set the Work of b.

func (Block) Hash

func (b Block) Hash() (string, error)

Hash calculates the block's hash and returns it's string representation.

func (*Block) Sign

func (b *Block) Sign(privateKey *big.Int) error

Sign computes and sets the Signature of b.

func (Block) Submit

func (b Block) Submit(node string) error

Submit submits the Block to the given node. Work and Signature of b must be populated beforehand.

May return ErrWorkMissing or ErrSignatureMissing.

type BlockSubType

type BlockSubType int64

BlockSubType represents the sub-type of a block.

const (
	// SubTypeReceive denotes blocks which raise the balance.
	SubTypeReceive BlockSubType = iota

	// SubTypeChange denotes blocks which change the representative.

	// SubTypeSend denotes blocks which lower the balance.

type Pending

type Pending struct {
	Hash   string
	Amount string
	Source string

Pending represents a block that is waiting to be received.


Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL