├── .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 | [](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 |
--------------------------------------------------------------------------------