├── .gitignore ├── crypto ├── readme.md ├── uuid.go ├── sha3.go ├── uuid_test.go ├── math.go ├── scrypt.go ├── sha3_test.go ├── randg_test.go ├── scrypt_test.go ├── randg.go ├── aes.go ├── keys_test.go ├── aes_test.go ├── math_test.go └── keys.go ├── os ├── crypto │ ├── readme.md │ ├── sha3 │ │ ├── testdata │ │ │ └── keccakKats.json.deflate │ │ ├── keccakf_amd64.go │ │ ├── xor.go │ │ ├── register.go │ │ ├── xor_generic.go │ │ ├── xor_unaligned.go │ │ ├── PATENTS │ │ ├── LICENSE │ │ ├── shake.go │ │ ├── hashes.go │ │ ├── doc.go │ │ ├── sha3.go │ │ └── sha3_test.go │ ├── uuid.go │ ├── uuid_test.go │ ├── math.go │ ├── vrf.go │ ├── sha3.go │ ├── scrypt.go │ ├── randg_test.go │ ├── randg.go │ ├── sha3_test.go │ ├── scrypt_test.go │ ├── aes.go │ ├── aes_test.go │ ├── math_test.go │ ├── keys_test.go │ └── keys.go ├── common │ ├── utils.go │ ├── big.go │ ├── prque │ │ ├── prque.go │ │ └── sstack.go │ ├── hexutil │ │ ├── json_example_test.go │ │ ├── hexutil_test.go │ │ ├── hexutil.go │ │ └── json.go │ ├── size.go │ └── bytes.go ├── app │ └── config │ │ ├── params.go │ │ └── config.go ├── p2p │ └── config │ │ ├── params.go │ │ └── config.go ├── log │ ├── log.go │ └── zap.go └── filesystem │ └── dirs.go ├── Makefile ├── client ├── transaction.go ├── backend.go └── json_http_client.go ├── go.mod ├── README.md ├── accounts ├── account.go └── store.go ├── main.go ├── repl ├── messages.go ├── mock.go ├── prompt.go └── repl.go ├── wallet ├── accounts │ ├── account.go │ ├── ops.go │ └── store.go └── address │ └── address.go ├── log └── log.go └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | accounts.json -------------------------------------------------------------------------------- /crypto/readme.md: -------------------------------------------------------------------------------- 1 | ## Testing Notes 2 | - We must get close to 100% coverage and stay there for all functions in this important package -------------------------------------------------------------------------------- /os/crypto/readme.md: -------------------------------------------------------------------------------- 1 | ## Testing Notes 2 | - We must get close to 100% coverage and stay there for all functions in this important package -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BINARY := cli_wallet 2 | LINUX=$(BINARY)_linux_amd64 3 | 4 | 5 | build-linux: 6 | env GOOS=linux GOARCH=amd64 go build -o $(LINUX) 7 | 8 | -------------------------------------------------------------------------------- /os/crypto/sha3/testdata/keccakKats.json.deflate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/libonomy/wallet-cli/HEAD/os/crypto/sha3/testdata/keccakKats.json.deflate -------------------------------------------------------------------------------- /crypto/uuid.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "github.com/google/uuid" 4 | 5 | // UUIDString returns a new random type-4 UUID string. 6 | func UUIDString() string { 7 | return uuid.New().String() 8 | } 9 | 10 | // UUID returns a new random type-4 UUID raw bytes. 11 | func UUID() []byte { 12 | return []byte(uuid.New().String()) 13 | } 14 | -------------------------------------------------------------------------------- /os/common/utils.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | func Min(a, b int) int { 4 | if a < b { 5 | return a 6 | } 7 | return b 8 | } 9 | 10 | func Min32(a, b uint32) uint32 { 11 | if a < b { 12 | return a 13 | } 14 | return b 15 | } 16 | 17 | func Min64(a, b uint64) uint64 { 18 | if a < b { 19 | return a 20 | } 21 | return b 22 | } 23 | -------------------------------------------------------------------------------- /crypto/sha3.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "golang.org/x/crypto/sha3" 5 | ) 6 | 7 | // Sha256 is a SHA-3-256 (not sha-256) hasher. It returns a 32 bytes (256 bits) hash of data. 8 | // data: arbitrary length bytes slice 9 | func Sha256(data ...[]byte) []byte { 10 | d := sha3.New256() 11 | for _, b := range data { 12 | d.Write(b) 13 | } 14 | return d.Sum(nil) 15 | } 16 | -------------------------------------------------------------------------------- /os/crypto/sha3/keccakf_amd64.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build amd64,!appengine,!gccgo 6 | 7 | package sha3 8 | 9 | // This function is implemented in keccakf_amd64.s. 10 | 11 | //go:noescape 12 | 13 | func keccakF1600(state *[25]uint64) 14 | -------------------------------------------------------------------------------- /os/app/config/params.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | // app params are non-configured consts 4 | const ( 5 | AppUsage = "the libonomy-p2p node" 6 | AppAuthor = "The libonomy-p2p authors" 7 | AppAuthorEmail = "info@libonomy.io" 8 | AppCopyrightNotice = "(c) 2017 The libonomy-p2p Authors" 9 | AccountsDirectoryName = "accounts" 10 | LogDirectoryName = "logs" 11 | ) 12 | -------------------------------------------------------------------------------- /os/crypto/uuid.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "github.com/google/uuid" 4 | 5 | // UUID is a 16-len byte array represnting a UUID 6 | type UUID [16]byte 7 | 8 | // UUIDString returns a new random type-4 UUID string. 9 | func UUIDString() string { 10 | return uuid.New().String() 11 | } 12 | 13 | // NewUUID returns a new random type-4 UUID raw bytes. 14 | func NewUUID() [16]byte { 15 | return uuid.New() 16 | } 17 | -------------------------------------------------------------------------------- /crypto/uuid_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/google/uuid" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestUUID(t *testing.T) { 11 | id := UUIDString() 12 | id1, err := uuid.Parse(id) 13 | assert.NoError(t, err, "unexpected error") 14 | id1Str := id1.String() 15 | assert.Equal(t, id, id1Str, "expected same uuid") 16 | 17 | id2 := UUID() 18 | assert.Equal(t, len(id2), 36, "expected 16") 19 | 20 | } 21 | -------------------------------------------------------------------------------- /os/crypto/uuid_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/google/uuid" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestUUID(t *testing.T) { 11 | id := UUIDString() 12 | id1, err := uuid.Parse(id) 13 | assert.NoError(t, err, "unexpected error") 14 | id1Str := id1.String() 15 | assert.Equal(t, id, id1Str, "expected same uuid") 16 | 17 | id2 := NewUUID() 18 | assert.Equal(t, len(id2), 16, "expected 16") 19 | 20 | } 21 | -------------------------------------------------------------------------------- /os/crypto/sha3/xor.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !amd64,!386,!ppc64le appengine 6 | 7 | package sha3 8 | 9 | var ( 10 | xorIn = xorInGeneric 11 | copyOut = copyOutGeneric 12 | xorInUnaligned = xorInGeneric 13 | copyOutUnaligned = copyOutGeneric 14 | ) 15 | 16 | const xorImplementationUnaligned = "generic" 17 | -------------------------------------------------------------------------------- /crypto/math.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | // MinInt32 returns x if x < y and y otherwise. 4 | func MinInt32(x, y int32) int32 { 5 | if x < y { 6 | return x 7 | } 8 | return y 9 | } 10 | 11 | // MinInt returns x if x < y and y otherwise. 12 | func MinInt(x, y int) int { 13 | if x < y { 14 | return x 15 | } 16 | return y 17 | } 18 | 19 | // MinInt64 returns x if x < y and y otherwise. 20 | func MinInt64(x, y int64) int64 { 21 | if x < y { 22 | return x 23 | } 24 | return y 25 | } 26 | -------------------------------------------------------------------------------- /os/crypto/math.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | // MinInt32 returns x if x < y and y otherwise. 4 | func MinInt32(x, y int32) int32 { 5 | if x < y { 6 | return x 7 | } 8 | return y 9 | } 10 | 11 | // MinInt returns x if x < y and y otherwise. 12 | func MinInt(x, y int) int { 13 | if x < y { 14 | return x 15 | } 16 | return y 17 | } 18 | 19 | // MinInt64 returns x if x < y and y otherwise. 20 | func MinInt64(x, y int64) int64 { 21 | if x < y { 22 | return x 23 | } 24 | return y 25 | } 26 | -------------------------------------------------------------------------------- /os/crypto/sha3/register.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.4 6 | 7 | package sha3 8 | 9 | import ( 10 | "crypto" 11 | ) 12 | 13 | func init() { 14 | crypto.RegisterHash(crypto.SHA3_224, New224) 15 | crypto.RegisterHash(crypto.SHA3_256, New256) 16 | crypto.RegisterHash(crypto.SHA3_384, New384) 17 | crypto.RegisterHash(crypto.SHA3_512, New512) 18 | } 19 | -------------------------------------------------------------------------------- /os/app/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | // Provide Config struct with default values 4 | 5 | // ConfigValues defines default values for app config params. 6 | var ConfigValues = Config{ 7 | AppIntParam: 20, 8 | AppBoolParam: true, 9 | DataFilePath: "~/.libonomy", 10 | } 11 | 12 | func init() { 13 | // todo: update default config params based on runtime env here 14 | } 15 | 16 | // Config defines app config params. 17 | type Config struct { 18 | ConfigFilePath string 19 | AppIntParam int 20 | AppBoolParam bool 21 | DataFilePath string 22 | } 23 | -------------------------------------------------------------------------------- /client/transaction.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import "github.com/libonomy/wallet-cli/wallet/address" 4 | 5 | // TODO rename to SerializableTransaction once we remove the old SerializableTransaction 6 | type InnerSerializableSignedTransaction struct { 7 | AccountNonce uint64 8 | Recipient address.Address 9 | GasLimit uint64 10 | Price uint64 11 | Amount uint64 12 | } 13 | 14 | // Once we support signed txs we should replace SerializableTransaction with this struct. Currently it is only used in the rpc server. 15 | type SerializableSignedTransaction struct { 16 | InnerSerializableSignedTransaction 17 | Signature [64]byte 18 | } 19 | -------------------------------------------------------------------------------- /os/p2p/config/params.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | // params are non-configurable (hard-coded) consts. To create a configurable param use Config. 4 | // add all node params here (non-configurable consts) - ideally most node params should be configurable. 5 | const ( 6 | ClientVersion = "libonomy-p2p/0.0.1" 7 | MinClientVersion = "0.0.1" 8 | NodesDirectoryName = "nodes" 9 | NodeDataFileName = "id.json" 10 | NodeDbFileName = "node.db" 11 | ) 12 | 13 | // NetworkID represents the network that the node lives in 14 | // that indicates what nodes it can communicate with, and which bootstrap nodes to use 15 | const ( 16 | MainNet = iota 17 | TestNet 18 | ) 19 | -------------------------------------------------------------------------------- /os/crypto/vrf.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "crypto/rand" 5 | "errors" 6 | "golang.org/x/crypto/ed25519" 7 | ) 8 | 9 | type VRFSigner struct { 10 | privateKey []byte 11 | } 12 | 13 | func (s *VRFSigner) Sign(message []byte) []byte { 14 | // TODO: replace with BLS (?) 15 | return ed25519.Sign(s.privateKey, message) 16 | } 17 | 18 | func NewVRFSigner(privateKey []byte) *VRFSigner { 19 | return &VRFSigner{privateKey: privateKey} 20 | } 21 | 22 | func ValidateVRF(message, signature, publicKey []byte) error { 23 | if !ed25519.Verify(publicKey, message, signature) { 24 | return errors.New("VRF validation failed") 25 | } 26 | return nil 27 | } 28 | 29 | func GenerateVRFKeys() (publicKey, privateKey []byte, err error) { 30 | return ed25519.GenerateKey(rand.Reader) 31 | } 32 | -------------------------------------------------------------------------------- /os/crypto/sha3/xor_generic.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sha3 6 | 7 | import "encoding/binary" 8 | 9 | // xorInGeneric xors the bytes in buf into the state; it 10 | // makes no non-portable assumptions about memory layout 11 | // or alignment. 12 | func xorInGeneric(d *state, buf []byte) { 13 | n := len(buf) / 8 14 | 15 | for i := 0; i < n; i++ { 16 | a := binary.LittleEndian.Uint64(buf) 17 | d.a[i] ^= a 18 | buf = buf[8:] 19 | } 20 | } 21 | 22 | // copyOutGeneric copies ulint64s to a byte buffer. 23 | func copyOutGeneric(d *state, b []byte) { 24 | for i := 0; len(b) >= 8; i++ { 25 | binary.LittleEndian.PutUint64(b, d.a[i]) 26 | b = b[8:] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/libonomy/wallet-cli 2 | 3 | require ( 4 | github.com/btcsuite/btcd v0.20.1-beta 5 | github.com/btcsuite/btcutil v1.0.2 6 | github.com/c-bata/go-prompt v0.2.3 7 | github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892 8 | github.com/google/uuid v1.1.1 9 | github.com/libonomy/ed25519 v0.0.0-20200515113020-867f8a7820c3 10 | github.com/mattn/go-colorable v0.1.2 // indirect 11 | github.com/mattn/go-runewidth v0.0.4 // indirect 12 | github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859 // indirect 13 | github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942 // indirect 14 | github.com/stretchr/testify v1.5.1 15 | go.uber.org/zap v1.15.0 16 | golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 17 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 18 | gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 19 | ) 20 | 21 | go 1.13 22 | -------------------------------------------------------------------------------- /os/crypto/sha3.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "github.com/libonomy/wallet-cli/os/common" 5 | "github.com/libonomy/wallet-cli/os/crypto/sha3" 6 | ) 7 | 8 | // Sha256 is a SHA-3-256 (not sha-256) hasher. It returns a 32 bytes (256 bits) hash of data. 9 | // data: arbitrary length bytes slice 10 | func Sha256(data ...[]byte) []byte { 11 | d := sha3.New256() 12 | for _, b := range data { 13 | d.Write(b) 14 | } 15 | return d.Sum(nil) 16 | } 17 | 18 | // Keccak256Hash calculates and returns the Keccak256 hash of the input data, 19 | // converting it to an internal Hash data structure. 20 | func Keccak256Hash(data ...[]byte) (h common.Hash) { 21 | d := sha3.NewKeccak256() 22 | for _, b := range data { 23 | d.Write(b) 24 | } 25 | d.Sum(h[:0]) 26 | return h 27 | } 28 | 29 | // Keccak256 calculates and returns the Keccak256 hash of the input data. 30 | func Keccak256(data ...[]byte) []byte { 31 | d := sha3.NewKeccak256() 32 | for _, b := range data { 33 | d.Write(b) 34 | } 35 | return d.Sum(nil) 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Libonomy Wallet Package For Libo-Nodes 2 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 3 | 4 | The package is developed for the the developers use only which they can utilize when they start exploring the libosystem. 5 | 6 | ## Functionality 7 | The wallet implements the 8 | - Account Creation 9 | - Account Details 10 | - Message Signature 11 | - Hex signature with pvt_key 12 | - Text signature using pvt_key 13 | - Account Reuse 14 | 15 | other functionalities will be released soon 16 | 17 | ## Guideline 18 | Make sure that GO is installed and gopath is set properly 19 | It is recommended that after you clone the repo , you should keep it out of the gopath. 20 | ### Build for linux: 21 | 22 | 23 | ```bash 24 | make build-linux 25 | ``` 26 | 27 | ## Using with a local node 28 | 29 | 1. Make sure that you have configured the local node properly and the url to local server is properly set. 30 | 31 | 2. Build the wallet using the : 32 | 33 | ```bash 34 | ./cli_wallet_linux_amd64 35 | ``` 36 | -------------------------------------------------------------------------------- /os/common/big.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package common 18 | 19 | import "math/big" 20 | 21 | // Common big integers often used 22 | var ( 23 | Big1 = big.NewInt(1) 24 | Big2 = big.NewInt(2) 25 | Big3 = big.NewInt(3) 26 | Big0 = big.NewInt(0) 27 | Big32 = big.NewInt(32) 28 | Big256 = big.NewInt(256) 29 | Big257 = big.NewInt(257) 30 | ) 31 | -------------------------------------------------------------------------------- /crypto/scrypt.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "golang.org/x/crypto/scrypt" 7 | ) 8 | 9 | // KDParams defines key derivation scheme params. 10 | type KDParams struct { 11 | N int `json:"n"` 12 | R int `json:"r"` 13 | P int `json:"p"` 14 | SaltLen int `json:"saltLen"` 15 | DKLen int `json:"dkLen"` 16 | Salt string `json:"salt"` // hex encoded 17 | } 18 | 19 | // DefaultCypherParams used for key derivation by the app. 20 | var DefaultCypherParams = KDParams{N: 262144, R: 8, P: 1, SaltLen: 16, DKLen: 32} 21 | 22 | // DeriveKeyFromPassword derives a key from password using the provided KDParams params. 23 | func DeriveKeyFromPassword(password string, p KDParams) ([]byte, error) { 24 | 25 | if len(p.Salt) == 0 { 26 | return nil, errors.New("invalid salt length param") 27 | } 28 | 29 | salt, err := hex.DecodeString(p.Salt) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | if len(salt) != p.SaltLen { 35 | return nil, errors.New("missing salt") 36 | } 37 | 38 | dkData, err := scrypt.Key([]byte(password), salt, p.N, p.R, p.P, p.DKLen) 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | return dkData, nil 44 | } 45 | -------------------------------------------------------------------------------- /os/crypto/scrypt.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "golang.org/x/crypto/scrypt" 7 | ) 8 | 9 | // KDParams defines key derivation scheme params. 10 | type KDParams struct { 11 | N int `json:"n"` 12 | R int `json:"r"` 13 | P int `json:"p"` 14 | SaltLen int `json:"saltLen"` 15 | DKLen int `json:"dkLen"` 16 | Salt string `json:"salt"` // hex encoded 17 | } 18 | 19 | // DefaultCypherParams used for key derivation by the app. 20 | var DefaultCypherParams = KDParams{N: 262144, R: 8, P: 1, SaltLen: 16, DKLen: 32} 21 | 22 | // DeriveKeyFromPassword derives a key from password using the provided KDParams params. 23 | func DeriveKeyFromPassword(password string, p KDParams) ([]byte, error) { 24 | 25 | if len(p.Salt) == 0 { 26 | return nil, errors.New("invalid salt length param") 27 | } 28 | 29 | salt, err := hex.DecodeString(p.Salt) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | if len(salt) != p.SaltLen { 35 | return nil, errors.New("missing salt") 36 | } 37 | 38 | dkData, err := scrypt.Key([]byte(password), salt, p.N, p.R, p.P, p.DKLen) 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | return dkData, nil 44 | } 45 | -------------------------------------------------------------------------------- /accounts/account.go: -------------------------------------------------------------------------------- 1 | package accounts 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | 7 | "github.com/libonomy/ed25519" 8 | "github.com/libonomy/wallet-cli/wallet/address" 9 | ) 10 | 11 | type Account struct { 12 | Name string 13 | PrivKey ed25519.PrivateKey // the pub & private key 14 | PubKey ed25519.PublicKey // only the pub key part 15 | } 16 | 17 | func (a *Account) Address() address.Address { 18 | return address.BytesToAddress(a.PubKey[:]) 19 | } 20 | 21 | func StringAddress(addr address.Address) string { 22 | return fmt.Sprintf("0x%s", hex.EncodeToString(addr.Bytes())) 23 | } 24 | 25 | type AccountInfo struct { 26 | Nonce string 27 | Balance string 28 | } 29 | 30 | func (s Store) GetAccount(name string) (*Account, error) { 31 | if acc, ok := s[name]; ok { 32 | priv, err := hex.DecodeString(acc.PrivKey) 33 | if err != nil { 34 | return nil, err 35 | } 36 | pub, err := hex.DecodeString(acc.PubKey) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | return &Account{name, priv, pub}, nil 42 | } 43 | return nil, fmt.Errorf("account not found") 44 | } 45 | 46 | func (s Store) ListAccounts() []string { 47 | lst := make([]string, 0, len(s)) 48 | for key := range s { 49 | lst = append(lst, key) 50 | } 51 | return lst 52 | } 53 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | //wallet-cli is a golang implementation of the libonomy node. 2 | //See - https://libonomy.io 3 | package main 4 | 5 | import ( 6 | "flag" 7 | "os" 8 | "syscall" 9 | 10 | "github.com/libonomy/wallet-cli/client" 11 | "github.com/libonomy/wallet-cli/repl" 12 | "github.com/libonomy/wallet-cli/wallet/accounts" 13 | ) 14 | 15 | type mockClient struct { 16 | } 17 | 18 | func (m mockClient) LocalAccount() *accounts.Account { 19 | return nil 20 | } 21 | 22 | func (m mockClient) AccountInfo(id string) { 23 | 24 | } 25 | func (m mockClient) Transfer(from, to, amount, passphrase string) error { 26 | return nil 27 | } 28 | 29 | func main() { 30 | serverHostPort := client.DefaultNodeHostPort 31 | datadir := Getwd() 32 | 33 | flag.StringVar(&serverHostPort, "server", serverHostPort, "host:port of the libonomy node HTTP server") 34 | flag.StringVar(&datadir, "datadir", datadir, "The directory to store the wallet data within") 35 | flag.Parse() 36 | 37 | _, err := syscall.Open("/dev/tty", syscall.O_RDONLY, 0) 38 | if err != nil { 39 | return 40 | } 41 | be, err := client.NewWalletBE(serverHostPort, datadir) 42 | if err != nil { 43 | return 44 | } 45 | repl.Start(be) 46 | } 47 | 48 | func Getwd() string { 49 | pwd, err := os.Getwd() 50 | if err != nil { 51 | panic(err) 52 | } 53 | return pwd 54 | } 55 | -------------------------------------------------------------------------------- /os/crypto/sha3/xor_unaligned.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build amd64 386 ppc64le 6 | // +build !appengine 7 | 8 | package sha3 9 | 10 | import "unsafe" 11 | 12 | func xorInUnaligned(d *state, buf []byte) { 13 | bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0])) 14 | n := len(buf) 15 | if n >= 72 { 16 | d.a[0] ^= bw[0] 17 | d.a[1] ^= bw[1] 18 | d.a[2] ^= bw[2] 19 | d.a[3] ^= bw[3] 20 | d.a[4] ^= bw[4] 21 | d.a[5] ^= bw[5] 22 | d.a[6] ^= bw[6] 23 | d.a[7] ^= bw[7] 24 | d.a[8] ^= bw[8] 25 | } 26 | if n >= 104 { 27 | d.a[9] ^= bw[9] 28 | d.a[10] ^= bw[10] 29 | d.a[11] ^= bw[11] 30 | d.a[12] ^= bw[12] 31 | } 32 | if n >= 136 { 33 | d.a[13] ^= bw[13] 34 | d.a[14] ^= bw[14] 35 | d.a[15] ^= bw[15] 36 | d.a[16] ^= bw[16] 37 | } 38 | if n >= 144 { 39 | d.a[17] ^= bw[17] 40 | } 41 | if n >= 168 { 42 | d.a[18] ^= bw[18] 43 | d.a[19] ^= bw[19] 44 | d.a[20] ^= bw[20] 45 | } 46 | } 47 | 48 | func copyOutUnaligned(d *state, buf []byte) { 49 | ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0])) 50 | copy(buf, ab[:]) 51 | } 52 | 53 | var ( 54 | xorIn = xorInUnaligned 55 | copyOut = copyOutUnaligned 56 | ) 57 | 58 | const xorImplementationUnaligned = "unaligned" 59 | -------------------------------------------------------------------------------- /os/crypto/sha3/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /os/crypto/randg_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "testing" 7 | "unsafe" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestGetRandomBytesToBuffer(t *testing.T) { 13 | n := 32 14 | tb := make([]byte, n) 15 | eb := make([]byte, n) 16 | // pass in a zero 17 | err := GetRandomBytesToBuffer(0, tb) 18 | assert.Error(t, err, "zero parameter should give error") 19 | // deliberately too large n parameter 20 | err = GetRandomBytesToBuffer(n+1, tb) 21 | assert.Error(t, err, "too large parameter should give error") 22 | // call with good parameters 23 | err = GetRandomBytesToBuffer(n, tb) 24 | assert.Equal(t, err, nil, fmt.Sprintf("unexpected error %v", err)) 25 | assert.False(t, bytes.Equal(tb, eb), "null data from GetRandomBytes") 26 | 27 | } 28 | 29 | func TestGetRandomBytes(t *testing.T) { 30 | n := 32 31 | eb := make([]byte, n) 32 | // pass in a zero 33 | tb, err := GetRandomBytes(0) 34 | assert.Error(t, err, "zero parameter should give error") 35 | // call with good parameters 36 | tb, err = GetRandomBytes(n) 37 | assert.Equal(t, err, nil, fmt.Sprintf("unexpected error %v", err)) 38 | assert.False(t, bytes.Equal(tb, eb), "null data from GetRandomBytes") 39 | } 40 | 41 | func TestGetRandomUInt32(t *testing.T) { 42 | var max = uint32(16384) 43 | x := GetRandomUInt32(max) 44 | assert.True(t, unsafe.Sizeof(x) == 4, "unexpected GetRandomUInt32 failure") 45 | } 46 | -------------------------------------------------------------------------------- /os/crypto/randg.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/binary" 6 | "errors" 7 | 8 | "github.com/libonomy/wallet-cli/os/log" 9 | ) 10 | 11 | // GetRandomBytesToBuffer puts n random bytes using go crypto.rand into provided buff slice. 12 | // buff: a slice allocated by called to hold n bytes. 13 | func GetRandomBytesToBuffer(n int, buff []byte) error { 14 | 15 | if n == 0 { 16 | return errors.New("invalid input param - n must be positive") 17 | } 18 | 19 | if len(buff) < n { 20 | return errors.New("invalid input param - buff must be allocated to hold n items") 21 | } 22 | 23 | _, err := rand.Read(buff) 24 | 25 | if err != nil { 26 | return err 27 | } 28 | 29 | return nil 30 | } 31 | 32 | // GetRandomBytes returns n random bytes. It returns an error if the system's pgn fails. 33 | func GetRandomBytes(n int) ([]byte, error) { 34 | 35 | if n == 0 { 36 | return nil, errors.New("invalid input param - n must be positive") 37 | } 38 | 39 | b := make([]byte, n) 40 | _, err := rand.Read(b) 41 | 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | return b, nil 47 | } 48 | 49 | // GetRandomUInt32 returns a uint32 in the range [0 - max). 50 | func GetRandomUInt32(max uint32) uint32 { 51 | 52 | b := make([]byte, 4) 53 | _, err := rand.Read(b) 54 | 55 | if err != nil { 56 | log.Panic("Failed to get entropy from system: ", err) 57 | } 58 | 59 | data := binary.BigEndian.Uint32(b) 60 | return data % max 61 | } 62 | -------------------------------------------------------------------------------- /crypto/sha3_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestSha256(t *testing.T) { 12 | 13 | // test vectors keys and values MUST be valid hex strings 14 | // source: https://www.di-mgt.com.au/sha_testvectors.html#testvectors 15 | testVectors := map[string]string{ 16 | 17 | "": "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", 18 | "616263": "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", 19 | "6162636462636465636465666465666765666768666768696768696A68696A6B696A6B6C6A6B6C6D6B6C6D6E6C6D6E6F6D6E6F706E6F7071": "41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376", 20 | "61626364656667686263646566676869636465666768696a6465666768696a6b65666768696a6b6c666768696a6b6c6d6768696a6b6c6d6e68696a6b6c6d6e6f696a6b6c6d6e6f706a6b6c6d6e6f70716b6c6d6e6f7071726c6d6e6f707172736d6e6f70717273746e6f707172737475": "916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18", 21 | } 22 | 23 | for k, v := range testVectors { 24 | data, err := hex.DecodeString(k) 25 | assert.NoError(t, err, "invalid input data") 26 | result := Sha256(data) 27 | expected, err := hex.DecodeString(v) 28 | assert.NoError(t, err, "invalid hex string %s", v) 29 | assert.True(t, bytes.Equal(result, expected), "unexpected result") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /os/crypto/sha3_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestSha256(t *testing.T) { 12 | 13 | // test vectors keys and values MUST be valid hex strings 14 | // source: https://www.di-mgt.com.au/sha_testvectors.html#testvectors 15 | testVectors := map[string]string{ 16 | 17 | "": "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", 18 | "616263": "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", 19 | "6162636462636465636465666465666765666768666768696768696A68696A6B696A6B6C6A6B6C6D6B6C6D6E6C6D6E6F6D6E6F706E6F7071": "41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376", 20 | "61626364656667686263646566676869636465666768696a6465666768696a6b65666768696a6b6c666768696a6b6c6d6768696a6b6c6d6e68696a6b6c6d6e6f696a6b6c6d6e6f706a6b6c6d6e6f70716b6c6d6e6f7071726c6d6e6f707172736d6e6f70717273746e6f707172737475": "916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18", 21 | } 22 | 23 | for k, v := range testVectors { 24 | data, err := hex.DecodeString(k) 25 | assert.NoError(t, err, "invalid input data") 26 | result := Sha256(data) 27 | expected, err := hex.DecodeString(v) 28 | assert.NoError(t, err, "invalid hex string %s", v) 29 | assert.True(t, bytes.Equal(result, expected), "unexpected result") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /os/crypto/sha3/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /accounts/store.go: -------------------------------------------------------------------------------- 1 | package accounts 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/hex" 6 | "encoding/json" 7 | "fmt" 8 | "os" 9 | 10 | "github.com/libonomy/ed25519" 11 | "github.com/libonomy/wallet-cli/os/log" 12 | ) 13 | 14 | type AccountKeys struct { 15 | PubKey string `json:"pubkey"` 16 | PrivKey string `json:"privkey"` 17 | } 18 | 19 | type Store map[string]AccountKeys 20 | 21 | func StoreAccounts(path string, store *Store) error { 22 | w, err := os.Create(path) 23 | if err != nil { 24 | return err 25 | } 26 | enc := json.NewEncoder(w) 27 | defer w.Close() 28 | if err := enc.Encode(store); err != nil { 29 | return err 30 | } 31 | return nil 32 | } 33 | 34 | func LoadAccounts(path string) (*Store, error) { 35 | _, err := os.Stat(path) 36 | if os.IsNotExist(err) { 37 | log.Warning("genesis config not lodad since file does not exist. file=%v", path) 38 | return nil, err 39 | } 40 | r, err := os.Open(path) 41 | if err != nil { 42 | return nil, fmt.Errorf("error opening file: %v", err) 43 | } 44 | defer r.Close() 45 | 46 | dec := json.NewDecoder(r) 47 | cfg := &Store{} 48 | err = dec.Decode(cfg) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | return cfg, nil 54 | } 55 | 56 | func (s Store) CreateAccount(alias string) *Account { 57 | sPub, key, err := ed25519.GenerateKey(rand.Reader) 58 | if err != nil { 59 | log.Error("cannot create account: %s", err) 60 | return nil 61 | } 62 | acc := &Account{Name: alias, PubKey: sPub, PrivKey: key} 63 | s[alias] = AccountKeys{PubKey: hex.EncodeToString(sPub), PrivKey: hex.EncodeToString(key)} 64 | return acc 65 | } 66 | -------------------------------------------------------------------------------- /crypto/randg_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "testing" 7 | "unsafe" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestGetRandomBytesToBuffer(t *testing.T) { 13 | n := 32 14 | tb := make([]byte, n) 15 | eb := make([]byte, n) 16 | // pass in a zero 17 | err := GetRandomBytesToBuffer(0, tb) 18 | assert.Error(t, err, "zero parameter should give error") 19 | // deliberately too large n parameter 20 | err = GetRandomBytesToBuffer(n+1, tb) 21 | assert.Error(t, err, "too large parameter should give error") 22 | // call with good parameters 23 | err = GetRandomBytesToBuffer(n, tb) 24 | assert.Equal(t, err, nil, fmt.Sprintf("unexpected error %v", err)) 25 | assert.False(t, bytes.Equal(tb, eb), "null data from GetRandomBytes") 26 | 27 | } 28 | 29 | func TestGetRandomBytes(t *testing.T) { 30 | n := 32 31 | eb := make([]byte, n) 32 | // pass in a zero 33 | tb, err := GetRandomBytes(0) 34 | assert.Error(t, err, "zero parameter should give error") 35 | // call with good parameters 36 | tb, err = GetRandomBytes(n) 37 | assert.Equal(t, err, nil, fmt.Sprintf("unexpected error %v", err)) 38 | assert.False(t, bytes.Equal(tb, eb), "null data from GetRandomBytes") 39 | } 40 | 41 | func TestRandomUserPort(t *testing.T) { 42 | x := GetRandomUserPort() 43 | assert.True(t, x > 1023, "unexpected small value") 44 | assert.True(t, x < 49151, "unexpected large value") 45 | } 46 | 47 | func TestGetRandomUInt32(t *testing.T) { 48 | var max = uint32(16384) 49 | x := GetRandomUInt32(max) 50 | assert.True(t, unsafe.Sizeof(x) == 4, "unexpected GetRandomUInt32 failure") 51 | } 52 | -------------------------------------------------------------------------------- /crypto/scrypt_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "fmt" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestDeriveKey(t *testing.T) { 13 | // declared in scrypt.go 14 | var dead = DefaultCypherParams 15 | var good = DefaultCypherParams 16 | const pass = "beagles" 17 | 18 | //test for hex decode error 19 | dead.Salt = "IJKL" 20 | data, err := DeriveKeyFromPassword(pass, dead) 21 | assert.True(t, bytes.Equal(data, []byte("")), fmt.Sprintf("hex decode error should give nil but gave %v", data)) 22 | assert.Error(t, err, fmt.Sprint("hex decode should give error")) 23 | 24 | dead.SaltLen = 4 25 | dead.Salt = "ABCD" 26 | // force error in scrypt.Key 27 | // N must be a power of two so this should give error 28 | dead.N = 3 29 | data, err = DeriveKeyFromPassword(pass, dead) 30 | assert.True(t, bytes.Equal(data, []byte("")), fmt.Sprintf("scrypt.Key error should give [] but gave %v", data)) 31 | assert.Error(t, err, fmt.Sprint("scrypt.Key should give error")) 32 | 33 | // test derivation without a valid set salt 34 | data, err = DeriveKeyFromPassword(pass, good) 35 | assert.Error(t, err, "expected no salt error") 36 | 37 | s, err := GetRandomBytes(good.SaltLen) 38 | assert.NoError(t, err, "failed to generate salt") 39 | good.Salt = hex.EncodeToString(s) 40 | 41 | // try good parameters and get valid result 42 | data, err = DeriveKeyFromPassword(pass, good) 43 | assert.Nil(t, err, fmt.Sprintf("scrypt good output error %v", err)) 44 | assert.True(t, len(data) == good.DKLen, fmt.Sprintf("return value size %d, %d expected", len(data), good.DKLen)) 45 | 46 | } 47 | -------------------------------------------------------------------------------- /os/common/prque/prque.go: -------------------------------------------------------------------------------- 1 | // This is a duplicated and slightly modified version of "gopkg.in/karalabe/cookiejar.v2/collections/prque". 2 | 3 | package prque 4 | 5 | import ( 6 | "container/heap" 7 | ) 8 | 9 | // Priority queue data structure. 10 | type Prque struct { 11 | cont *sstack 12 | } 13 | 14 | // Creates a new priority queue. 15 | func New(setIndex setIndexCallback) *Prque { 16 | return &Prque{newSstack(setIndex)} 17 | } 18 | 19 | // Pushes a value with a given priority into the queue, expanding if necessary. 20 | func (p *Prque) Push(data interface{}, priority int64) { 21 | heap.Push(p.cont, &item{data, priority}) 22 | } 23 | 24 | // Pops the value with the greates priority off the stack and returns it. 25 | // Currently no shrinking is done. 26 | func (p *Prque) Pop() (interface{}, int64) { 27 | item := heap.Pop(p.cont).(*item) 28 | return item.value, item.priority 29 | } 30 | 31 | // Pops only the item from the queue, dropping the associated priority value. 32 | func (p *Prque) PopItem() interface{} { 33 | return heap.Pop(p.cont).(*item).value 34 | } 35 | 36 | // Remove removes the element with the given index. 37 | func (p *Prque) Remove(i int) interface{} { 38 | if i < 0 { 39 | return nil 40 | } 41 | return heap.Remove(p.cont, i) 42 | } 43 | 44 | // Checks whether the priority queue is empty. 45 | func (p *Prque) Empty() bool { 46 | return p.cont.Len() == 0 47 | } 48 | 49 | // Returns the number of element in the priority queue. 50 | func (p *Prque) Size() int { 51 | return p.cont.Len() 52 | } 53 | 54 | // Clears the contents of the priority queue. 55 | func (p *Prque) Reset() { 56 | *p = *New(p.cont.setIndex) 57 | } 58 | -------------------------------------------------------------------------------- /os/crypto/scrypt_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "fmt" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestDeriveKey(t *testing.T) { 13 | // declared in scrypt.go 14 | var dead = DefaultCypherParams 15 | var good = DefaultCypherParams 16 | const pass = "beagles" 17 | 18 | //test for hex decode error 19 | dead.Salt = "IJKL" 20 | data, err := DeriveKeyFromPassword(pass, dead) 21 | assert.True(t, bytes.Equal(data, []byte("")), fmt.Sprintf("hex decode error should give nil but gave %v", data)) 22 | assert.Error(t, err, fmt.Sprint("hex decode should give error")) 23 | 24 | dead.SaltLen = 4 25 | dead.Salt = "ABCD" 26 | // force error in scrypt.Key 27 | // N must be a power of two so this should give error 28 | dead.N = 3 29 | data, err = DeriveKeyFromPassword(pass, dead) 30 | assert.True(t, bytes.Equal(data, []byte("")), fmt.Sprintf("scrypt.Key error should give [] but gave %v", data)) 31 | assert.Error(t, err, fmt.Sprint("scrypt.Key should give error")) 32 | 33 | // test derivation without a valid set salt 34 | data, err = DeriveKeyFromPassword(pass, good) 35 | assert.Error(t, err, "expected no salt error") 36 | 37 | s, err := GetRandomBytes(good.SaltLen) 38 | assert.NoError(t, err, "failed to generate salt") 39 | good.Salt = hex.EncodeToString(s) 40 | 41 | // try good parameters and get valid result 42 | data, err = DeriveKeyFromPassword(pass, good) 43 | assert.Nil(t, err, fmt.Sprintf("scrypt good output error %v", err)) 44 | assert.True(t, len(data) == good.DKLen, fmt.Sprintf("return value size %d, %d expected", len(data), good.DKLen)) 45 | 46 | } 47 | -------------------------------------------------------------------------------- /os/common/hexutil/json_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package hexutil_test 18 | 19 | import ( 20 | "encoding/json" 21 | "fmt" 22 | 23 | "github.com/libonomy/wallet-cli/os/common/hexutil" 24 | ) 25 | 26 | type MyType [5]byte 27 | 28 | func (v *MyType) UnmarshalText(input []byte) error { 29 | return hexutil.UnmarshalFixedText("MyType", input, v[:]) 30 | } 31 | 32 | func (v MyType) String() string { 33 | return hexutil.Bytes(v[:]).String() 34 | } 35 | 36 | func ExampleUnmarshalFixedText() { 37 | var v1, v2 MyType 38 | fmt.Println("v1 error:", json.Unmarshal([]byte(`"0x01"`), &v1)) 39 | fmt.Println("v2 error:", json.Unmarshal([]byte(`"0x0101010101"`), &v2)) 40 | fmt.Println("v2:", v2) 41 | // Output: 42 | // v1 error: hex string has length 2, want 10 for MyType 43 | // v2 error: 44 | // v2: 0x0101010101 45 | } 46 | -------------------------------------------------------------------------------- /crypto/randg.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/binary" 6 | "errors" 7 | 8 | "github.com/libonomy/wallet-cli/os/log" 9 | ) 10 | 11 | // GetRandomBytesToBuffer puts n random bytes using go crypto.rand into provided buff slice. 12 | // buff: a slice allocated by called to hold n bytes. 13 | func GetRandomBytesToBuffer(n int, buff []byte) error { 14 | 15 | if n == 0 { 16 | return errors.New("invalid input param - n must be positive") 17 | } 18 | 19 | if len(buff) < n { 20 | return errors.New("invalid input param - buff must be allocated to hold n items") 21 | } 22 | 23 | _, err := rand.Read(buff) 24 | 25 | if err != nil { 26 | return err 27 | } 28 | 29 | return nil 30 | } 31 | 32 | // GetRandomBytes returns n random bytes. It returns an error if the system's pgn fails. 33 | func GetRandomBytes(n int) ([]byte, error) { 34 | 35 | if n == 0 { 36 | return nil, errors.New("invalid input param - n must be positive") 37 | } 38 | 39 | b := make([]byte, n) 40 | _, err := rand.Read(b) 41 | 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | return b, nil 47 | } 48 | 49 | // GetRandomUserPort returns a random port number in the range of [1024-49151). 50 | func GetRandomUserPort() uint32 { 51 | return GetRandomUInt32(48127) + 1024 52 | } 53 | 54 | // GetRandomUInt32 returns a uint32 in the range [0 - max). 55 | func GetRandomUInt32(max uint32) uint32 { 56 | 57 | b := make([]byte, 4) 58 | _, err := rand.Read(b) 59 | 60 | if err != nil { 61 | log.Error("Failed to get entropy from system", err) 62 | panic(err) 63 | } 64 | 65 | data := binary.BigEndian.Uint32(b) 66 | return data % max 67 | } 68 | -------------------------------------------------------------------------------- /os/common/size.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package common 18 | 19 | import ( 20 | "fmt" 21 | ) 22 | 23 | // StorageSize is a wrapper around a float value that supports user friendly 24 | // formatting. 25 | type StorageSize float64 26 | 27 | // String implements the stringer interface. 28 | func (s StorageSize) String() string { 29 | if s > 1000000 { 30 | return fmt.Sprintf("%.2f mB", s/1000000) 31 | } else if s > 1000 { 32 | return fmt.Sprintf("%.2f kB", s/1000) 33 | } else { 34 | return fmt.Sprintf("%.2f B", s) 35 | } 36 | } 37 | 38 | // TerminalString implements log.TerminalStringer, formatting a string for console 39 | // output during logging. 40 | func (s StorageSize) TerminalString() string { 41 | if s > 1000000 { 42 | return fmt.Sprintf("%.2fmB", s/1000000) 43 | } else if s > 1000 { 44 | return fmt.Sprintf("%.2fkB", s/1000) 45 | } else { 46 | return fmt.Sprintf("%.2fB", s) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /repl/messages.go: -------------------------------------------------------------------------------- 1 | package repl 2 | 3 | const ( 4 | welcomeMsg = "Kindly Create Your Wallet !" 5 | generateMsg = "Generate account passphrase? (y/n) " 6 | accountInfoMsg = "Add account info (enter text or ENTER):" 7 | accountNotFoundoMsg = "Local account not found. Create one? (y/n) " 8 | initialTransferMsg = "Transfer coins from local account to another account." 9 | transferFromLocalAccountMsg = "Transfer from local account %s ? (y/n) " 10 | transferFromAccountMsg = "Enter or paste account id: " 11 | destAddressMsg = "Enter or paste destination address: " 12 | amountToTransferMsg = "Enter amount to transfer in Smidge (SMD): " 13 | accountPassphrase = "Enter local account passphrase: " 14 | confirmTransactionMsg = "Confirm transaction (y/n): " 15 | newFlagsAndParamsMsg = "provide CLI flags and params or press ENTER for none: " 16 | userExecutingCommandMsg = "User executing command: %s" 17 | requiresSetupMsg = "libonomy requires a minimum of 300GB of free disk space. 250GB are used for POST and 50GB are reserved for the global computer state. You may allocate additional disk space for POST in 300GB increments. " 18 | restartNodeMsg = "Restart node?" 19 | createAccountMsg = "Account alias (name): " 20 | useDefaultGasMsg = "Use default gas price (1 Smidge)? (y/n) " 21 | enterGasPrice = "Enter transaction gas price:" 22 | getAccountInfoMsg = "Enter account id to query" 23 | libonomyDatadirMsg = "Enter data file directory: " 24 | libonomySpaceAllocationMsg = "Enter space allocation (GB): " 25 | msgSignMsg = "Enter message to sign (in hex): " 26 | msgTextSignMsg = "Enter text message to sign: " 27 | ) 28 | -------------------------------------------------------------------------------- /repl/mock.go: -------------------------------------------------------------------------------- 1 | package repl 2 | 3 | import "github.com/libonomy/wallet-cli/wallet/accounts" 4 | 5 | // Mock struct created temporarily while node 6 | // doesn't implement the methods. 7 | type Mock struct{} 8 | 9 | // CreateAccount creates accountInfo. 10 | func (Mock) CreateAccount(generatePassphrase bool, accountInfo string) error { 11 | return nil 12 | } 13 | 14 | // CurrentAccount returns local accountInfo. 15 | func (Mock) LocalAccount() *accounts.Account { 16 | acct, err := accounts.NewAccount("") 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | return acct 22 | } 23 | 24 | // Unlock unlock local accountInfo or the accountInfo by passphrase. 25 | func (Mock) Unlock(passphrase string) error { 26 | return nil 27 | } 28 | 29 | // IsAccountUnLock checks if the accountInfo with id is unlock. 30 | func (Mock) IsAccountUnLock(id string) bool { 31 | return false 32 | } 33 | 34 | // Lock locks local accountInfo or the accountInfo by passphrase. 35 | func (Mock) Lock(passphrase string) error { 36 | return nil 37 | } 38 | 39 | // AccountInfo prints accountInfo info. 40 | func (Mock) AccountInfo(id string) { 41 | } 42 | 43 | // Transfer transfers the amount from an accountInfo to the other. 44 | func (Mock) Transfer(from, to, amount, passphrase string) error { 45 | return nil 46 | } 47 | 48 | // SetVariables sets params or CLI flags values. 49 | func (Mock) SetVariables(params, flags []string) error { 50 | return nil 51 | } 52 | 53 | // GetVariable gets variable in node by key 54 | func (Mock) GetVariable(key string) string { 55 | if key == "Mock" { 56 | return "mock" 57 | } 58 | 59 | return "" 60 | } 61 | 62 | // NeedRestartNode checks if the params and flags that will be set need 63 | // restart the node. 64 | func (Mock) NeedRestartNode(params, flags []string) bool { 65 | return false 66 | } 67 | 68 | // Restart restarts node. 69 | func (Mock) Restart(params, flags []string) error { 70 | return nil 71 | } 72 | 73 | // Setup setup POST. 74 | func (Mock) Setup(allocation string) error { 75 | return nil 76 | } 77 | -------------------------------------------------------------------------------- /crypto/aes.go: -------------------------------------------------------------------------------- 1 | // Package crypto provides funcs and types used by other packages to perform crypto related ops 2 | package crypto 3 | 4 | import ( 5 | "bytes" 6 | "crypto/aes" 7 | "crypto/cipher" 8 | "errors" 9 | ) 10 | 11 | // AesCTRXOR is an AES cipher following https://leanpub.com/gocrypto/read#leanpub-auto-aes-cbc . 12 | func AesCTRXOR(key, input, nonce []byte) ([]byte, error) { 13 | aesBlock, err := aes.NewCipher(key) 14 | if err != nil { 15 | return nil, err 16 | } 17 | 18 | stream := cipher.NewCTR(aesBlock, nonce) 19 | output := make([]byte, len(input)) 20 | stream.XORKeyStream(output, input) 21 | return output, nil 22 | } 23 | 24 | // Pkcs7Pad returns input with padding using the pkcs7 padding spec. 25 | func Pkcs7Pad(in []byte) []byte { 26 | padding := 16 - (len(in) % 16) 27 | for i := 0; i < padding; i++ { 28 | in = append(in, byte(padding)) 29 | } 30 | return in 31 | } 32 | 33 | // Pkcs7Unpad returned in without padded data using pkcs7 padding spec. 34 | func Pkcs7Unpad(in []byte) []byte { 35 | if len(in) == 0 { 36 | return nil 37 | } 38 | 39 | padding := in[len(in)-1] 40 | if int(padding) > len(in) || padding > aes.BlockSize { 41 | return nil 42 | } else if padding == 0 { 43 | return nil 44 | } 45 | 46 | for i := len(in) - 1; i > len(in)-int(padding)-1; i-- { 47 | if in[i] != padding { 48 | return nil 49 | } 50 | } 51 | return in[:len(in)-int(padding)] 52 | } 53 | 54 | // AddPKCSPadding Adds padding to a block of data. 55 | func AddPKCSPadding(src []byte) []byte { 56 | padding := aes.BlockSize - len(src)%aes.BlockSize 57 | padtext := bytes.Repeat([]byte{byte(padding)}, padding) 58 | return append(src, padtext...) 59 | } 60 | 61 | // RemovePKCSPadding Removes padding from data that was added using AddPKCSPadding. 62 | func RemovePKCSPadding(src []byte) ([]byte, error) { 63 | length := len(src) 64 | padLength := int(src[length-1]) 65 | if padLength > aes.BlockSize || length < aes.BlockSize { 66 | return nil, errors.New("invalid PKCS#7 padding") 67 | } 68 | 69 | return src[:length-padLength], nil 70 | } 71 | -------------------------------------------------------------------------------- /os/crypto/aes.go: -------------------------------------------------------------------------------- 1 | // Package crypto provides funcs and types used by other packages to perform crypto related ops 2 | package crypto 3 | 4 | import ( 5 | "bytes" 6 | "crypto/aes" 7 | "crypto/cipher" 8 | "errors" 9 | ) 10 | 11 | // AesCTRXOR is an AES cipher following https://leanpub.com/gocrypto/read#leanpub-auto-aes-cbc . 12 | func AesCTRXOR(key, input, nonce []byte) ([]byte, error) { 13 | aesBlock, err := aes.NewCipher(key) 14 | if err != nil { 15 | return nil, err 16 | } 17 | 18 | stream := cipher.NewCTR(aesBlock, nonce) 19 | output := make([]byte, len(input)) 20 | stream.XORKeyStream(output, input) 21 | return output, nil 22 | } 23 | 24 | // Pkcs7Pad returns input with padding using the pkcs7 padding spec. 25 | func Pkcs7Pad(in []byte) []byte { 26 | padding := 16 - (len(in) % 16) 27 | for i := 0; i < padding; i++ { 28 | in = append(in, byte(padding)) 29 | } 30 | return in 31 | } 32 | 33 | // Pkcs7Unpad returned in without padded data using pkcs7 padding spec. 34 | func Pkcs7Unpad(in []byte) []byte { 35 | if len(in) == 0 { 36 | return nil 37 | } 38 | 39 | padding := in[len(in)-1] 40 | if int(padding) > len(in) || padding > aes.BlockSize { 41 | return nil 42 | } else if padding == 0 { 43 | return nil 44 | } 45 | 46 | for i := len(in) - 1; i > len(in)-int(padding)-1; i-- { 47 | if in[i] != padding { 48 | return nil 49 | } 50 | } 51 | return in[:len(in)-int(padding)] 52 | } 53 | 54 | // AddPKCSPadding Adds padding to a block of data. 55 | func AddPKCSPadding(src []byte) []byte { 56 | padding := aes.BlockSize - len(src)%aes.BlockSize 57 | padtext := bytes.Repeat([]byte{byte(padding)}, padding) 58 | return append(src, padtext...) 59 | } 60 | 61 | // RemovePKCSPadding Removes padding from data that was added using AddPKCSPadding. 62 | func RemovePKCSPadding(src []byte) ([]byte, error) { 63 | length := len(src) 64 | padLength := int(src[length-1]) 65 | if padLength > aes.BlockSize || length < aes.BlockSize { 66 | return nil, errors.New("invalid PKCS#7 padding") 67 | } 68 | 69 | return src[:length-padLength], nil 70 | } 71 | -------------------------------------------------------------------------------- /os/crypto/sha3/shake.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sha3 6 | 7 | // This file defines the ShakeHash interface, and provides 8 | // functions for creating SHAKE instances, as well as utility 9 | // functions for hashing bytes to arbitrary-length output. 10 | 11 | import ( 12 | "io" 13 | ) 14 | 15 | // ShakeHash defines the interface to hash functions that 16 | // support arbitrary-length output. 17 | type ShakeHash interface { 18 | // Write absorbs more data into the hash's state. It panics if input is 19 | // written to it after output has been read from it. 20 | io.Writer 21 | 22 | // Read reads more output from the hash; reading affects the hash's 23 | // state. (ShakeHash.Read is thus very different from Hash.Sum) 24 | // It never returns an error. 25 | io.Reader 26 | 27 | // Clone returns a copy of the ShakeHash in its current state. 28 | Clone() ShakeHash 29 | 30 | // Reset resets the ShakeHash to its initial state. 31 | Reset() 32 | } 33 | 34 | func (d *state) Clone() ShakeHash { 35 | return d.clone() 36 | } 37 | 38 | // NewShake128 creates a new SHAKE128 variable-output-length ShakeHash. 39 | // Its generic security strength is 128 bits against all attacks if at 40 | // least 32 bytes of its output are used. 41 | func NewShake128() ShakeHash { return &state{rate: 168, dsbyte: 0x1f} } 42 | 43 | // NewShake256 creates a new SHAKE128 variable-output-length ShakeHash. 44 | // Its generic security strength is 256 bits against all attacks if 45 | // at least 64 bytes of its output are used. 46 | func NewShake256() ShakeHash { return &state{rate: 136, dsbyte: 0x1f} } 47 | 48 | // ShakeSum128 writes an arbitrary-length digest of data into hash. 49 | func ShakeSum128(hash, data []byte) { 50 | h := NewShake128() 51 | h.Write(data) 52 | h.Read(hash) 53 | } 54 | 55 | // ShakeSum256 writes an arbitrary-length digest of data into hash. 56 | func ShakeSum256(hash, data []byte) { 57 | h := NewShake256() 58 | h.Write(data) 59 | h.Read(hash) 60 | } 61 | -------------------------------------------------------------------------------- /client/backend.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "path" 7 | 8 | xdr "github.com/davecgh/go-xdr/xdr2" 9 | "github.com/libonomy/ed25519" 10 | "github.com/libonomy/wallet-cli/accounts" 11 | "github.com/libonomy/wallet-cli/os/log" 12 | "github.com/libonomy/wallet-cli/wallet/address" 13 | ) 14 | 15 | const accountsFileName = "accounts.json" 16 | 17 | type WalletBE struct { 18 | *HTTPRequester 19 | accounts.Store 20 | accountsFilePath string 21 | currentAccount *accounts.Account 22 | } 23 | 24 | func NewWalletBE(serverHostPort, datadir string) (*WalletBE, error) { 25 | accountsFilePath := path.Join(datadir, accountsFileName) 26 | acc, err := accounts.LoadAccounts(accountsFilePath) 27 | if err != nil { 28 | log.Error("cannot load account from file %s: %s", accountsFilePath, err) 29 | acc = &accounts.Store{} 30 | } 31 | 32 | url := fmt.Sprintf("http://%s/v1", serverHostPort) 33 | return &WalletBE{NewHTTPRequester(url), *acc, accountsFilePath, nil}, nil 34 | } 35 | 36 | func (w *WalletBE) CurrentAccount() *accounts.Account { 37 | return w.currentAccount 38 | } 39 | 40 | func (w *WalletBE) SetCurrentAccount(a *accounts.Account) { 41 | w.currentAccount = a 42 | } 43 | 44 | func InterfaceToBytes(i interface{}) ([]byte, error) { 45 | var w bytes.Buffer 46 | if _, err := xdr.Marshal(&w, &i); err != nil { 47 | return nil, err 48 | } 49 | return w.Bytes(), nil 50 | } 51 | 52 | func (w *WalletBE) StoreAccounts() error { 53 | return accounts.StoreAccounts(w.accountsFilePath, &w.Store) 54 | } 55 | 56 | func (w *WalletBE) Transfer(recipient address.Address, nonce, amount, gasPrice, gasLimit uint64, key ed25519.PrivateKey) (string, error) { 57 | tx := SerializableSignedTransaction{} 58 | tx.AccountNonce = nonce 59 | tx.Amount = amount 60 | tx.Recipient = recipient 61 | tx.GasLimit = gasLimit 62 | tx.Price = gasPrice 63 | 64 | buf, _ := InterfaceToBytes(&tx.InnerSerializableSignedTransaction) 65 | copy(tx.Signature[:], ed25519.Sign2(key, buf)) 66 | b, err := InterfaceToBytes(&tx) 67 | if err != nil { 68 | return "", err 69 | } 70 | return w.HTTPRequester.Send(b) 71 | } 72 | -------------------------------------------------------------------------------- /os/crypto/sha3/hashes.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sha3 6 | 7 | // This file provides functions for creating instances of the SHA-3 8 | // and SHAKE hash functions, as well as utility functions for hashing 9 | // bytes. 10 | 11 | import ( 12 | "hash" 13 | ) 14 | 15 | // NewKeccak256 creates a new Keccak-256 hash. 16 | func NewKeccak256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x01} } 17 | 18 | // NewKeccak512 creates a new Keccak-512 hash. 19 | func NewKeccak512() hash.Hash { return &state{rate: 72, outputLen: 64, dsbyte: 0x01} } 20 | 21 | // New224 creates a new SHA3-224 hash. 22 | // Its generic security strength is 224 bits against preimage attacks, 23 | // and 112 bits against collision attacks. 24 | func New224() hash.Hash { return &state{rate: 144, outputLen: 28, dsbyte: 0x06} } 25 | 26 | // New256 creates a new SHA3-256 hash. 27 | // Its generic security strength is 256 bits against preimage attacks, 28 | // and 128 bits against collision attacks. 29 | func New256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x06} } 30 | 31 | // New384 creates a new SHA3-384 hash. 32 | // Its generic security strength is 384 bits against preimage attacks, 33 | // and 192 bits against collision attacks. 34 | func New384() hash.Hash { return &state{rate: 104, outputLen: 48, dsbyte: 0x06} } 35 | 36 | // New512 creates a new SHA3-512 hash. 37 | // Its generic security strength is 512 bits against preimage attacks, 38 | // and 256 bits against collision attacks. 39 | func New512() hash.Hash { return &state{rate: 72, outputLen: 64, dsbyte: 0x06} } 40 | 41 | // Sum224 returns the SHA3-224 digest of the data. 42 | func Sum224(data []byte) (digest [28]byte) { 43 | h := New224() 44 | h.Write(data) 45 | h.Sum(digest[:0]) 46 | return 47 | } 48 | 49 | // Sum256 returns the SHA3-256 digest of the data. 50 | func Sum256(data []byte) (digest [32]byte) { 51 | h := New256() 52 | h.Write(data) 53 | h.Sum(digest[:0]) 54 | return 55 | } 56 | 57 | // Sum384 returns the SHA3-384 digest of the data. 58 | func Sum384(data []byte) (digest [48]byte) { 59 | h := New384() 60 | h.Write(data) 61 | h.Sum(digest[:0]) 62 | return 63 | } 64 | 65 | // Sum512 returns the SHA3-512 digest of the data. 66 | func Sum512(data []byte) (digest [64]byte) { 67 | h := New512() 68 | h.Write(data) 69 | h.Sum(digest[:0]) 70 | return 71 | } 72 | -------------------------------------------------------------------------------- /repl/prompt.go: -------------------------------------------------------------------------------- 1 | package repl 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "syscall" 8 | 9 | "github.com/c-bata/go-prompt" 10 | ) 11 | 12 | var emptyComplete = func(prompt.Document) []prompt.Suggest { return []prompt.Suggest{} } 13 | 14 | func runPrompt(executor func(string), completer func(prompt.Document) []prompt.Suggest, 15 | firstTime func(), length uint16) { 16 | p := prompt.New( 17 | executor, 18 | completer, 19 | prompt.OptionPrefix(prefix), 20 | prompt.OptionPrefixTextColor(prompt.LightGray), 21 | prompt.OptionMaxSuggestion(length), 22 | prompt.OptionShowCompletionAtStart(), 23 | prompt.OptionAddKeyBind( 24 | prompt.KeyBind{prompt.ControlC, func(*prompt.Buffer){ 25 | _ = syscall.Kill(syscall.Getpid(), syscall.SIGINT) 26 | }}), 27 | ) 28 | firstTime() 29 | p.Run() 30 | } 31 | 32 | // executes prompt waiting for an input with y or n 33 | func yesOrNoQuestion(msg string) string { 34 | var input string 35 | for { 36 | input = prompt.Input(prefix+msg, 37 | emptyComplete, 38 | prompt.OptionPrefixTextColor(prompt.LightGray)) 39 | 40 | if input == "y" || input == "n" { 41 | break 42 | } 43 | 44 | fmt.Println(printPrefix, "invalid command.") 45 | } 46 | 47 | return input 48 | } 49 | 50 | func multipleChoice(names []string) string { 51 | var input string 52 | accounts := make(map[string]struct{}) 53 | for _, name := range names { 54 | accounts[name] = struct{}{} 55 | } 56 | if len(accounts) == 0 { 57 | return "" 58 | } 59 | for { 60 | for ac := range accounts { 61 | fmt.Println(printPrefix, ac) 62 | } 63 | input = prompt.Input(prefix, 64 | emptyComplete, 65 | prompt.OptionPrefixTextColor(prompt.LightGray)) 66 | 67 | if _, ok := accounts[input]; ok { 68 | return input 69 | } 70 | 71 | s := strings.TrimSpace(input) 72 | if s == "quit" || s == "exit" { 73 | fmt.Println("Bye!") 74 | os.Exit(0) 75 | return "" 76 | } 77 | 78 | fmt.Println(printPrefix, "invalid command.") 79 | 80 | } 81 | } 82 | 83 | // executes prompt waiting an input not blank 84 | func inputNotBlank(msg string) string { 85 | var input string 86 | for { 87 | input = prompt.Input(prefix+msg, 88 | emptyComplete, 89 | prompt.OptionPrefixTextColor(prompt.LightGray)) 90 | 91 | if strings.TrimSpace(input) != "" { 92 | break 93 | } 94 | 95 | fmt.Println(printPrefix, "please enter a value.") 96 | } 97 | 98 | return input 99 | } 100 | -------------------------------------------------------------------------------- /crypto/keys_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "fmt" 7 | "testing" 8 | 9 | "github.com/libonomy/wallet-cli/os/log" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestBasicApi(t *testing.T) { 14 | 15 | badData, _ := hex.DecodeString("1234") 16 | _, err := NewPublicKey(badData) 17 | assert.Error(t, err, "expected error for bad key data") 18 | 19 | _, err = NewPrivateKey(badData) 20 | assert.Error(t, err, "expected error for bad key data") 21 | 22 | _, err = NewPrivateKeyFromString("1234") 23 | assert.Error(t, err, "expected error for bad key data") 24 | 25 | priv, pub, err := GenerateKeyPair() 26 | 27 | assert.Nil(t, err, "failed to generate keys") 28 | log.Debug("priv: %s, pub: %s", priv.Pretty(), pub.Pretty()) 29 | log.Debug("priv: %s, pub: %s", priv.String(), pub.String()) 30 | 31 | pub1 := priv.GetPublicKey() 32 | assert.True(t, bytes.Equal(pub.Bytes(), pub1.Bytes()), fmt.Sprintf("expected same pub key, %s, %s", 33 | pub.String(), pub1.String())) 34 | 35 | // serialization tests 36 | priv1, err := NewPrivateKey(priv.Bytes()) 37 | assert.NoError(t, err, "unexpected error") 38 | assert.True(t, bytes.Equal(priv1.Bytes(), priv.Bytes()), fmt.Sprintf("expected same private key, %s, %s", 39 | priv1.String(), priv.String())) 40 | 41 | priv2, err := NewPrivateKeyFromString(priv.String()) 42 | assert.NoError(t, err, "unexpected error") 43 | assert.True(t, bytes.Equal(priv2.Bytes(), priv.Bytes()), fmt.Sprintf("expected same private key, %s, %s", 44 | priv2.String(), priv.String())) 45 | 46 | pub2, err := NewPublicKey(pub.Bytes()) 47 | assert.Nil(t, err, fmt.Sprintf("New pub key from bin error: %v", err)) 48 | 49 | assert.True(t, bytes.Equal(pub2.Bytes(), pub.Bytes()), fmt.Sprintf("expected same public key, %s, %s", 50 | pub2.String(), pub.String())) 51 | 52 | pub3, err := NewPublicKeyFromString(pub.String()) 53 | 54 | assert.Nil(t, err, fmt.Sprintf("New pub key from bin error: %v", err)) 55 | 56 | assert.True(t, bytes.Equal(pub3.Bytes(), pub.Bytes()), fmt.Sprintf("Expected same public key, %s, %s", 57 | pub3.String(), pub.String())) 58 | } 59 | 60 | func TestCryptoApi(t *testing.T) { 61 | 62 | priv, pub, err := GenerateKeyPair() 63 | 64 | assert.Nil(t, err, "Failed to generate keys") 65 | 66 | const msg = "hello world" 67 | msgData := []byte(msg) 68 | 69 | // test signatures 70 | signature, err := priv.Sign(msgData) 71 | 72 | assert.Nil(t, err, fmt.Sprintf("signing error: %v", err)) 73 | ok, err := pub.Verify(msgData, signature) 74 | assert.Nil(t, err, fmt.Sprintf("sign verification error: %v", err)) 75 | 76 | assert.True(t, ok, "Failed to verify signature") 77 | 78 | // test encrypting a message for pub by pub - anyone w pub can do that 79 | cypherText, err := pub.Encrypt(msgData) 80 | 81 | assert.Nil(t, err, fmt.Sprintf("enc error: %v", err)) 82 | 83 | // test decryption 84 | clearText, err := priv.Decrypt(cypherText) 85 | assert.Nil(t, err, fmt.Sprintf("dec error: %v", err)) 86 | 87 | assert.True(t, bytes.Equal(msgData, clearText), "expected same dec message") 88 | 89 | } 90 | -------------------------------------------------------------------------------- /os/p2p/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/libonomy/wallet-cli/os/log" 7 | ) 8 | 9 | // ConfigValues specifies default values for node config params. 10 | var ( 11 | ConfigValues = DefaultConfig() 12 | SwarmConfigValues = ConfigValues.SwarmConfig 13 | ) 14 | 15 | func init() { 16 | // set default config params based on runtime here 17 | } 18 | 19 | func duration(duration string) (dur time.Duration) { 20 | dur, err := time.ParseDuration(duration) 21 | if err != nil { 22 | log.Error("Could not parse duration string returning 0, error:", err) 23 | } 24 | return dur 25 | } 26 | 27 | // Config defines the configuration options for the libonomy peer-to-peer networking layer 28 | type Config struct { 29 | TCPPort int `mapstructure:"tcp-port"` 30 | NodeID string `mapstructure:"node-id"` 31 | NewNode bool `mapstructure:"new-node"` 32 | DialTimeout time.Duration `mapstructure:"dial-timeout"` 33 | ConnKeepAlive time.Duration `mapstructure:"conn-keepalive"` 34 | NetworkID int8 `mapstructure:"network-id"` 35 | ResponseTimeout time.Duration `mapstructure:"response-timeout"` 36 | SessionTimeout time.Duration `mapstructure:"session-timeout"` 37 | MaxPendingConnections int `mapstructure:"max-pending-connections"` 38 | OutboundPeersTarget int `mapstructure:"outbound-target"` 39 | MaxInboundPeers int `mapstructure:"max-inbound"` 40 | SwarmConfig SwarmConfig `mapstructure:"swarm"` 41 | BufferSize int `mapstructure:"buffer-size"` 42 | } 43 | 44 | // SwarmConfig specifies swarm config params. 45 | type SwarmConfig struct { 46 | Gossip bool `mapstructure:"gossip"` 47 | Bootstrap bool `mapstructure:"bootstrap"` 48 | RoutingTableBucketSize int `mapstructure:"bucketsize"` 49 | RoutingTableAlpha int `mapstructure:"alpha"` 50 | RandomConnections int `mapstructure:"randcon"` 51 | BootstrapNodes []string `mapstructure:"bootnodes"` 52 | PeersFile string `mapstructure:"peers-file"` 53 | } 54 | 55 | // DefaultConfig defines the default p2p configuration 56 | func DefaultConfig() Config { 57 | 58 | // SwarmConfigValues defines default values for swarm config params. 59 | var SwarmConfigValues = SwarmConfig{ 60 | Gossip: true, 61 | Bootstrap: false, 62 | RoutingTableBucketSize: 20, 63 | RoutingTableAlpha: 3, 64 | RandomConnections: 5, 65 | BootstrapNodes: []string{ // these should be the libonomy foundation bootstrap nodes 66 | }, 67 | PeersFile: "peers.json", // located under data-dir// not loaded or save if empty string is given. 68 | } 69 | 70 | return Config{ 71 | TCPPort: 7512, 72 | NodeID: "", 73 | NewNode: false, 74 | DialTimeout: duration("1m"), 75 | ConnKeepAlive: duration("48h"), 76 | NetworkID: TestNet, 77 | ResponseTimeout: duration("15s"), 78 | SessionTimeout: duration("5s"), 79 | MaxPendingConnections: 100, 80 | OutboundPeersTarget: 10, 81 | MaxInboundPeers: 100, 82 | SwarmConfig: SwarmConfigValues, 83 | BufferSize: 100, 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /os/crypto/sha3/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package sha3 implements the SHA-3 fixed-output-length hash functions and 6 | // the SHAKE variable-output-length hash functions defined by FIPS-202. 7 | // 8 | // Both types of hash function use the "sponge" construction and the Keccak 9 | // permutation. For a detailed specification see http://keccak.noekeon.org/ 10 | // 11 | // 12 | // Guidance 13 | // 14 | // If you aren't sure what function you need, use SHAKE256 with at least 64 15 | // bytes of output. The SHAKE instances are faster than the SHA3 instances; 16 | // the latter have to allocate memory to conform to the hash.Hash interface. 17 | // 18 | // If you need a secret-key MAC (message authentication code), prepend the 19 | // secret key to the input, hash with SHAKE256 and read at least 32 bytes of 20 | // output. 21 | // 22 | // 23 | // Security strengths 24 | // 25 | // The SHA3-x (x equals 224, 256, 384, or 512) functions have a security 26 | // strength against preimage attacks of x bits. Since they only produce "x" 27 | // bits of output, their collision-resistance is only "x/2" bits. 28 | // 29 | // The SHAKE-256 and -128 functions have a generic security strength of 256 and 30 | // 128 bits against all attacks, provided that at least 2x bits of their output 31 | // is used. Requesting more than 64 or 32 bytes of output, respectively, does 32 | // not increase the collision-resistance of the SHAKE functions. 33 | // 34 | // 35 | // The sponge construction 36 | // 37 | // A sponge builds a pseudo-random function from a public pseudo-random 38 | // permutation, by applying the permutation to a state of "rate + capacity" 39 | // bytes, but hiding "capacity" of the bytes. 40 | // 41 | // A sponge starts out with a zero state. To hash an input using a sponge, up 42 | // to "rate" bytes of the input are XORed into the sponge's state. The sponge 43 | // is then "full" and the permutation is applied to "empty" it. This process is 44 | // repeated until all the input has been "absorbed". The input is then padded. 45 | // The digest is "squeezed" from the sponge in the same way, except that output 46 | // output is copied out instead of input being XORed in. 47 | // 48 | // A sponge is parameterized by its generic security strength, which is equal 49 | // to half its capacity; capacity + rate is equal to the permutation's width. 50 | // Since the KeccakF-1600 permutation is 1600 bits (200 bytes) wide, this means 51 | // that the security strength of a sponge instance is equal to (1600 - bitrate) / 2. 52 | // 53 | // 54 | // Recommendations 55 | // 56 | // The SHAKE functions are recommended for most new uses. They can produce 57 | // output of arbitrary length. SHAKE256, with an output length of at least 58 | // 64 bytes, provides 256-bit security against all attacks. The Keccak team 59 | // recommends it for most applications upgrading from SHA2-512. (NIST chose a 60 | // much stronger, but much slower, sponge instance for SHA3-512.) 61 | // 62 | // The SHA-3 functions are "drop-in" replacements for the SHA-2 functions. 63 | // They produce output of the same length, with the same security strengths 64 | // against all attacks. This means, in particular, that SHA3-256 only has 65 | // 128-bit collision resistance, because its output length is 32 bytes. 66 | package sha3 67 | -------------------------------------------------------------------------------- /wallet/accounts/account.go: -------------------------------------------------------------------------------- 1 | // Package accounts provides types for working with libonomy blockchain accounts 2 | package accounts 3 | 4 | import ( 5 | "crypto/aes" 6 | "encoding/hex" 7 | "errors" 8 | 9 | "github.com/libonomy/wallet-cli/os/crypto" 10 | "github.com/libonomy/wallet-cli/os/log" 11 | "github.com/libonomy/wallet-cli/os/p2p/config" 12 | ) 13 | 14 | // Registry maintains a list of known locked and unlocked accounts. 15 | type Registry struct { 16 | All map[string]*Account 17 | Unlocked map[string]*Account 18 | } 19 | 20 | // Account is an end user blockchain account 21 | type Account struct { 22 | PrivKey crypto.PrivateKey 23 | PubKey crypto.PublicKey 24 | cryptoData CryptoData 25 | kdParams crypto.KDParams 26 | NetworkID int8 27 | } 28 | 29 | var ( 30 | // Accounts Registry app singleton 31 | Accounts *Registry 32 | ) 33 | 34 | func init() { 35 | Accounts = &Registry{ 36 | All: make(map[string]*Account), 37 | Unlocked: make(map[string]*Account), 38 | } 39 | } 40 | 41 | // NewAccount Creates a new account using the provided passphrase. 42 | // Clients should persist newly created accounts - without this the account only lasts for one app session. 43 | func NewAccount(passphrase string) (*Account, error) { 44 | 45 | // account crypto data 46 | priv, pub, err := crypto.GenerateKeyPair() 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | // derive key from passphrase 52 | kdfParams := crypto.DefaultCypherParams 53 | 54 | // add new salt to params 55 | saltData, err := crypto.GetRandomBytes(kdfParams.SaltLen) 56 | if err != nil { 57 | return nil, errors.New("failed to generate random salt") 58 | } 59 | kdfParams.Salt = hex.EncodeToString(saltData) 60 | 61 | dk, err := crypto.DeriveKeyFromPassword(passphrase, kdfParams) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | // extract 16 bytes aes-128-ctr key from the derived key 67 | aesKey := dk[:16] 68 | 69 | // data to encrypt 70 | privKeyBytes := priv.Bytes() 71 | 72 | // compute nonce 73 | nonce, err := crypto.GetRandomBytes(aes.BlockSize) 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | // aes encrypt data 79 | cipherText, err := crypto.AesCTRXOR(aesKey, privKeyBytes, nonce) 80 | 81 | if err != nil { 82 | log.Error("Failed to encrypt private key", err) 83 | return nil, err 84 | } 85 | 86 | // use last 16 bytes from derived key and cipher text to create a mac 87 | mac := crypto.Sha256(dk[16:32], cipherText) 88 | 89 | // store aes cipher data 90 | cryptoData := CryptoData{ 91 | Cipher: "AES-128-CTR", // 16 bytes key 92 | CipherText: hex.EncodeToString(cipherText), 93 | CipherIv: hex.EncodeToString(nonce), 94 | Mac: hex.EncodeToString(mac), 95 | } 96 | 97 | // store kd data 98 | kdParams := crypto.KDParams{ 99 | N: kdfParams.N, 100 | R: kdfParams.R, 101 | P: kdfParams.P, 102 | SaltLen: kdfParams.SaltLen, 103 | DKLen: kdfParams.DKLen, 104 | Salt: kdfParams.Salt, 105 | } 106 | 107 | NetworkID := config.ConfigValues.NetworkID 108 | 109 | // save all data in newly created account obj 110 | acct := &Account{priv, 111 | pub, 112 | cryptoData, 113 | kdParams, 114 | NetworkID} 115 | 116 | Accounts.All[acct.String()] = acct 117 | 118 | // newly created account are unlocked in the current app session 119 | Accounts.Unlocked[acct.String()] = acct 120 | 121 | return acct, nil 122 | } 123 | -------------------------------------------------------------------------------- /os/common/prque/sstack.go: -------------------------------------------------------------------------------- 1 | // This is a duplicated and slightly modified version of "gopkg.in/karalabe/cookiejar.v2/collections/prque". 2 | 3 | package prque 4 | 5 | // The size of a block of data 6 | const blockSize = 4096 7 | 8 | // A prioritized item in the sorted stack. 9 | // 10 | // Note: priorities can "wrap around" the int64 range, a comes before b if (a.priority - b.priority) > 0. 11 | // The difference between the lowest and highest priorities in the queue at any point should be less than 2^63. 12 | type item struct { 13 | value interface{} 14 | priority int64 15 | } 16 | 17 | // setIndexCallback is called when the element is moved to a new index. 18 | // Providing setIndexCallback is optional, it is needed only if the application needs 19 | // to delete elements other than the top one. 20 | type setIndexCallback func(a interface{}, i int) 21 | 22 | // Internal sortable stack data structure. Implements the Push and Pop ops for 23 | // the stack (heap) functionality and the Len, Less and Swap methods for the 24 | // sortability requirements of the heaps. 25 | type sstack struct { 26 | setIndex setIndexCallback 27 | size int 28 | capacity int 29 | offset int 30 | 31 | blocks [][]*item 32 | active []*item 33 | } 34 | 35 | // Creates a new, empty stack. 36 | func newSstack(setIndex setIndexCallback) *sstack { 37 | result := new(sstack) 38 | result.setIndex = setIndex 39 | result.active = make([]*item, blockSize) 40 | result.blocks = [][]*item{result.active} 41 | result.capacity = blockSize 42 | return result 43 | } 44 | 45 | // Pushes a value onto the stack, expanding it if necessary. Required by 46 | // heap.Interface. 47 | func (s *sstack) Push(data interface{}) { 48 | if s.size == s.capacity { 49 | s.active = make([]*item, blockSize) 50 | s.blocks = append(s.blocks, s.active) 51 | s.capacity += blockSize 52 | s.offset = 0 53 | } else if s.offset == blockSize { 54 | s.active = s.blocks[s.size/blockSize] 55 | s.offset = 0 56 | } 57 | if s.setIndex != nil { 58 | s.setIndex(data.(*item).value, s.size) 59 | } 60 | s.active[s.offset] = data.(*item) 61 | s.offset++ 62 | s.size++ 63 | } 64 | 65 | // Pops a value off the stack and returns it. Currently no shrinking is done. 66 | // Required by heap.Interface. 67 | func (s *sstack) Pop() (res interface{}) { 68 | s.size-- 69 | s.offset-- 70 | if s.offset < 0 { 71 | s.offset = blockSize - 1 72 | s.active = s.blocks[s.size/blockSize] 73 | } 74 | res, s.active[s.offset] = s.active[s.offset], nil 75 | if s.setIndex != nil { 76 | s.setIndex(res.(*item).value, -1) 77 | } 78 | return 79 | } 80 | 81 | // Returns the length of the stack. Required by sort.Interface. 82 | func (s *sstack) Len() int { 83 | return s.size 84 | } 85 | 86 | // Compares the priority of two elements of the stack (higher is first). 87 | // Required by sort.Interface. 88 | func (s *sstack) Less(i, j int) bool { 89 | return (s.blocks[i/blockSize][i%blockSize].priority - s.blocks[j/blockSize][j%blockSize].priority) > 0 90 | } 91 | 92 | // Swaps two elements in the stack. Required by sort.Interface. 93 | func (s *sstack) Swap(i, j int) { 94 | ib, io, jb, jo := i/blockSize, i%blockSize, j/blockSize, j%blockSize 95 | a, b := s.blocks[jb][jo], s.blocks[ib][io] 96 | if s.setIndex != nil { 97 | s.setIndex(a.value, i) 98 | s.setIndex(b.value, j) 99 | } 100 | s.blocks[ib][io], s.blocks[jb][jo] = a, b 101 | } 102 | 103 | // Resets the stack, effectively clearing its contents. 104 | func (s *sstack) Reset() { 105 | *s = *newSstack(s.setIndex) 106 | } 107 | -------------------------------------------------------------------------------- /crypto/aes_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "crypto/aes" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestAesCTRXORandPkcs7Pad(t *testing.T) { 12 | const msg = "does not matter" 13 | msgData := []byte(msg) 14 | 15 | dudtestkey := make([]byte, 17, 17) 16 | var nb = []byte(nil) 17 | // dudtestkey is initially not a multiple of 16 so nil is returned 18 | _, err := AesCTRXOR(dudtestkey, msgData, nb) 19 | assert.NotNil(t, err, fmt.Sprintf("incorrect length should error <%v>", err)) 20 | assert.Equal(t, fmt.Sprintf("%s", err), "crypto/aes: invalid key size 17", fmt.Sprintf("incorrect length should error (%s)", err)) 21 | // Pkcs7Pad makes a good lengthed key from the bad one 22 | happytestkey := Pkcs7Pad(dudtestkey) 23 | assert.Equal(t, len(happytestkey), 32, fmt.Sprintf("Pkcs7Pad incorrect length %d", len(happytestkey))) 24 | // now call AesCTRXOR with good parameters and get a nice return value 25 | happynonce := make([]byte, 16, 16) 26 | happy, err := AesCTRXOR(happytestkey, msgData, happynonce) 27 | // check no error 28 | assert.Nil(t, err, "should be happy aesctr err") 29 | // check something like data is in return value 30 | assert.NotNil(t, happy, "should be happy aesctr data") 31 | } 32 | 33 | func TestPkcs7Unpad(t *testing.T) { 34 | zerolength := []byte("") 35 | // check that zero length payload gives nil 36 | rv := Pkcs7Unpad(zerolength) 37 | assert.Equal(t, len(rv), 0, fmt.Sprintf("zero length payload should return nil but is %v", rv)) 38 | // 26 letters + 7 dots 39 | wrongsize := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ.......") 40 | // trigger padding > blocksize, expect nil 41 | wrongsize[len(wrongsize)-1] = byte(aes.BlockSize + 1) 42 | rv = Pkcs7Unpad(wrongsize) 43 | assert.Equal(t, len(rv), 0, fmt.Sprintf("padding > blocksize should return nil but is %v", rv)) 44 | // trigger padding ==0 , expect nil 45 | wrongsize[len(wrongsize)-1] = 0 46 | rv = Pkcs7Unpad(wrongsize) 47 | assert.Equal(t, len(rv), 0, fmt.Sprintf("padding == 0 should return nil but is %v", rv)) 48 | // trigger non padding byte found during reduction 49 | pad := 7 50 | for i := len(wrongsize) - 1; i > len(wrongsize)-pad; i-- { 51 | wrongsize[i] = byte(pad) 52 | } 53 | rv = Pkcs7Unpad(wrongsize) 54 | assert.Equal(t, len(rv), 0, fmt.Sprintf("non padding byte found should return nil but is %v", rv)) 55 | // actually remove some padding and get a proper good result 56 | for i := len(wrongsize) - 1; i > len(wrongsize)-pad-1; i-- { 57 | wrongsize[i] = byte(pad) 58 | } 59 | rightsize := Pkcs7Unpad(wrongsize) 60 | assert.True(t, len(rightsize) > 0 && len(rightsize) == 26, fmt.Sprintf("should not be sized %d", len(rightsize))) 61 | 62 | } 63 | func TestPKCSPadding(t *testing.T) { 64 | // 26 letters + 7 dots is 33 bytes 65 | wrongsize := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ.......") 66 | // should pad up to a size of a multiple of 16 67 | padout := AddPKCSPadding(wrongsize) 68 | assert.True(t, len(padout) == 48, fmt.Sprintf("should not be sized %d", len(padout))) 69 | unpad, err := RemovePKCSPadding(padout) 70 | assert.True(t, len(unpad) == len(wrongsize), fmt.Sprintf("should not be sized %d", len(padout))) 71 | assert.Nil(t, err, fmt.Sprintf("error from RemovePKCSPadding %d", err)) 72 | // force error by setting pad length incorrectly 73 | breaklength := padout 74 | breaklength[len(breaklength)-1] = byte(17) 75 | breaklengthrv, errb := RemovePKCSPadding(padout) 76 | assert.Equal(t, len(breaklengthrv), 0, fmt.Sprintf("should not be sized %d", len(breaklengthrv))) 77 | assert.NotNil(t, errb, fmt.Sprintf("no error from RemovePKCSPadding %d", errb)) 78 | } 79 | -------------------------------------------------------------------------------- /os/crypto/aes_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "crypto/aes" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestAesCTRXORandPkcs7Pad(t *testing.T) { 12 | const msg = "does not matter" 13 | msgData := []byte(msg) 14 | 15 | dudtestkey := make([]byte, 17, 17) 16 | var nb = []byte(nil) 17 | // dudtestkey is initially not a multiple of 16 so nil is returned 18 | _, err := AesCTRXOR(dudtestkey, msgData, nb) 19 | assert.NotNil(t, err, fmt.Sprintf("incorrect length should error <%v>", err)) 20 | assert.Equal(t, fmt.Sprintf("%s", err), "crypto/aes: invalid key size 17", fmt.Sprintf("incorrect length should error (%s)", err)) 21 | // Pkcs7Pad makes a good lengthed key from the bad one 22 | happytestkey := Pkcs7Pad(dudtestkey) 23 | assert.Equal(t, len(happytestkey), 32, fmt.Sprintf("Pkcs7Pad incorrect length %d", len(happytestkey))) 24 | // now call AesCTRXOR with good parameters and get a nice return value 25 | happynonce := make([]byte, 16, 16) 26 | happy, err := AesCTRXOR(happytestkey, msgData, happynonce) 27 | // check no error 28 | assert.Nil(t, err, "should be happy aesctr err") 29 | // check something like data is in return value 30 | assert.NotNil(t, happy, "should be happy aesctr data") 31 | } 32 | 33 | func TestPkcs7Unpad(t *testing.T) { 34 | zerolength := []byte("") 35 | // check that zero length payload gives nil 36 | rv := Pkcs7Unpad(zerolength) 37 | assert.Equal(t, len(rv), 0, fmt.Sprintf("zero length payload should return nil but is %v", rv)) 38 | // 26 letters + 7 dots 39 | wrongsize := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ.......") 40 | // trigger padding > blocksize, expect nil 41 | wrongsize[len(wrongsize)-1] = byte(aes.BlockSize + 1) 42 | rv = Pkcs7Unpad(wrongsize) 43 | assert.Equal(t, len(rv), 0, fmt.Sprintf("padding > blocksize should return nil but is %v", rv)) 44 | // trigger padding ==0 , expect nil 45 | wrongsize[len(wrongsize)-1] = 0 46 | rv = Pkcs7Unpad(wrongsize) 47 | assert.Equal(t, len(rv), 0, fmt.Sprintf("padding == 0 should return nil but is %v", rv)) 48 | // trigger non padding byte found during reduction 49 | pad := 7 50 | for i := len(wrongsize) - 1; i > len(wrongsize)-pad; i-- { 51 | wrongsize[i] = byte(pad) 52 | } 53 | rv = Pkcs7Unpad(wrongsize) 54 | assert.Equal(t, len(rv), 0, fmt.Sprintf("non padding byte found should return nil but is %v", rv)) 55 | // actually remove some padding and get a proper good result 56 | for i := len(wrongsize) - 1; i > len(wrongsize)-pad-1; i-- { 57 | wrongsize[i] = byte(pad) 58 | } 59 | rightsize := Pkcs7Unpad(wrongsize) 60 | assert.True(t, len(rightsize) > 0 && len(rightsize) == 26, fmt.Sprintf("should not be sized %d", len(rightsize))) 61 | 62 | } 63 | func TestPKCSPadding(t *testing.T) { 64 | // 26 letters + 7 dots is 33 bytes 65 | wrongsize := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ.......") 66 | // should pad up to a size of a multiple of 16 67 | padout := AddPKCSPadding(wrongsize) 68 | assert.True(t, len(padout) == 48, fmt.Sprintf("should not be sized %d", len(padout))) 69 | unpad, err := RemovePKCSPadding(padout) 70 | assert.True(t, len(unpad) == len(wrongsize), fmt.Sprintf("should not be sized %d", len(padout))) 71 | assert.Nil(t, err, fmt.Sprintf("error from RemovePKCSPadding %d", err)) 72 | // force error by setting pad length incorrectly 73 | breaklength := padout 74 | breaklength[len(breaklength)-1] = byte(17) 75 | breaklengthrv, errb := RemovePKCSPadding(padout) 76 | assert.Equal(t, len(breaklengthrv), 0, fmt.Sprintf("should not be sized %d", len(breaklengthrv))) 77 | assert.NotNil(t, errb, fmt.Sprintf("no error from RemovePKCSPadding %d", errb)) 78 | } 79 | -------------------------------------------------------------------------------- /wallet/accounts/ops.go: -------------------------------------------------------------------------------- 1 | package accounts 2 | 3 | import ( 4 | "crypto/subtle" 5 | "encoding/hex" 6 | "errors" 7 | "fmt" 8 | 9 | "github.com/libonomy/wallet-cli/os/crypto" 10 | "github.com/libonomy/wallet-cli/os/log" 11 | ) 12 | 13 | // Pretty returns an account logging string 14 | func (a *Account) Pretty() string { 15 | return fmt.Sprintf("Account %s", a.PubKey.Pretty()) 16 | } 17 | 18 | func (a *Account) String() string { 19 | return a.PubKey.String() 20 | } 21 | 22 | // Log account info 23 | func (a *Account) Log() { 24 | 25 | pubKey := a.PubKey.String() 26 | log.Debug("Account id: %s", a.String()) 27 | 28 | if a.PrivKey != nil { 29 | privKey := a.PrivKey.String() 30 | log.Debug(" Private key: %s", privKey) 31 | } 32 | 33 | log.Debug(" Public key: %s", pubKey) 34 | log.Debug(" IsUnlocked: %t ", a.IsAccountUnlocked()) 35 | log.Debug(" Crypto params: %+v", a.cryptoData) 36 | log.Debug(" kdParams: %+v", a.kdParams) 37 | } 38 | 39 | // IsAccountLocked returns true iff account is locked. 40 | func (a *Account) IsAccountLocked() bool { 41 | return a.PrivKey == nil 42 | } 43 | 44 | // IsAccountUnlocked returns true iff account is unlocked. 45 | func (a *Account) IsAccountUnlocked() bool { 46 | return !a.IsAccountLocked() 47 | } 48 | 49 | // LockAccount locks an account with user provided passphrase. 50 | func (a *Account) LockAccount(passphrase string) { 51 | a.PrivKey = nil 52 | delete(Accounts.Unlocked, a.String()) 53 | } 54 | 55 | // UnlockAccount unlocks an account using the user provided passphrase 56 | func (a *Account) UnlockAccount(passphrase string) error { 57 | 58 | if a.IsAccountUnlocked() { 59 | // account already unlocked 60 | return nil 61 | } 62 | 63 | // get derived key from params and pass-phrase 64 | dk, err := crypto.DeriveKeyFromPassword(passphrase, a.kdParams) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | // extract 16 bytes aes-128-ctr key from the derived key 70 | aesKey := dk[:16] 71 | cipherText, err := hex.DecodeString(a.cryptoData.CipherText) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | nonce, err := hex.DecodeString(a.cryptoData.CipherIv) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | mac, err := hex.DecodeString(a.cryptoData.Mac) 82 | if err != nil { 83 | return err 84 | } 85 | 86 | // authenticate cipherText using mac 87 | expectedMac := crypto.Sha256(dk[16:32], cipherText) 88 | 89 | if subtle.ConstantTimeCompare(mac, expectedMac) != 1 { 90 | return errors.New("mac auth error") 91 | } 92 | 93 | // aes decrypt private key 94 | privKeyData, err := crypto.AesCTRXOR(aesKey, cipherText, nonce) 95 | if err != nil { 96 | log.Error("failed to aes decode private key", err) 97 | return err 98 | } 99 | 100 | privateKey, err := crypto.NewPrivateKey(privKeyData) 101 | if err != nil { 102 | return err 103 | } 104 | 105 | err = a.validatePublicKey(privateKey) 106 | if err != nil { 107 | return err 108 | } 109 | 110 | // store decrypted key and update accounts 111 | a.PrivKey = privateKey 112 | Accounts.Unlocked[a.String()] = a 113 | 114 | return nil 115 | } 116 | 117 | // Validate that the account's private key matches the provided private key 118 | func (a *Account) validatePublicKey(privateKey crypto.PrivateKey) error { 119 | 120 | publicKey := privateKey.GetPublicKey() 121 | publicKeyStr := publicKey.String() 122 | accountPubKeyStr := a.PubKey.String() 123 | 124 | if accountPubKeyStr != publicKeyStr { 125 | return fmt.Errorf("invalid extracted public key %s %s", accountPubKeyStr, publicKeyStr) 126 | } 127 | 128 | return nil 129 | } 130 | -------------------------------------------------------------------------------- /crypto/math_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestMinXY(t *testing.T) { 11 | const MaxUintValue = ^uint(0) 12 | const MinUintValue = 0 13 | const MaxIntValue = int(MaxUintValue >> 1) 14 | const MinIntValue = -MaxIntValue - 1 15 | 16 | const MaxUint32Value = ^uint32(0) 17 | const MinUint32Value = 0 18 | const MaxInt32Value = int32(MaxUint32Value >> 1) 19 | const MinInt32Value = -MaxInt32Value - 1 20 | 21 | const MaxUint64Value = ^uint64(0) 22 | const MinUint64Value = 0 23 | const MaxInt64Value = int64(MaxUint64Value >> 1) 24 | const MinInt64Value = -MaxInt64Value - 1 25 | 26 | const FavouriteInt = 17 27 | const FavouriteInt32 = int32(17) 28 | const FavouriteInt64 = int64(17) 29 | 30 | //int 31 | // biggest vs smallest 32 | try := MinInt(MaxIntValue, MinIntValue) 33 | assert.Equal(t, try, MinIntValue, fmt.Sprintf("big vs small should be %d, is %d", MinIntValue, try)) 34 | // smallest vs biggest 35 | try = MinInt(MinIntValue, MaxIntValue) 36 | assert.Equal(t, try, MinIntValue, fmt.Sprintf("small vs big should be %d, is %d", MinIntValue, try)) 37 | // biggest -1 vs biggest 38 | try = MinInt(MaxIntValue-1, MaxIntValue) 39 | assert.Equal(t, try, MaxIntValue-1, fmt.Sprintf("big vs biggest should be %d, is %d", MaxIntValue-1, try)) 40 | // smallest+1 vs smallest 41 | try = MinInt(MinIntValue+1, MinIntValue) 42 | assert.Equal(t, try, MinIntValue, fmt.Sprintf("tiny vs smallest should be %d, is %d", MinIntValue, try)) 43 | // the same 44 | try = MinInt(FavouriteInt, FavouriteInt) 45 | assert.Equal(t, try, FavouriteInt, fmt.Sprintf("equal values should be %d, is %d", FavouriteInt, try)) 46 | 47 | //int32 48 | // biggest vs smallest 49 | try32 := MinInt32(MaxInt32Value, MinInt32Value) 50 | assert.Equal(t, try32, MinInt32Value, fmt.Sprintf("int32 big vs small should be %d, is %d", MinInt32Value, try32)) 51 | // smallest vs biggest 52 | try32 = MinInt32(MinInt32Value, MaxInt32Value) 53 | assert.Equal(t, try32, MinInt32Value, fmt.Sprintf("int32 small vs big should be %d, is %d", MinInt32Value, try32)) 54 | // biggest -1 vs biggest 55 | try32 = MinInt32(MaxInt32Value-1, MaxInt32Value) 56 | assert.Equal(t, try32, MaxInt32Value-1, fmt.Sprintf("int32 big vs biggest should be %d, is %d", MaxInt32Value-1, try32)) 57 | // smallest+1 vs smallest 58 | try32 = MinInt32(MinInt32Value+1, MinInt32Value) 59 | assert.Equal(t, try32, MinInt32Value, fmt.Sprintf("int32 tiny vs smallest should be %d, is %d", MinInt32Value, try32)) 60 | // the same 61 | try32 = MinInt32(FavouriteInt32, FavouriteInt32) 62 | assert.Equal(t, try32, FavouriteInt32, fmt.Sprintf("int32 equal values should be %d, is %d", FavouriteInt32, try32)) 63 | 64 | //int64 65 | // biggest vs smallest 66 | try64 := MinInt64(MaxInt64Value, MinInt64Value) 67 | assert.Equal(t, try64, MinInt64Value, fmt.Sprintf("int64 big vs small should be %d, is %d", MinInt64Value, try64)) 68 | // smallest vs biggest 69 | try64 = MinInt64(MinInt64Value, MaxInt64Value) 70 | assert.Equal(t, try64, MinInt64Value, fmt.Sprintf("int64 small vs big should be %d, is %d", MinInt64Value, try64)) 71 | // biggest -1 vs biggest 72 | try64 = MinInt64(MaxInt64Value-1, MaxInt64Value) 73 | assert.Equal(t, try64, MaxInt64Value-1, fmt.Sprintf("int64 big vs biggest should be %d, is %d", MaxInt64Value-1, try64)) 74 | // smallest+1 vs smallest 75 | try64 = MinInt64(MinInt64Value+1, MinInt64Value) 76 | assert.Equal(t, try64, MinInt64Value, fmt.Sprintf("int64 tiny vs smallest should be %d, is %d", MinInt64Value, try64)) 77 | // the same 78 | try64 = MinInt64(FavouriteInt64, FavouriteInt64) 79 | assert.Equal(t, try64, FavouriteInt64, fmt.Sprintf("int64 equal values should be %d, is %d", FavouriteInt64, try64)) 80 | 81 | } 82 | -------------------------------------------------------------------------------- /os/crypto/math_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestMinXY(t *testing.T) { 11 | const MaxUintValue = ^uint(0) 12 | const MinUintValue = 0 13 | const MaxIntValue = int(MaxUintValue >> 1) 14 | const MinIntValue = -MaxIntValue - 1 15 | 16 | const MaxUint32Value = ^uint32(0) 17 | const MinUint32Value = 0 18 | const MaxInt32Value = int32(MaxUint32Value >> 1) 19 | const MinInt32Value = -MaxInt32Value - 1 20 | 21 | const MaxUint64Value = ^uint64(0) 22 | const MinUint64Value = 0 23 | const MaxInt64Value = int64(MaxUint64Value >> 1) 24 | const MinInt64Value = -MaxInt64Value - 1 25 | 26 | const FavouriteInt = 17 27 | const FavouriteInt32 = int32(17) 28 | const FavouriteInt64 = int64(17) 29 | 30 | //int 31 | // biggest vs smallest 32 | try := MinInt(MaxIntValue, MinIntValue) 33 | assert.Equal(t, try, MinIntValue, fmt.Sprintf("big vs small should be %d, is %d", MinIntValue, try)) 34 | // smallest vs biggest 35 | try = MinInt(MinIntValue, MaxIntValue) 36 | assert.Equal(t, try, MinIntValue, fmt.Sprintf("small vs big should be %d, is %d", MinIntValue, try)) 37 | // biggest -1 vs biggest 38 | try = MinInt(MaxIntValue-1, MaxIntValue) 39 | assert.Equal(t, try, MaxIntValue-1, fmt.Sprintf("big vs biggest should be %d, is %d", MaxIntValue-1, try)) 40 | // smallest+1 vs smallest 41 | try = MinInt(MinIntValue+1, MinIntValue) 42 | assert.Equal(t, try, MinIntValue, fmt.Sprintf("tiny vs smallest should be %d, is %d", MinIntValue, try)) 43 | // the same 44 | try = MinInt(FavouriteInt, FavouriteInt) 45 | assert.Equal(t, try, FavouriteInt, fmt.Sprintf("equal values should be %d, is %d", FavouriteInt, try)) 46 | 47 | //int32 48 | // biggest vs smallest 49 | try32 := MinInt32(MaxInt32Value, MinInt32Value) 50 | assert.Equal(t, try32, MinInt32Value, fmt.Sprintf("int32 big vs small should be %d, is %d", MinInt32Value, try32)) 51 | // smallest vs biggest 52 | try32 = MinInt32(MinInt32Value, MaxInt32Value) 53 | assert.Equal(t, try32, MinInt32Value, fmt.Sprintf("int32 small vs big should be %d, is %d", MinInt32Value, try32)) 54 | // biggest -1 vs biggest 55 | try32 = MinInt32(MaxInt32Value-1, MaxInt32Value) 56 | assert.Equal(t, try32, MaxInt32Value-1, fmt.Sprintf("int32 big vs biggest should be %d, is %d", MaxInt32Value-1, try32)) 57 | // smallest+1 vs smallest 58 | try32 = MinInt32(MinInt32Value+1, MinInt32Value) 59 | assert.Equal(t, try32, MinInt32Value, fmt.Sprintf("int32 tiny vs smallest should be %d, is %d", MinInt32Value, try32)) 60 | // the same 61 | try32 = MinInt32(FavouriteInt32, FavouriteInt32) 62 | assert.Equal(t, try32, FavouriteInt32, fmt.Sprintf("int32 equal values should be %d, is %d", FavouriteInt32, try32)) 63 | 64 | //int64 65 | // biggest vs smallest 66 | try64 := MinInt64(MaxInt64Value, MinInt64Value) 67 | assert.Equal(t, try64, MinInt64Value, fmt.Sprintf("int64 big vs small should be %d, is %d", MinInt64Value, try64)) 68 | // smallest vs biggest 69 | try64 = MinInt64(MinInt64Value, MaxInt64Value) 70 | assert.Equal(t, try64, MinInt64Value, fmt.Sprintf("int64 small vs big should be %d, is %d", MinInt64Value, try64)) 71 | // biggest -1 vs biggest 72 | try64 = MinInt64(MaxInt64Value-1, MaxInt64Value) 73 | assert.Equal(t, try64, MaxInt64Value-1, fmt.Sprintf("int64 big vs biggest should be %d, is %d", MaxInt64Value-1, try64)) 74 | // smallest+1 vs smallest 75 | try64 = MinInt64(MinInt64Value+1, MinInt64Value) 76 | assert.Equal(t, try64, MinInt64Value, fmt.Sprintf("int64 tiny vs smallest should be %d, is %d", MinInt64Value, try64)) 77 | // the same 78 | try64 = MinInt64(FavouriteInt64, FavouriteInt64) 79 | assert.Equal(t, try64, FavouriteInt64, fmt.Sprintf("int64 equal values should be %d, is %d", FavouriteInt64, try64)) 80 | 81 | } 82 | -------------------------------------------------------------------------------- /wallet/address/address.go: -------------------------------------------------------------------------------- 1 | package address 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "math/big" 7 | "reflect" 8 | 9 | "github.com/libonomy/wallet-cli/os/common" 10 | "github.com/libonomy/wallet-cli/os/crypto/sha3" 11 | ) 12 | 13 | // Address represents the 20 byte address of an libonomy account. 14 | type Address [common.AddressLength]byte 15 | 16 | var addressT = reflect.TypeOf(Address{}) 17 | 18 | // BytesToAddress returns Address with value b. 19 | // If b is larger than len(h), b will be cropped from the left. 20 | func BytesToAddress(b []byte) Address { 21 | var a Address 22 | a.SetBytes(b) 23 | return a 24 | } 25 | 26 | // BigToAddress returns Address with byte values of b. 27 | // If b is larger than len(h), b will be cropped from the left. 28 | func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) } 29 | 30 | // HexToAddress returns Address with byte values of s. 31 | // If s is larger than len(h), s will be cropped from the left. 32 | func HexToAddress(s string) Address { return BytesToAddress(common.FromHex(s)) } 33 | 34 | // Bytes gets the string representation of the underlying address. 35 | func (a Address) Bytes() []byte { return a[:] } 36 | 37 | // Big converts an address to a big integer. 38 | func (a Address) Big() *big.Int { return new(big.Int).SetBytes(a[:]) } 39 | 40 | // Hash converts an address to a hash by left-padding it with zeros. 41 | func (a Address) Hash() common.Hash { return common.BytesToHash(a[:]) } 42 | 43 | // Hex returns an EIP55-compliant hex string representation of the address. 44 | func (a Address) Hex() string { 45 | unchecksummed := hex.EncodeToString(a[:]) 46 | sha := sha3.NewKeccak256() 47 | sha.Write([]byte(unchecksummed)) 48 | hash := sha.Sum(nil) 49 | 50 | result := []byte(unchecksummed) 51 | for i := 0; i < len(result); i++ { 52 | hashByte := hash[i/2] 53 | if i%2 == 0 { 54 | hashByte = hashByte >> 4 55 | } else { 56 | hashByte &= 0xf 57 | } 58 | if result[i] > '9' && hashByte > 7 { 59 | result[i] -= 32 60 | } 61 | } 62 | return "0x" + string(result) 63 | } 64 | 65 | // String implements fmt.Stringer. 66 | func (a Address) String() string { 67 | return a.Hex() 68 | } 69 | 70 | // Format implements fmt.Formatter, forcing the byte slice to be formatted as is, 71 | // without going through the stringer interface used for logging. 72 | func (a Address) Format(s fmt.State, c rune) { 73 | fmt.Fprintf(s, "%"+string(c), a[:]) 74 | } 75 | 76 | // SetBytes sets the address to the value of b. 77 | // If b is larger than len(a) it will panic. 78 | func (a *Address) SetBytes(b []byte) { 79 | if len(b) > len(a) { 80 | b = b[len(b)-common.AddressLength:] 81 | } 82 | copy(a[common.AddressLength-len(b):], b) 83 | } 84 | 85 | /* 86 | // MarshalText returns the hex representation of a. 87 | func (a Address) MarshalText() ([]byte, error) { 88 | return hexutil.Bytes(a[:]).MarshalText() 89 | } 90 | 91 | // UnmarshalText parses a hash in hex syntax. 92 | func (a *Address) UnmarshalText(input []byte) error { 93 | return hexutil.UnmarshalFixedText("Address", input, a[:]) 94 | } 95 | 96 | // UnmarshalJSON parses a hash in hex syntax. 97 | func (a *Address) UnmarshalJSON(input []byte) error { 98 | return hexutil.UnmarshalFixedJSON(addressT, input, a[:]) 99 | } 100 | 101 | // Scan implements Scanner for database/sql. 102 | func (a *Address) Scan(src interface{}) error { 103 | srcB, ok := src.([]byte) 104 | if !ok { 105 | return fmt.Errorf("can't scan %T into Address", src) 106 | } 107 | if len(srcB) != AddressLength { 108 | return fmt.Errorf("can't scan []byte of len %d into Address, want %d", len(srcB), AddressLength) 109 | } 110 | copy(a[:], srcB) 111 | return nil 112 | } 113 | */ 114 | -------------------------------------------------------------------------------- /wallet/accounts/store.go: -------------------------------------------------------------------------------- 1 | package accounts 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "path/filepath" 8 | "strings" 9 | 10 | "github.com/libonomy/wallet-cli/os/crypto" 11 | "github.com/libonomy/wallet-cli/os/filesystem" 12 | "github.com/libonomy/wallet-cli/os/log" 13 | ) 14 | 15 | // AccountData is used to persist an account. 16 | type AccountData struct { 17 | PublicKey string `json:"publicKey"` 18 | CryptoData CryptoData `json:"crypto"` 19 | KDParams crypto.KDParams `json:"kd"` 20 | NetworkID int8 `json:"networkId"` 21 | } 22 | 23 | // CryptoData is the data use to encrypt/decrypt locally stored account data. 24 | type CryptoData struct { 25 | Cipher string `json:"cipher"` 26 | CipherText string `json:"cipherText"` // encrypted private key 27 | CipherIv string `json:"cipherIv"` 28 | Mac string `json:"mac"` 29 | } 30 | 31 | // LoadAllAccounts loads all account persisted to store 32 | func LoadAllAccounts() error { 33 | 34 | accountsDataFolder, err := filesystem.GetAccountsDataDirectoryPath() 35 | if err != nil { 36 | return err 37 | } 38 | 39 | files, err := ioutil.ReadDir(accountsDataFolder) 40 | if err != nil { 41 | log.Error("Failed to read account directory files", err) 42 | return nil 43 | } 44 | 45 | for _, f := range files { 46 | fileName := f.Name() 47 | if !f.IsDir() && strings.HasSuffix(fileName, ".json") { 48 | accountID := fileName[:strings.LastIndex(fileName, ".")] 49 | _, err := NewAccountFromStore(accountID, accountsDataFolder) 50 | if err != nil { 51 | log.Error(fmt.Sprintf("failed to load account %s", accountID), err) 52 | } 53 | } 54 | } 55 | 56 | return nil 57 | 58 | } 59 | 60 | // NewAccountFromStore creates a new account by id and stored data. 61 | // Account will be locked after creation as there's no persisted passphrase. 62 | // accountsDataPath: os-specific full path to accounts data folder. 63 | func NewAccountFromStore(accountID string, accountsDataPath string) (*Account, error) { 64 | 65 | log.Debug("Loading account from store. ID: %s ...", accountID) 66 | 67 | fileName := accountID + ".json" 68 | dataFilePath := filepath.Join(accountsDataPath, fileName) 69 | 70 | data, err := ioutil.ReadFile(dataFilePath) 71 | if err != nil { 72 | return nil, err 73 | } 74 | 75 | var accountData AccountData 76 | err = json.Unmarshal(data, &accountData) 77 | if err != nil { 78 | return nil, err 79 | } 80 | 81 | pubKey, err := crypto.NewPublicKeyFromString(accountData.PublicKey) 82 | if err != nil { 83 | return nil, err 84 | } 85 | 86 | acct := &Account{nil, 87 | pubKey, 88 | accountData.CryptoData, 89 | accountData.KDParams, 90 | accountData.NetworkID} 91 | 92 | log.Debug("Loaded account from store: %s", pubKey.String()) 93 | 94 | Accounts.All[acct.String()] = acct 95 | 96 | return acct, nil 97 | } 98 | 99 | // Persist all account data to store 100 | // Passphrases are never persisted to store 101 | // accountsDataPath: os-specific full path to accounts data folder 102 | // Returns full path of persisted file (useful for testing) 103 | func (a *Account) Persist(accountsDataPath string) (string, error) { 104 | 105 | pubKeyStr := a.PubKey.String() 106 | 107 | data := &AccountData{ 108 | pubKeyStr, 109 | a.cryptoData, 110 | a.kdParams, 111 | a.NetworkID, 112 | } 113 | 114 | bytes, err := json.MarshalIndent(data, "", " ") 115 | if err != nil { 116 | log.Error("Failed to marshal node data to json", err) 117 | return "", err 118 | } 119 | 120 | fileName := a.String() + ".json" 121 | dataFilePath := filepath.Join(accountsDataPath, fileName) 122 | err = ioutil.WriteFile(dataFilePath, bytes, filesystem.OwnerReadWrite) 123 | if err != nil { 124 | log.Error("Failed to write account to file", err) 125 | return "", err 126 | } 127 | 128 | log.Debug("Persisted account to store. ID: %s", a.String()) 129 | 130 | return dataFilePath, nil 131 | } 132 | -------------------------------------------------------------------------------- /os/crypto/keys_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "fmt" 7 | "testing" 8 | 9 | "github.com/libonomy/wallet-cli/os/log" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestBasicApi(t *testing.T) { 14 | 15 | badData, _ := hex.DecodeString("1234") 16 | _, err := NewPublicKey(badData) 17 | assert.Error(t, err, "expected error for bad key data") 18 | 19 | _, err = NewPrivateKey(badData) 20 | assert.Error(t, err, "expected error for bad key data") 21 | 22 | _, err = NewPrivateKeyFromString("1234") 23 | assert.Error(t, err, "expected error for bad key data") 24 | 25 | priv, pub, err := GenerateKeyPair() 26 | 27 | assert.Nil(t, err, "failed to generate keys") 28 | log.Debug("priv: %s, pub: %s", priv.Pretty(), pub.Pretty()) 29 | log.Debug("priv: %s, pub: %s", priv.String(), pub.String()) 30 | 31 | pub1 := priv.GetPublicKey() 32 | assert.True(t, bytes.Equal(pub.Bytes(), pub1.Bytes()), fmt.Sprintf("expected same pub key, %s, %s", 33 | pub.String(), pub1.String())) 34 | 35 | // serialization tests 36 | priv1, err := NewPrivateKey(priv.Bytes()) 37 | assert.NoError(t, err, "unexpected error") 38 | assert.True(t, bytes.Equal(priv1.Bytes(), priv.Bytes()), fmt.Sprintf("expected same private key, %s, %s", 39 | priv1.String(), priv.String())) 40 | 41 | priv2, err := NewPrivateKeyFromString(priv.String()) 42 | assert.NoError(t, err, "unexpected error") 43 | assert.True(t, bytes.Equal(priv2.Bytes(), priv.Bytes()), fmt.Sprintf("expected same private key, %s, %s", 44 | priv2.String(), priv.String())) 45 | 46 | pub2, err := NewPublicKey(pub.Bytes()) 47 | assert.Nil(t, err, fmt.Sprintf("New pub key from bin error: %v", err)) 48 | 49 | assert.True(t, bytes.Equal(pub2.Bytes(), pub.Bytes()), fmt.Sprintf("expected same public key, %s, %s", 50 | pub2.String(), pub.String())) 51 | 52 | pub3, err := NewPublicKeyFromString(pub.String()) 53 | 54 | assert.Nil(t, err, fmt.Sprintf("New pub key from bin error: %v", err)) 55 | 56 | assert.True(t, bytes.Equal(pub3.Bytes(), pub.Bytes()), fmt.Sprintf("Expected same public key, %s, %s", 57 | pub3.String(), pub.String())) 58 | } 59 | 60 | func TestCryptoApi(t *testing.T) { 61 | 62 | priv, pub, err := GenerateKeyPair() 63 | 64 | assert.Nil(t, err, "Failed to generate keys") 65 | 66 | const msg = "hello world" 67 | msgData := []byte(msg) 68 | 69 | // test signatures 70 | signature, err := priv.Sign(msgData) 71 | 72 | assert.Nil(t, err, fmt.Sprintf("signing error: %v", err)) 73 | ok, err := pub.Verify(msgData, signature) 74 | assert.Nil(t, err, fmt.Sprintf("sign verification error: %v", err)) 75 | 76 | assert.True(t, ok, "Failed to verify signature") 77 | 78 | ok, err = pub.VerifyString(msgData, hex.EncodeToString(signature)) 79 | assert.Nil(t, err, fmt.Sprintf("sign verification error: %v", err)) 80 | assert.True(t, ok, "Failed to verify signature") 81 | 82 | _, pub2, _ := GenerateKeyPair() 83 | ok, err = pub2.Verify(msgData, signature) 84 | assert.NoError(t, err) 85 | assert.False(t, ok) 86 | 87 | ok, err = pub2.VerifyString(msgData, hex.EncodeToString(signature)) 88 | assert.Nil(t, err, fmt.Sprintf("sign verification error: %v", err)) 89 | assert.False(t, ok, "succeed to verify wrong signature") 90 | 91 | // test encrypting a message for pub by pub - anyone w pub can do that 92 | cypherText, err := pub.Encrypt(msgData) 93 | 94 | assert.Nil(t, err, fmt.Sprintf("enc error: %v", err)) 95 | 96 | // test decryption 97 | clearText, err := priv.Decrypt(cypherText) 98 | assert.Nil(t, err, fmt.Sprintf("dec error: %v", err)) 99 | 100 | assert.True(t, bytes.Equal(msgData, clearText), "expected same dec message") 101 | 102 | } 103 | 104 | func BenchmarkVerify(b *testing.B) { 105 | b.StopTimer() 106 | 107 | priv, pub, err := GenerateKeyPair() 108 | 109 | assert.Nil(b, err, "Failed to generate keys") 110 | 111 | const msg = "hello world" 112 | msgData := []byte(msg) 113 | 114 | // test signatures 115 | signature, err := priv.Sign(msgData) 116 | 117 | assert.Nil(b, err, fmt.Sprintf("signing error: %v", err)) 118 | 119 | b.StartTimer() 120 | for i := 0; i < b.N; i++ { 121 | //ok, err := pub.Verify(msgData, signature) 122 | pub.Verify(msgData, signature) 123 | 124 | } 125 | b.StopTimer() 126 | 127 | } 128 | -------------------------------------------------------------------------------- /log/log.go: -------------------------------------------------------------------------------- 1 | // Package log provides the both file and console (general) logging capabilities 2 | // to libonomy modules such as app and identity. 3 | package log 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | 10 | "gopkg.in/natefinch/lumberjack.v2" 11 | "gopkg.in/op/go-logging.v1" 12 | ) 13 | 14 | // Log is an exported type that embeds our logger. 15 | // logging library can be replaced as long as it implements same functionality used across the project. 16 | type Log struct { 17 | *logging.Logger 18 | } 19 | 20 | // smlogger is the local app singleton logger. 21 | var AppLog Log 22 | var debugMode = false 23 | 24 | func init() { 25 | 26 | // create a basic temp os.Stdout logger 27 | // This logger is used until the app calls InitlibonomyLoggingSystem(). 28 | log := logging.MustGetLogger("app") 29 | log.ExtraCalldepth = 1 30 | logFormat := ` %{color}%{level:.4s} %{id:03x} %{time:15:04:05.000} ▶%{color:reset} %{message}` 31 | leveledBackend := getBackendLevel("app", "", logFormat) 32 | log.SetBackend(leveledBackend) 33 | AppLog = Log{Logger: log} 34 | } 35 | 36 | // getAllBackend returns level backends with an exception to Debug leve 37 | // which is only returned if the flag debug is set 38 | func getBackendLevel(module, prefix, format string) logging.LeveledBackend { 39 | logFormat := logging.MustStringFormatter(format) 40 | 41 | backend := logging.NewLogBackend(os.Stdout, prefix, 0) 42 | backendFormatter := logging.NewBackendFormatter(backend, logFormat) 43 | leveledBackend := logging.AddModuleLevel(backendFormatter) 44 | 45 | if debugMode { 46 | leveledBackend.SetLevel(logging.DEBUG, module) 47 | } else { 48 | leveledBackend.SetLevel(logging.INFO, module) 49 | } 50 | 51 | return leveledBackend 52 | } 53 | 54 | // DebugMode sets log debug level 55 | func DebugMode(mode bool) { 56 | debugMode = mode 57 | } 58 | 59 | // New creates a logger for a module. e.g. p2p instance logger. 60 | func New(module string, dataFolderPath string, logFileName string) Log { 61 | log := logging.MustGetLogger(module) 62 | log.ExtraCalldepth = 1 63 | logFormat := ` %{color:reset}%{color}%{level:.4s} %{id:03x} %{time:15:04:05.000} %{shortpkg}.%{shortfunc} ▶%{color:reset} %{message}` 64 | logFileFormat := `%{time:15:04:05.000} %{level:.4s} %{id:03x} %{shortpkg}.%{shortfunc} ▶ %{message}` 65 | // module name is set is message prefix 66 | 67 | backends := getBackendLevelWithFileBackend(module, module, logFormat, logFileFormat, dataFolderPath, logFileName) 68 | 69 | log.SetBackend(logging.MultiLogger(backends...)) 70 | 71 | return Log{log} 72 | } 73 | 74 | // getBackendLevelWithFileBackend returns backends level including log file backend 75 | func getBackendLevelWithFileBackend(module, prefix, logFormat, fileFormat, dataFolderPath, logFileName string) []logging.Backend { 76 | leveledBackends := []logging.Backend{getBackendLevel(module, prefix, logFormat)} 77 | 78 | if dataFolderPath != "" && logFileName != "" { 79 | fileName := filepath.Join(dataFolderPath, logFileName) 80 | 81 | fileLogger := &lumberjack.Logger{ 82 | Filename: fileName, 83 | MaxSize: 500, // megabytes 84 | MaxBackups: 3, 85 | MaxAge: 28, // days 86 | Compress: false, 87 | } 88 | 89 | fileLoggerBackend := logging.NewLogBackend(fileLogger, "", 0) 90 | logFileFormat := logging.MustStringFormatter(fileFormat) 91 | fileBackendFormatter := logging.NewBackendFormatter(fileLoggerBackend, logFileFormat) 92 | leveledBackends = append(leveledBackends, logging.AddModuleLevel(fileBackendFormatter)) 93 | } 94 | 95 | return leveledBackends 96 | } 97 | 98 | // InitlibonomyLoggingSystem initializes app logging system. 99 | func InitlibonomyLoggingSystem(dataFolderPath string, logFileName string) { 100 | log := logging.MustGetLogger("app") 101 | 102 | // we wrap all log calls so we need to add 1 to call depth 103 | log.ExtraCalldepth = 1 104 | 105 | logFormat := ` %{color}%{level:.4s} %{id:03x} %{time:15:04:05.000} %{shortpkg}.%{shortfunc}%{color:reset} ▶ %{message}` 106 | logFileFormat := `%{time:15:04:05.000} %{level:.4s}-%{id:03x} %{shortpkg}.%{shortfunc} ▶ %{message}` 107 | 108 | backends := getBackendLevelWithFileBackend("app", "", logFormat, logFileFormat, dataFolderPath, logFileName) 109 | 110 | log.SetBackend(logging.MultiLogger(backends...)) 111 | 112 | AppLog = Log{log} 113 | } 114 | 115 | // public wrappers abstracting away logging lib impl 116 | 117 | // Info prints formatted info level log message. 118 | func Info(format string, args ...interface{}) { 119 | AppLog.Info(format, args...) 120 | } 121 | 122 | // Debug prints formatted debug level log message. 123 | func Debug(format string, args ...interface{}) { 124 | AppLog.Debug(format, args...) 125 | } 126 | 127 | // Error prints formatted error level log message. 128 | func Error(format string, args ...interface{}) { 129 | AppLog.Error(format, args...) 130 | } 131 | 132 | // Warning prints formatted warning level log message. 133 | func Warning(format string, args ...interface{}) { 134 | AppLog.Warning(format, args...) 135 | } 136 | 137 | // PrettyID formats ID. 138 | func PrettyID(id string) string { 139 | m := 6 140 | if len(id) < m { 141 | m = len(id) 142 | } 143 | return fmt.Sprintf("", id[:m]) 144 | } 145 | -------------------------------------------------------------------------------- /os/log/log.go: -------------------------------------------------------------------------------- 1 | // Package log provides the both file and console (general) logging capabilities 2 | // to libonomy modules such as app and identity. 3 | package log 4 | 5 | import ( 6 | "io" 7 | "os" 8 | "path/filepath" 9 | 10 | "go.uber.org/zap/zapcore" 11 | "gopkg.in/natefinch/lumberjack.v2" 12 | 13 | "go.uber.org/zap" 14 | ) 15 | 16 | const mainLoggerName = "00000.defaultLogger" 17 | 18 | // determine the level of messages we show. 19 | var debugMode = false 20 | 21 | // should we format out logs in json 22 | var jsonLog = false 23 | 24 | var DebugLevel = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 25 | return lvl >= zapcore.DebugLevel 26 | }) 27 | 28 | var InfoLevel = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 29 | return lvl >= zapcore.InfoLevel 30 | }) 31 | 32 | var ErrorLevel = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 33 | return lvl >= zapcore.ErrorLevel 34 | }) 35 | 36 | func logLevel() zap.LevelEnablerFunc { 37 | if debugMode { 38 | return DebugLevel 39 | } else { 40 | return InfoLevel 41 | } 42 | } 43 | 44 | func LogLvl() zapcore.Level { 45 | if debugMode { 46 | return zapcore.DebugLevel 47 | } else { 48 | return zapcore.InfoLevel 49 | } 50 | } 51 | 52 | type Logger interface { 53 | Info(format string, args ...interface{}) 54 | Debug(format string, args ...interface{}) 55 | Error(format string, args ...interface{}) 56 | Warning(format string, args ...interface{}) 57 | WithName(prefix string) Log 58 | } 59 | 60 | func encoder() zapcore.Encoder { 61 | if jsonLog { 62 | return zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig()) 63 | } 64 | return zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()) 65 | } 66 | 67 | // AppLog is the local app singleton logger. 68 | var AppLog Log 69 | 70 | func init() { 71 | // create a basic temp os.Stdout logger 72 | // This logger is used until the app calls InitlibonomyLoggingSystem(). 73 | AppLog = NewDefault(mainLoggerName) 74 | } 75 | 76 | // DebugMode sets log debug level 77 | func DebugMode(mode bool) { 78 | debugMode = mode 79 | } 80 | 81 | func JSONLog(b bool) { 82 | jsonLog = b 83 | } 84 | 85 | // New creates a logger for a module. e.g. p2p instance logger. 86 | func New(module string, dataFolderPath string, logFileName string) Log { 87 | var cores []zapcore.Core 88 | 89 | consoleSyncer := zapcore.AddSync(os.Stdout) 90 | enc := encoder() 91 | 92 | cores = append(cores, zapcore.NewCore(enc, consoleSyncer, logLevel())) 93 | 94 | if dataFolderPath != "" && logFileName != "" { 95 | wr := getFileWriter(dataFolderPath, logFileName) 96 | fs := zapcore.AddSync(wr) 97 | cores = append(cores, zapcore.NewCore(enc, fs, DebugLevel)) 98 | } 99 | 100 | core := zapcore.NewTee(cores...) 101 | 102 | log := zap.New(core) 103 | log = log.Named(module) 104 | lvl := zap.NewAtomicLevelAt(LogLvl()) 105 | return Log{log, log.Sugar(), &lvl} 106 | } 107 | 108 | // New creates a logger for a module. e.g. p2p instance logger. 109 | func NewWithErrorLevel(module string, dataFolderPath string, logFileName string) Log { 110 | var cores []zapcore.Core 111 | 112 | consoleSyncer := zapcore.AddSync(os.Stdout) 113 | enc := encoder() 114 | 115 | cores = append(cores, zapcore.NewCore(enc, consoleSyncer, ErrorLevel)) 116 | 117 | if dataFolderPath != "" && logFileName != "" { 118 | wr := getFileWriter(dataFolderPath, logFileName) 119 | fs := zapcore.AddSync(wr) 120 | cores = append(cores, zapcore.NewCore(enc, fs, DebugLevel)) 121 | } 122 | 123 | core := zapcore.NewTee(cores...) 124 | 125 | log := zap.New(core) 126 | log = log.Named(module) 127 | lvl := zap.NewAtomicLevelAt(zap.ErrorLevel) 128 | return Log{log, log.Sugar(), &lvl} 129 | } 130 | 131 | func NewDefault(module string) Log { 132 | return New(module, "", "") 133 | } 134 | 135 | // getBackendLevelWithFileBackend returns backends level including log file backend 136 | func getFileWriter(dataFolderPath, logFileName string) io.Writer { 137 | fileName := filepath.Join(dataFolderPath, logFileName) 138 | 139 | fileLogger := &lumberjack.Logger{ 140 | Filename: fileName, 141 | MaxSize: 500, // megabytes 142 | MaxBackups: 3, 143 | MaxAge: 28, // days 144 | Compress: false, 145 | } 146 | 147 | return fileLogger 148 | } 149 | 150 | // InitlibonomyLoggingSystem initializes app logging system. 151 | func InitlibonomyLoggingSystem(dataFolderPath string, logFileName string) { 152 | AppLog = NewDefault(mainLoggerName) 153 | } 154 | 155 | // public wrappers abstracting away logging lib impl 156 | 157 | // Info prints formatted info level log message. 158 | func Info(msg string, args ...interface{}) { 159 | AppLog.Info(msg, args...) 160 | } 161 | 162 | // Debug prints formatted debug level log message. 163 | func Debug(msg string, args ...interface{}) { 164 | AppLog.Debug(msg, args...) 165 | } 166 | 167 | // Error prints formatted error level log message. 168 | func Error(msg string, args ...interface{}) { 169 | AppLog.Error(msg, args...) 170 | } 171 | 172 | // Warning prints formatted warning level log message. 173 | func Warning(msg string, args ...interface{}) { 174 | AppLog.Warning(msg, args...) 175 | } 176 | 177 | func With() fieldLogger { 178 | return fieldLogger{AppLog.logger} 179 | } 180 | 181 | func Event() fieldLogger { 182 | return AppLog.Event() 183 | } 184 | 185 | func Panic(msg string, args ...interface{}) { 186 | AppLog.Panic(msg, args...) 187 | } 188 | -------------------------------------------------------------------------------- /os/filesystem/dirs.go: -------------------------------------------------------------------------------- 1 | // Package filesystem provides functionality for interacting with directories and files in a cross-platform manner. 2 | package filesystem 3 | 4 | import ( 5 | "os" 6 | "os/user" 7 | "path" 8 | "path/filepath" 9 | "strings" 10 | 11 | "github.com/libonomy/wallet-cli/os/app/config" 12 | "github.com/libonomy/wallet-cli/os/log" 13 | ) 14 | 15 | // Using a function pointer to get the current user so we can more easily mock in tests 16 | var currentUser = user.Current 17 | 18 | // Directory and paths funcs 19 | 20 | // OwnerReadWriteExec is a standard owner read / write / exec file permission. 21 | const OwnerReadWriteExec = 0700 22 | 23 | // OwnerReadWrite is a standard owner read / write file permission. 24 | const OwnerReadWrite = 0600 25 | 26 | // PathExists returns true iff file exists in local store and is accessible. 27 | func PathExists(path string) bool { 28 | _, err := os.Stat(path) 29 | if os.IsNotExist(err) { 30 | return false 31 | } 32 | return err == nil 33 | } 34 | 35 | // GetLibonomyDataDirectoryPath gets the full os-specific path to the libonomy top-level data directory. 36 | func GetLibonomyDataDirectoryPath() (string, error) { 37 | return GetFullDirectoryPath(config.ConfigValues.DataFilePath) 38 | } 39 | 40 | // GetLibonomyTempDirectoryPath gets the libonomy temp files dir so we don't have to work with convoluted os specific temp folders. 41 | func GetLibonomyTempDirectoryPath() (string, error) { 42 | return ensureDataSubDirectory("temp") 43 | } 44 | 45 | // DeleteAllTempFiles deletes all temp files from the temp dir and creates a new temp dir. 46 | func DeleteAllTempFiles() error { 47 | tempDir, err := GetLibonomyTempDirectoryPath() 48 | if err != nil { 49 | return err 50 | } 51 | 52 | err = os.RemoveAll(tempDir) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | // create temp dir again 58 | _, err = GetLibonomyTempDirectoryPath() 59 | return err 60 | } 61 | 62 | // EnsureLibonomyDataDirectories return the os-specific path to the libonomy data directory. 63 | // It creates the directory and all predefined sub directories on demand. 64 | func EnsureLibonomyDataDirectories() (string, error) { 65 | dataPath, err := GetLibonomyDataDirectoryPath() 66 | if err != nil { 67 | log.Error("Can't get or create libonomy data folder") 68 | return "", err 69 | } 70 | 71 | log.Debug("Data directory: %s", dataPath) 72 | 73 | // ensure sub folders exist - create them on demand 74 | _, err = GetAccountsDataDirectoryPath() 75 | if err != nil { 76 | return "", err 77 | } 78 | 79 | _, err = GetLogsDataDirectoryPath() 80 | if err != nil { 81 | return "", err 82 | } 83 | 84 | return dataPath, nil 85 | } 86 | 87 | // ensureDataSubDirectory ensure a sub-directory exists. 88 | func ensureDataSubDirectory(dirName string) (string, error) { 89 | dataPath, err := GetLibonomyDataDirectoryPath() 90 | if err != nil { 91 | log.Error("Failed to ensure data dir", err) 92 | return "", err 93 | } 94 | 95 | pathName := filepath.Join(dataPath, dirName) 96 | aPath, err := GetFullDirectoryPath(pathName) 97 | if err != nil { 98 | log.Error("Can't access libonomy folder", pathName, "Erorr:", err) 99 | return "", err 100 | } 101 | return aPath, nil 102 | } 103 | 104 | // GetAccountsDataDirectoryPath returns the path to the accounts data directory. 105 | // It will create the directory if it doesn't already exist. 106 | func GetAccountsDataDirectoryPath() (string, error) { 107 | return ensureDataSubDirectory(config.AccountsDirectoryName) 108 | } 109 | 110 | // GetLogsDataDirectoryPath returns the path to the app logs data directory. 111 | // It will create the directory if it doesn't already exist. 112 | func GetLogsDataDirectoryPath() (string, error) { 113 | return ensureDataSubDirectory(config.LogDirectoryName) 114 | } 115 | 116 | // GetUserHomeDirectory returns the current user's home directory if one is set by the system. 117 | func GetUserHomeDirectory() string { 118 | 119 | if home := os.Getenv("HOME"); home != "" { 120 | return home 121 | } 122 | if usr, err := currentUser(); err == nil { 123 | return usr.HomeDir 124 | } 125 | return "" 126 | } 127 | 128 | // GetCanonicalPath returns an os-specific full path following these rules: 129 | // - replace ~ with user's home dir path 130 | // - expand any ${vars} or $vars 131 | // - resolve relative paths /.../ 132 | // p: source path name 133 | func GetCanonicalPath(p string) string { 134 | 135 | if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") { 136 | if home := GetUserHomeDirectory(); home != "" { 137 | p = home + p[1:] 138 | } 139 | } 140 | return path.Clean(os.ExpandEnv(p)) 141 | } 142 | 143 | // GetFullDirectoryPath gets the OS specific full path for a named directory. 144 | // The directory is created if it doesn't exist. 145 | func GetFullDirectoryPath(name string) (string, error) { 146 | 147 | aPath := GetCanonicalPath(name) 148 | 149 | // create dir if it doesn't exist 150 | err := os.MkdirAll(aPath, OwnerReadWriteExec) 151 | 152 | return aPath, err 153 | } 154 | 155 | // EnsureNodesDataDirectory Gets the os-specific full path to the nodes master data directory. 156 | // Attempts to create the directory on-demand. 157 | func EnsureNodesDataDirectory(nodesDirectoryName string) (string, error) { 158 | dataPath, err := GetLibonomyDataDirectoryPath() 159 | if err != nil { 160 | return "", err 161 | } 162 | 163 | nodesDir := filepath.Join(dataPath, nodesDirectoryName) 164 | return GetFullDirectoryPath(nodesDir) 165 | } 166 | 167 | // EnsureNodeDataDirectory Gets the path to the node's data directory, e.g. /nodes/[node-id]/ 168 | // Directory will be created on demand if it doesn't exist. 169 | func EnsureNodeDataDirectory(nodesDataDir string, nodeID string) (string, error) { 170 | return GetFullDirectoryPath(filepath.Join(nodesDataDir, nodeID)) 171 | } 172 | 173 | // NodeDataFile Returns the os-specific full path to the node's data file. 174 | func NodeDataFile(nodesDataDir, NodeDataFileName, nodeID string) string { 175 | return filepath.Join(nodesDataDir, nodeID, NodeDataFileName) 176 | } 177 | -------------------------------------------------------------------------------- /crypto/keys.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "fmt" 7 | 8 | "github.com/btcsuite/btcd/btcec" 9 | "github.com/btcsuite/btcutil/base58" 10 | "github.com/libonomy/wallet-cli/os/log" 11 | ) 12 | 13 | // Key defines basic key caps. 14 | type Key interface { 15 | String() string // this is a base58 encoded of Bytes() 16 | Bytes() []byte // raw key binary data 17 | Pretty() string // pretty print key id 18 | } 19 | 20 | // PrivateKey defines a private encryption key. 21 | type PrivateKey interface { 22 | Key 23 | 24 | GetPublicKey() PublicKey // get the pub key corresponding to this priv key 25 | Sign([]byte) ([]byte, error) 26 | 27 | // Decrypt binary data encrypted with the public key of this private key 28 | Decrypt(in []byte) ([]byte, error) 29 | 30 | InternalKey() *btcec.PrivateKey 31 | } 32 | 33 | // PublicKey defines a public encryption key. 34 | type PublicKey interface { // 33 bytes 35 | Key 36 | Verify(data []byte, sig []byte) (bool, error) 37 | VerifyString(data []byte, sig string) (bool, error) 38 | 39 | // encrypt data so it is only decryptable w the private key of this key 40 | Encrypt(in []byte) ([]byte, error) 41 | 42 | InternalKey() *btcec.PublicKey 43 | } 44 | 45 | type publicKeyImpl struct { 46 | k *btcec.PublicKey 47 | } 48 | 49 | type privateKeyImpl struct { 50 | k *btcec.PrivateKey 51 | } 52 | 53 | // GenerateKeyPair generates a private and public key pair. 54 | func GenerateKeyPair() (PrivateKey, PublicKey, error) { 55 | privKey, err := btcec.NewPrivateKey(btcec.S256()) 56 | if err != nil { 57 | return nil, nil, err 58 | } 59 | 60 | return &privateKeyImpl{privKey}, &publicKeyImpl{privKey.PubKey()}, nil 61 | } 62 | 63 | // NewPrivateKey creates a new private key from data 64 | func NewPrivateKey(data []byte) (PrivateKey, error) { 65 | 66 | if len(data) != 32 { 67 | return nil, errors.New("expected 32 bytes input") 68 | } 69 | 70 | privk, _ := btcec.PrivKeyFromBytes(btcec.S256(), data) 71 | return &privateKeyImpl{privk}, nil 72 | } 73 | 74 | // NewPrivateKeyFromString creates a new private key a base58 encoded string. 75 | func NewPrivateKeyFromString(s string) (PrivateKey, error) { 76 | data := base58.Decode(s) 77 | return NewPrivateKey(data) 78 | } 79 | 80 | // InternalKey gets the internal key associated with a private key 81 | func (p *privateKeyImpl) InternalKey() *btcec.PrivateKey { 82 | return p.k 83 | } 84 | 85 | // Bytes returns the private key binary data 86 | func (p *privateKeyImpl) Bytes() []byte { 87 | return p.k.Serialize() 88 | } 89 | 90 | // String returns a base58 encoded string of the private key binary data 91 | func (p *privateKeyImpl) String() string { 92 | bytes := p.Bytes() 93 | return base58.Encode(bytes) 94 | } 95 | 96 | // GetPublicKey generates and returns the public key associated with a private key 97 | func (p *privateKeyImpl) GetPublicKey() PublicKey { 98 | pubKey := p.k.PubKey() 99 | return &publicKeyImpl{k: pubKey} 100 | } 101 | 102 | // Pretty returns a readable string of the private key data. 103 | func (p *privateKeyImpl) Pretty() string { 104 | pstr := p.String() 105 | maxRunes := 6 106 | if len(pstr) < maxRunes { 107 | maxRunes = len(pstr) 108 | } 109 | return fmt.Sprintf("", pstr[:maxRunes]) 110 | } 111 | 112 | // Sign signs binary data with the private key. 113 | func (p *privateKeyImpl) Sign(in []byte) ([]byte, error) { 114 | signature, err := p.k.Sign(in) 115 | if err != nil { 116 | return nil, err 117 | } 118 | return signature.Serialize(), nil 119 | } 120 | 121 | // Decrypt decrypts data encrypted with a public key using its matching private key 122 | func (p *privateKeyImpl) Decrypt(in []byte) ([]byte, error) { 123 | return btcec.Decrypt(p.k, in) 124 | } 125 | 126 | // NewPublicKey creates a new public key from provided binary key data. 127 | func NewPublicKey(data []byte) (PublicKey, error) { 128 | k, err := btcec.ParsePubKey(data, btcec.S256()) 129 | if err != nil { 130 | log.Error("Failed to parse public key from binay data", err) 131 | return nil, err 132 | } 133 | 134 | return &publicKeyImpl{k}, nil 135 | } 136 | 137 | // NewPublicKeyFromString creates a new public key from a base58 encoded string data. 138 | func NewPublicKeyFromString(s string) (PublicKey, error) { 139 | data := base58.Decode(s) 140 | return NewPublicKey(data) 141 | } 142 | 143 | // InternalKey returns the internal public key data. 144 | func (p *publicKeyImpl) InternalKey() *btcec.PublicKey { 145 | return p.k 146 | } 147 | 148 | // Bytes returns the raw public key data (33 bytes compressed format). 149 | func (p *publicKeyImpl) Bytes() []byte { 150 | return p.k.SerializeCompressed() 151 | } 152 | 153 | // String returns a base58 encoded string of the key binary data. 154 | func (p *publicKeyImpl) String() string { 155 | return base58.Encode(p.Bytes()) 156 | } 157 | 158 | // Pretty returns a readable short string of the public key. 159 | func (p *publicKeyImpl) Pretty() string { 160 | pstr := p.String() 161 | maxRunes := 6 162 | if len(pstr) < maxRunes { 163 | maxRunes = len(pstr) 164 | } 165 | return fmt.Sprintf("", pstr[:maxRunes]) 166 | } 167 | 168 | // VerifyString verifies data signed with the private key matching this public key. 169 | // sig: hex encoded binary data string 170 | func (p *publicKeyImpl) VerifyString(data []byte, sig string) (bool, error) { 171 | bin, err := hex.DecodeString(sig) 172 | if err != nil { 173 | return false, err 174 | } 175 | return p.Verify(data, bin) 176 | } 177 | 178 | // Verify verifies data was signed by provided signature. 179 | // It returns true iff data was signed using the private key matching this public key. 180 | /// sig: signature binary data 181 | func (p *publicKeyImpl) Verify(data []byte, sig []byte) (bool, error) { 182 | signature, err := btcec.ParseSignature(sig, btcec.S256()) 183 | if err != nil { 184 | return false, err 185 | } 186 | 187 | verified := signature.Verify(data, p.k) 188 | return verified, nil 189 | } 190 | 191 | // Encrypt encrypts data that can only be decrypted using the private key matching this public key. 192 | func (p *publicKeyImpl) Encrypt(in []byte) ([]byte, error) { 193 | return btcec.Encrypt(p.k, in) 194 | } 195 | -------------------------------------------------------------------------------- /os/crypto/keys.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "fmt" 7 | 8 | "github.com/btcsuite/btcd/btcec" 9 | "github.com/btcsuite/btcutil/base58" 10 | "github.com/libonomy/wallet-cli/os/log" 11 | ) 12 | 13 | //TODO: check if caching strings worth it. 14 | 15 | // Key defines basic key caps. 16 | type Key interface { 17 | String() string // this is a base58 encoded of Bytes() 18 | Bytes() []byte // raw key binary data 19 | Pretty() string // pretty print key id 20 | } 21 | 22 | // PrivateKey defines a private encryption key. 23 | type PrivateKey interface { 24 | Key 25 | 26 | GetPublicKey() PublicKey // get the pub key corresponding to this priv key 27 | Sign([]byte) ([]byte, error) 28 | 29 | // Decrypt binary data encrypted with the public key of this private key 30 | Decrypt(in []byte) ([]byte, error) 31 | 32 | InternalKey() *btcec.PrivateKey 33 | } 34 | 35 | // PublicKey defines a public encryption key. 36 | type PublicKey interface { // 33 bytes 37 | Key 38 | Verify(data []byte, sig []byte) (bool, error) 39 | VerifyString(data []byte, sig string) (bool, error) 40 | 41 | // encrypt data so it is only decryptable w the private key of this key 42 | Encrypt(in []byte) ([]byte, error) 43 | 44 | InternalKey() *btcec.PublicKey 45 | } 46 | 47 | type publicKeyImpl struct { 48 | k *btcec.PublicKey 49 | } 50 | 51 | type privateKeyImpl struct { 52 | k *btcec.PrivateKey 53 | } 54 | 55 | // GenerateKeyPair generates a private and public key pair. 56 | func GenerateKeyPair() (PrivateKey, PublicKey, error) { 57 | privKey, err := btcec.NewPrivateKey(btcec.S256()) 58 | if err != nil { 59 | return nil, nil, err 60 | } 61 | 62 | return &privateKeyImpl{privKey}, &publicKeyImpl{privKey.PubKey()}, nil 63 | } 64 | 65 | // NewPrivateKey creates a new private key from data 66 | func NewPrivateKey(data []byte) (PrivateKey, error) { 67 | 68 | if len(data) != 32 { 69 | return nil, errors.New("expected 32 bytes input") 70 | } 71 | 72 | privk, _ := btcec.PrivKeyFromBytes(btcec.S256(), data) 73 | return &privateKeyImpl{privk}, nil 74 | } 75 | 76 | // NewPrivateKeyFromString creates a new private key a base58 encoded string. 77 | func NewPrivateKeyFromString(s string) (PrivateKey, error) { 78 | data := base58.Decode(s) 79 | return NewPrivateKey(data) 80 | } 81 | 82 | // InternalKey gets the internal key associated with a private key 83 | func (p *privateKeyImpl) InternalKey() *btcec.PrivateKey { 84 | return p.k 85 | } 86 | 87 | // Bytes returns the private key binary data 88 | func (p *privateKeyImpl) Bytes() []byte { 89 | return p.k.Serialize() 90 | } 91 | 92 | // String returns a base58 encoded string of the private key binary data 93 | func (p *privateKeyImpl) String() string { 94 | bytes := p.Bytes() 95 | return base58.Encode(bytes) 96 | } 97 | 98 | // GetPublicKey generates and returns the public key associated with a private key 99 | func (p *privateKeyImpl) GetPublicKey() PublicKey { 100 | pubKey := p.k.PubKey() 101 | return &publicKeyImpl{k: pubKey} 102 | } 103 | 104 | // Pretty returns a readable string of the private key data. 105 | func (p *privateKeyImpl) Pretty() string { 106 | pstr := p.String() 107 | maxRunes := 6 108 | if len(pstr) < maxRunes { 109 | maxRunes = len(pstr) 110 | } 111 | return fmt.Sprintf("", pstr[:maxRunes]) 112 | } 113 | 114 | // Sign signs binary data with the private key. 115 | func (p *privateKeyImpl) Sign(in []byte) ([]byte, error) { 116 | signature, err := p.k.Sign(in) 117 | if err != nil { 118 | return nil, err 119 | } 120 | return signature.Serialize(), nil 121 | } 122 | 123 | // Decrypt decrypts data encrypted with a public key using its matching private key 124 | func (p *privateKeyImpl) Decrypt(in []byte) ([]byte, error) { 125 | return btcec.Decrypt(p.k, in) 126 | } 127 | 128 | // NewPublicKey creates a new public key from provided binary key data. 129 | func NewPublicKey(data []byte) (PublicKey, error) { 130 | k, err := btcec.ParsePubKey(data, btcec.S256()) 131 | if err != nil { 132 | log.Error("Failed to parse public key from binay data", err) 133 | return nil, err 134 | } 135 | 136 | return &publicKeyImpl{k}, nil 137 | } 138 | 139 | // NewPublicKeyFromString creates a new public key from a base58 encoded string data. 140 | func NewPublicKeyFromString(s string) (PublicKey, error) { 141 | data := base58.Decode(s) 142 | return NewPublicKey(data) 143 | } 144 | 145 | // InternalKey returns the internal public key data. 146 | func (p *publicKeyImpl) InternalKey() *btcec.PublicKey { 147 | return p.k 148 | } 149 | 150 | // Bytes returns the raw public key data (33 bytes compressed format). 151 | func (p *publicKeyImpl) Bytes() []byte { 152 | return p.k.SerializeCompressed() 153 | } 154 | 155 | // String returns a base58 encoded string of the key binary data. 156 | func (p *publicKeyImpl) String() string { 157 | return base58.Encode(p.Bytes()) 158 | } 159 | 160 | // Pretty returns a readable short string of the public key. 161 | func (p *publicKeyImpl) Pretty() string { 162 | pstr := p.String() 163 | maxRunes := 6 164 | if len(pstr) < maxRunes { 165 | maxRunes = len(pstr) 166 | } 167 | return fmt.Sprintf("", pstr[:maxRunes]) 168 | } 169 | 170 | // VerifyString verifies data signed with the private key matching this public key. 171 | // sig: hex encoded binary data string 172 | func (p *publicKeyImpl) VerifyString(data []byte, sig string) (bool, error) { 173 | bin, err := hex.DecodeString(sig) 174 | if err != nil { 175 | return false, err 176 | } 177 | return p.Verify(data, bin) 178 | } 179 | 180 | // Verify verifies data was signed by provided signature. 181 | // It returns true iff data was signed using the private key matching this public key. 182 | /// sig: signature binary data 183 | func (p *publicKeyImpl) Verify(data []byte, sig []byte) (bool, error) { 184 | signature, err := btcec.ParseSignature(sig, btcec.S256()) 185 | if err != nil { 186 | return false, err 187 | } 188 | 189 | verified := signature.Verify(data, p.k) 190 | return verified, nil 191 | } 192 | 193 | // Encrypt encrypts data that can only be decrypted using the private key matching this public key. 194 | func (p *publicKeyImpl) Encrypt(in []byte) ([]byte, error) { 195 | return btcec.Encrypt(p.k, in) 196 | } 197 | -------------------------------------------------------------------------------- /client/json_http_client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "io/ioutil" 9 | "net/http" 10 | "strconv" 11 | 12 | "github.com/libonomy/wallet-cli/accounts" 13 | "github.com/libonomy/wallet-cli/os/log" 14 | ) 15 | 16 | const DefaultNodeHostPort = "localhost:9090" 17 | 18 | type Requester interface { 19 | Get(api, data string) []byte 20 | } 21 | 22 | type HTTPRequester struct { 23 | *http.Client 24 | url string 25 | } 26 | 27 | func NewHTTPRequester(url string) *HTTPRequester { 28 | return &HTTPRequester{&http.Client{}, url} 29 | } 30 | 31 | func (hr *HTTPRequester) Get(api, data string, logIO bool) (map[string]interface{}, error) { 32 | var jsonStr = []byte(data) 33 | url := hr.url + api 34 | if logIO { 35 | log.Info("request: %v, body: %v", url, data) 36 | } 37 | req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr)) 38 | if err != nil { 39 | return nil, err 40 | } 41 | req.Header.Set("Content-Type", "application/json") 42 | 43 | res, err := hr.Do(req) 44 | if err != nil { 45 | return nil, err 46 | } 47 | defer res.Body.Close() 48 | 49 | resBody, _ := ioutil.ReadAll(res.Body) 50 | if logIO { 51 | log.Info("response body: %s", resBody) 52 | } 53 | 54 | if res.StatusCode != http.StatusOK { 55 | return nil, fmt.Errorf("`%v` response status code: %d", api, res.StatusCode) 56 | } 57 | 58 | var f interface{} 59 | err = json.NewDecoder(bytes.NewBuffer(resBody)).Decode(&f) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | return f.(map[string]interface{}), nil 65 | } 66 | 67 | type HttpClient struct { 68 | Requester 69 | } 70 | 71 | func (hr *HTTPRequester) NodeURL() string { 72 | return hr.url 73 | } 74 | 75 | func printBuffer(b []byte) string { 76 | st := "[" 77 | for _, byt := range b { 78 | st += strconv.Itoa(int(byt)) + "," 79 | } 80 | st = st[:len(st)-1] + "]" 81 | return st 82 | } 83 | 84 | func (m HTTPRequester) AccountInfo(address string) (*accounts.AccountInfo, error) { 85 | str := fmt.Sprintf(`{ "address": "0x%s"}`, address) 86 | output, err := m.Get("/nonce", str, true) 87 | if err != nil { 88 | return nil, err 89 | } 90 | 91 | acc := accounts.AccountInfo{} 92 | if val, ok := output["value"]; ok { 93 | acc.Nonce = val.(string) 94 | } else { 95 | return nil, fmt.Errorf("cant get nonce %v", output) 96 | } 97 | 98 | output, err = m.Get("/balance", str, true) 99 | if err != nil { 100 | return nil, err 101 | } 102 | 103 | if val, ok := output["value"]; ok { 104 | acc.Balance = val.(string) 105 | } else { 106 | return nil, fmt.Errorf("cant get balance %v", output) 107 | } 108 | 109 | return &acc, nil 110 | } 111 | 112 | type NodeInfo struct { 113 | Synced bool 114 | SyncedLayer string 115 | CurrentLayer string 116 | VerifiedLayer string 117 | Peers string 118 | MinPeers string 119 | MaxPeers string 120 | LibonomyDatadir string 121 | LibonomyStatus string 122 | LibonomyCoinbase string 123 | LibonomyRemainingBytes string 124 | } 125 | 126 | func (m HTTPRequester) NodeInfo() (*NodeInfo, error) { 127 | nodeStatus, err := m.Get("/nodestatus", "", true) 128 | if err != nil { 129 | return nil, err 130 | } 131 | 132 | stats, err := m.Get("/stats", "", true) 133 | if err != nil { 134 | return nil, err 135 | } 136 | 137 | info := &NodeInfo{ 138 | SyncedLayer: "0", 139 | CurrentLayer: "0", 140 | VerifiedLayer: "0", 141 | Peers: "0", 142 | MinPeers: "0", 143 | MaxPeers: "0", 144 | LibonomyRemainingBytes: "0", 145 | } 146 | 147 | if val, ok := nodeStatus["synced"]; ok { 148 | info.Synced = val.(bool) 149 | } 150 | if val, ok := nodeStatus["syncedLayer"]; ok { 151 | info.SyncedLayer = val.(string) 152 | } 153 | if val, ok := nodeStatus["currentLayer"]; ok { 154 | info.CurrentLayer = val.(string) 155 | } 156 | if val, ok := nodeStatus["verifiedLayer"]; ok { 157 | info.VerifiedLayer = val.(string) 158 | } 159 | if val, ok := nodeStatus["peers"]; ok { 160 | info.Peers = val.(string) 161 | } 162 | if val, ok := nodeStatus["minPeers"]; ok { 163 | info.MinPeers = val.(string) 164 | } 165 | if val, ok := nodeStatus["maxPeers"]; ok { 166 | info.MaxPeers = val.(string) 167 | } 168 | if val, ok := stats["dataDir"]; ok { 169 | info.LibonomyDatadir = val.(string) 170 | } 171 | if val, ok := stats["status"]; ok { 172 | switch val.(float64) { 173 | case 1: 174 | info.LibonomyStatus = "`idle`" 175 | case 2: 176 | info.LibonomyStatus = "`in-progress`" 177 | case 3: 178 | info.LibonomyStatus = "`done`" 179 | } 180 | } 181 | if val, ok := stats["coinbase"]; ok { 182 | info.LibonomyCoinbase = val.(string) 183 | } 184 | if val, ok := stats["remainingBytes"]; ok { 185 | info.LibonomyRemainingBytes = val.(string) 186 | } 187 | 188 | return info, nil 189 | } 190 | 191 | func (m HTTPRequester) Send(b []byte) (string, error) { 192 | str := fmt.Sprintf(`{ "tx": %s}`, printBuffer(b)) 193 | res, err := m.Get("/submittransaction", str, true) 194 | if err != nil { 195 | return "", err 196 | } 197 | 198 | val, ok := res["id"] 199 | if !ok { 200 | return "", errors.New("failed to submit tx") 201 | } 202 | return val.(string), nil 203 | } 204 | 205 | func (m HTTPRequester) Rebel(datadir string, space uint, coinbase string) error { 206 | str := fmt.Sprintf(`{ "logicalDrive": "%s", "commitmentSize": %d, "coinbase": "%s"}`, datadir, space, coinbase) 207 | _, err := m.Get("/startmining", str, true) 208 | if err != nil { 209 | return err 210 | } 211 | 212 | return nil 213 | } 214 | 215 | func (m HTTPRequester) ListTxs(address string) ([]string, error) { 216 | str := fmt.Sprintf(`{ "account": { "address": "%s"} }`, address) 217 | res, err := m.Get("/accounttxs", str, true) 218 | if err != nil { 219 | return nil, err 220 | } 221 | 222 | txs := make([]string, 0) 223 | val, ok := res["txs"] 224 | if !ok { 225 | return txs, nil 226 | } 227 | for _, val := range val.([]interface{}) { 228 | txs = append(txs, val.(string)) 229 | } 230 | return txs, nil 231 | } 232 | 233 | func (m HTTPRequester) SetCoinbase(coinbase string) error { 234 | str := fmt.Sprintf(`{ "address": "%s"}`, coinbase) 235 | _, err := m.Get("/setawardsaddr", str, true) 236 | if err != nil { 237 | return err 238 | } 239 | 240 | return nil 241 | } 242 | 243 | func (m HTTPRequester) Sanity() error { 244 | _, err := m.Get("/example/echo", "", false) 245 | if err != nil { 246 | return err 247 | } 248 | 249 | return nil 250 | } 251 | -------------------------------------------------------------------------------- /os/crypto/sha3/sha3.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sha3 6 | 7 | // spongeDirection indicates the direction bytes are flowing through the sponge. 8 | type spongeDirection int 9 | 10 | const ( 11 | // spongeAbsorbing indicates that the sponge is absorbing input. 12 | spongeAbsorbing spongeDirection = iota 13 | // spongeSqueezing indicates that the sponge is being squeezed. 14 | spongeSqueezing 15 | ) 16 | 17 | const ( 18 | // maxRate is the maximum size of the internal buffer. SHAKE-256 19 | // currently needs the largest buffer. 20 | maxRate = 168 21 | ) 22 | 23 | type state struct { 24 | // Generic sponge components. 25 | a [25]uint64 // main state of the hash 26 | buf []byte // points into storage 27 | rate int // the number of bytes of state to use 28 | 29 | // dsbyte contains the "domain separation" bits and the first bit of 30 | // the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the 31 | // SHA-3 and SHAKE functions by appending bitstrings to the message. 32 | // Using a little-endian bit-ordering convention, these are "01" for SHA-3 33 | // and "1111" for SHAKE, or 00000010b and 00001111b, respectively. Then the 34 | // padding rule from section 5.1 is applied to pad the message to a multiple 35 | // of the rate, which involves adding a "1" bit, zero or more "0" bits, and 36 | // a final "1" bit. We merge the first "1" bit from the padding into dsbyte, 37 | // giving 00000110b (0x06) and 00011111b (0x1f). 38 | // [1] http://csrc.nist.gov/publications/drafts/fips-202/fips_202_draft.pdf 39 | // "Draft FIPS 202: SHA-3 Standard: Permutation-Based Hash and 40 | // Extendable-Output Functions (May 2014)" 41 | dsbyte byte 42 | storage [maxRate]byte 43 | 44 | // Specific to SHA-3 and SHAKE. 45 | outputLen int // the default output size in bytes 46 | state spongeDirection // whether the sponge is absorbing or squeezing 47 | } 48 | 49 | // BlockSize returns the rate of sponge underlying this hash function. 50 | func (d *state) BlockSize() int { return d.rate } 51 | 52 | // Size returns the output size of the hash function in bytes. 53 | func (d *state) Size() int { return d.outputLen } 54 | 55 | // Reset clears the internal state by zeroing the sponge state and 56 | // the byte buffer, and setting Sponge.state to absorbing. 57 | func (d *state) Reset() { 58 | // Zero the permutation's state. 59 | for i := range d.a { 60 | d.a[i] = 0 61 | } 62 | d.state = spongeAbsorbing 63 | d.buf = d.storage[:0] 64 | } 65 | 66 | func (d *state) clone() *state { 67 | ret := *d 68 | if ret.state == spongeAbsorbing { 69 | ret.buf = ret.storage[:len(ret.buf)] 70 | } else { 71 | ret.buf = ret.storage[d.rate-cap(d.buf) : d.rate] 72 | } 73 | 74 | return &ret 75 | } 76 | 77 | // permute applies the KeccakF-1600 permutation. It handles 78 | // any input-output buffering. 79 | func (d *state) permute() { 80 | switch d.state { 81 | case spongeAbsorbing: 82 | // If we're absorbing, we need to xor the input into the state 83 | // before applying the permutation. 84 | xorIn(d, d.buf) 85 | d.buf = d.storage[:0] 86 | keccakF1600(&d.a) 87 | case spongeSqueezing: 88 | // If we're squeezing, we need to apply the permutatin before 89 | // copying more output. 90 | keccakF1600(&d.a) 91 | d.buf = d.storage[:d.rate] 92 | copyOut(d, d.buf) 93 | } 94 | } 95 | 96 | // pads appends the domain separation bits in dsbyte, applies 97 | // the multi-bitrate 10..1 padding rule, and permutes the state. 98 | func (d *state) padAndPermute(dsbyte byte) { 99 | if d.buf == nil { 100 | d.buf = d.storage[:0] 101 | } 102 | // Pad with this instance's domain-separator bits. We know that there's 103 | // at least one byte of space in d.buf because, if it were full, 104 | // permute would have been called to empty it. dsbyte also contains the 105 | // first one bit for the padding. See the comment in the state struct. 106 | d.buf = append(d.buf, dsbyte) 107 | zerosStart := len(d.buf) 108 | d.buf = d.storage[:d.rate] 109 | for i := zerosStart; i < d.rate; i++ { 110 | d.buf[i] = 0 111 | } 112 | // This adds the final one bit for the padding. Because of the way that 113 | // bits are numbered from the LSB upwards, the final bit is the MSB of 114 | // the last byte. 115 | d.buf[d.rate-1] ^= 0x80 116 | // Apply the permutation 117 | d.permute() 118 | d.state = spongeSqueezing 119 | d.buf = d.storage[:d.rate] 120 | copyOut(d, d.buf) 121 | } 122 | 123 | // Write absorbs more data into the hash's state. It produces an error 124 | // if more data is written to the ShakeHash after writing 125 | func (d *state) Write(p []byte) (written int, err error) { 126 | if d.state != spongeAbsorbing { 127 | panic("sha3: write to sponge after read") 128 | } 129 | if d.buf == nil { 130 | d.buf = d.storage[:0] 131 | } 132 | written = len(p) 133 | 134 | for len(p) > 0 { 135 | if len(d.buf) == 0 && len(p) >= d.rate { 136 | // The fast path; absorb a full "rate" bytes of input and apply the permutation. 137 | xorIn(d, p[:d.rate]) 138 | p = p[d.rate:] 139 | keccakF1600(&d.a) 140 | } else { 141 | // The slow path; buffer the input until we can fill the sponge, and then xor it in. 142 | todo := d.rate - len(d.buf) 143 | if todo > len(p) { 144 | todo = len(p) 145 | } 146 | d.buf = append(d.buf, p[:todo]...) 147 | p = p[todo:] 148 | 149 | // If the sponge is full, apply the permutation. 150 | if len(d.buf) == d.rate { 151 | d.permute() 152 | } 153 | } 154 | } 155 | 156 | return 157 | } 158 | 159 | // Read squeezes an arbitrary number of bytes from the sponge. 160 | func (d *state) Read(out []byte) (n int, err error) { 161 | // If we're still absorbing, pad and apply the permutation. 162 | if d.state == spongeAbsorbing { 163 | d.padAndPermute(d.dsbyte) 164 | } 165 | 166 | n = len(out) 167 | 168 | // Now, do the squeezing. 169 | for len(out) > 0 { 170 | n := copy(out, d.buf) 171 | d.buf = d.buf[n:] 172 | out = out[n:] 173 | 174 | // Apply the permutation if we've squeezed the sponge dry. 175 | if len(d.buf) == 0 { 176 | d.permute() 177 | } 178 | } 179 | 180 | return 181 | } 182 | 183 | // Sum applies padding to the hash state and then squeezes out the desired 184 | // number of output bytes. 185 | func (d *state) Sum(in []byte) []byte { 186 | // Make a copy of the original hash so that caller can keep writing 187 | // and summing. 188 | dup := d.clone() 189 | hash := make([]byte, dup.outputLen) 190 | dup.Read(hash) 191 | return append(in, hash...) 192 | } 193 | -------------------------------------------------------------------------------- /os/common/bytes.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | b64 "encoding/base64" 5 | "encoding/binary" 6 | "encoding/hex" 7 | "fmt" 8 | "math/big" 9 | "math/rand" 10 | "reflect" 11 | 12 | "github.com/libonomy/wallet-cli/os/common/hexutil" 13 | ) 14 | 15 | const ( 16 | // HashLength is the expected length of the hash 17 | HashLength = 32 18 | // AddressLength is the expected length of the address 19 | AddressLength = 20 20 | ) 21 | 22 | func BytesToUint32(i []byte) uint32 { return binary.LittleEndian.Uint32(i) } 23 | 24 | func Uint32ToBytes(i uint32) []byte { 25 | a := make([]byte, 4) 26 | binary.LittleEndian.PutUint32(a, i) 27 | return a 28 | } 29 | 30 | func BytesToUint64(i []byte) uint64 { return binary.LittleEndian.Uint64(i) } 31 | 32 | func Uint64ToBytes(i uint64) []byte { 33 | a := make([]byte, 8) 34 | binary.LittleEndian.PutUint64(a, i) 35 | return a 36 | } 37 | 38 | // Hash represents the 32 byte Keccak256 hash of arbitrary data. 39 | type Hash [HashLength]byte 40 | 41 | var ( 42 | hashT = reflect.TypeOf(Hash{}) 43 | ) 44 | 45 | // BytesToHash sets b to hash. 46 | // If b is larger than len(h), b will be cropped from the left. 47 | func BytesToHash(b []byte) Hash { 48 | var h Hash 49 | h.SetBytes(b) 50 | return h 51 | } 52 | 53 | // BigToHash sets byte representation of b to hash. 54 | // If b is larger than len(h), b will be cropped from the left. 55 | func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) } 56 | 57 | // HexToHash sets byte representation of s to hash. 58 | // If b is larger than len(h), b will be cropped from the left. 59 | func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) } 60 | 61 | // Bytes gets the byte representation of the underlying hash. 62 | func (h Hash) Bytes() []byte { return h[:] } 63 | 64 | // Big converts a hash to a big integer. 65 | func (h Hash) Big() *big.Int { return new(big.Int).SetBytes(h[:]) } 66 | 67 | // Hex converts a hash to a hex string. 68 | func (h Hash) Hex() string { return hexutil.Encode(h[:]) } 69 | 70 | // TerminalString implements log.TerminalStringer, formatting a string for console 71 | // output during logging. 72 | func (h Hash) TerminalString() string { 73 | return fmt.Sprintf("%x…%x", h[:3], h[29:]) 74 | } 75 | 76 | // String implements the stringer interface and is used also by the logger when 77 | // doing full logging into a file. 78 | func (h Hash) String() string { 79 | return h.Hex() 80 | } 81 | 82 | func (h Hash) ShortString() string { 83 | l := len(h.Hex()) 84 | return h.Hex()[Min(2, l):Min(7, l)] 85 | } 86 | 87 | // Format implements fmt.Formatter, forcing the byte slice to be formatted as is, 88 | // without going through the stringer interface used for logging. 89 | func (h Hash) Format(s fmt.State, c rune) { 90 | fmt.Fprintf(s, "%"+string(c), h[:]) 91 | } 92 | 93 | // UnmarshalText parses a hash in hex syntax. 94 | func (h *Hash) UnmarshalText(input []byte) error { 95 | return hexutil.UnmarshalFixedText("Hash", input, h[:]) 96 | } 97 | 98 | // UnmarshalJSON parses a hash in hex syntax. 99 | func (h *Hash) UnmarshalJSON(input []byte) error { 100 | return hexutil.UnmarshalFixedJSON(hashT, input, h[:]) 101 | } 102 | 103 | // MarshalText returns the hex representation of h. 104 | func (h Hash) MarshalText() ([]byte, error) { 105 | return hexutil.Bytes(h[:]).MarshalText() 106 | } 107 | 108 | // SetBytes sets the hash to the value of b. 109 | // If b is larger than len(h), b will be cropped from the left. 110 | func (h *Hash) SetBytes(b []byte) { 111 | if len(b) > len(h) { 112 | b = b[len(b)-HashLength:] 113 | } 114 | 115 | copy(h[HashLength-len(b):], b) 116 | } 117 | 118 | // FromHex returns the bytes represented by the hexadecimal string s. 119 | // s may be prefixed with "0x". 120 | func FromHex(s string) []byte { 121 | if len(s) > 1 { 122 | if s[0:2] == "0x" || s[0:2] == "0X" { 123 | s = s[2:] 124 | } 125 | } 126 | if len(s)%2 == 1 { 127 | s = "0" + s 128 | } 129 | return Hex2Bytes(s) 130 | } 131 | 132 | // Hex2Bytes returns the bytes represented by the hexadecimal string str. 133 | func Hex2Bytes(str string) []byte { 134 | h, _ := hex.DecodeString(str) 135 | return h 136 | } 137 | 138 | // Bytes2Hex returns the hexadecimal encoding of d. 139 | func Bytes2Hex(d []byte) string { 140 | return hex.EncodeToString(d) 141 | } 142 | 143 | // Generate implements testing/quick.Generator. 144 | func (h Hash) Generate(rand *rand.Rand, size int) reflect.Value { 145 | m := rand.Intn(len(h)) 146 | for i := len(h) - 1; i > m; i-- { 147 | h[i] = byte(rand.Uint32()) 148 | } 149 | return reflect.ValueOf(h) 150 | } 151 | 152 | // Scan implements Scanner for database/sql. 153 | func (h *Hash) Scan(src interface{}) error { 154 | srcB, ok := src.([]byte) 155 | if !ok { 156 | return fmt.Errorf("can't scan %T into Hash", src) 157 | } 158 | if len(srcB) != HashLength { 159 | return fmt.Errorf("can't scan []byte of len %d into Hash, want %d", len(srcB), HashLength) 160 | } 161 | copy(h[:], srcB) 162 | return nil 163 | } 164 | 165 | // CopyBytes returns an exact copy of the provided bytes. 166 | func CopyBytes(b []byte) (copiedBytes []byte) { 167 | if b == nil { 168 | return nil 169 | } 170 | copiedBytes = make([]byte, len(b)) 171 | copy(copiedBytes, b) 172 | 173 | return 174 | } 175 | 176 | // RightPadBytes zero-pads slice to the right up to length l. 177 | func RightPadBytes(slice []byte, l int) []byte { 178 | if l <= len(slice) { 179 | return slice 180 | } 181 | 182 | padded := make([]byte, l) 183 | copy(padded, slice) 184 | 185 | return padded 186 | } 187 | 188 | // LeftPadBytes zero-pads slice to the left up to length l. 189 | func LeftPadBytes(slice []byte, l int) []byte { 190 | if l <= len(slice) { 191 | return slice 192 | } 193 | 194 | padded := make([]byte, l) 195 | copy(padded[l-len(slice):], slice) 196 | 197 | return padded 198 | } 199 | 200 | // hasHexPrefix validates str begins with '0x' or '0X'. 201 | func hasHexPrefix(str string) bool { 202 | return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X') 203 | } 204 | 205 | // isHexCharacter returns bool of c being a valid hexadecimal. 206 | func isHexCharacter(c byte) bool { 207 | return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F') 208 | } 209 | 210 | // isHex validates whether each byte is valid hexadecimal string. 211 | func isHex(str string) bool { 212 | if len(str)%2 != 0 { 213 | return false 214 | } 215 | for _, c := range []byte(str) { 216 | if !isHexCharacter(c) { 217 | return false 218 | } 219 | } 220 | return true 221 | } 222 | 223 | // Decode base64 string into a byte array 224 | func Base64ToBytes(str string) ([]byte, error) { 225 | return b64.StdEncoding.DecodeString(str) 226 | } 227 | 228 | // Encode byte array into base64 string 229 | func BytesToBase64(bytes []byte) string { 230 | return b64.StdEncoding.EncodeToString(bytes) 231 | } 232 | -------------------------------------------------------------------------------- /os/log/zap.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "go.uber.org/zap" 6 | "go.uber.org/zap/zapcore" 7 | "os" 8 | "runtime/debug" 9 | "time" 10 | ) 11 | 12 | var NilLogger Log 13 | 14 | // Log is an exported type that embeds our logger. 15 | type Log struct { 16 | logger *zap.Logger 17 | sugar *zap.SugaredLogger 18 | lvl *zap.AtomicLevel 19 | } 20 | 21 | // Exported from Log basic logging options. 22 | 23 | // Info prints formatted info level log message. 24 | func (l Log) Info(format string, args ...interface{}) { 25 | l.sugar.Infof(format, args...) 26 | } 27 | 28 | // Debug prints formatted debug level log message. 29 | func (l Log) Debug(format string, args ...interface{}) { 30 | l.sugar.Debugf(format, args...) 31 | } 32 | 33 | // Error prints formatted error level log message. 34 | func (l Log) Error(format string, args ...interface{}) { 35 | l.sugar.Errorf(format, args...) 36 | } 37 | 38 | // Warning prints formatted warning level log message. 39 | func (l Log) Warning(format string, args ...interface{}) { 40 | l.sugar.Warnf(format, args...) 41 | } 42 | 43 | func (l Log) Panic(format string, args ...interface{}) { 44 | l.sugar.Error("Fatal: goroutine panicked. Stacktrace: ", string(debug.Stack())) 45 | l.sugar.Panicf(format, args...) 46 | } 47 | 48 | // Wrap and export field logic 49 | 50 | // Field is a log field holding a name and value 51 | type Field zap.Field 52 | 53 | func (f Field) Field() Field { return f } 54 | 55 | // String returns a string Field 56 | func String(name, val string) Field { 57 | return Field(zap.String(name, val)) 58 | } 59 | 60 | // ByteString returns a byte string ([]byte) Field 61 | func ByteString(name string, val []byte) Field { 62 | return Field(zap.ByteString(name, val)) 63 | } 64 | 65 | // Int returns an int Field 66 | func Int(name string, val int) Field { 67 | return Field(zap.Int(name, val)) 68 | } 69 | 70 | func Int32(name string, val int32) Field { 71 | return Field(zap.Int32(name, val)) 72 | } 73 | 74 | func Uint32(name string, val uint32) Field { 75 | return Field(zap.Uint32(name, val)) 76 | } 77 | 78 | // Uint64 returns an uint64 Field 79 | func Uint64(name string, val uint64) Field { 80 | return Field(zap.Uint64(name, val)) 81 | } 82 | 83 | // Namespace make next fields be inside a namespace. 84 | func Namespace(name string) Field { 85 | return Field(zap.Namespace(name)) 86 | } 87 | 88 | // Bool returns a bool field 89 | func Bool(name string, val bool) Field { 90 | return Field(zap.Bool(name, val)) 91 | } 92 | 93 | // Duration returns a duration field 94 | func Duration(name string, val time.Duration) Field { 95 | return Field(zap.Duration(name, val)) 96 | } 97 | 98 | // LayerId return a Uint64 field (key - "layer_id") 99 | func LayerId(val uint64) Field { 100 | return Uint64("layer_id", val) 101 | } 102 | 103 | // TxId return a String field (key - "tx_id") 104 | func TxId(val string) Field { 105 | return String("tx_id", val) 106 | } 107 | 108 | // AtxId return a String field (key - "atx_id") 109 | func AtxId(val string) Field { 110 | return String("atx_id", val) 111 | } 112 | 113 | // BlockId return a Uint64 field (key - "block_id") 114 | func BlockId(val string) Field { 115 | return String("block_id", val) 116 | } 117 | 118 | // EpochId return a Uint64 field (key - "epoch_id") 119 | func EpochId(val uint64) Field { 120 | return Uint64("epoch_id", val) 121 | } 122 | 123 | // NodeId return a String field (key - "node_id") 124 | func NodeId(val string) Field { 125 | return String("node_id", val) 126 | } 127 | 128 | // Err returns an error field 129 | func Err(v error) Field { 130 | return Field(zap.Error(v)) 131 | } 132 | 133 | type LoggableField interface { 134 | Field() Field 135 | } 136 | 137 | func unpack(fields []LoggableField) []zap.Field { 138 | flds := make([]zap.Field, len(fields)) 139 | for i, f := range fields { 140 | flds[i] = zap.Field(f.Field()) 141 | } 142 | return flds 143 | } 144 | 145 | type fieldLogger struct { 146 | l *zap.Logger 147 | } 148 | 149 | // With returns a logger object that logs fields 150 | func (l Log) With() fieldLogger { 151 | return fieldLogger{l.logger} 152 | } 153 | 154 | func (l Log) SetLevel(level *zap.AtomicLevel) Log { 155 | lgr := l.logger.WithOptions(AddDynamicLevel(level)) 156 | return Log{ 157 | lgr, 158 | lgr.Sugar(), 159 | level, 160 | } 161 | } 162 | 163 | // LogWith returns a logger the given fields 164 | func (l Log) WithName(prefix string) Log { 165 | lgr := l.logger.Named(fmt.Sprintf("%-13s", prefix)).WithOptions(AddDynamicLevel(l.lvl)) 166 | return Log{ 167 | lgr, 168 | lgr.Sugar(), 169 | l.lvl, 170 | } 171 | } 172 | 173 | func AddDynamicLevel(level *zap.AtomicLevel) zap.Option { 174 | return zap.WrapCore(func(core zapcore.Core) zapcore.Core { 175 | return &coreWithLevel{ 176 | Core: core, 177 | lvl: level, 178 | } 179 | }) 180 | } 181 | 182 | type coreWithLevel struct { 183 | zapcore.Core 184 | lvl *zap.AtomicLevel 185 | } 186 | 187 | func (c *coreWithLevel) Enabled(level zapcore.Level) bool { 188 | return c.lvl.Enabled(level) 189 | } 190 | 191 | func (c *coreWithLevel) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { 192 | if !c.lvl.Enabled(e.Level) { 193 | return ce 194 | } 195 | return ce.AddCore(e, c.Core) 196 | } 197 | 198 | func (l Log) WithFields(fields ...LoggableField) Log { 199 | lgr := l.logger.With(unpack(fields)...).WithOptions(AddDynamicLevel(l.lvl)) 200 | return Log{ 201 | logger: lgr, 202 | sugar: lgr.Sugar(), 203 | lvl: l.lvl, 204 | } 205 | } 206 | 207 | const event_key = "event" 208 | 209 | func (l Log) Event() fieldLogger { 210 | return fieldLogger{l: l.logger.With(zap.Field(Bool(event_key, true)))} 211 | } 212 | 213 | func EnableLevelOption(enabler zapcore.LevelEnabler) zap.Option { 214 | return zap.WrapCore(func(core zapcore.Core) zapcore.Core { 215 | consoleSyncer := zapcore.AddSync(os.Stdout) 216 | return zapcore.NewCore(encoder(), consoleSyncer, enabler) 217 | }) 218 | } 219 | 220 | var Nop = zap.WrapCore(func(zapcore.Core) zapcore.Core { 221 | return zapcore.NewNopCore() 222 | }) 223 | 224 | func (l Log) WithOptions(opts ...zap.Option) Log { 225 | lgr := l.logger.WithOptions(opts...) 226 | return Log{ 227 | logger: lgr, 228 | sugar: lgr.Sugar(), 229 | lvl: l.lvl, 230 | } 231 | } 232 | 233 | // Info prints message with fields 234 | func (fl fieldLogger) Info(msg string, fields ...LoggableField) { 235 | fl.l.Info(msg, unpack(fields)...) 236 | } 237 | 238 | // Debug prints message with fields 239 | func (fl fieldLogger) Debug(msg string, fields ...LoggableField) { 240 | fl.l.Debug(msg, unpack(fields)...) 241 | } 242 | 243 | // Error prints message with fields 244 | func (fl fieldLogger) Error(msg string, fields ...LoggableField) { 245 | fl.l.Error(msg, unpack(fields)...) 246 | } 247 | 248 | // Warning prints message with fields 249 | func (fl fieldLogger) Warning(msg string, fields ...LoggableField) { 250 | fl.l.Warn(msg, unpack(fields)...) 251 | } 252 | -------------------------------------------------------------------------------- /os/common/hexutil/hexutil_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package hexutil 18 | 19 | import ( 20 | "bytes" 21 | "math/big" 22 | "testing" 23 | ) 24 | 25 | type marshalTest struct { 26 | input interface{} 27 | want string 28 | } 29 | 30 | type unmarshalTest struct { 31 | input string 32 | want interface{} 33 | wantErr error // if set, decoding must fail on any platform 34 | wantErr32bit error // if set, decoding must fail on 32bit platforms (used for Uint tests) 35 | } 36 | 37 | var ( 38 | encodeBytesTests = []marshalTest{ 39 | {[]byte{}, "0x"}, 40 | {[]byte{0}, "0x00"}, 41 | {[]byte{0, 0, 1, 2}, "0x00000102"}, 42 | } 43 | 44 | encodeBigTests = []marshalTest{ 45 | {referenceBig("0"), "0x0"}, 46 | {referenceBig("1"), "0x1"}, 47 | {referenceBig("ff"), "0xff"}, 48 | {referenceBig("112233445566778899aabbccddeeff"), "0x112233445566778899aabbccddeeff"}, 49 | {referenceBig("80a7f2c1bcc396c00"), "0x80a7f2c1bcc396c00"}, 50 | {referenceBig("-80a7f2c1bcc396c00"), "-0x80a7f2c1bcc396c00"}, 51 | } 52 | 53 | encodeUint64Tests = []marshalTest{ 54 | {uint64(0), "0x0"}, 55 | {uint64(1), "0x1"}, 56 | {uint64(0xff), "0xff"}, 57 | {uint64(0x1122334455667788), "0x1122334455667788"}, 58 | } 59 | 60 | encodeUintTests = []marshalTest{ 61 | {uint(0), "0x0"}, 62 | {uint(1), "0x1"}, 63 | {uint(0xff), "0xff"}, 64 | {uint(0x11223344), "0x11223344"}, 65 | } 66 | 67 | decodeBytesTests = []unmarshalTest{ 68 | // invalid 69 | {input: ``, wantErr: ErrEmptyString}, 70 | {input: `0`, wantErr: ErrMissingPrefix}, 71 | {input: `0x0`, wantErr: ErrOddLength}, 72 | {input: `0x023`, wantErr: ErrOddLength}, 73 | {input: `0xxx`, wantErr: ErrSyntax}, 74 | {input: `0x01zz01`, wantErr: ErrSyntax}, 75 | // valid 76 | {input: `0x`, want: []byte{}}, 77 | {input: `0X`, want: []byte{}}, 78 | {input: `0x02`, want: []byte{0x02}}, 79 | {input: `0X02`, want: []byte{0x02}}, 80 | {input: `0xffffffffff`, want: []byte{0xff, 0xff, 0xff, 0xff, 0xff}}, 81 | { 82 | input: `0xffffffffffffffffffffffffffffffffffff`, 83 | want: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 84 | }, 85 | } 86 | 87 | decodeBigTests = []unmarshalTest{ 88 | // invalid 89 | {input: `0`, wantErr: ErrMissingPrefix}, 90 | {input: `0x`, wantErr: ErrEmptyNumber}, 91 | {input: `0x01`, wantErr: ErrLeadingZero}, 92 | {input: `0xx`, wantErr: ErrSyntax}, 93 | {input: `0x1zz01`, wantErr: ErrSyntax}, 94 | { 95 | input: `0x10000000000000000000000000000000000000000000000000000000000000000`, 96 | wantErr: ErrBig256Range, 97 | }, 98 | // valid 99 | {input: `0x0`, want: big.NewInt(0)}, 100 | {input: `0x2`, want: big.NewInt(0x2)}, 101 | {input: `0x2F2`, want: big.NewInt(0x2f2)}, 102 | {input: `0X2F2`, want: big.NewInt(0x2f2)}, 103 | {input: `0x1122aaff`, want: big.NewInt(0x1122aaff)}, 104 | {input: `0xbBb`, want: big.NewInt(0xbbb)}, 105 | {input: `0xfffffffff`, want: big.NewInt(0xfffffffff)}, 106 | { 107 | input: `0x112233445566778899aabbccddeeff`, 108 | want: referenceBig("112233445566778899aabbccddeeff"), 109 | }, 110 | { 111 | input: `0xffffffffffffffffffffffffffffffffffff`, 112 | want: referenceBig("ffffffffffffffffffffffffffffffffffff"), 113 | }, 114 | { 115 | input: `0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`, 116 | want: referenceBig("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), 117 | }, 118 | } 119 | 120 | decodeUint64Tests = []unmarshalTest{ 121 | // invalid 122 | {input: `0`, wantErr: ErrMissingPrefix}, 123 | {input: `0x`, wantErr: ErrEmptyNumber}, 124 | {input: `0x01`, wantErr: ErrLeadingZero}, 125 | {input: `0xfffffffffffffffff`, wantErr: ErrUint64Range}, 126 | {input: `0xx`, wantErr: ErrSyntax}, 127 | {input: `0x1zz01`, wantErr: ErrSyntax}, 128 | // valid 129 | {input: `0x0`, want: uint64(0)}, 130 | {input: `0x2`, want: uint64(0x2)}, 131 | {input: `0x2F2`, want: uint64(0x2f2)}, 132 | {input: `0X2F2`, want: uint64(0x2f2)}, 133 | {input: `0x1122aaff`, want: uint64(0x1122aaff)}, 134 | {input: `0xbbb`, want: uint64(0xbbb)}, 135 | {input: `0xffffffffffffffff`, want: uint64(0xffffffffffffffff)}, 136 | } 137 | ) 138 | 139 | func TestEncode(t *testing.T) { 140 | for _, test := range encodeBytesTests { 141 | enc := Encode(test.input.([]byte)) 142 | if enc != test.want { 143 | t.Errorf("input %x: wrong encoding %s", test.input, enc) 144 | } 145 | } 146 | } 147 | 148 | func TestDecode(t *testing.T) { 149 | for _, test := range decodeBytesTests { 150 | dec, err := Decode(test.input) 151 | if !checkError(t, test.input, err, test.wantErr) { 152 | continue 153 | } 154 | if !bytes.Equal(test.want.([]byte), dec) { 155 | t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want) 156 | continue 157 | } 158 | } 159 | } 160 | 161 | func TestEncodeBig(t *testing.T) { 162 | for _, test := range encodeBigTests { 163 | enc := EncodeBig(test.input.(*big.Int)) 164 | if enc != test.want { 165 | t.Errorf("input %x: wrong encoding %s", test.input, enc) 166 | } 167 | } 168 | } 169 | 170 | func TestDecodeBig(t *testing.T) { 171 | for _, test := range decodeBigTests { 172 | dec, err := DecodeBig(test.input) 173 | if !checkError(t, test.input, err, test.wantErr) { 174 | continue 175 | } 176 | if dec.Cmp(test.want.(*big.Int)) != 0 { 177 | t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want) 178 | continue 179 | } 180 | } 181 | } 182 | 183 | func TestEncodeUint64(t *testing.T) { 184 | for _, test := range encodeUint64Tests { 185 | enc := EncodeUint64(test.input.(uint64)) 186 | if enc != test.want { 187 | t.Errorf("input %x: wrong encoding %s", test.input, enc) 188 | } 189 | } 190 | } 191 | 192 | func TestDecodeUint64(t *testing.T) { 193 | for _, test := range decodeUint64Tests { 194 | dec, err := DecodeUint64(test.input) 195 | if !checkError(t, test.input, err, test.wantErr) { 196 | continue 197 | } 198 | if dec != test.want.(uint64) { 199 | t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want) 200 | continue 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /os/common/hexutil/hexutil.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | /* 18 | Package hexutil implements hex encoding with 0x prefix. 19 | This encoding is used by the Ethereum RPC API to transport binary data in JSON payloads. 20 | 21 | Encoding Rules 22 | 23 | All hex data must have prefix "0x". 24 | 25 | For byte slices, the hex data must be of even length. An empty byte slice 26 | encodes as "0x". 27 | 28 | Integers are encoded using the least amount of digits (no leading zero digits). Their 29 | encoding may be of uneven length. The number zero encodes as "0x0". 30 | */ 31 | package hexutil 32 | 33 | import ( 34 | "encoding/hex" 35 | "fmt" 36 | "math/big" 37 | "strconv" 38 | ) 39 | 40 | const uintBits = 32 << (uint64(^uint(0)) >> 63) 41 | 42 | // Errors 43 | var ( 44 | ErrEmptyString = &decError{"empty hex string"} 45 | ErrSyntax = &decError{"invalid hex string"} 46 | ErrMissingPrefix = &decError{"hex string without 0x prefix"} 47 | ErrOddLength = &decError{"hex string of odd length"} 48 | ErrEmptyNumber = &decError{"hex string 0x"} 49 | ErrLeadingZero = &decError{"hex number with leading zero digits"} 50 | ErrUint64Range = &decError{"hex number > 64 bits"} 51 | ErrUintRange = &decError{fmt.Sprintf("hex number > %d bits", uintBits)} 52 | ErrBig256Range = &decError{"hex number > 256 bits"} 53 | ) 54 | 55 | type decError struct{ msg string } 56 | 57 | func (err decError) Error() string { return err.msg } 58 | 59 | // Decode decodes a hex string with 0x prefix. 60 | func Decode(input string) ([]byte, error) { 61 | if len(input) == 0 { 62 | return nil, ErrEmptyString 63 | } 64 | if !has0xPrefix(input) { 65 | return nil, ErrMissingPrefix 66 | } 67 | b, err := hex.DecodeString(input[2:]) 68 | if err != nil { 69 | err = mapError(err) 70 | } 71 | return b, err 72 | } 73 | 74 | // MustDecode decodes a hex string with 0x prefix. It panics for invalid input. 75 | func MustDecode(input string) []byte { 76 | dec, err := Decode(input) 77 | if err != nil { 78 | panic(err) 79 | } 80 | return dec 81 | } 82 | 83 | // Encode encodes b as a hex string with 0x prefix. 84 | func Encode(b []byte) string { 85 | enc := make([]byte, len(b)*2+2) 86 | copy(enc, "0x") 87 | hex.Encode(enc[2:], b) 88 | return string(enc) 89 | } 90 | 91 | // DecodeUint64 decodes a hex string with 0x prefix as a quantity. 92 | func DecodeUint64(input string) (uint64, error) { 93 | raw, err := checkNumber(input) 94 | if err != nil { 95 | return 0, err 96 | } 97 | dec, err := strconv.ParseUint(raw, 16, 64) 98 | if err != nil { 99 | err = mapError(err) 100 | } 101 | return dec, err 102 | } 103 | 104 | // MustDecodeUint64 decodes a hex string with 0x prefix as a quantity. 105 | // It panics for invalid input. 106 | func MustDecodeUint64(input string) uint64 { 107 | dec, err := DecodeUint64(input) 108 | if err != nil { 109 | panic(err) 110 | } 111 | return dec 112 | } 113 | 114 | // EncodeUint64 encodes i as a hex string with 0x prefix. 115 | func EncodeUint64(i uint64) string { 116 | enc := make([]byte, 2, 10) 117 | copy(enc, "0x") 118 | return string(strconv.AppendUint(enc, i, 16)) 119 | } 120 | 121 | var bigWordNibbles int 122 | 123 | func init() { 124 | // This is a weird way to compute the number of nibbles required for big.Word. 125 | // The usual way would be to use constant arithmetic but go vet can't handle that. 126 | b, _ := new(big.Int).SetString("FFFFFFFFFF", 16) 127 | switch len(b.Bits()) { 128 | case 1: 129 | bigWordNibbles = 16 130 | case 2: 131 | bigWordNibbles = 8 132 | default: 133 | panic("weird big.Word size") 134 | } 135 | } 136 | 137 | // DecodeBig decodes a hex string with 0x prefix as a quantity. 138 | // Numbers larger than 256 bits are not accepted. 139 | func DecodeBig(input string) (*big.Int, error) { 140 | raw, err := checkNumber(input) 141 | if err != nil { 142 | return nil, err 143 | } 144 | if len(raw) > 64 { 145 | return nil, ErrBig256Range 146 | } 147 | words := make([]big.Word, len(raw)/bigWordNibbles+1) 148 | end := len(raw) 149 | for i := range words { 150 | start := end - bigWordNibbles 151 | if start < 0 { 152 | start = 0 153 | } 154 | for ri := start; ri < end; ri++ { 155 | nib := decodeNibble(raw[ri]) 156 | if nib == badNibble { 157 | return nil, ErrSyntax 158 | } 159 | words[i] *= 16 160 | words[i] += big.Word(nib) 161 | } 162 | end = start 163 | } 164 | dec := new(big.Int).SetBits(words) 165 | return dec, nil 166 | } 167 | 168 | // MustDecodeBig decodes a hex string with 0x prefix as a quantity. 169 | // It panics for invalid input. 170 | func MustDecodeBig(input string) *big.Int { 171 | dec, err := DecodeBig(input) 172 | if err != nil { 173 | panic(err) 174 | } 175 | return dec 176 | } 177 | 178 | // EncodeBig encodes bigint as a hex string with 0x prefix. 179 | // The sign of the integer is ignored. 180 | func EncodeBig(bigint *big.Int) string { 181 | nbits := bigint.BitLen() 182 | if nbits == 0 { 183 | return "0x0" 184 | } 185 | return fmt.Sprintf("%#x", bigint) 186 | } 187 | 188 | func has0xPrefix(input string) bool { 189 | return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') 190 | } 191 | 192 | func checkNumber(input string) (raw string, err error) { 193 | if len(input) == 0 { 194 | return "", ErrEmptyString 195 | } 196 | if !has0xPrefix(input) { 197 | return "", ErrMissingPrefix 198 | } 199 | input = input[2:] 200 | if len(input) == 0 { 201 | return "", ErrEmptyNumber 202 | } 203 | if len(input) > 1 && input[0] == '0' { 204 | return "", ErrLeadingZero 205 | } 206 | return input, nil 207 | } 208 | 209 | const badNibble = ^uint64(0) 210 | 211 | func decodeNibble(in byte) uint64 { 212 | switch { 213 | case in >= '0' && in <= '9': 214 | return uint64(in - '0') 215 | case in >= 'A' && in <= 'F': 216 | return uint64(in - 'A' + 10) 217 | case in >= 'a' && in <= 'f': 218 | return uint64(in - 'a' + 10) 219 | default: 220 | return badNibble 221 | } 222 | } 223 | 224 | func mapError(err error) error { 225 | if err, ok := err.(*strconv.NumError); ok { 226 | switch err.Err { 227 | case strconv.ErrRange: 228 | return ErrUint64Range 229 | case strconv.ErrSyntax: 230 | return ErrSyntax 231 | } 232 | } 233 | if _, ok := err.(hex.InvalidByteError); ok { 234 | return ErrSyntax 235 | } 236 | if err == hex.ErrLength { 237 | return ErrOddLength 238 | } 239 | return err 240 | } 241 | -------------------------------------------------------------------------------- /os/common/hexutil/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package hexutil 18 | 19 | import ( 20 | "encoding/hex" 21 | "encoding/json" 22 | "fmt" 23 | "math/big" 24 | "reflect" 25 | "strconv" 26 | ) 27 | 28 | var ( 29 | bytesT = reflect.TypeOf(Bytes(nil)) 30 | bigT = reflect.TypeOf((*Big)(nil)) 31 | uintT = reflect.TypeOf(Uint(0)) 32 | uint64T = reflect.TypeOf(Uint64(0)) 33 | ) 34 | 35 | // Bytes marshals/unmarshals as a JSON string with 0x prefix. 36 | // The empty slice marshals as "0x". 37 | type Bytes []byte 38 | 39 | // MarshalText implements encoding.TextMarshaler 40 | func (b Bytes) MarshalText() ([]byte, error) { 41 | result := make([]byte, len(b)*2+2) 42 | copy(result, `0x`) 43 | hex.Encode(result[2:], b) 44 | return result, nil 45 | } 46 | 47 | // UnmarshalJSON implements json.Unmarshaler. 48 | func (b *Bytes) UnmarshalJSON(input []byte) error { 49 | if !isString(input) { 50 | return errNonString(bytesT) 51 | } 52 | return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), bytesT) 53 | } 54 | 55 | // UnmarshalText implements encoding.TextUnmarshaler. 56 | func (b *Bytes) UnmarshalText(input []byte) error { 57 | raw, err := checkText(input, true) 58 | if err != nil { 59 | return err 60 | } 61 | dec := make([]byte, len(raw)/2) 62 | if _, err = hex.Decode(dec, raw); err != nil { 63 | err = mapError(err) 64 | } else { 65 | *b = dec 66 | } 67 | return err 68 | } 69 | 70 | // String returns the hex encoding of b. 71 | func (b Bytes) String() string { 72 | return Encode(b) 73 | } 74 | 75 | // UnmarshalFixedJSON decodes the input as a string with 0x prefix. The length of out 76 | // determines the required input length. This function is commonly used to implement the 77 | // UnmarshalJSON method for fixed-size types. 78 | func UnmarshalFixedJSON(typ reflect.Type, input, out []byte) error { 79 | if !isString(input) { 80 | return errNonString(typ) 81 | } 82 | return wrapTypeError(UnmarshalFixedText(typ.String(), input[1:len(input)-1], out), typ) 83 | } 84 | 85 | // UnmarshalFixedText decodes the input as a string with 0x prefix. The length of out 86 | // determines the required input length. This function is commonly used to implement the 87 | // UnmarshalText method for fixed-size types. 88 | func UnmarshalFixedText(typname string, input, out []byte) error { 89 | raw, err := checkText(input, true) 90 | if err != nil { 91 | return err 92 | } 93 | if len(raw)/2 != len(out) { 94 | return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname) 95 | } 96 | // Pre-verify syntax before modifying out. 97 | for _, b := range raw { 98 | if decodeNibble(b) == badNibble { 99 | return ErrSyntax 100 | } 101 | } 102 | hex.Decode(out, raw) 103 | return nil 104 | } 105 | 106 | // UnmarshalFixedUnprefixedText decodes the input as a string with optional 0x prefix. The 107 | // length of out determines the required input length. This function is commonly used to 108 | // implement the UnmarshalText method for fixed-size types. 109 | func UnmarshalFixedUnprefixedText(typname string, input, out []byte) error { 110 | raw, err := checkText(input, false) 111 | if err != nil { 112 | return err 113 | } 114 | if len(raw)/2 != len(out) { 115 | return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname) 116 | } 117 | // Pre-verify syntax before modifying out. 118 | for _, b := range raw { 119 | if decodeNibble(b) == badNibble { 120 | return ErrSyntax 121 | } 122 | } 123 | hex.Decode(out, raw) 124 | return nil 125 | } 126 | 127 | // Big marshals/unmarshals as a JSON string with 0x prefix. 128 | // The zero value marshals as "0x0". 129 | // 130 | // Negative integers are not supported at this time. Attempting to marshal them will 131 | // return an error. Values larger than 256bits are rejected by Unmarshal but will be 132 | // marshaled without error. 133 | type Big big.Int 134 | 135 | // MarshalText implements encoding.TextMarshaler 136 | func (b Big) MarshalText() ([]byte, error) { 137 | return []byte(EncodeBig((*big.Int)(&b))), nil 138 | } 139 | 140 | // UnmarshalJSON implements json.Unmarshaler. 141 | func (b *Big) UnmarshalJSON(input []byte) error { 142 | if !isString(input) { 143 | return errNonString(bigT) 144 | } 145 | return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), bigT) 146 | } 147 | 148 | // UnmarshalText implements encoding.TextUnmarshaler 149 | func (b *Big) UnmarshalText(input []byte) error { 150 | raw, err := checkNumberText(input) 151 | if err != nil { 152 | return err 153 | } 154 | if len(raw) > 64 { 155 | return ErrBig256Range 156 | } 157 | words := make([]big.Word, len(raw)/bigWordNibbles+1) 158 | end := len(raw) 159 | for i := range words { 160 | start := end - bigWordNibbles 161 | if start < 0 { 162 | start = 0 163 | } 164 | for ri := start; ri < end; ri++ { 165 | nib := decodeNibble(raw[ri]) 166 | if nib == badNibble { 167 | return ErrSyntax 168 | } 169 | words[i] *= 16 170 | words[i] += big.Word(nib) 171 | } 172 | end = start 173 | } 174 | var dec big.Int 175 | dec.SetBits(words) 176 | *b = (Big)(dec) 177 | return nil 178 | } 179 | 180 | // ToInt converts b to a big.Int. 181 | func (b *Big) ToInt() *big.Int { 182 | return (*big.Int)(b) 183 | } 184 | 185 | // String returns the hex encoding of b. 186 | func (b *Big) String() string { 187 | return EncodeBig(b.ToInt()) 188 | } 189 | 190 | // Uint64 marshals/unmarshals as a JSON string with 0x prefix. 191 | // The zero value marshals as "0x0". 192 | type Uint64 uint64 193 | 194 | // MarshalText implements encoding.TextMarshaler. 195 | func (b Uint64) MarshalText() ([]byte, error) { 196 | buf := make([]byte, 2, 10) 197 | copy(buf, `0x`) 198 | buf = strconv.AppendUint(buf, uint64(b), 16) 199 | return buf, nil 200 | } 201 | 202 | // UnmarshalJSON implements json.Unmarshaler. 203 | func (b *Uint64) UnmarshalJSON(input []byte) error { 204 | if !isString(input) { 205 | return errNonString(uint64T) 206 | } 207 | return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), uint64T) 208 | } 209 | 210 | // UnmarshalText implements encoding.TextUnmarshaler 211 | func (b *Uint64) UnmarshalText(input []byte) error { 212 | raw, err := checkNumberText(input) 213 | if err != nil { 214 | return err 215 | } 216 | if len(raw) > 16 { 217 | return ErrUint64Range 218 | } 219 | var dec uint64 220 | for _, byte := range raw { 221 | nib := decodeNibble(byte) 222 | if nib == badNibble { 223 | return ErrSyntax 224 | } 225 | dec *= 16 226 | dec += nib 227 | } 228 | *b = Uint64(dec) 229 | return nil 230 | } 231 | 232 | // String returns the hex encoding of b. 233 | func (b Uint64) String() string { 234 | return EncodeUint64(uint64(b)) 235 | } 236 | 237 | // Uint marshals/unmarshals as a JSON string with 0x prefix. 238 | // The zero value marshals as "0x0". 239 | type Uint uint 240 | 241 | // MarshalText implements encoding.TextMarshaler. 242 | func (b Uint) MarshalText() ([]byte, error) { 243 | return Uint64(b).MarshalText() 244 | } 245 | 246 | // UnmarshalJSON implements json.Unmarshaler. 247 | func (b *Uint) UnmarshalJSON(input []byte) error { 248 | if !isString(input) { 249 | return errNonString(uintT) 250 | } 251 | return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), uintT) 252 | } 253 | 254 | // UnmarshalText implements encoding.TextUnmarshaler. 255 | func (b *Uint) UnmarshalText(input []byte) error { 256 | var u64 Uint64 257 | err := u64.UnmarshalText(input) 258 | if u64 > Uint64(^uint(0)) || err == ErrUint64Range { 259 | return ErrUintRange 260 | } else if err != nil { 261 | return err 262 | } 263 | *b = Uint(u64) 264 | return nil 265 | } 266 | 267 | // String returns the hex encoding of b. 268 | func (b Uint) String() string { 269 | return EncodeUint64(uint64(b)) 270 | } 271 | 272 | func isString(input []byte) bool { 273 | return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' 274 | } 275 | 276 | func bytesHave0xPrefix(input []byte) bool { 277 | return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') 278 | } 279 | 280 | func checkText(input []byte, wantPrefix bool) ([]byte, error) { 281 | if len(input) == 0 { 282 | return nil, nil // empty strings are allowed 283 | } 284 | if bytesHave0xPrefix(input) { 285 | input = input[2:] 286 | } else if wantPrefix { 287 | return nil, ErrMissingPrefix 288 | } 289 | if len(input)%2 != 0 { 290 | return nil, ErrOddLength 291 | } 292 | return input, nil 293 | } 294 | 295 | func checkNumberText(input []byte) (raw []byte, err error) { 296 | if len(input) == 0 { 297 | return nil, nil // empty strings are allowed 298 | } 299 | if !bytesHave0xPrefix(input) { 300 | return nil, ErrMissingPrefix 301 | } 302 | input = input[2:] 303 | if len(input) == 0 { 304 | return nil, ErrEmptyNumber 305 | } 306 | if len(input) > 1 && input[0] == '0' { 307 | return nil, ErrLeadingZero 308 | } 309 | return input, nil 310 | } 311 | 312 | func wrapTypeError(err error, typ reflect.Type) error { 313 | if _, ok := err.(*decError); ok { 314 | return &json.UnmarshalTypeError{Value: err.Error(), Type: typ} 315 | } 316 | return err 317 | } 318 | 319 | func errNonString(typ reflect.Type) error { 320 | return &json.UnmarshalTypeError{Value: "non-string", Type: typ} 321 | } 322 | -------------------------------------------------------------------------------- /os/crypto/sha3/sha3_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sha3 6 | 7 | // Tests include all the ShortMsgKATs provided by the Keccak team at 8 | // https://github.com/gvanas/KeccakCodePackage 9 | // 10 | // They only include the zero-bit case of the bitwise testvectors 11 | // published by NIST in the draft of FIPS-202. 12 | 13 | import ( 14 | "bytes" 15 | "compress/flate" 16 | "encoding/hex" 17 | "encoding/json" 18 | "hash" 19 | "os" 20 | "strings" 21 | "testing" 22 | ) 23 | 24 | const ( 25 | testString = "brekeccakkeccak koax koax" 26 | katFilename = "testdata/keccakKats.json.deflate" 27 | ) 28 | 29 | // Internal-use instances of SHAKE used to test against KATs. 30 | func newHashShake128() hash.Hash { 31 | return &state{rate: 168, dsbyte: 0x1f, outputLen: 512} 32 | } 33 | func newHashShake256() hash.Hash { 34 | return &state{rate: 136, dsbyte: 0x1f, outputLen: 512} 35 | } 36 | 37 | // testDigests contains functions returning hash.Hash instances 38 | // with output-length equal to the KAT length for both SHA-3 and 39 | // SHAKE instances. 40 | var testDigests = map[string]func() hash.Hash{ 41 | "SHA3-224": New224, 42 | "SHA3-256": New256, 43 | "SHA3-384": New384, 44 | "SHA3-512": New512, 45 | "SHAKE128": newHashShake128, 46 | "SHAKE256": newHashShake256, 47 | } 48 | 49 | // testShakes contains functions that return ShakeHash instances for 50 | // testing the ShakeHash-specific interface. 51 | var testShakes = map[string]func() ShakeHash{ 52 | "SHAKE128": NewShake128, 53 | "SHAKE256": NewShake256, 54 | } 55 | 56 | // structs used to marshal JSON test-cases. 57 | type KeccakKats struct { 58 | Kats map[string][]struct { 59 | Digest string `json:"digest"` 60 | Length int64 `json:"length"` 61 | Message string `json:"message"` 62 | } 63 | } 64 | 65 | func testUnalignedAndGeneric(t *testing.T, testf func(impl string)) { 66 | xorInOrig, copyOutOrig := xorIn, copyOut 67 | xorIn, copyOut = xorInGeneric, copyOutGeneric 68 | testf("generic") 69 | if xorImplementationUnaligned != "generic" { 70 | xorIn, copyOut = xorInUnaligned, copyOutUnaligned 71 | testf("unaligned") 72 | } 73 | xorIn, copyOut = xorInOrig, copyOutOrig 74 | } 75 | 76 | // TestKeccakKats tests the SHA-3 and Shake implementations against all the 77 | // ShortMsgKATs from https://github.com/gvanas/KeccakCodePackage 78 | // (The testvectors are stored in keccakKats.json.deflate due to their length.) 79 | func TestKeccakKats(t *testing.T) { 80 | testUnalignedAndGeneric(t, func(impl string) { 81 | // Read the KATs. 82 | deflated, err := os.Open(katFilename) 83 | if err != nil { 84 | t.Errorf("error opening %s: %s", katFilename, err) 85 | } 86 | file := flate.NewReader(deflated) 87 | dec := json.NewDecoder(file) 88 | var katSet KeccakKats 89 | err = dec.Decode(&katSet) 90 | if err != nil { 91 | t.Errorf("error decoding KATs: %s", err) 92 | } 93 | 94 | // Do the KATs. 95 | for functionName, kats := range katSet.Kats { 96 | d := testDigests[functionName]() 97 | for _, kat := range kats { 98 | d.Reset() 99 | in, err := hex.DecodeString(kat.Message) 100 | if err != nil { 101 | t.Errorf("error decoding KAT: %s", err) 102 | } 103 | d.Write(in[:kat.Length/8]) 104 | got := strings.ToUpper(hex.EncodeToString(d.Sum(nil))) 105 | if got != kat.Digest { 106 | t.Errorf("function=%s, implementation=%s, length=%d\nmessage:\n %s\ngot:\n %s\nwanted:\n %s", 107 | functionName, impl, kat.Length, kat.Message, got, kat.Digest) 108 | t.Logf("wanted %+v", kat) 109 | t.FailNow() 110 | } 111 | continue 112 | } 113 | } 114 | }) 115 | } 116 | 117 | // TestUnalignedWrite tests that writing data in an arbitrary pattern with 118 | // small input buffers. 119 | func TestUnalignedWrite(t *testing.T) { 120 | testUnalignedAndGeneric(t, func(impl string) { 121 | buf := sequentialBytes(0x10000) 122 | for alg, df := range testDigests { 123 | d := df() 124 | d.Reset() 125 | d.Write(buf) 126 | want := d.Sum(nil) 127 | d.Reset() 128 | for i := 0; i < len(buf); { 129 | // Cycle through offsets which make a 137 byte sequence. 130 | // Because 137 is prime this sequence should exercise all corner cases. 131 | offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1} 132 | for _, j := range offsets { 133 | if v := len(buf) - i; v < j { 134 | j = v 135 | } 136 | d.Write(buf[i : i+j]) 137 | i += j 138 | } 139 | } 140 | got := d.Sum(nil) 141 | if !bytes.Equal(got, want) { 142 | t.Errorf("Unaligned writes, implementation=%s, alg=%s\ngot %q, want %q", impl, alg, got, want) 143 | } 144 | } 145 | }) 146 | } 147 | 148 | // TestAppend checks that appending works when reallocation is necessary. 149 | func TestAppend(t *testing.T) { 150 | testUnalignedAndGeneric(t, func(impl string) { 151 | d := New224() 152 | 153 | for capacity := 2; capacity <= 66; capacity += 64 { 154 | // The first time around the loop, Sum will have to reallocate. 155 | // The second time, it will not. 156 | buf := make([]byte, 2, capacity) 157 | d.Reset() 158 | d.Write([]byte{0xcc}) 159 | buf = d.Sum(buf) 160 | expected := "0000DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39" 161 | if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected { 162 | t.Errorf("got %s, want %s", got, expected) 163 | } 164 | } 165 | }) 166 | } 167 | 168 | // TestAppendNoRealloc tests that appending works when no reallocation is necessary. 169 | func TestAppendNoRealloc(t *testing.T) { 170 | testUnalignedAndGeneric(t, func(impl string) { 171 | buf := make([]byte, 1, 200) 172 | d := New224() 173 | d.Write([]byte{0xcc}) 174 | buf = d.Sum(buf) 175 | expected := "00DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39" 176 | if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected { 177 | t.Errorf("%s: got %s, want %s", impl, got, expected) 178 | } 179 | }) 180 | } 181 | 182 | // TestSqueezing checks that squeezing the full output a single time produces 183 | // the same output as repeatedly squeezing the instance. 184 | func TestSqueezing(t *testing.T) { 185 | testUnalignedAndGeneric(t, func(impl string) { 186 | for functionName, newShakeHash := range testShakes { 187 | d0 := newShakeHash() 188 | d0.Write([]byte(testString)) 189 | ref := make([]byte, 32) 190 | d0.Read(ref) 191 | 192 | d1 := newShakeHash() 193 | d1.Write([]byte(testString)) 194 | var multiple []byte 195 | for range ref { 196 | one := make([]byte, 1) 197 | d1.Read(one) 198 | multiple = append(multiple, one...) 199 | } 200 | if !bytes.Equal(ref, multiple) { 201 | t.Errorf("%s (%s): squeezing %d bytes one at a time failed", functionName, impl, len(ref)) 202 | } 203 | } 204 | }) 205 | } 206 | 207 | // sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing. 208 | func sequentialBytes(size int) []byte { 209 | result := make([]byte, size) 210 | for i := range result { 211 | result[i] = byte(i) 212 | } 213 | return result 214 | } 215 | 216 | // BenchmarkPermutationFunction measures the speed of the permutation function 217 | // with no input data. 218 | func BenchmarkPermutationFunction(b *testing.B) { 219 | b.SetBytes(int64(200)) 220 | var lanes [25]uint64 221 | for i := 0; i < b.N; i++ { 222 | keccakF1600(&lanes) 223 | } 224 | } 225 | 226 | // benchmarkHash tests the speed to hash num buffers of buflen each. 227 | func benchmarkHash(b *testing.B, h hash.Hash, size, num int) { 228 | b.StopTimer() 229 | h.Reset() 230 | data := sequentialBytes(size) 231 | b.SetBytes(int64(size * num)) 232 | b.StartTimer() 233 | 234 | var state []byte 235 | for i := 0; i < b.N; i++ { 236 | for j := 0; j < num; j++ { 237 | h.Write(data) 238 | } 239 | state = h.Sum(state[:0]) 240 | } 241 | b.StopTimer() 242 | h.Reset() 243 | } 244 | 245 | // benchmarkShake is specialized to the Shake instances, which don't 246 | // require a copy on reading output. 247 | func benchmarkShake(b *testing.B, h ShakeHash, size, num int) { 248 | b.StopTimer() 249 | h.Reset() 250 | data := sequentialBytes(size) 251 | d := make([]byte, 32) 252 | 253 | b.SetBytes(int64(size * num)) 254 | b.StartTimer() 255 | 256 | for i := 0; i < b.N; i++ { 257 | h.Reset() 258 | for j := 0; j < num; j++ { 259 | h.Write(data) 260 | } 261 | h.Read(d) 262 | } 263 | } 264 | 265 | func BenchmarkSha3_512_MTU(b *testing.B) { benchmarkHash(b, New512(), 1350, 1) } 266 | func BenchmarkSha3_384_MTU(b *testing.B) { benchmarkHash(b, New384(), 1350, 1) } 267 | func BenchmarkSha3_256_MTU(b *testing.B) { benchmarkHash(b, New256(), 1350, 1) } 268 | func BenchmarkSha3_224_MTU(b *testing.B) { benchmarkHash(b, New224(), 1350, 1) } 269 | 270 | func BenchmarkShake128_MTU(b *testing.B) { benchmarkShake(b, NewShake128(), 1350, 1) } 271 | func BenchmarkShake256_MTU(b *testing.B) { benchmarkShake(b, NewShake256(), 1350, 1) } 272 | func BenchmarkShake256_16x(b *testing.B) { benchmarkShake(b, NewShake256(), 16, 1024) } 273 | func BenchmarkShake256_1MiB(b *testing.B) { benchmarkShake(b, NewShake256(), 1024, 1024) } 274 | 275 | func BenchmarkSha3_512_1MiB(b *testing.B) { benchmarkHash(b, New512(), 1024, 1024) } 276 | 277 | func Example_sum() { 278 | buf := []byte("some data to hash") 279 | // A hash needs to be 64 bytes long to have 256-bit collision resistance. 280 | h := make([]byte, 64) 281 | // Compute a 64-byte hash of buf and put it in h. 282 | ShakeSum256(h, buf) 283 | } 284 | 285 | func Example_mac() { 286 | k := []byte("this is a secret key; you should generate a strong random key that's at least 32 bytes long") 287 | buf := []byte("and this is some data to authenticate") 288 | // A MAC with 32 bytes of output has 256-bit security strength -- if you use at least a 32-byte-long key. 289 | h := make([]byte, 32) 290 | d := NewShake256() 291 | // Write the key into the hash. 292 | d.Write(k) 293 | // Now write the data. 294 | d.Write(buf) 295 | // Read 32 bytes of output from the hash into h. 296 | d.Read(h) 297 | } 298 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= 4 | github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= 5 | github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= 6 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= 7 | github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= 8 | github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= 9 | github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= 10 | github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= 11 | github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= 12 | github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= 13 | github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= 14 | github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= 15 | github.com/c-bata/go-prompt v0.2.3 h1:jjCS+QhG/sULBhAaBdjb2PlMRVaKXQgn+4yzaauvs2s= 16 | github.com/c-bata/go-prompt v0.2.3/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= 17 | github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 18 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 19 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 20 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 21 | github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892 h1:qg9VbHo1TlL0KDM0vYvBG9EY0X0Yku5WYIPoFWt8f6o= 22 | github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892/go.mod h1:CTDl0pzVzE5DEzZhPfvhY/9sPFMQIxaJ9VAMs9AagrE= 23 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 24 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 25 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 26 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 27 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 28 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 29 | github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 30 | github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= 31 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 32 | github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= 33 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 34 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 35 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 36 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 37 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 38 | github.com/libonomy/ed25519 v0.0.0-20200515113020-867f8a7820c3 h1:T25PCqDoLRIgmgMaEiCHLk7KCiP234eUfzS7aZ10KV4= 39 | github.com/libonomy/ed25519 v0.0.0-20200515113020-867f8a7820c3/go.mod h1:UIjlAiC8OVBHCk7ut1z6E/eFyZyLmAzRlylirY/Nd9c= 40 | github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= 41 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 42 | github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= 43 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 44 | github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= 45 | github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 46 | github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859 h1:smQbSzmT3EHl4EUwtFwFGmGIpiYgIiiPeVv1uguIQEE= 47 | github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= 48 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 49 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 50 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 51 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 52 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 53 | github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942 h1:A7GG7zcGjl3jqAqGPmcNjd/D9hzL95SuoOQAaFNdLU0= 54 | github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= 55 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 56 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 57 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 58 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 59 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 60 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 61 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 62 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 63 | go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= 64 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 65 | go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= 66 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 67 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= 68 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 69 | go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= 70 | go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= 71 | golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 72 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 73 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 74 | golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 75 | golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= 76 | golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 77 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= 78 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 79 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 80 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 81 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 82 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 83 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 84 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 85 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 86 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 87 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 88 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 89 | golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= 90 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 91 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 92 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 93 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 94 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 95 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= 96 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 97 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 98 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 99 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 100 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 101 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 102 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 103 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 104 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 105 | gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE= 106 | gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog= 107 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 108 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 109 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 110 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 111 | honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= 112 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 113 | -------------------------------------------------------------------------------- /repl/repl.go: -------------------------------------------------------------------------------- 1 | package repl 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/libonomy/ed25519" 11 | "github.com/libonomy/wallet-cli/accounts" 12 | "github.com/libonomy/wallet-cli/client" 13 | "github.com/libonomy/wallet-cli/log" 14 | "github.com/libonomy/wallet-cli/wallet/address" 15 | 16 | "github.com/c-bata/go-prompt" 17 | ) 18 | 19 | const ( 20 | prefix = "$ " 21 | printPrefix = ">" 22 | ) 23 | 24 | // TestMode variable used for check if unit test is running 25 | var TestMode = false 26 | 27 | type command struct { 28 | text string 29 | description string 30 | fn func() 31 | } 32 | 33 | type repl struct { 34 | commands []command 35 | client Client 36 | input string 37 | } 38 | 39 | // Client interface to REPL clients. 40 | type Client interface { 41 | CreateAccount(alias string) *accounts.Account 42 | CurrentAccount() *accounts.Account 43 | SetCurrentAccount(a *accounts.Account) 44 | AccountInfo(address string) (*accounts.AccountInfo, error) 45 | NodeInfo() (*client.NodeInfo, error) 46 | Sanity() error 47 | Transfer(recipient address.Address, nonce, amount, gasPrice, gasLimit uint64, key ed25519.PrivateKey) (string, error) 48 | ListAccounts() []string 49 | GetAccount(name string) (*accounts.Account, error) 50 | StoreAccounts() error 51 | NodeURL() string 52 | Rebel(datadir string, space uint, coinbase string) error 53 | ListTxs(address string) ([]string, error) 54 | SetCoinbase(coinbase string) error 55 | 56 | //Unlock(passphrase string) error 57 | //IsAccountUnLock(id string) bool 58 | //Lock(passphrase string) error 59 | //SetVariables(params, flags []string) error 60 | //GetVariable(key string) string 61 | //Restart(params, flags []string) error 62 | //NeedRestartNode(params, flags []string) bool 63 | //Setup(allocation string) error 64 | } 65 | 66 | // Start starts REPL. 67 | func Start(c Client) { 68 | if !TestMode { 69 | r := &repl{client: c} 70 | r.initializeCommands() 71 | 72 | runPrompt(r.executor, r.completer, r.firstTime, uint16(len(r.commands))) 73 | } else { 74 | // holds for unit test purposes 75 | hold := make(chan bool) 76 | <-hold 77 | } 78 | } 79 | 80 | func (r *repl) initializeCommands() { 81 | r.commands = []command{ 82 | {"create-account", "Create a new account (key pair) and set as current", r.createAccount}, 83 | {"use-previous", "Set one of the previously created accounts as current", r.chooseAccount}, 84 | {"info", "Display the current account info", r.accountInfo}, 85 | {"status", "Display the node status", r.nodeInfo}, 86 | {"sign", "Sign a hex message with the current account private key", r.sign}, 87 | {"textsign", "Sign a text message with the current account private key", r.textsign}, 88 | {"quit", "Quit the CLI", r.quit}, 89 | } 90 | } 91 | 92 | func (r *repl) executor(text string) { 93 | for _, c := range r.commands { 94 | if len(text) >= len(c.text) && text[:len(c.text)] == c.text { 95 | r.input = text 96 | //log.Debug(userExecutingCommandMsg, c.text) 97 | c.fn() 98 | return 99 | } 100 | } 101 | 102 | fmt.Println(printPrefix, "invalid command.") 103 | } 104 | 105 | func (r *repl) completer(in prompt.Document) []prompt.Suggest { 106 | suggets := make([]prompt.Suggest, 0) 107 | for _, command := range r.commands { 108 | s := prompt.Suggest{ 109 | Text: command.text, 110 | Description: command.description, 111 | } 112 | 113 | suggets = append(suggets, s) 114 | } 115 | 116 | return prompt.FilterHasPrefix(suggets, in.GetWordBeforeCursor(), true) 117 | } 118 | 119 | func (r *repl) firstTime() { 120 | 121 | if err := r.client.Sanity(); err != nil { 122 | log.Error("Failed to connect to node at %v: %v", r.client.NodeURL(), err) 123 | r.quit() 124 | } 125 | 126 | fmt.Println("Welcome to libonomy. Connected to node at ", r.client.NodeURL()) 127 | } 128 | 129 | func (r *repl) chooseAccount() { 130 | accs := r.client.ListAccounts() 131 | if len(accs) == 0 { 132 | r.createAccount() 133 | return 134 | } 135 | 136 | fmt.Println(printPrefix, "Choose an account to load:") 137 | accName := multipleChoice(accs) 138 | account, err := r.client.GetAccount(accName) 139 | if err != nil { 140 | panic("wtf") 141 | } 142 | fmt.Printf("%s Loaded account alias: `%s`, address: %s \n", printPrefix, account.Name, accounts.StringAddress(account.Address())) 143 | 144 | r.client.SetCurrentAccount(account) 145 | } 146 | 147 | func (r *repl) createAccount() { 148 | fmt.Println(printPrefix, "Create a new account") 149 | alias := inputNotBlank(createAccountMsg) 150 | 151 | ac := r.client.CreateAccount(alias) 152 | err := r.client.StoreAccounts() 153 | if err != nil { 154 | log.Error("failed to create account: %v", err) 155 | return 156 | } 157 | 158 | fmt.Printf("%s Created account alias: `%s`, address: %s \n", printPrefix, ac.Name, accounts.StringAddress(ac.Address())) 159 | r.client.SetCurrentAccount(ac) 160 | } 161 | 162 | func (r *repl) commandLineParams(idx int, input string) string { 163 | c := r.commands[idx] 164 | params := strings.Replace(input, c.text, "", -1) 165 | 166 | return strings.TrimSpace(params) 167 | } 168 | 169 | func (r *repl) accountInfo() { 170 | acc := r.client.CurrentAccount() 171 | if acc == nil { 172 | r.chooseAccount() 173 | acc = r.client.CurrentAccount() 174 | } 175 | 176 | address := address.BytesToAddress(acc.PubKey) 177 | 178 | info, err := r.client.AccountInfo(hex.EncodeToString(address.Bytes())) 179 | if err != nil { 180 | log.Error("failed to get account info: %v", err) 181 | info = &accounts.AccountInfo{} 182 | } 183 | 184 | fmt.Println(printPrefix, "Local alias: ", acc.Name) 185 | fmt.Println(printPrefix, "Address: ", accounts.StringAddress(address)) 186 | fmt.Println(printPrefix, "Balance: ", info.Balance) 187 | fmt.Println(printPrefix, "Nonce: ", info.Nonce) 188 | fmt.Println(printPrefix, fmt.Sprintf("Public key: 0x%s", hex.EncodeToString(acc.PubKey))) 189 | fmt.Println(printPrefix, fmt.Sprintf("Private key: 0x%s", hex.EncodeToString(acc.PrivKey))) 190 | } 191 | 192 | func (r *repl) nodeInfo() { 193 | info, err := r.client.NodeInfo() 194 | if err != nil { 195 | log.Error("failed to get node info: %v", err) 196 | return 197 | } 198 | 199 | fmt.Println(printPrefix, "Synced:", info.Synced) 200 | fmt.Println(printPrefix, "Synced layer:", info.SyncedLayer) 201 | fmt.Println(printPrefix, "Current layer:", info.CurrentLayer) 202 | fmt.Println(printPrefix, "Verified layer:", info.VerifiedLayer) 203 | fmt.Println(printPrefix, "Peers:", info.Peers) 204 | fmt.Println(printPrefix, "Min peers:", info.MinPeers) 205 | fmt.Println(printPrefix, "Max peers:", info.MaxPeers) 206 | fmt.Println(printPrefix, "Libonomy data directory:", info.LibonomyDatadir) 207 | fmt.Println(printPrefix, "Libonomy status:", info.LibonomyStatus) 208 | fmt.Println(printPrefix, "Libonomy coinbase:", info.LibonomyCoinbase) 209 | fmt.Println(printPrefix, "Libonomy remaining bytes:", info.LibonomyRemainingBytes) 210 | } 211 | 212 | func (r *repl) transferCoins() { 213 | fmt.Println(printPrefix, initialTransferMsg) 214 | acc := r.client.CurrentAccount() 215 | if acc == nil { 216 | r.chooseAccount() 217 | acc = r.client.CurrentAccount() 218 | } 219 | 220 | srcAddress := address.BytesToAddress(acc.PubKey) 221 | info, err := r.client.AccountInfo(hex.EncodeToString(srcAddress.Bytes())) 222 | if err != nil { 223 | log.Error("failed to get account info: %v", err) 224 | return 225 | } 226 | 227 | destAddressStr := inputNotBlank(destAddressMsg) 228 | destAddress := address.HexToAddress(destAddressStr) 229 | 230 | amountStr := inputNotBlank(amountToTransferMsg) 231 | 232 | gas := uint64(1) 233 | if yesOrNoQuestion(useDefaultGasMsg) == "n" { 234 | gasStr := inputNotBlank(enterGasPrice) 235 | gas, err = strconv.ParseUint(gasStr, 10, 64) 236 | if err != nil { 237 | log.Error("invalid gas", err) 238 | return 239 | } 240 | } 241 | 242 | fmt.Println(printPrefix, "Transaction summary:") 243 | fmt.Println(printPrefix, "From: ", srcAddress.String()) 244 | fmt.Println(printPrefix, "To: ", destAddress.String()) 245 | fmt.Println(printPrefix, "Amount:", amountStr) 246 | fmt.Println(printPrefix, "Gas: ", gas) 247 | fmt.Println(printPrefix, "Nonce: ", info.Nonce) 248 | 249 | nonce, err := strconv.ParseUint(info.Nonce, 10, 64) 250 | amount, err := strconv.ParseUint(amountStr, 10, 64) 251 | 252 | if yesOrNoQuestion(confirmTransactionMsg) == "y" { 253 | id, err := r.client.Transfer(destAddress, nonce, amount, gas, 100, acc.PrivKey) 254 | if err != nil { 255 | log.Error(err.Error()) 256 | return 257 | } 258 | fmt.Println(printPrefix, fmt.Sprintf("tx submitted, id: %v", id)) 259 | } 260 | } 261 | 262 | func (r *repl) rebel() { 263 | acc := r.client.CurrentAccount() 264 | if acc == nil { 265 | r.chooseAccount() 266 | acc = r.client.CurrentAccount() 267 | } 268 | 269 | datadir := inputNotBlank(libonomyDatadirMsg) 270 | 271 | spaceStr := inputNotBlank(libonomySpaceAllocationMsg) 272 | space, err := strconv.ParseUint(spaceStr, 10, 32) 273 | if err != nil { 274 | log.Error("failed to parse: %v", err) 275 | return 276 | } 277 | 278 | if err := r.client.Rebel(datadir, uint(space)<<30, accounts.StringAddress(acc.Address())); err != nil { 279 | log.Error("failed to start libonomy: %v", err) 280 | return 281 | } 282 | } 283 | 284 | func (r *repl) listTxs() { 285 | acc := r.client.CurrentAccount() 286 | if acc == nil { 287 | r.chooseAccount() 288 | acc = r.client.CurrentAccount() 289 | } 290 | 291 | txs, err := r.client.ListTxs(accounts.StringAddress(acc.Address())) 292 | if err != nil { 293 | log.Error("failed to list txs: %v", err) 294 | return 295 | } 296 | 297 | fmt.Println(printPrefix, fmt.Sprintf("txs: %v", txs)) 298 | } 299 | 300 | func (r *repl) quit() { 301 | os.Exit(0) 302 | } 303 | 304 | func (r *repl) coinbase() { 305 | acc := r.client.CurrentAccount() 306 | if acc == nil { 307 | r.chooseAccount() 308 | acc = r.client.CurrentAccount() 309 | } 310 | 311 | if err := r.client.SetCoinbase(accounts.StringAddress(acc.Address())); err != nil { 312 | log.Error("failed to set coinbase: %v", err) 313 | return 314 | } 315 | } 316 | 317 | func (r *repl) sign() { 318 | acc := r.client.CurrentAccount() 319 | if acc == nil { 320 | r.chooseAccount() 321 | acc = r.client.CurrentAccount() 322 | } 323 | 324 | msgStr := inputNotBlank(msgSignMsg) 325 | msg, err := hex.DecodeString(msgStr) 326 | if err != nil { 327 | log.Error("failed to decode msg hex string: %v", err) 328 | return 329 | } 330 | 331 | signature := ed25519.Sign2(acc.PrivKey, msg) 332 | 333 | fmt.Println(printPrefix, fmt.Sprintf("signature (in hex): %x", signature)) 334 | } 335 | 336 | func (r *repl) textsign() { 337 | acc := r.client.CurrentAccount() 338 | if acc == nil { 339 | r.chooseAccount() 340 | acc = r.client.CurrentAccount() 341 | } 342 | 343 | msg := inputNotBlank(msgTextSignMsg) 344 | signature := ed25519.Sign2(acc.PrivKey, []byte(msg)) 345 | 346 | fmt.Println(printPrefix, fmt.Sprintf("signature (in hex): %x", signature)) 347 | } 348 | 349 | /* 350 | func (r *repl) unlockAccount() { 351 | passphrase := r.commandLineParams(1, r.input) 352 | err := r.client.Unlock(passphrase) 353 | if err != nil { 354 | log.Debug(err.Error()) 355 | return 356 | } 357 | 358 | acctCmd := r.commands[3] 359 | r.executor(fmt.Sprintf("%s %s", acctCmd.text, passphrase)) 360 | } 361 | 362 | func (r *repl) lockAccount() { 363 | passphrase := r.commandLineParams(2, r.input) 364 | err := r.client.Lock(passphrase) 365 | if err != nil { 366 | log.Debug(err.Error()) 367 | return 368 | } 369 | 370 | acctCmd := r.commands[3] 371 | r.executor(fmt.Sprintf("%s %s", acctCmd.text, passphrase)) 372 | }*/ 373 | --------------------------------------------------------------------------------