OSDN Git Service

add wallet
authorjianyixun <317316abcd@163.com>
Mon, 18 Sep 2017 18:00:09 +0000 (02:00 +0800)
committerjianyixun <317316abcd@163.com>
Mon, 18 Sep 2017 18:00:09 +0000 (02:00 +0800)
hsm and remote hsm added

59 files changed:
blockchain/hsm.go [new file with mode: 0644]
blockchain/hsm_test.go [new file with mode: 0644]
blockchain/pseudohsm/addrcache.go [new file with mode: 0644]
blockchain/pseudohsm/addrcache_test.go [new file with mode: 0644]
blockchain/pseudohsm/key.go [new file with mode: 0644]
blockchain/pseudohsm/keystore_passphrase.go [new file with mode: 0644]
blockchain/pseudohsm/keystore_passphrase_test.go [new file with mode: 0644]
blockchain/pseudohsm/pseudohsm.go [new file with mode: 0644]
blockchain/pseudohsm/pseudohsm_test.go [new file with mode: 0644]
blockchain/pseudohsm/testdata/bytom-very-light-scrypt.json [new file with mode: 0644]
blockchain/pseudohsm/testdata/keystore/.hidden [new file with mode: 0644]
blockchain/pseudohsm/testdata/keystore/README [new file with mode: 0644]
blockchain/pseudohsm/testdata/keystore/UTC--2017-09-13T07-11-07.863320100Z--bm1pktmny6q69dlqulja2p2ja28k2vd6wvqpk5r76a [new file with mode: 0644]
blockchain/pseudohsm/testdata/keystore/aaa [new file with mode: 0644]
blockchain/pseudohsm/testdata/keystore/empty [new file with mode: 0644]
blockchain/pseudohsm/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e [new file with mode: 0644]
blockchain/pseudohsm/testdata/keystore/garbage [new file with mode: 0644]
blockchain/pseudohsm/testdata/keystore/no-address [new file with mode: 0644]
blockchain/pseudohsm/testdata/keystore/zero [new file with mode: 0644]
blockchain/pseudohsm/testdata/keystore/zzz [new file with mode: 0644]
blockchain/pseudohsm/testdata/v1_test_vector.json [new file with mode: 0644]
blockchain/pseudohsm/testdata/v3_test_vector.json [new file with mode: 0644]
blockchain/pseudohsm/watch.go [new file with mode: 0644]
blockchain/pseudohsm/watch_fallback.go [new file with mode: 0644]
cmd/bytom/pseudohsm.go [new file with mode: 0644]
cmd/bytom/remotehsm.go [new file with mode: 0644]
common/README.md [new file with mode: 0644]
common/bech32.go [new file with mode: 0644]
common/bech32_test.go [new file with mode: 0644]
common/big.go [new file with mode: 0644]
common/big_test.go [new file with mode: 0644]
common/bytes.go [new file with mode: 0644]
common/bytes_test.go [new file with mode: 0644]
common/debug.go [new file with mode: 0644]
common/defaults.go [new file with mode: 0644]
common/icap.go [new file with mode: 0644]
common/icap_test.go [new file with mode: 0644]
common/list.go [new file with mode: 0644]
common/main_test.go [new file with mode: 0644]
common/math/dist.go [new file with mode: 0644]
common/math/dist_test.go [new file with mode: 0644]
common/number/int.go [new file with mode: 0644]
common/number/uint_test.go [new file with mode: 0644]
common/path.go [new file with mode: 0644]
common/size.go [new file with mode: 0644]
common/size_test.go [new file with mode: 0644]
common/test_utils.go [new file with mode: 0644]
common/types.go [new file with mode: 0644]
common/types_template.go [new file with mode: 0644]
common/types_test.go [new file with mode: 0644]
crypto/crypto.go [new file with mode: 0644]
crypto/crypto_test.go [new file with mode: 0644]
crypto/ed25519/chainkd/chainkd.go
crypto/ed25519/chainkd/util.go
crypto/ed25519/ecmath/point.go
crypto/ed25519/ecmath/scalar.go
crypto/ed25519/ed25519.go
crypto/ed25519/ed25519_test.go
crypto/randentropy/rand_entropy.go [new file with mode: 0644]

diff --git a/blockchain/hsm.go b/blockchain/hsm.go
new file mode 100644 (file)
index 0000000..ce39f2b
--- /dev/null
@@ -0,0 +1,195 @@
+//+build !no_Pseudohsm
+
+package core
+
+import (
+       "context"
+       "bytom/core/pseudohsm"
+       "bytom/core/txbuilder"
+       "bytom/crypto/ed25519/chainkd"
+       "bytom/net/http/httperror"
+       "bytom/net/http/httpjson"
+)
+
+func init() {
+       errorFormatter.Errors[Pseudohsm.ErrDuplicateKeyAlias] = httperror.Info{400, "BTM050", "Alias already exists"}
+       errorFormatter.Errors[Pseudohsm.ErrInvalidAfter] = httperror.Info{400, "BTM801", "Invalid `after` in query"}
+       errorFormatter.Errors[Pseudohsm.ErrTooManyAliasesToList] = httperror.Info{400, "BTM802", "Too many aliases to list"}
+}
+
+type requestQuery struct {
+       Filter       string        `json:"filter,omitempty"`
+       FilterParams []interface{} `json:"filter_params,omitempty"`
+       SumBy        []string      `json:"sum_by,omitempty"`
+       PageSize     int           `json:"page_size"`
+
+       // AscLongPoll and Timeout are used by /list-transactions
+       // to facilitate notifications.
+       AscLongPoll bool          `json:"ascending_with_long_poll,omitempty"`
+       Timeout     json.Duration `json:"timeout"`
+
+       // After is a completely opaque cursor, indicating that only
+       // items in the result set after the one identified by `After`
+       // should be included. It has no relationship to time.
+       After string `json:"after"`
+
+       // These two are used for time-range queries like /list-transactions
+       StartTimeMS uint64 `json:"start_time,omitempty"`
+       EndTimeMS   uint64 `json:"end_time,omitempty"`
+
+       // This is used for point-in-time queries like /list-balances
+       // TODO(bobg): Different request structs for endpoints with different needs
+       TimestampMS uint64 `json:"timestamp,omitempty"`
+
+       // This is used for filtering results from /list-access-tokens
+       // Value must be "client" or "network"
+       Type string `json:"type"`
+
+       // Aliases is used to filter results from /mockshm/list-keys
+       Aliases []string `json:"aliases,omitempty"`
+}
+
+
+// PseudoHSM configures the Core to expose the PseudoHSM endpoints. It
+// is only included in non-production builds.
+func PseudoHSM(hsm *Pseudohsm.HSM) RunOption {
+       return func(api *API) {
+
+               h := &pseudoHSMHandler{PseudoHSM: hsm}
+               needConfig := api.needConfig()
+               api.mux.Handle("/hsm/create-key", needConfig(h.pseudohsmCreateKey))
+               api.mux.Handle("/hsm/list-keys", needConfig(h.pseudohsmListKeys))
+               api.mux.Handle("/hsm/delete-key", needConfig(h.pseudohsmDeleteKey))
+               api.mux.Handle("/hsm/sign-transaction", needConfig(h.pseudohsmSignTemplates))
+               api.mux.Handle("/hsm/reset-password", needConfig(h.pseudohsmResetPassword))
+               api.mux.Handle("/hsm/update-alias", needConfig(h.pseudohsmUpdateAlias))
+
+
+       }
+}
+
+
+
+type pseudoHSMHandler struct {
+       PseudoHSM *Pseudohsm.HSM
+}
+
+func (h *PseudoHSMHandler) pseudohsmCreateKey(password string, in struct{ Alias string }) (result *Pseudohsm.XPub, err error) {
+       return h.PseudoHSM.XCreate(password, in.Alias)
+}
+
+func (h *PseudoHSMHandler) pseudohsmListKeys(query requestQuery) (page, error) {
+       limit := query.PageSize
+       if limit == 0 {
+               limit = defGenericPageSize  // defGenericPageSize = 100
+       }
+
+       xpubs, after, err := h.PseudoHSM.ListKeys(query.After, limit)
+       if err != nil {
+               return page{}, err
+       }
+
+       var items []interface{}
+       for _, xpub := range xpubs {
+               items = append(items, xpub)
+       }
+
+       query.After = after
+
+       return page{
+               Items:    httpjson.Array(items),
+               LastPage: len(xpubs) < limit,
+               Next:     query,
+       }, nil
+}
+
+func (h *PseudoHSMHandler) pseudohsmDeleteKey(xpub chainkd.XPub, password string) error {
+       return h.PseudoHSM.XDelete(xpub, password)
+}
+
+func (h *PseudoHSMHandler) pseudohsmSignTemplates(x struct {
+       Txs   []*txbuilder.Template `json:"transactions"`
+       XPubs []chainkd.XPub        `json:"xpubs"`
+}) []interface{} {
+       resp := make([]interface{}, 0, len(x.Txs))
+       for _, tx := range x.Txs {
+               err := txbuilder.Sign(tx, x.XPubs, h.pseudohsmSignTemplate)
+               if err != nil {
+                       info := errorFormatter.Format(err)
+                       resp = append(resp, info)
+               } else {
+                       resp = append(resp, tx)
+               }
+       }
+       return resp
+}
+
+func (h *PseudoHSMHandler) pseudohsmSignTemplate(xpub chainkd.XPub, path [][]byte, data [32]byte) ([]byte, error) {
+       sigBytes, err := h.PseudoHSM.XSign(xpub, path, data[:])
+       if err == Pseudohsm.ErrNoKey {
+               return nil, nil
+       }
+       return sigBytes, err
+}
+
+// remote hsm used
+
+func RemoteHSM(hsm *remoteHSM) RunOption {
+       return func(api *API) {
+               h := &retmoteHSMHandler{RemoteHSM: hsm}
+               needConfig := api.needConfig()
+               api.mux.Handle("/hsm/sign-transaction", needConfig(h.Sign))
+       }
+}
+
+/*
+type remoteHSM struct {
+       Client *rpc.Client
+}
+
+func remoteHSMHandler struct {
+       RemoteHSM  *remoteHSM
+}
+
+func New(conf *config.Config) *HSM {
+
+       httpClient := new(http.Client)
+       httpClient.Transport = &http.Transport{
+               TLSClientConfig: tlsConfig,
+               // The following fields are default values
+               // copied from DefaultTransport.
+               // (When you change them, be sure to move them
+               // above this line so this comment stays true.)
+               DialContext: (&net.Dialer{
+                       Timeout:   30 * time.Second,
+                       KeepAlive: 30 * time.Second,
+                       DualStack: true,
+               }).DialContext,
+               MaxIdleConns:          100,
+               IdleConnTimeout:       90 * time.Second,
+               TLSHandshakeTimeout:   10 * time.Second,
+               ExpectContinueTimeout: 1 * time.Second,
+       }
+
+       return &remoteHSM{Client: &rpc.Client{
+                       BaseURL:      conf.HsmUrl,
+                       AccessToken:  conf.HsmAccessToken,
+                       Username:     conf.processID,
+                       CoreID:       conf.Id,
+                       Version:      conf.version,
+                       BlockchainID: conf.BlockchainId.String(),
+                       Client:       httpClient,
+               }}
+}
+
+
+func (h *remoteHSM) Sign(ctx context.Context, pk ed25519.PublicKey, date [32]byte)([]byte, err error) {
+       body := struct {
+               Block *legacy.TxHeader    `json:"txheader"`
+               Pub   json.HexBytes       `json:"pubkey"`
+       }{data, json.HexBytes(pk[:])}
+
+       err = h.Client.Call(ctx, "/sign-transaction", body, &sigBytes)
+       return sigBytes
+}
+*/
\ No newline at end of file
diff --git a/blockchain/hsm_test.go b/blockchain/hsm_test.go
new file mode 100644 (file)
index 0000000..b7f043b
--- /dev/null
@@ -0,0 +1,108 @@
+package core
+
+import (
+       "context"
+       "testing"
+       "time"
+
+       "chain/core/account"
+       "chain/core/asset"
+       "chain/core/coretest"
+       "chain/core/generator"
+       "chain/core/pseudohsm"
+       "chain/core/pin"
+       "chain/core/query"
+       "chain/core/txbuilder"
+       "chain/crypto/ed25519/chainkd"
+       "chain/database/pg/pgtest"
+       "chain/protocol/bc"
+       "chain/protocol/prottest"
+       "chain/testutil"
+)
+
+func TestPseudoHSM((t *testing.T) {
+       c := prottest.NewChain(t)
+       g := generator.New(c, nil, db)
+       pinStore := pin.NewStore(db)
+       assets := asset.NewRegistry(db, c, pinStore)
+       accounts := account.NewManager(db, c, pinStore)
+       coretest.CreatePins(ctx, t, pinStore)
+       accounts.IndexAccounts(query.NewIndexer(db, c, pinStore))
+       go accounts.ProcessBlocks(ctx)
+       pseudohsm := pseudohsm.New(db)
+
+       xpub1, err := pseudohsm.XCreate("langyu", "nopassword")
+       if err != nil {
+               t.Fatal(err)
+       }
+       acct1, err := accounts.Create(ctx, []chainkd.XPub{xpub1.XPub}, 1, "", nil, "")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       _, xpub2, err := chainkd.NewXKeys(nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       acct2, err := accounts.Create(ctx, []chainkd.XPub{xpub2}, 1, "", nil, "")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       assetDef1 := map[string]interface{}{"foo": 1}
+       assetDef2 := map[string]interface{}{"foo": 2}
+
+       asset1ID := coretest.CreateAsset(ctx, t, assets, assetDef1, "", nil)
+       asset2ID := coretest.CreateAsset(ctx, t, assets, assetDef2, "", nil)
+
+       issueSrc1 := txbuilder.Action(assets.NewIssueAction(bc.AssetAmount{AssetId: &asset1ID, Amount: 100}, nil))
+       issueSrc2 := txbuilder.Action(assets.NewIssueAction(bc.AssetAmount{AssetId: &asset2ID, Amount: 200}, nil))
+       issueDest1 := accounts.NewControlAction(bc.AssetAmount{AssetId: &asset1ID, Amount: 100}, acct1.ID, nil)
+       issueDest2 := accounts.NewControlAction(bc.AssetAmount{AssetId: &asset2ID, Amount: 200}, acct2.ID, nil)
+       tmpl, err := txbuilder.Build(ctx, nil, []txbuilder.Action{issueSrc1, issueSrc2, issueDest1, issueDest2}, time.Now().Add(time.Minute))
+       if err != nil {
+               t.Fatal(err)
+       }
+       coretest.SignTxTemplate(t, ctx, tmpl, &testutil.TestXPrv)
+       err = txbuilder.FinalizeTx(ctx, c, g, tmpl.Transaction)
+       if err != nil {
+               t.Fatal(err)
+       }
+       handler := &mockHSMHandler{MockHSM: mockhsm}
+       outTmpls := handler.mockhsmSignTemplates(ctx, struct {
+               Txs   []*txbuilder.Template `json:"transactions"`
+               XPubs []chainkd.XPub        `json:"xpubs"`
+       }{[]*txbuilder.Template{tmpl}, []chainkd.XPub{xpub1.XPub}})
+       if len(outTmpls) != 1 {
+               t.Fatalf("expected 1 output template, got %d", len(outTmpls))
+       }
+       outTmpl, ok := outTmpls[0].(*txbuilder.Template)
+       if !ok {
+               t.Fatalf("expected a *txbuilder.Template, got %T (%v)", outTmpls[0], outTmpls[0])
+       }
+       if len(outTmpl.SigningInstructions) != 2 {
+               t.Fatalf("expected 2 signing instructions, got %d", len(outTmpl.SigningInstructions))
+       }
+
+       inspectSigInst(t, outTmpl.SigningInstructions[0], true)
+       inspectSigInst(t, outTmpl.SigningInstructions[1], false)
+}
+
+func inspectSigInst(t *testing.T, si *txbuilder.SigningInstruction, expectSig bool) {
+       if len(si.SignatureWitnesses) != 1 {
+               t.Fatalf("len(si.SignatureWitnesses) is %d, want 1", len(si.SignatureWitnesses))
+       }
+       s := si.SignatureWitnesses[0]
+       if len(s.Sigs) != 1 {
+               t.Fatalf("len(s.Sigs) is %d, want 1", len(s.Sigs))
+       }
+       if expectSig {
+               if len(s.Sigs[0]) == 0 {
+                       t.Errorf("expected a signature in s.Sigs[0]")
+               }
+       } else {
+               if len(s.Sigs[0]) != 0 {
+                       t.Errorf("expected no signature in s.Sigs[0], got %x", s.Sigs[0])
+               }
+       }
+}
diff --git a/blockchain/pseudohsm/addrcache.go b/blockchain/pseudohsm/addrcache.go
new file mode 100644 (file)
index 0000000..60e6b8c
--- /dev/null
@@ -0,0 +1,275 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package pseudohsm
+
+import (
+       "bufio"
+       "encoding/json"
+       "fmt"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "sort"
+       "strings"
+       "sync"
+       "time"
+
+       "bytom/common"
+       _"bytom/errors"
+)
+
+// Minimum amount of time between cache reloads. This limit applies if the platform does
+// not support change notifications. It also applies if the keystore directory does not
+// exist yet, the code will attempt to create a watcher at most this often.
+const minReloadInterval = 2 * time.Second
+
+type keysByFile []XPub
+func (s keysByFile) Len() int           { return len(s) }
+func (s keysByFile) Less(i, j int) bool { return s[i].File < s[j].File }
+func (s keysByFile) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+
+
+// AmbiguousAddrError is returned when attempting to unlock
+// an address for which more than one file exists.
+type AmbiguousAddrError struct {
+       Addr    common.Address
+       Matches []XPub
+}
+
+func (err *AmbiguousAddrError) Error() string {
+       files := ""
+       for i, a := range err.Matches {
+               files += a.File
+               if i < len(err.Matches)-1 {
+                       files += ", "
+               }
+       }
+       return fmt.Sprintf("multiple keys match address (%s)", files)
+}
+
+// addrCache is a live index of all keys in the keystore.
+type addrCache struct {
+       keydir   string
+       watcher  *watcher
+       mu       sync.Mutex
+       all      keysByFile
+       byAddr   map[common.Address][]XPub
+       throttle *time.Timer
+}
+
+func newAddrCache(keydir string) *addrCache {
+       ac := &addrCache{
+               keydir: keydir,
+               byAddr: make(map[common.Address][]XPub),
+       }
+       ac.watcher = newWatcher(ac)
+       return ac
+}
+
+func (ac *addrCache) hasAddress(addr common.Address) bool {
+       ac.maybeReload()
+       ac.mu.Lock()
+       defer ac.mu.Unlock()
+       return len(ac.byAddr[addr]) > 0
+}
+
+func (ac *addrCache) add(newKey XPub) {
+       ac.mu.Lock()
+       defer ac.mu.Unlock()
+
+       i := sort.Search(len(ac.all), func(i int) bool { return ac.all[i].File >= newKey.File })
+       if i < len(ac.all) && ac.all[i] == newKey {
+               return
+       }
+       // newKey is not in the cache.
+       ac.all = append(ac.all, XPub{})
+       copy(ac.all[i+1:], ac.all[i:])
+       ac.all[i] = newKey
+       ac.byAddr[newKey.Address] = append(ac.byAddr[newKey.Address], newKey)
+}
+
+func (ac *addrCache) keys() []XPub {
+       ac.maybeReload()
+       ac.mu.Lock()
+       defer ac.mu.Unlock()
+       cpy := make([]XPub, len(ac.all))
+       copy(cpy, ac.all)
+       return cpy
+}
+
+func (ac *addrCache) maybeReload() {
+       ac.mu.Lock()
+       defer ac.mu.Unlock()
+       
+       if ac.watcher.running {
+               return // A watcher is running and will keep the cache up-to-date.
+       }
+
+       if ac.throttle == nil {
+               ac.throttle = time.NewTimer(0)
+       } else {
+               select {
+               case <-ac.throttle.C:
+               default:
+                       return // The cache was reloaded recently.
+               }
+       }
+       ac.watcher.start()
+       ac.reload()
+       ac.throttle.Reset(minReloadInterval)
+}
+
+// find returns the cached keys for address if there is a unique match.
+// The exact matching rules are explained by the documentation of Account.
+// Callers must hold ac.mu.
+func (ac *addrCache) find(xpub XPub) (XPub, error) {
+       // Limit search to address candidates if possible.
+       matches := ac.all
+       if (xpub.Address != common.Address{}) {
+               matches = ac.byAddr[xpub.Address]
+       }
+       if xpub.File != "" {
+               // If only the basename is specified, complete the path.
+               if !strings.ContainsRune(xpub.File, filepath.Separator) {
+                       xpub.File = filepath.Join(ac.keydir, xpub.File)
+               }
+               for i := range matches {
+                       if matches[i].File == xpub.File {
+                               return matches[i], nil
+                       }
+               }
+               if (xpub.Address == common.Address{}) {
+                       return XPub{}, ErrNoKey
+               }
+       }
+       switch len(matches) {
+       case 1:
+               return matches[0], nil
+       case 0:
+               return XPub{}, ErrNoKey
+       default:
+               err := &AmbiguousAddrError{Addr: xpub.Address, Matches: make([]XPub, len(matches))}
+               copy(err.Matches, matches)
+               return XPub{}, err
+       }
+}
+
+// reload caches addresses of existing key.
+// Callers must hold ac.mu.
+func (ac *addrCache) reload() {
+       keys, err := ac.scan()
+       if err != nil {
+               //log.Printf("can't load keys: %v", err.Error())
+               fmt.Printf("can't load keys: %v\n", err.Error())
+
+       }
+       ac.all = keys
+       sort.Sort(ac.all)
+       for k := range ac.byAddr {
+               delete(ac.byAddr, k)
+       }
+       for _, k := range keys {
+               ac.byAddr[k.Address] = append(ac.byAddr[k.Address], k)
+       }
+       //log.Printf("reloaded keys, cache has %d keys", len(ac.all))
+       fmt.Printf("reloaded keys, cache has %d keys\n", len(ac.all))
+}
+
+func (ac *addrCache) scan() ([]XPub, error) {
+       files, err := ioutil.ReadDir(ac.keydir)
+       if err != nil {
+               return nil, err
+       }
+       var (
+               buf     = new(bufio.Reader)
+               keys    []XPub
+               keyJSON struct {
+                       Address common.Address `json:"address"`
+                       Alias   string             `json:"alias"`
+               }
+       )
+       for _, fi := range files {
+               path := filepath.Join(ac.keydir, fi.Name())
+               if skipKeyFile(fi) {
+                       //log.Printf("ignoring file %v", path)
+                       //fmt.Printf("ignoring file %v", path)
+                       continue
+               }
+               fd, err := os.Open(path)
+               if err != nil {
+                       //log.Printf(err)
+                       fmt.Printf("err")
+                       continue
+               }
+               buf.Reset(fd)
+               // Parse the address.
+               keyJSON.Address = common.Address{}
+               err = json.NewDecoder(buf).Decode(&keyJSON)
+               switch {
+               case err != nil:
+                       //log.Printf("can't decode key %s: %v", path, err)
+                       fmt.Printf("can't decode key %s: %v", path, err)
+               case (keyJSON.Address == common.Address{}):
+                       fmt.Printf("can't decode key %s: missing or zero address", path)
+               default:
+                       keys = append(keys, XPub{Address: keyJSON.Address, Alias: keyJSON.Alias, File: path})
+               }
+               fd.Close()
+       }
+       return keys, err
+}
+
+func (ac *addrCache) delete(removed XPub) {
+       ac.mu.Lock()
+       defer ac.mu.Unlock()
+       ac.all = removeKey(ac.all, removed)
+       if ba := removeKey(ac.byAddr[removed.Address], removed); len(ba) == 0 {
+               delete(ac.byAddr, removed.Address)
+       } else {
+               ac.byAddr[removed.Address] = ba
+       }
+}
+
+func removeKey(slice []XPub, elem XPub) []XPub {
+       for i := range slice {
+               if slice[i] == elem {
+                       return append(slice[:i], slice[i+1:]...)
+               }
+       }
+       return slice
+}
+
+func skipKeyFile(fi os.FileInfo) bool {
+       // Skip editor backups and UNIX-style hidden files.
+       if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
+               return true
+       }
+       // Skip misc special files, directories (yes, symlinks too).
+       if fi.IsDir() || fi.Mode()&os.ModeType != 0 {
+               return true
+       }
+       return false
+}
+
+func (ac *addrCache) close() {
+       ac.mu.Lock()
+       ac.watcher.close()
+       if ac.throttle != nil {
+               ac.throttle.Stop()
+       }
+       ac.mu.Unlock()
+}
diff --git a/blockchain/pseudohsm/addrcache_test.go b/blockchain/pseudohsm/addrcache_test.go
new file mode 100644 (file)
index 0000000..024631b
--- /dev/null
@@ -0,0 +1,299 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package pseudohsm
+
+import (
+       "fmt"
+       "math/rand"
+       "os"
+       "path/filepath"
+       "io/ioutil"
+       "reflect"
+       "sort"
+       "testing"
+       "time"
+
+       "bytom/common"
+       "github.com/cespare/cp"
+       "github.com/davecgh/go-spew/spew"
+)
+
+
+var (
+       cachetestDir, _   = filepath.Abs(filepath.Join("testdata", "keystore"))
+       cachetestKeys = []XPub{
+               {
+                       Alias:   "langyu",
+                       Address: common.StringToAddress("bm1pktmny6q69dlqulja2p2ja28k2vd6wvqpk5r76a"),
+                       File:    filepath.Join(cachetestDir, "UTC--2017-09-13T07-11-07.863320100Z--bm1pktmny6q69dlqulja2p2ja28k2vd6wvqpk5r76a"),
+               },
+               {
+                       Alias:   "aaatest",
+                       Address: common.StringToAddress("bm1p8wuwq0jn706ntcv95pftv8evj48m2x5nmmpdlj"),
+                       File:    filepath.Join(cachetestDir, "aaa"),
+               },
+               {
+                       Alias:   "zzztest",
+                       Address: common.StringToAddress("bm1pzye4ep30gghfnxe9c7dcch5m87lfrl3hrvegkc"),
+                       File:    filepath.Join(cachetestDir, "zzz"),
+               },
+       }
+)
+
+func TestWatchNewFile(t *testing.T) {
+       t.Parallel()
+
+       dir, ac := tmpManager(t)
+       //defer os.RemoveAll(dir)
+
+       // Ensure the watcher is started before adding any files.
+       ac.keys()
+       time.Sleep(200 * time.Millisecond)
+       // Move in the files.
+       wantKeystores:= make([]XPub, len(cachetestKeys))
+       for i := range cachetestKeys {
+               a := cachetestKeys[i]
+               a.File = filepath.Join(dir, filepath.Base(a.File))
+               wantKeystores[i] = a
+               if err := cp.CopyFile(a.File, cachetestKeys[i].File); err != nil {
+                       t.Fatal(err)
+               }
+       }
+
+       // ac should see the accounts.
+       var list []XPub
+       for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 {
+               list = ac.keys()
+               if reflect.DeepEqual(list, wantKeystores) {
+                       return
+               }
+               time.Sleep(d)
+       }
+       t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantKeystores))
+}
+
+
+func TestWatchNoDir(t *testing.T) {
+       t.Parallel()
+
+       // Create am but not the directory that it watches.
+       rand.Seed(time.Now().UnixNano())
+       dir := filepath.Join(os.TempDir(), fmt.Sprintf("bytom-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
+       ac := newAddrCache(dir)
+       list := ac.keys()
+       if len(list) > 0 {
+               t.Error("initial account list not empty:", list)
+       }
+       time.Sleep(100 * time.Millisecond)
+
+       // Create the directory and copy a key file into it.
+       os.MkdirAll(dir, 0700)
+       defer os.RemoveAll(dir)
+       file := filepath.Join(dir, "aaa")
+       if err := cp.CopyFile(file, cachetestKeys[0].File); err != nil {
+               t.Fatal(err)
+       }
+
+       // am should see the account.
+       wantKeys := []XPub{cachetestKeys[0]}
+       wantKeys[0].File = file
+       for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
+               list = ac.keys()
+               if reflect.DeepEqual(list, wantKeys) {
+                       return
+               }
+               time.Sleep(d)
+       }
+       t.Errorf("\ngot  %v\nwant %v", list, wantKeys)
+}
+
+func TestCacheInitialReload(t *testing.T) {
+       cache := newAddrCache(cachetestDir)
+       keys := cache.keys()
+       if !reflect.DeepEqual(keys, cachetestKeys) {
+               t.Fatalf("got initial accounts: %swant %s", spew.Sdump(keys), spew.Sdump(cachetestKeys))
+       }
+}
+
+
+func TestCacheAddDeleteOrder(t *testing.T) {
+       cache := newAddrCache("testdata/no-such-dir")
+       cache.watcher.running = true // prevent unexpected reloads
+
+       keys := []XPub{
+               {
+                       Address: common.StringToAddress("bm1pvheagygs9d72stp79u9vduhmdyjpnvud0y89y7"),
+                       File:    "-309830980",
+               },
+               {
+                       Address: common.StringToAddress("bm1pyk3qny8gzem6p4fx8t5d344tnldguv8lvx2aww"),
+                       File:    "ggg",
+               },
+               {
+                       Address: common.StringToAddress("bm1p6s0ckxrudy7hqht4n5fhcs4gp69krv3c84jn9x"),
+                       File:    "zzzzzz-the-very-last-one.keyXXX",
+               },
+               {
+                       Address: common.StringToAddress("bm1p7xkfhsw50y44t63mk0dfxxkvuyg6t3s0r6xs54"),
+                       File:    "SOMETHING.key",
+               },
+               {
+                       Address: common.StringToAddress("bm1peu9ql7x8c7aeca60j40sg5w4kylpf7l3jmau0g"),
+                       File:    "UTC--2016-03-22T12-57-55.920751759Z--bm1peu9ql7x8c7aeca60j40sg5w4kylpf7l3jmau0g",
+               },
+               {
+                       Address: common.StringToAddress("bm1p0s68e4ggp0vy5ue2lztsxvl2smpnqp9al8jyvh"),
+                       File:    "aaa",
+               },
+               {
+                       Address: common.StringToAddress("bm1pjq8ttfl7ppqtcc5qqff0s45p7ew9l9pjmlu5xw"),
+                       File:    "zzz",
+               },
+       }
+       for _, a := range keys {
+               cache.add(a)
+       }
+       // Add some of them twice to check that they don't get reinserted.
+       cache.add(keys[0])
+       cache.add(keys[2])
+
+       // Check that the account list is sorted by filename.
+       wantKeys := make([]XPub, len(keys))
+       copy(wantKeys , keys)
+       sort.Sort(keysByFile(wantKeys))
+       list := cache.keys()
+       
+       if !reflect.DeepEqual(list, wantKeys) {
+               t.Fatalf("got keys: %s\nwant %s", spew.Sdump(keys), spew.Sdump(wantKeys))
+       }
+       
+       for _, a := range keys {
+               if !cache.hasAddress(a.Address) {
+                       t.Errorf("expected hasAccount(%x) to return true", a.Address)
+               }
+       }
+       if cache.hasAddress(common.StringToAddress("bm1pug2xpcvpzepdf0paulnndhpxtpjvre8ypd0jtj")) {
+               t.Errorf("expected hasAccount(%x) to return false", common.StringToAddress("bm1pug2xpcvpzepdf0paulnndhpxtpjvre8ypd0jtj"))
+       }
+
+       // Delete a few keys from the cache.
+       for i := 0; i < len(keys); i += 2 {
+               cache.delete(wantKeys[i])
+       }
+       cache.delete(XPub{Address: common.StringToAddress("bm1pug2xpcvpzepdf0paulnndhpxtpjvre8ypd0jtj"), File: "something"})
+
+       // Check content again after deletion.
+       wantKeysAfterDelete := []XPub{
+               wantKeys[1],
+               wantKeys[3],
+               wantKeys[5],
+       }
+       list = cache.keys()
+       if !reflect.DeepEqual(list, wantKeysAfterDelete) {
+               t.Fatalf("got keys after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantKeysAfterDelete))
+       }
+       for _, a := range wantKeysAfterDelete {
+               if !cache.hasAddress(a.Address) {
+                       t.Errorf("expected hasAccount(%x) to return true", a.Address)
+               }
+       }
+       if cache.hasAddress(wantKeys[0].Address) {
+               t.Errorf("expected hasAccount(%x) to return false", wantKeys[0].Address)
+       }
+}
+
+
+func TestCacheFind(t *testing.T) {
+       dir := filepath.Join("testdata", "dir")
+       cache := newAddrCache(dir)
+       cache.watcher.running = true // prevent unexpected reloads
+
+       keys := []XPub{
+               {
+                       Address: common.StringToAddress("bm1pmv9kg68j3edvqrv62lxllev4ugjv0zf6g5pwf6"),
+                       File:    filepath.Join(dir, "a.key"),
+               },
+               {
+                       Address: common.StringToAddress("bm1ptspg4x6kjjp642gdpzan0ynq9zr7z4m34nqpet"),
+                       File:    filepath.Join(dir, "b.key"),
+               },
+               {
+                       Address: common.StringToAddress("bm1pmlpy0946zsvdg29v80gw0mkq2n0ghkg0fpmhav"),
+                       File:    filepath.Join(dir, "c.key"),
+               },
+               {
+                       Address: common.StringToAddress("bm1pmlpy0946zsvdg29v80gw0mkq2n0ghkg0fpmhav"),
+                       File:    filepath.Join(dir, "c2.key"),
+               },
+       }
+       for _, a := range keys {
+               cache.add(a)
+       }
+
+       nomatchKey := XPub{
+               Address: common.StringToAddress("bm1pu2vmgps4d9e3mrsuzp58w777apky4rjgn5rn9e"),
+               File:    filepath.Join(dir, "something"),
+       }
+       tests := []struct {
+               Query      XPub
+               WantResult XPub
+               WantError  error
+       }{
+               // by address
+               {Query: XPub{Address: keys[0].Address}, WantResult: keys[0]},
+               // by file
+               {Query: XPub{File: keys[0].File}, WantResult: keys[0]},
+               // by basename
+               {Query: XPub{File: filepath.Base(keys[0].File)}, WantResult: keys[0]},
+               // by file and address
+               {Query: keys[0], WantResult: keys[0]},
+               // ambiguous address, tie resolved by file
+               {Query: keys[2], WantResult: keys[2]},
+               // ambiguous address error
+               {
+                       Query: XPub{Address: keys[2].Address},
+                       WantError: &AmbiguousAddrError{
+                               Addr:    keys[2].Address,
+                               Matches: []XPub{keys[2], keys[3]},
+                       },
+               },
+               // no match error
+               {Query: nomatchKey, WantError: ErrNoKey},
+               {Query: XPub{File: nomatchKey.File}, WantError: ErrNoKey},
+               {Query: XPub{File: filepath.Base(nomatchKey.File)}, WantError: ErrNoKey},
+               {Query: XPub{Address: nomatchKey.Address}, WantError: ErrNoKey},
+       }
+       for i, test := range tests {
+               a, err := cache.find(test.Query)
+               if !reflect.DeepEqual(err, test.WantError) {
+                       t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError)
+                       continue
+               }
+               if a != test.WantResult {
+                       t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult)
+                       continue
+               }
+       }
+}
+
+func tmpManager(t *testing.T) (string, *addrCache) {
+       d, err := ioutil.TempDir("", "bytom-keystore-test")
+       if err != nil {
+               t.Fatal(err)
+       }
+       return d, newAddrCache(d)
+}
diff --git a/blockchain/pseudohsm/key.go b/blockchain/pseudohsm/key.go
new file mode 100644 (file)
index 0000000..42b47d1
--- /dev/null
@@ -0,0 +1,172 @@
+package pseudohsm
+
+import (
+       _"encoding/hex"
+       //"encoding/json"
+       "fmt"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "time"
+
+       "bytom/common"
+       "bytom/crypto/ed25519/chainkd"
+
+       "github.com/pborman/uuid"
+
+)
+
+const (
+       version = 1
+       keytype = "bytom_kd"
+)
+
+type XKey struct {
+       Id      uuid.UUID
+       KeyType string
+       Alias   string
+       Address common.Address
+       XPrv    chainkd.XPrv
+       XPub    chainkd.XPub
+}
+
+type keyStore interface {
+       // Loads and decrypts the key from disk.
+       GetKey(addr common.Address, filename string, auth string) (*XKey, error)
+       // Writes and encrypts the key.
+       StoreKey(filename string, k *XKey, auth string) error
+       // Joins filename with the key directory unless it is already absolute.
+       JoinPath(filename string) string
+}
+
+type encryptedKeyJSON struct {
+       Address string     `json:"address"`
+       Crypto  cryptoJSON `json:"crypto"`
+       Id      string     `json:"id"`
+       Type    string     `json:"type"`
+       Version int        `json:"version"`
+       Alias   string     `json:"alias"`
+}
+
+type cryptoJSON struct {
+       Cipher       string                 `json:"cipher"`
+       CipherText   string                 `json:"ciphertext"`
+       CipherParams cipherparamsJSON       `json:"cipherparams"`
+       KDF          string                 `json:"kdf"`
+       KDFParams    map[string]interface{} `json:"kdfparams"`
+       MAC          string                 `json:"mac"`
+}
+
+type cipherparamsJSON struct {
+       IV string `json:"iv"`
+}
+
+type scryptParamsJSON struct {
+       N     int    `json:"n"`
+       R     int    `json:"r"`
+       P     int    `json:"p"`
+       DkLen int    `json:"dklen"`
+       Salt  string `json:"salt"`
+}
+
+/*
+func (k *XKey) MarshalJSON() (j []byte, err error) {
+       jStruct := plainKeyJSON{
+               hex.EncodeToString(k.Address[:]),
+               hex.EncodeToString(k.XPrv[:]),
+               hex.EncodeToString(k.XPub[:]),
+               k.Id.String(),
+               k.KeyType,
+               version,
+       }
+       j, err = json.Marshal(jStruct)
+       return j, err
+}
+
+
+func (k *XKey) UnmarshalJSON(j []byte) (err error) {
+       keyJSON := new(plainKeyJSON)
+       err = json.Unmarshal(j, &keyJSON)
+       if err != nil {
+               return err
+       }
+       u := new(uuid.UUID)
+       *u = uuid.Parse(keyJSON.Id)
+       k.Id = *u
+       addr, err := hex.DecodeString(keyJSON.Address)
+       if err != nil {
+               return err
+       }
+
+       privkey, err := hex.DecodeString(keyJSON.PrivateKey)
+       if err != nil {
+               return err
+       }
+
+       pubkey, err := hex.DecodeString(keyJSON.PublicKey)
+       if err != nil {
+               return err
+       }
+
+       ktype, err := hex.DecodeString(keyJSON.Type)
+       if err != nil {
+               return err
+       }
+       k.KeyType = hex.EncodeToString(ktype)
+       if k.KeyType != keytype {
+               return ErrInvalidKeyType
+       }
+       
+       k.Address = common.BytesToAddress(addr)
+
+       copy(k.XPrv[:], privkey)
+       copy(k.XPub[:], pubkey)
+       return nil
+}
+*/
+func writeKeyFile(file string, content []byte) error {
+       // Create the keystore directory with appropriate permissions
+       // in case it is not present yet.
+       const dirPerm = 0700
+       if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil {
+               return err
+       }
+       // Atomic write: create a temporary hidden file first
+       // then move it into place. TempFile assigns mode 0600.
+       f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
+       if err != nil {
+               return err
+       }
+       if _, err := f.Write(content); err != nil {
+               f.Close()
+               os.Remove(f.Name())
+               return err
+       }
+       f.Close()
+       return os.Rename(f.Name(), file)
+}
+
+func zeroKey(k *XKey) {
+       b := k.XPrv
+       for i := range b {
+               b[i] = 0
+       }
+}
+
+// keyFileName implements the naming convention for keyfiles:
+// UTC--<created_at UTC ISO8601>-<address hex>
+func keyFileName(keyAddr common.Address) string {
+       ts := time.Now().UTC()
+       return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), keyAddr.Str())
+}
+
+func toISO8601(t time.Time) string {
+       var tz string
+       name, offset := t.Zone()
+       if name == "UTC" {
+               tz = "Z"
+       } else {
+               tz = fmt.Sprintf("%03d00", offset/3600)
+       }
+       return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
+}
diff --git a/blockchain/pseudohsm/keystore_passphrase.go b/blockchain/pseudohsm/keystore_passphrase.go
new file mode 100644 (file)
index 0000000..9faead1
--- /dev/null
@@ -0,0 +1,320 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+/*
+
+This key store behaves as KeyStorePlain with the difference that
+the private key is encrypted and on disk uses another JSON encoding.
+
+The crypto is documented at https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
+
+*/
+
+package pseudohsm
+
+import (
+       "bytes"
+       "crypto/aes"
+       "crypto/cipher"
+       "crypto/sha256"
+       "encoding/hex"
+       "encoding/json"
+       "fmt"
+       "io/ioutil"
+       "path/filepath"
+
+       "bytom/common"
+       "bytom/crypto"
+       "bytom/crypto/randentropy"
+       "bytom/crypto/ed25519/chainkd"
+       "github.com/pborman/uuid"
+       "golang.org/x/crypto/pbkdf2"
+       "golang.org/x/crypto/scrypt"
+)
+
+const (
+       keyHeaderKDF = "scrypt"
+
+       // n,r,p = 2^18, 8, 1 uses 256MB memory and approx 1s CPU time on a modern CPU.
+       StandardScryptN = 1 << 18
+       StandardScryptP = 1
+
+       // n,r,p = 2^12, 8, 6 uses 4MB memory and approx 100ms CPU time on a modern CPU.
+       LightScryptN = 1 << 12
+       LightScryptP = 6
+
+       scryptR     = 8
+       scryptDKLen = 32
+)
+
+type keyStorePassphrase struct {
+       keysDirPath string
+       scryptN     int
+       scryptP     int
+}
+
+func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*XKey, error) {
+       // Load the key from the keystore and decrypt its contents
+       keyjson, err := ioutil.ReadFile(filename)
+       if err != nil {
+               return nil, err
+       }
+       key, err := DecryptKey(keyjson, auth)
+       if err != nil {
+               return nil, err
+       }
+       // Make sure we're really operating on the requested key (no swap attacks)
+       if key.Address != addr {
+               return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr)
+       }
+       return key, nil
+}
+
+func (ks keyStorePassphrase) StoreKey(filename string, key *XKey, auth string) error {
+       keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
+       if err != nil {
+               return err
+       }
+       return writeKeyFile(filename, keyjson)
+}
+
+func (ks keyStorePassphrase) JoinPath(filename string) string {
+       if filepath.IsAbs(filename) {
+               return filename
+       } else {
+               return filepath.Join(ks.keysDirPath, filename)
+       }
+}
+
+// EncryptKey encrypts a key using the specified scrypt parameters into a json
+// blob that can be decrypted later on.
+func EncryptKey(key *XKey, auth string, scryptN, scryptP int) ([]byte, error) {
+       authArray := []byte(auth)
+       salt := randentropy.GetEntropyCSPRNG(32)
+       derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen)
+       if err != nil {
+               return nil, err
+       }
+       encryptKey := derivedKey[:16]
+       keyBytes := key.XPrv[:]
+
+       iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16
+       cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)
+       if err != nil {
+               return nil, err
+       }
+       mac := crypto.Sha256(derivedKey[16:32], cipherText)
+       scryptParamsJSON := make(map[string]interface{}, 5)
+       scryptParamsJSON["n"] = scryptN
+       scryptParamsJSON["r"] = scryptR
+       scryptParamsJSON["p"] = scryptP
+       scryptParamsJSON["dklen"] = scryptDKLen
+       scryptParamsJSON["salt"] = hex.EncodeToString(salt)
+
+       cipherParamsJSON := cipherparamsJSON{
+               IV: hex.EncodeToString(iv),
+       }
+       cryptoStruct := cryptoJSON{
+               Cipher:       "aes-128-ctr",
+               CipherText:   hex.EncodeToString(cipherText),
+               CipherParams: cipherParamsJSON,
+               KDF:          "scrypt",
+               KDFParams:    scryptParamsJSON,
+               MAC:          hex.EncodeToString(mac),
+       }
+       encryptedKeyJSON := encryptedKeyJSON{
+               key.Address.Str(),
+               cryptoStruct,
+               key.Id.String(),
+               key.KeyType,
+               version,
+               key.Alias,
+       }
+       return json.Marshal(encryptedKeyJSON)
+}
+
+// DecryptKey decrypts a key from a json blob, returning the private key itself.
+func DecryptKey(keyjson []byte, auth string) (*XKey, error) {
+       // Parse the json into a simple map to fetch the key version
+       m := make(map[string]interface{})
+       if err := json.Unmarshal(keyjson, &m); err != nil {
+               return nil, err
+       }
+       // Depending on the version try to parse one way or another
+       var (
+               keyBytes, keyId []byte
+               err             error
+       )
+       k := new(encryptedKeyJSON)
+       if err := json.Unmarshal(keyjson, k); err != nil {
+               return nil, err
+       }
+       //fmt.Printf("-----%v-------", k)
+       //fmt.Printf("\n---decrypt %v\n", k)
+
+       keyBytes, keyId, err = decryptKey(k, auth)
+       // Handle any decryption errors and return the key
+       if err != nil {
+               return nil, err
+       }
+       var  xprv chainkd.XPrv
+       copy(xprv[:], keyBytes[:])
+       xpub := xprv.XPub()
+
+       //key := crypto.ToECDSA(keyBytes)
+       return &XKey{
+               Id:         uuid.UUID(keyId),
+               Address:    crypto.PubkeyToAddress(xpub[:]),
+               XPrv:           xprv,
+               XPub:           xpub,
+               KeyType:        k.Type,
+               Alias:          k.Alias,
+       }, nil
+}
+
+func decryptKey(keyProtected *encryptedKeyJSON, auth string) (keyBytes []byte, keyId []byte, err error) {
+       if keyProtected.Version != version {
+               return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
+       }
+
+       if keyProtected.Type != keytype {
+               return nil, nil, fmt.Errorf("Key type not supported: %v", keyProtected.Type)
+       }
+
+       if keyProtected.Crypto.Cipher != "aes-128-ctr" {
+               return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher)
+       }
+
+       keyId = uuid.Parse(keyProtected.Id)
+       mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
+       if err != nil {
+               return nil, nil, err
+       }
+
+       iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
+       if err != nil {
+               return nil, nil, err
+       }
+
+       cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
+       if err != nil {
+               return nil, nil, err
+       }
+
+       derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
+       if err != nil {
+               return nil, nil, err
+       }
+
+       calculatedMAC := crypto.Sha256(derivedKey[16:32], cipherText)
+
+       if !bytes.Equal(calculatedMAC, mac) {
+               return nil, nil, ErrDecrypt
+       }
+
+       plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
+       if err != nil {
+               return nil, nil, err
+       }
+       return plainText, keyId, err
+}
+
+
+func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) {
+       authArray := []byte(auth)
+       salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
+       if err != nil {
+               return nil, err
+       }
+       dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
+
+       if cryptoJSON.KDF == "scrypt" {
+               n := ensureInt(cryptoJSON.KDFParams["n"])
+               r := ensureInt(cryptoJSON.KDFParams["r"])
+               p := ensureInt(cryptoJSON.KDFParams["p"])
+               return scrypt.Key(authArray, salt, n, r, p, dkLen)
+
+       } else if cryptoJSON.KDF == "pbkdf2" {
+               c := ensureInt(cryptoJSON.KDFParams["c"])
+               prf := cryptoJSON.KDFParams["prf"].(string)
+               if prf != "hmac-sha256" {
+                       return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
+               }
+               key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
+               return key, nil
+       }
+
+       return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
+}
+
+// TODO: can we do without this when unmarshalling dynamic JSON?
+// why do integers in KDF params end up as float64 and not int after
+// unmarshal?
+func ensureInt(x interface{}) int {
+       res, ok := x.(int)
+       if !ok {
+               res = int(x.(float64))
+       }
+       return res
+}
+
+func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
+       // AES-128 is selected due to size of encryptKey.
+       aesBlock, err := aes.NewCipher(key)
+       if err != nil {
+               return nil, err
+       }
+       stream := cipher.NewCTR(aesBlock, iv)
+       outText := make([]byte, len(inText))
+       stream.XORKeyStream(outText, inText)
+       return outText, err
+}
+
+func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
+       aesBlock, err := aes.NewCipher(key)
+       if err != nil {
+               return nil, err
+       }
+       decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
+       paddedPlaintext := make([]byte, len(cipherText))
+       decrypter.CryptBlocks(paddedPlaintext, cipherText)
+       plaintext := pkcs7Unpad(paddedPlaintext)
+       if plaintext == nil {
+               return nil, ErrDecrypt
+       }
+       return plaintext, err
+}
+
+// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
+func pkcs7Unpad(in []byte) []byte {
+       if len(in) == 0 {
+               return nil
+       }
+
+       padding := in[len(in)-1]
+       if int(padding) > len(in) || padding > aes.BlockSize {
+               return nil
+       } else if padding == 0 {
+               return nil
+       }
+
+       for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
+               if in[i] != padding {
+                       return nil
+               }
+       }
+       return in[:len(in)-int(padding)]
+}
\ No newline at end of file
diff --git a/blockchain/pseudohsm/keystore_passphrase_test.go b/blockchain/pseudohsm/keystore_passphrase_test.go
new file mode 100644 (file)
index 0000000..5449dfd
--- /dev/null
@@ -0,0 +1,90 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-etherem library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package pseudohsm
+
+import (
+       "io/ioutil"
+       "testing"
+
+       "bytom/common"
+       "bytom/crypto"
+       "bytom/crypto/ed25519/chainkd"
+
+       "github.com/pborman/uuid"
+
+)
+
+const (
+       veryLightScryptN = 2
+       veryLightScryptP = 1
+)
+
+
+// Tests that a json key file can be decrypted and encrypted in multiple rounds.
+func TestKeyEncryptDecrypt(t *testing.T) {
+       keyjson, err := ioutil.ReadFile("testdata/bytom-very-light-scrypt.json")
+       if err != nil {
+               t.Fatal(err)
+       }
+       password := "bytomtest"
+       address := common.StringToAddress("bm1pcwfm9xnkrf62pg405tcgjzzk7ur670jqhtm3cq")
+       
+       // Do a few rounds of decryption and encryption
+       for i := 0; i < 3; i++ {
+               // Try a bad password first
+               
+               if _, err := DecryptKey(keyjson, password+"bad"); err == nil {
+                       t.Errorf("test %d: json key decrypted with bad password", i)
+               }
+       
+               // Decrypt with the correct password
+               key, err := DecryptKey(keyjson, password)
+               if err != nil {
+                       t.Errorf("test %d: json key failed to decrypt: %v", i, err)
+               }
+               if key.Address != address {
+                       t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address)
+               }
+               
+               // Recrypt with a new password and start over
+               //password += "new data appended"
+               if _, err = EncryptKey(key, password, veryLightScryptN, veryLightScryptP); err != nil {
+                       t.Errorf("test %d: failed to recrypt key %v", i, err)
+               }
+       }
+}
+
+
+func TestGenerateFile(t *testing.T) {
+       xprv, xpub, err := chainkd.NewXKeys(nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       id := uuid.NewRandom()
+       key := &XKey{
+               Id: id, 
+               KeyType: "bytom_kd", 
+               Address: crypto.PubkeyToAddress(xpub[:]),
+               XPub: xpub, 
+               XPrv: xprv,
+       }
+       t.Log(key)
+       password := "bytomtest"
+       xkey, err := EncryptKey(key, password, veryLightScryptN, veryLightScryptP)
+       writeKeyFile(keyFileName(key.Address), xkey)
+       //writeKeyFile("zzz", xkey)
+}
diff --git a/blockchain/pseudohsm/pseudohsm.go b/blockchain/pseudohsm/pseudohsm.go
new file mode 100644 (file)
index 0000000..e95b7d3
--- /dev/null
@@ -0,0 +1,196 @@
+// Package pseudohsm provides a pseudo HSM for development environments.
+package pseudohsm
+
+import (
+       //"context"
+       _"fmt"
+       "strconv"
+       "path/filepath"
+       "sync"
+       "os"
+
+       "bytom/crypto/ed25519/chainkd"
+       "bytom/common"
+       "bytom/errors"
+       "bytom/crypto"
+       //"bytom/protocol/bc/legacy"
+
+       "bytom/blockchain/config"
+       "github.com/pborman/uuid"
+)
+
+// listKeyMaxAliases limits the alias filter to a sane maximum size.
+const listKeyMaxAliases = 200
+
+var (
+       ErrInvalidAfter         = errors.New("invalid after")
+       ErrNoKey                = errors.New("key not found")
+       ErrInvalidKeySize       = errors.New("key invalid size")
+       ErrTooManyAliasesToList = errors.New("requested aliases exceeds limit")
+       ErrAmbiguousAddr                = errors.New("multiple keys match address")
+       ErrDecrypt                              = errors.New("could not decrypt key with given passphrase")
+       ErrInvalidKeyType               = errors.New("key type stored invalid")
+)
+
+type HSM struct {
+       cacheMu  sync.Mutex
+       keyStore keyStore
+       cache    *addrCache
+       kdCache  map[chainkd.XPub]chainkd.XPrv
+}
+
+type XPub struct {
+       Alias    string            `json:"alias"`
+       Address common.Address `json:"address"`
+       XPub  chainkd.XPub         `json:"xpub"`
+       File    string             `json:"file"`
+}
+
+func New(conf *config.Config) *HSM {
+       keydir, _ := filepath.Abs(conf.KeyPath)
+       return &HSM{
+               keyStore:   &keyStorePassphrase{keydir, LightScryptN, LightScryptP},
+               cache:          newAddrCache(keydir),
+               kdCache:        make(map[chainkd.XPub]chainkd.XPrv),
+       }
+}
+
+// XCreate produces a new random xprv and stores it in the db.
+func (h *HSM) XCreate(auth string, alias string) (*XPub, error) {
+       xpub, _, err := h.createChainKDKey(auth, alias, false)
+       if err != nil {
+               return nil, err
+       }
+       h.cache.add(*xpub)
+       return xpub, err
+}
+
+func (h *HSM) createChainKDKey(auth string, alias string, get bool) (*XPub, bool, error) {
+       xprv, xpub, err := chainkd.NewXKeys(nil)
+       if err != nil {
+               return nil, false, err
+       }
+       id := uuid.NewRandom()
+       key := &XKey{
+               Id: id, 
+               KeyType: "bytom_kd", 
+               Address: crypto.PubkeyToAddress(xpub[:]),
+               XPub: xpub, 
+               XPrv: xprv,
+               Alias: alias,
+       }
+       file := h.keyStore.JoinPath(keyFileName(key.Address))
+       if err := h.keyStore.StoreKey(file, key, auth); err != nil {
+               return nil, false, errors.Wrap(err, "storing keys")
+       }
+       return &XPub{XPub: xpub, Address: key.Address, Alias: alias, File: file}, true, nil
+}
+
+
+// ListKeys returns a list of all xpubs from the store
+func (h *HSM) ListKeys(after int , limit int) ([]XPub, string, error) {
+
+       xpubs := h.cache.keys()
+       start, end := 0, len(xpubs)
+       if len(xpubs) > after {
+               start = after
+       } else {
+               return nil, "", errors.WithDetailf(ErrInvalidAfter, "value: %v", after)
+       }
+       if len(xpubs) > after+limit {
+               end = after+limit
+       }
+       return xpubs[start:end], strconv.FormatInt(int64(start), 10), nil
+}
+
+// XSign looks up the xprv given the xpub, optionally derives a new
+// xprv with the given path (but does not store the new xprv), and
+// signs the given msg.
+func (h *HSM) XSign(xpub chainkd.XPub, path [][]byte, msg []byte, auth string) ([]byte, error) {
+       xprv, err := h.loadChainKDKey(xpub, auth)
+       if err != nil {
+               return nil, err
+       }
+       if len(path) > 0 {
+               xprv = xprv.Derive(path)
+       }
+       return xprv.Sign(msg), nil
+}
+
+func (h *HSM) loadChainKDKey(xpub chainkd.XPub, auth string) (xprv chainkd.XPrv, err error) {
+       h.cacheMu.Lock()
+       defer h.cacheMu.Unlock()
+
+       if xprv, ok := h.kdCache[xpub]; ok {
+               return xprv, nil
+       }
+
+       xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
+       if err != nil {
+               return xprv, ErrNoKey
+       }
+       h.kdCache[xpb.XPub] = xkey.XPrv
+       return xkey.XPrv, nil
+}
+
+
+// XDelete deletes the key matched by xpub if the passphrase is correct.
+// If a contains no filename, the address must match a unique key.
+func (h *HSM) XDelete(xpub chainkd.XPub, auth string) error {
+       // Decrypting the key isn't really necessary, but we do
+       // it anyway to check the password and zero out the key
+       // immediately afterwards.
+       
+       xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
+       if xkey != nil {
+               zeroKey(xkey)
+       }
+       if err != nil {
+               return err
+       }
+
+       // The order is crucial here. The key is dropped from the
+       // cache after the file is gone so that a reload happening in
+       // between won't insert it into the cache again.
+       err = os.Remove(xpb.File)
+       if err == nil {
+               h.cache.delete(xpb)
+       }
+       h.cacheMu.Lock()
+       delete(h.kdCache, xpub)
+       h.cacheMu.Unlock()
+       return err
+}
+
+func (h *HSM) loadDecryptedKey(xpub chainkd.XPub, auth string) (XPub, *XKey, error) {
+       h.cache.maybeReload()
+       h.cache.mu.Lock()
+       xpb, err := h.cache.find(XPub{XPub: xpub, Address: crypto.PubkeyToAddress(xpub[:])})
+
+       h.cache.mu.Unlock()
+       if err != nil {
+               return xpb, nil, err
+       }
+       xkey, err := h.keyStore.GetKey(xpb.Address, xpb.File, auth)
+
+       return xpb, xkey, err
+}
+
+// Update alias  of an existing xpub
+func (h *HSM) UpdateAlias(xpub chainkd.XPub, auth, newAlias string) error {
+       xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
+       if err != nil {
+               return err
+       }
+       xkey.Alias = newAlias
+       return h.keyStore.StoreKey(xpb.File, xkey, auth)
+}
+
+// Update changes the passphrase of an existing xpub
+func (h *HSM) ResetPassword(xpub chainkd.XPub, auth, newAuth string) error {
+       xpb, xkey, err := h.loadDecryptedKey(xpub, auth)
+       if err != nil {
+               return err
+       }
+       return h.keyStore.StoreKey(xpb.File, xkey, newAuth)
+}
\ No newline at end of file
diff --git a/blockchain/pseudohsm/pseudohsm_test.go b/blockchain/pseudohsm/pseudohsm_test.go
new file mode 100644 (file)
index 0000000..111193f
--- /dev/null
@@ -0,0 +1,113 @@
+package pseudohsm
+
+import (
+       _"context"
+       "testing"
+       _"github.com/davecgh/go-spew/spew"
+
+       "bytom/blockchain/config"
+       "bytom/errors"
+       //"bytom/protocol/bc/legacy"
+)
+
+const dirPath = "testdata/pseudo"
+
+
+func TestPseudoHSMChainKDKeys(t *testing.T) {
+       hsm := New(&config.Config{KeyPath: dirPath})
+       xpub, err := hsm.XCreate("password", "")
+       if err != nil {
+               t.Fatal(err)
+       }
+       xpub2, err := hsm.XCreate("nopassword", "bytom")
+       if err != nil {
+               t.Fatal(err)
+       }
+       msg := []byte("In the face of ignorance and resistance I wrote financial systems into existence")
+       sig, err := hsm.XSign(xpub.XPub, nil, msg, "password")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !xpub.XPub.Verify(msg, sig) {
+               t.Error("expected verify to succeed")
+       }
+       if xpub2.XPub.Verify(msg, sig) {
+               t.Error("expected verify with wrong pubkey to fail")
+       }
+       path := [][]byte{{3, 2, 6, 3, 8, 2, 7}}
+       sig, err = hsm.XSign(xpub2.XPub, path, msg, "nopassword")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if xpub2.XPub.Verify(msg, sig) {
+               t.Error("expected verify with underived pubkey of sig from derived privkey to fail")
+       }
+       if !xpub2.XPub.Derive(path).Verify(msg, sig) {
+               t.Error("expected verify with derived pubkey of sig from derived privkey to succeed")
+       }
+       xpubs, _, err := hsm.ListKeys(0, 100)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(xpubs) != 2 {
+               t.Error("expected 2 entries in the db")
+       }
+       err = hsm.UpdateAlias(xpub.XPub, "password", "updatealias")
+       if err != nil {
+               t.Fatal(err)
+       }
+       err = hsm.ResetPassword(xpub2.XPub, "nopassword", "1password")
+       if err != nil {
+               t.Fatal(err)
+       }
+       
+       err = hsm.XDelete(xpub.XPub, "password")
+       if err != nil {
+               t.Fatal(err)
+       }
+       err = hsm.XDelete(xpub2.XPub, "1password")
+       if err != nil {
+               t.Fatal(err)
+       }
+       
+}
+
+func TestKeyWithEmptyAlias(t *testing.T) {
+       hsm := New(&config.Config{KeyPath: dirPath})
+       for i := 0; i < 2; i++ {
+               xpub, err := hsm.XCreate("xx", "")
+               if errors.Root(err) != nil {
+                       t.Fatal(err)
+               }
+               err = hsm.XDelete(xpub.XPub, "xx")
+               if err != nil {
+                       t.Fatal(err)
+               }
+       }
+}
+
+
+func BenchmarkSign(b *testing.B) {
+       b.StopTimer()
+       auth := "nowpasswd"
+
+       hsm := New(&config.Config{KeyPath: dirPath})
+       xpub, err := hsm.XCreate(auth, "")
+       if err != nil {
+               b.Fatal(err)
+       }
+
+       msg := []byte("In the face of ignorance and resistance I wrote financial systems into existence")
+
+       b.StartTimer()
+       for i := 0; i < b.N; i++ {
+               _, err := hsm.XSign(xpub.XPub, nil, msg, auth)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+       err = hsm.XDelete(xpub.XPub, auth)
+       if err != nil {
+               b.Fatal(err)
+       }
+}
diff --git a/blockchain/pseudohsm/testdata/bytom-very-light-scrypt.json b/blockchain/pseudohsm/testdata/bytom-very-light-scrypt.json
new file mode 100644 (file)
index 0000000..c5b7287
--- /dev/null
@@ -0,0 +1 @@
+{"address":"bm1pcwfm9xnkrf62pg405tcgjzzk7ur670jqhtm3cq","crypto":{"cipher":"aes-128-ctr","ciphertext":"816eaeb03071deaec9e7a8825ce8e4a4c55d6eaf734ee0779fc6430613c7a2a439f5cc7528688b8ac5e4f46aa99d27f380a8b5b87ba77223209f69a08d83bd42","cipherparams":{"iv":"c8c1d9c061fd778b6585097ebf374b10"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":2,"p":1,"r":8,"salt":"dcd958b6d1573aa3d2111f32af2ada8add253ac78d3b9247eb7cf9af7b5643a4"},"mac":"c0116604d053ed3470c2ca3d69f334bea01d9cd62710ec94c5875696faf00349"},"id":"a3aa6cf7-f308-4e02-8be7-1b3e64f0a1bc","type":"bytom_kd","version":1,"alias":""}
\ No newline at end of file
diff --git a/blockchain/pseudohsm/testdata/keystore/.hidden b/blockchain/pseudohsm/testdata/keystore/.hidden
new file mode 100644 (file)
index 0000000..ce83667
--- /dev/null
@@ -0,0 +1 @@
+{"address":"bm1pcd9yf8qq5sjqecz8au0yjltcx0mpjttfdte24w","crypto":{"cipher":"aes-128-ctr","ciphertext":"49bdd3ab772cd9372eca868a4dcb87636b3129ba71c788da91b5ee6901694a47328eed08aeb4cdaf027842f3103dcfd4ad62be91351f056913fe813ef47407de","cipherparams":{"iv":"b38db561f1787642e9184ef05dba7761"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":2,"p":1,"r":8,"salt":"a818d9fbfa603b1bc00adb405d95d34640197d8f2f803d8dada687af3bbfd3b6"},"mac":"ad212ee042d843d2c3bdbc873d7e45845aac958ca539bd1245838407a7b998ad"},"id":"a9696f79-b610-4c52-b3b5-5bd847172b41","type":"bytom_kd","version":1,"alias":".hidden"}
\ No newline at end of file
diff --git a/blockchain/pseudohsm/testdata/keystore/README b/blockchain/pseudohsm/testdata/keystore/README
new file mode 100644 (file)
index 0000000..a5a86f9
--- /dev/null
@@ -0,0 +1,21 @@
+This directory contains accounts for testing.
+The passphrase that unlocks them is "foobar".
+
+The "good" key files which are supposed to be loadable are:
+
+- File: UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
+  Address: 0x7ef5a6135f1fd6a02593eedc869c6d41d934aef8
+- File: aaa
+  Address: 0xf466859ead1932d743d622cb74fc058882e8648a
+- File: zzz
+  Address: 0x289d485d9771714cce91d3393d764e1311907acc
+
+The other files (including this README) are broken in various ways
+and should not be picked up by package accounts:
+
+- File: no-address (missing address field, otherwise same as "aaa")
+- File: garbage (file with random data)
+- File: empty (file with no content)
+- File: swapfile~ (should be skipped)
+- File: .hiddenfile (should be skipped)
+- File: foo/... (should be skipped because it is a directory)
diff --git a/blockchain/pseudohsm/testdata/keystore/UTC--2017-09-13T07-11-07.863320100Z--bm1pktmny6q69dlqulja2p2ja28k2vd6wvqpk5r76a b/blockchain/pseudohsm/testdata/keystore/UTC--2017-09-13T07-11-07.863320100Z--bm1pktmny6q69dlqulja2p2ja28k2vd6wvqpk5r76a
new file mode 100644 (file)
index 0000000..f50a329
--- /dev/null
@@ -0,0 +1 @@
+{"address":"bm1pktmny6q69dlqulja2p2ja28k2vd6wvqpk5r76a","crypto":{"cipher":"aes-128-ctr","ciphertext":"b6a09cc0de6ad73f0548aed0417fa8a0e7945dc41365de7d577791d407895da4c5ad7d43ac5e351d5de908b67ebd72287606b385bae87736edc060131b9da09a","cipherparams":{"iv":"44d71ed6bdd2b9925994e67c7730e675"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":2,"p":1,"r":8,"salt":"f536b08ff6d46d628f308bdfcd4264dac8182562ba240748dd2b7617a8cb07ac"},"mac":"23dfac9b066dbf1040b7ec512aa73fb1580467e1c3a882111d2e87e42d23ed1d"},"id":"2327b275-826a-4d9e-8871-02eb95b87648","type":"bytom_kd","version":1,"alias":"langyu"}
\ No newline at end of file
diff --git a/blockchain/pseudohsm/testdata/keystore/aaa b/blockchain/pseudohsm/testdata/keystore/aaa
new file mode 100644 (file)
index 0000000..8bb8e34
--- /dev/null
@@ -0,0 +1 @@
+{"address":"bm1p8wuwq0jn706ntcv95pftv8evj48m2x5nmmpdlj","crypto":{"cipher":"aes-128-ctr","ciphertext":"79005ccdd35cd178912a2660d59d02cf1b311a1d3e9694a760aeceeb26c95583aadc187fce37689bfeb2f81bf9721bb22c609541edc4e7233173e40714a71ea3","cipherparams":{"iv":"876e6a0b10843bc50019ece4740a637a"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":2,"p":1,"r":8,"salt":"2954a36edb945850949703250bbb0282741a7d6de008831e868fe951ae276c25"},"mac":"8513d5e81b25e8f7422d3312698ebe744b427f5baf54c51cc4afdd925466a2dd"},"id":"aad72596-c7bf-457e-88f6-39c617282571","type":"bytom_kd","version":1,"alias":"aaatest"}
\ No newline at end of file
diff --git a/blockchain/pseudohsm/testdata/keystore/empty b/blockchain/pseudohsm/testdata/keystore/empty
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/blockchain/pseudohsm/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e b/blockchain/pseudohsm/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e
new file mode 100644 (file)
index 0000000..309841e
--- /dev/null
@@ -0,0 +1 @@
+{"address":"fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e","crypto":{"cipher":"aes-128-ctr","ciphertext":"8124d5134aa4a927c79fd852989e4b5419397566f04b0936a1eb1d168c7c68a5","cipherparams":{"iv":"e2febe17176414dd2cda28287947eb2f"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":4096,"p":6,"r":8,"salt":"44b415ede89f3bdd6830390a21b78965f571b347a589d1d943029f016c5e8bd5"},"mac":"5e149ff25bfd9dd45746a84bb2bcd2f015f2cbca2b6d25c5de8c29617f71fe5b"},"id":"d6ac5452-2b2c-4d3c-ad80-4bf0327d971c","version":3}
\ No newline at end of file
diff --git a/blockchain/pseudohsm/testdata/keystore/garbage b/blockchain/pseudohsm/testdata/keystore/garbage
new file mode 100644 (file)
index 0000000..ff45091
Binary files /dev/null and b/blockchain/pseudohsm/testdata/keystore/garbage differ
diff --git a/blockchain/pseudohsm/testdata/keystore/no-address b/blockchain/pseudohsm/testdata/keystore/no-address
new file mode 100644 (file)
index 0000000..050b1c1
--- /dev/null
@@ -0,0 +1 @@
+{"crypto":{"cipher":"aes-128-ctr","ciphertext":"645577b1466edd05dd582709c041634a0081322968b5890f005795f72f667219bde5255aaf4c50b241bc7733cd705e777a8619ff8bb981a5303079abc26adb1c","cipherparams":{"iv":"5dc8a2c137c81fec72d4ba472b7bc4a7"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":2,"p":1,"r":8,"salt":"2ecd7274a44b29919d3fefa6f7c6a492ff05d705af5e79965c0bda62183b22d2"},"mac":"7e78beb34a351d759cb95b985659ac72e5daea32a2c21c05e2646f2ac62dff7c"},"id":"a5474c6e-b8b2-401c-bc00-339dd5c3c28b","type":"bytom_kd","version":1}
\ No newline at end of file
diff --git a/blockchain/pseudohsm/testdata/keystore/zero b/blockchain/pseudohsm/testdata/keystore/zero
new file mode 100644 (file)
index 0000000..b52617f
--- /dev/null
@@ -0,0 +1 @@
+{"address":"0000000000000000000000000000000000000000","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3}
\ No newline at end of file
diff --git a/blockchain/pseudohsm/testdata/keystore/zzz b/blockchain/pseudohsm/testdata/keystore/zzz
new file mode 100644 (file)
index 0000000..8d56f2f
--- /dev/null
@@ -0,0 +1 @@
+{"address":"bm1pzye4ep30gghfnxe9c7dcch5m87lfrl3hrvegkc","crypto":{"cipher":"aes-128-ctr","ciphertext":"a83c1ba64ffb5dae1f0bddd232dae2ce3daea8a01bddc669c311be842f5b234ef77822902773143c472dd3ee2cf33a5f48cf69abc307fc73f747141312f45c66","cipherparams":{"iv":"a946410710c0a466ab528706cf11fed6"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":2,"p":1,"r":8,"salt":"63399bf7f46a072ab0520282a0106d7408299f96a4132283855eb764d3b5e660"},"mac":"5f55fcc86d0ee11ced32196bdf8716170ceaaa2d55b5f93fc0137a8d6f0dc7b4"},"id":"e0747776-fe54-4a64-984e-2a74001302b3","type":"bytom_kd","version":1,"alias":"zzztest"}
\ No newline at end of file
diff --git a/blockchain/pseudohsm/testdata/v1_test_vector.json b/blockchain/pseudohsm/testdata/v1_test_vector.json
new file mode 100644 (file)
index 0000000..3d09b55
--- /dev/null
@@ -0,0 +1,28 @@
+{
+    "test1": {
+        "json": {
+            "Crypto": {
+                "cipher": "aes-128-cbc",
+                "cipherparams": {
+                    "iv": "35337770fc2117994ecdcad026bccff4"
+                },
+                "ciphertext": "6143d3192db8b66eabd693d9c4e414dcfaee52abda451af79ccf474dafb35f1bfc7ea013aa9d2ee35969a1a2e8d752d0",
+                "kdf": "scrypt",
+                "kdfparams": {
+                    "dklen": 32,
+                    "n": 262144,
+                    "p": 1,
+                    "r": 8,
+                    "salt": "9afcddebca541253a2f4053391c673ff9fe23097cd8555d149d929e4ccf1257f"
+                },
+                "mac": "3f3d5af884b17a100b0b3232c0636c230a54dc2ac8d986227219b0dd89197644",
+                "version": "1"
+            },
+            "address": "cb61d5a9c4896fb9658090b597ef0e7be6f7b67e",
+            "id": "e25f7c1f-d318-4f29-b62c-687190d4d299",
+            "version": "1"
+        },
+        "password": "g",
+        "priv": "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d"
+    }
+}
diff --git a/blockchain/pseudohsm/testdata/v3_test_vector.json b/blockchain/pseudohsm/testdata/v3_test_vector.json
new file mode 100644 (file)
index 0000000..e9d7b62
--- /dev/null
@@ -0,0 +1,49 @@
+{
+    "wikipage_test_vector_scrypt": {
+        "json": {
+            "crypto" : {
+                "cipher" : "aes-128-ctr",
+                "cipherparams" : {
+                    "iv" : "83dbcc02d8ccb40e466191a123791e0e"
+                },
+                "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c",
+                "kdf" : "scrypt",
+                "kdfparams" : {
+                    "dklen" : 32,
+                    "n" : 262144,
+                    "r" : 1,
+                    "p" : 8,
+                    "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19"
+                },
+                "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097"
+            },
+            "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",
+            "version" : 3
+        },
+        "password": "testpassword",
+        "priv": "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d"
+    },
+    "wikipage_test_vector_pbkdf2": {
+        "json": {
+            "crypto" : {
+                "cipher" : "aes-128-ctr",
+                "cipherparams" : {
+                    "iv" : "6087dab2f9fdbbfaddc31a909735c1e6"
+                },
+                "ciphertext" : "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46",
+                "kdf" : "pbkdf2",
+                "kdfparams" : {
+                    "c" : 262144,
+                    "dklen" : 32,
+                    "prf" : "hmac-sha256",
+                    "salt" : "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd"
+                },
+                "mac" : "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2"
+            },
+            "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",
+            "version" : 3
+        },
+        "password": "testpassword",
+        "priv": "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d"
+    }
+}
diff --git a/blockchain/pseudohsm/watch.go b/blockchain/pseudohsm/watch.go
new file mode 100644 (file)
index 0000000..8dd5f40
--- /dev/null
@@ -0,0 +1,111 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build darwin,!ios freebsd linux,!arm64 netbsd solaris windows
+
+package pseudohsm
+
+import (
+    "time"
+    "fmt"
+    "github.com/rjeczalik/notify"
+)
+
+type watcher struct {
+    ac       *addrCache
+    starting bool
+    running  bool
+    ev       chan notify.EventInfo
+    quit     chan struct{}
+}
+
+func newWatcher(ac *addrCache) *watcher {
+    return &watcher{
+        ac:   ac,
+        ev:   make(chan notify.EventInfo, 10),
+        quit: make(chan struct{}),
+    }
+}
+
+// starts the watcher loop in the background.
+// Start a watcher in the background if that's not already in progress.
+// The caller must hold w.ac.mu.
+func (w *watcher) start() {
+    if w.starting || w.running {
+        return
+    }
+    w.starting = true
+    go w.loop()
+}
+
+func (w *watcher) close() {
+    close(w.quit)
+}
+
+func (w *watcher) loop() {
+    defer func() {
+        w.ac.mu.Lock()
+        w.running = false
+        w.starting = false
+        w.ac.mu.Unlock()
+    }()
+
+    err := notify.Watch(w.ac.keydir, w.ev, notify.All)
+    if err != nil {
+        fmt.Printf("can't watch %s: %v", w.ac.keydir, err)
+        return
+    }
+    defer notify.Stop(w.ev)
+    fmt.Printf("now watching %s", w.ac.keydir)
+    defer fmt.Printf("no longer watching %s", w.ac.keydir)
+
+    w.ac.mu.Lock()
+    w.running = true
+    w.ac.mu.Unlock()
+
+    // Wait for file system events and reload.
+    // When an event occurs, the reload call is delayed a bit so that
+    // multiple events arriving quickly only cause a single reload.
+    var (
+        debounce          = time.NewTimer(0)
+        debounceDuration  = 500 * time.Millisecond
+        inCycle, hadEvent bool
+    )
+    defer debounce.Stop()
+    for {
+        select {
+        case <-w.quit:
+            return
+        case <-w.ev:
+            if !inCycle {
+                debounce.Reset(debounceDuration)
+                inCycle = true
+            } else {
+                hadEvent = true
+            }
+        case <-debounce.C:
+            w.ac.mu.Lock()
+            w.ac.reload()
+            w.ac.mu.Unlock()
+            if hadEvent {
+                debounce.Reset(debounceDuration)
+                inCycle, hadEvent = true, false
+            } else {
+                inCycle, hadEvent = false, false
+            }
+        }
+    }
+}
diff --git a/blockchain/pseudohsm/watch_fallback.go b/blockchain/pseudohsm/watch_fallback.go
new file mode 100644 (file)
index 0000000..6faa069
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build ios linux,arm64 !darwin,!freebsd,!linux,!netbsd,!solaris,!windows
+
+// This is the fallback implementation of directory watching.
+// It is used on unsupported platforms.
+
+package pseudohsm
+
+type watcher struct{ running bool }
+
+func newWatcher(*addrCache) *watcher { return new(watcher) }
+func (*watcher) start()              {}
+func (*watcher) close()              {}
diff --git a/cmd/bytom/pseudohsm.go b/cmd/bytom/pseudohsm.go
new file mode 100644 (file)
index 0000000..c349cee
--- /dev/null
@@ -0,0 +1,17 @@
+//+build pseudohsm
+
+package main
+
+import (
+       "bytom/core"
+       "bytom/core/config"
+       "bytom/core/pseduokhsm"
+)
+
+func init() {
+       config.BuildConfig.PseudoHSM = true
+}
+
+func enablePseudoHSM(config *config.Config) []core.RunOption {
+       return []core.RunOption{core.PseudoHSM(Pseudohsm.New(config))}
+}
diff --git a/cmd/bytom/remotehsm.go b/cmd/bytom/remotehsm.go
new file mode 100644 (file)
index 0000000..21e3947
--- /dev/null
@@ -0,0 +1,16 @@
+//+build remotehsm
+
+package main
+
+import (
+       "bytom/core"
+       "bytom/core/config"
+)
+
+func init() {
+       config.BuildConfig.PseudoHSM = false
+}
+
+func enableHSM(config *config.Config) []core.RunOption {
+       return []core.RunOption{core.RemoteHSM(Remotehsm.New(config))}
+}
diff --git a/common/README.md b/common/README.md
new file mode 100644 (file)
index 0000000..adea022
--- /dev/null
@@ -0,0 +1,140 @@
+# common
+
+[![Build
+Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
+
+The common package contains the ethereum utility library.
+
+# Installation
+
+As a subdirectory the main go-ethereum repository, you get it with
+`go get github.com/ethereum/go-ethereum`.
+
+# Usage
+
+## RLP (Recursive Linear Prefix) Encoding
+
+RLP Encoding is an encoding scheme used by the Ethereum project. It
+encodes any native value or list to a string.
+
+More in depth information about the encoding scheme see the
+[Wiki](http://wiki.ethereum.org/index.php/RLP) article.
+
+```go
+rlp := common.Encode("doge")
+fmt.Printf("%q\n", rlp) // => "\0x83dog"
+
+rlp = common.Encode([]interface{}{"dog", "cat"})
+fmt.Printf("%q\n", rlp) // => "\0xc8\0x83dog\0x83cat"
+decoded := common.Decode(rlp)
+fmt.Println(decoded) // => ["dog" "cat"]
+```
+
+## Patricia Trie
+
+Patricie Tree is a merkle trie used by the Ethereum project.
+
+More in depth information about the (modified) Patricia Trie can be
+found on the [Wiki](http://wiki.ethereum.org/index.php/Patricia_Tree).
+
+The patricia trie uses a db as backend and could be anything as long as
+it satisfies the Database interface found in `common/db.go`.
+
+```go
+db := NewDatabase()
+
+// db, root
+trie := common.NewTrie(db, "")
+
+trie.Put("puppy", "dog")
+trie.Put("horse", "stallion")
+trie.Put("do", "verb")
+trie.Put("doge", "coin")
+
+// Look up the key "do" in the trie
+out := trie.Get("do")
+fmt.Println(out) // => verb
+
+trie.Delete("puppy")
+```
+
+The patricia trie, in combination with RLP, provides a robust,
+cryptographically authenticated data structure that can be used to store
+all (key, value) bindings.
+
+```go
+// ... Create db/trie
+
+// Note that RLP uses interface slices as list
+value := common.Encode([]interface{}{"one", 2, "three", []interface{}{42}})
+// Store the RLP encoded value of the list
+trie.Put("mykey", value)
+```
+
+## Value
+
+Value is a Generic Value which is used in combination with RLP data or
+`([])interface{}` structures. It may serve as a bridge between RLP data
+and actual real values and takes care of all the type checking and
+casting. Unlike Go's `reflect.Value` it does not panic if it's unable to
+cast to the requested value. It simple returns the base value of that
+type (e.g. `Slice()` returns []interface{}, `Uint()` return 0, etc).
+
+### Creating a new Value
+
+`NewEmptyValue()` returns a new \*Value with it's initial value set to a
+`[]interface{}`
+
+`AppendList()` appends a list to the current value.
+
+`Append(v)` appends the value (v) to the current value/list.
+
+```go
+val := common.NewEmptyValue().Append(1).Append("2")
+val.AppendList().Append(3)
+```
+
+### Retrieving values
+
+`Get(i)` returns the `i` item in the list.
+
+`Uint()` returns the value as an unsigned int64.
+
+`Slice()` returns the value as a interface slice.
+
+`Str()` returns the value as a string.
+
+`Bytes()` returns the value as a byte slice.
+
+`Len()` assumes current to be a slice and returns its length.
+
+`Byte()` returns the value as a single byte.
+
+```go
+val := common.NewValue([]interface{}{1,"2",[]interface{}{3}})
+val.Get(0).Uint() // => 1
+val.Get(1).Str()  // => "2"
+s := val.Get(2)   // => Value([]interface{}{3})
+s.Get(0).Uint()   // => 3
+```
+
+## Decoding
+
+Decoding streams of RLP data is simplified
+
+```go
+val := common.NewValueFromBytes(rlpData)
+val.Get(0).Uint()
+```
+
+## Encoding
+
+Encoding from Value to RLP is done with the `Encode` method. The
+underlying value can be anything RLP can encode (int, str, lists, bytes)
+
+```go
+val := common.NewValue([]interface{}{1,"2",[]interface{}{3}})
+rlp := val.Encode()
+// Store the rlp data
+Store(rlp)
+```
diff --git a/common/bech32.go b/common/bech32.go
new file mode 100644 (file)
index 0000000..2e18204
--- /dev/null
@@ -0,0 +1,217 @@
+// Package bech32 reference implementation for Bech32 and segwit addresses.
+// Copyright (c) 2017 Takatoshi Nakagawa
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+package common
+
+import (
+    "bytes"
+    "fmt"
+    "strings"
+)
+
+var charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
+
+var generator = []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3}
+
+func polymod(values []int) int {
+    chk := 1
+    for _, v := range values {
+        top := chk >> 25
+        chk = (chk&0x1ffffff)<<5 ^ v
+        for i := 0; i < 5; i++ {
+            if (top>>uint(i))&1 == 1 {
+                chk ^= generator[i]
+            }
+        }
+    }
+    return chk
+}
+
+func hrpExpand(hrp string) []int {
+    ret := []int{}
+    for _, c := range hrp {
+        ret = append(ret, int(c>>5))
+    }
+    ret = append(ret, 0)
+    for _, c := range hrp {
+        ret = append(ret, int(c&31))
+    }
+    return ret
+}
+
+func verifyChecksum(hrp string, data []int) bool {
+    return polymod(append(hrpExpand(hrp), data...)) == 1
+}
+
+func createChecksum(hrp string, data []int) []int {
+    values := append(append(hrpExpand(hrp), data...), []int{0, 0, 0, 0, 0, 0}...)
+    mod := polymod(values) ^ 1
+    ret := make([]int, 6)
+    for p := 0; p < len(ret); p++ {
+        ret[p] = (mod >> uint(5*(5-p))) & 31
+    }
+    return ret
+}
+
+// Encode encodes hrp(human-readable part) and data(32bit data array), returns Bech32 / or error
+// if hrp is uppercase, return uppercase Bech32
+func Encode(hrp string, data []int) (string, error) {
+    if (len(hrp) + len(data) + 7) > 90 {
+        return "", fmt.Errorf("too long : hrp length=%d, data length=%d", len(hrp), len(data))
+    }
+    if len(hrp) < 1 {
+        return "", fmt.Errorf("invalid hrp : hrp=%v", hrp)
+    }
+    for p, c := range hrp {
+        if c < 33 || c > 126 {
+            return "", fmt.Errorf("invalid character human-readable part : hrp[%d]=%d", p, c)
+        }
+    }
+    if strings.ToUpper(hrp) != hrp && strings.ToLower(hrp) != hrp {
+        return "", fmt.Errorf("mix case : hrp=%v", hrp)
+    }
+    lower := strings.ToLower(hrp) == hrp
+    hrp = strings.ToLower(hrp)
+    combined := append(data, createChecksum(hrp, data)...)
+    var ret bytes.Buffer
+    ret.WriteString(hrp)
+    ret.WriteString("1")
+    for idx, p := range combined {
+        if p < 0 || p >= len(charset) {
+            return "", fmt.Errorf("invalid data : data[%d]=%d", idx, p)
+        }
+        ret.WriteByte(charset[p])
+    }
+    if lower {
+        return ret.String(), nil
+    }
+    return strings.ToUpper(ret.String()), nil
+}
+
+// Decode decodes bechString(Bech32) returns hrp(human-readable part) and data(32bit data array) / or error
+func Decode(bechString string) (string, []int, error) {
+    if len(bechString) > 90 {
+        return "", nil, fmt.Errorf("too long : len=%d", len(bechString))
+    }
+    if strings.ToLower(bechString) != bechString && strings.ToUpper(bechString) != bechString {
+        return "", nil, fmt.Errorf("mixed case")
+    }
+    bechString = strings.ToLower(bechString)
+    pos := strings.LastIndex(bechString, "1")
+    if pos < 1 || pos+7 > len(bechString) {
+        return "", nil, fmt.Errorf("separator '1' at invalid position : pos=%d , len=%d", pos, len(bechString))
+    }
+    hrp := bechString[0:pos]
+    for p, c := range hrp {
+        if c < 33 || c > 126 {
+            return "", nil, fmt.Errorf("invalid character human-readable part : bechString[%d]=%d", p, c)
+        }
+    }
+    data := []int{}
+    for p := pos + 1; p < len(bechString); p++ {
+        d := strings.Index(charset, fmt.Sprintf("%c", bechString[p]))
+        if d == -1 {
+            return "", nil, fmt.Errorf("invalid character data part : bechString[%d]=%d", p, bechString[p])
+        }
+        data = append(data, d)
+    }
+    if !verifyChecksum(hrp, data) {
+        return "", nil, fmt.Errorf("invalid checksum")
+    }
+    return hrp, data[:len(data)-6], nil
+}
+
+func convertbits(data []int, frombits, tobits uint, pad bool) ([]int, error) {
+    acc := 0
+    bits := uint(0)
+    ret := []int{}
+    maxv := (1 << tobits) - 1
+    for idx, value := range data {
+        if value < 0 || (value>>frombits) != 0 {
+            return nil, fmt.Errorf("invalid data range : data[%d]=%d (frombits=%d)", idx, value, frombits)
+        }
+        acc = (acc << frombits) | value
+        bits += frombits
+        for bits >= tobits {
+            bits -= tobits
+            ret = append(ret, (acc>>bits)&maxv)
+        }
+    }
+    if pad {
+        if bits > 0 {
+            ret = append(ret, (acc<<(tobits-bits))&maxv)
+        }
+    } else if bits >= frombits {
+        return nil, fmt.Errorf("illegal zero padding")
+    } else if ((acc << (tobits - bits)) & maxv) != 0 {
+        return nil, fmt.Errorf("non-zero padding")
+    }
+    return ret, nil
+}
+
+// AddressDecode decodes hrp(human-readable part) Address(string), returns version(int) and data(bytes array) / or error
+func AddressDecode(hrp, addr string) (int, []int, error) {
+    dechrp, data, err := Decode(addr)
+    if err != nil {
+        return -1, nil, err
+    }
+    if dechrp != hrp {
+        return -1, nil, fmt.Errorf("invalid human-readable part : %s != %s", hrp, dechrp)
+    }
+    if len(data) < 1 {
+        return -1, nil, fmt.Errorf("invalid decode data length : %d", len(data))
+    }
+    if data[0] > 16 {
+        return -1, nil, fmt.Errorf("invalid address version : %d", data[0])
+    }
+    res, err := convertbits(data[1:], 5, 8, false)
+    if err != nil {
+        return -1, nil, err
+    }
+    if len(res) < 2 || len(res) > 40 {
+        return -1, nil, fmt.Errorf("invalid convertbits length : %d", len(res))
+    }
+    if data[0] == 0 && len(res) != 20 && len(res) != 32 {
+        return -1, nil, fmt.Errorf("invalid program length for witness version 0 (per BIP141) : %d", len(res))
+    }
+    return data[0], res, nil
+}
+
+// AddressEncode encodes hrp(human-readable part) , version(int) and data(bytes array), returns  Address / or error
+func AddressEncode(hrp string, version int, pubkey []int) (string, error) {
+    if version < 0 || version > 16 {
+        return "", fmt.Errorf("invalid version : %d", version)
+    }
+    if len(pubkey) < 2 || len(pubkey) > 40 {
+        return "", fmt.Errorf("invalid pubkey hash length : %d", len(pubkey))
+    }
+    if version == 0 && len(pubkey) != 20 && len(pubkey) != 32 {
+        return "", fmt.Errorf("invalid program length for witness version 0 (per BIP141) : %d", len(pubkey))
+    }
+    data, err := convertbits(pubkey, 8, 5, true)
+    if err != nil {
+        return "", err
+    }
+    ret, err := Encode(hrp, append([]int{version}, data...))
+    if err != nil {
+        return "", err
+    }
+    return ret, nil
+}
\ No newline at end of file
diff --git a/common/bech32_test.go b/common/bech32_test.go
new file mode 100644 (file)
index 0000000..6ebc1f2
--- /dev/null
@@ -0,0 +1,381 @@
+// Copyright (c) 2017 Takatoshi Nakagawa
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+package common
+
+import (
+    "reflect"
+    "strings"
+    "testing"
+)
+
+func ScriptPubkey(version int, program []int) []int {
+    if version != 0 {
+        version += 0x50
+    }
+    return append(append([]int{version}, len(program)), program...)
+}
+
+var validChecksum = []string{
+    "A12UEL5L",
+    "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs",
+    "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
+    "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j",
+    "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w",
+}
+
+type item struct {
+    address      string
+    scriptpubkey []int
+}
+
+var validAddress = []item{
+    item{"BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4",
+        []int{
+            0x00, 0x14, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54,
+            0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
+        },
+    },
+    item{"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
+        []int{
+            0x00, 0x20, 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04,
+            0xbd, 0x19, 0x20, 0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78,
+            0xcd, 0x4d, 0x27, 0xa1, 0xb8, 0xc6, 0x32, 0x96, 0x04, 0x90, 0x32,
+            0x62,
+        },
+    },
+    item{"bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx",
+        []int{
+            0x51, 0x28, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54,
+            0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
+            0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
+            0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
+        },
+    },
+    item{"BC1SW50QA3JX3S",
+        []int{
+            0x60, 0x02, 0x75, 0x1e,
+        },
+    },
+    item{"bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj",
+        []int{
+            0x52, 0x10, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54,
+            0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23,
+        },
+    },
+    item{"tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",
+        []int{
+            0x00, 0x20, 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21,
+            0xb2, 0xa1, 0x87, 0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5,
+            0xe9, 0x1c, 0x6c, 0xe2, 0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64,
+            0x33,
+        },
+    },
+}
+
+var invalidAddress = []string{
+    "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty",
+    "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5",
+    "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2",
+    "bc1rw5uspcuh",
+    "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90",
+    "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P",
+    "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7",
+    "tb1pw508d6qejxtdg4y5r3zarqfsj6c3",
+    "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv",
+}
+
+func TestValidChecksum(t *testing.T) {
+    for _, test := range validChecksum {
+        hrp, data, err := Decode(test)
+        if err != nil {
+            t.Errorf("Valid checksum for %s : FAIL / error %+v\n", test, err)
+        } else {
+            t.Logf("Valid checksum for %s : ok / hrp : %+v , data : %+v\n", test, hrp, data)
+        }
+    }
+}
+
+func TestValidAddress(t *testing.T) {
+    for _, test := range validAddress {
+        hrp := "bc"
+        version, program, err := AddressDecode(hrp, test.address)
+        if err != nil {
+            hrp = "tb"
+            version, program, err = AddressDecode(hrp, test.address)
+        }
+        ok := err == nil
+        if ok {
+            output := ScriptPubkey(version, program)
+            ok = reflect.DeepEqual(output, test.scriptpubkey)
+        }
+        if ok {
+            recreate, err := AddressEncode(hrp, version, program)
+            if err == nil {
+                ok = recreate == strings.ToLower(test.address)
+            }
+        }
+        if ok {
+            t.Logf("Valid address %v : ok\n", test.address)
+        } else {
+            t.Errorf("Valid address %v : FAIL\n", test.address)
+        }
+    }
+}
+
+
+func TestInvalidAddress(t *testing.T) {
+    for _, test := range invalidAddress {
+        _, _, bcErr := AddressDecode("bc", test)
+        t.Logf("bc error:%v\n", bcErr)
+        _, _, tbErr := AddressDecode("tb", test)
+        t.Logf("tb error:%v\n", tbErr)
+        if bcErr != nil && tbErr != nil {
+            t.Logf("Invalid address %v : ok\n", test)
+        } else {
+            t.Errorf("Invalid address %v : FAIL\n", test)
+        }
+    }
+}
+
+
+// add coverage tests
+
+func TestCoverage(t *testing.T) {
+    var err error
+    var bech32String string
+    var hrp string
+    var data []int
+
+    // AddressEncode
+    bech32String, err = AddressEncode("bc", 1, []int{0, 1})
+    if err != nil {
+        t.Errorf("Coverage AddressEncode normal case : FAIL / error : %+v\n", err)
+    } else {
+        t.Log("Coverage AddressEncode normal case : ok / bech32String :", bech32String)
+    }
+    data = make([]int, 40)
+    bech32String, err = AddressEncode("bc", 16, data)
+    if err != nil {
+        t.Errorf("Coverage AddressEncode normal case : FAIL / error : %+v\n", err)
+    } else {
+        t.Log("Coverage AddressEncode normal case : ok / bech32String :", bech32String)
+    }
+    data = make([]int, 20)
+    bech32String, err = AddressEncode("bc", 0, data)
+    if err != nil {
+        t.Errorf("Coverage AddressEncode normal case : FAIL / error : %+v\n", err)
+    } else {
+        t.Log("Coverage AddressEncode normal case : ok / bech32String :", bech32String)
+    }
+    data = make([]int, 32)
+    bech32String, err = AddressEncode("bc", 0, data)
+    if err != nil {
+        t.Errorf("Coverage AddressEncode normal case : FAIL / error : %+v\n", err)
+    } else {
+        t.Log("Coverage AddressEncode normal case : ok / bech32String :", bech32String)
+    }
+    data = make([]int, 1)
+    _, err = AddressEncode("bc", 1, data)
+    if err == nil {
+        t.Errorf("Coverage AddressEncode invalid program length error case : FAIL")
+    } else {
+        t.Log("Coverage AddressEncode invalid program length error case : ok / error :", err)
+    }
+    data = make([]int, 41)
+    _, err = AddressEncode("bc", 1, data)
+    if err == nil {
+        t.Errorf("Coverage AddressEncode invalid program length error case : FAIL")
+    } else {
+        t.Log("Coverage AddressEncode invalid program length error case : ok / error :", err)
+    }
+    data = make([]int, 26)
+    _, err = AddressEncode("bc", 0, data)
+    if err == nil {
+        t.Errorf("Coverage AddressEncode invalid program length for witness version 0 (per BIP141) error case : FAIL")
+    } else {
+        t.Log("Coverage AddressEncode invalid program length for witness version 0 (per BIP141) error case : ok / error :", err)
+    }
+    data = make([]int, 20)
+    _, err = AddressEncode("Bc", 0, data)
+    if err == nil {
+        t.Errorf("Coverage AddressEncode Encode error case : FAIL")
+    } else {
+        t.Log("Coverage AddressEncode Encode error case : ok / error :", err)
+    }
+    _, err = AddressEncode("bc", 1, []int{-1, 0})
+    if err == nil {
+        t.Errorf("Coverage AddressEncode invalid data range error case : FAIL")
+    } else {
+        t.Log("Coverage AddressEncode invalid data range error case : ok / error :", err)
+    }
+    _, err = AddressEncode("bc", -1, data)
+    if err == nil {
+        t.Errorf("Coverage AddressEncode invalid witness version error case : FAIL")
+    } else {
+        t.Log("Coverage AddressEncode invalid witness version error case : ok / error :", err)
+    }
+    _, err = AddressEncode("bc", 17, data)
+    if err == nil {
+        t.Errorf("Coverage AddressEncode invalid witness version error case : FAIL")
+    } else {
+        t.Log("Coverage AddressEncode invalid witness version error case : ok / error :", err)
+    }
+
+    // SegwitAddrDecode
+    _, _, err = AddressDecode("a", "A12UEL5L")
+    if err == nil {
+        t.Errorf("Coverage SegwitAddrDecode invalid decode data length error case : FAIL")
+    } else {
+        t.Log("Coverage SegwitAddrDecode invalid decode data length error case : ok / error :", err)
+    }
+
+    // Decode
+    _, _, err = Decode("!~1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc356v3")
+    if err != nil {
+        t.Errorf("Coverage Decode normal case : FAIL / error :%v", err)
+    } else {
+        t.Log("Coverage Decode normal case : ok")
+    }
+    _, _, err = Decode("a1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq")
+    if err == nil {
+        t.Errorf("Coverage Decode too long error case : FAIL")
+    } else {
+        t.Log("Coverage Decode too long error case : ok / error :", err)
+    }
+    _, _, err = Decode("1")
+    if err == nil {
+        t.Errorf("Coverage Decode separator '1' at invalid position error case : FAIL")
+    } else {
+        t.Log("Coverage Decode separator '1' at invalid position error case : ok / error :", err)
+    }
+    _, _, err = Decode("a1qqqqq")
+    if err == nil {
+        t.Errorf("Coverage Decode separator '1' at invalid position error case : FAIL")
+    } else {
+        t.Log("Coverage Decode separator '1' at invalid position error case : ok / error :", err)
+    }
+    _, _, err = Decode("a" + string(32) + "1qqqqqq")
+    if err == nil {
+        t.Errorf("Coverage Decode invalid character human-readable part error case : FAIL")
+    } else {
+        t.Log("Coverage Decode invalid character human-readable part error case : ok / error :", err)
+    }
+    _, _, err = Decode("a" + string(127) + "1qqqqqq")
+    if err == nil {
+        t.Errorf("Coverage Decode invalid character human-readable part error case : FAIL")
+    } else {
+        t.Log("Coverage Decode invalid character human-readable part error case : ok / error :", err)
+    }
+    _, _, err = Decode("a1qqqqqb")
+    if err == nil {
+        t.Errorf("Coverage Decode invalid character data part error case : FAIL")
+    } else {
+        t.Log("Coverage Decode invalid character data part erroer case : ok / error :", err)
+    }
+
+    // Encode
+    hrp = "bc"
+    data = []int{}
+    bech32String, err = Encode(hrp, data)
+    if err != nil || bech32String != strings.ToLower(bech32String) {
+        t.Errorf("Coverage Encode lower case : FAIL / bech32String : %v , error : %v", bech32String, err)
+    } else {
+        t.Log("Coverage Encode lower case : ok / bech32String : ", bech32String)
+    }
+    hrp = "BC"
+    bech32String, err = Encode(hrp, data)
+    if err != nil || bech32String != strings.ToUpper(bech32String) {
+        t.Errorf("Coverage Encode upper case : FAIL / bech32String : %v , error : %v", bech32String, err)
+    } else {
+        t.Log("Coverage Encode upper case : ok / bech32String : ", bech32String)
+    }
+    hrp = "bc"
+    data = make([]int, 90-7-len(hrp)+1)
+    bech32String, err = Encode(hrp, data)
+    if err == nil {
+        t.Errorf("Coverage Encode too long error case : FAIL / bech32String : %v", bech32String)
+    } else {
+        t.Log("Coverage Encode too long error case : ok / error : ", err)
+    }
+    hrp = ""
+    data = make([]int, 90-7-len(hrp))
+    bech32String, err = Encode(hrp, data)
+    if err == nil {
+        t.Errorf("Coverage Encode invalid hrp error case : FAIL / bech32String : %v", bech32String)
+    } else {
+        t.Log("Coverage Encode invalid hrp error case : ok / error : ", err)
+    }
+    hrp = "Bc"
+    data = make([]int, 90-7-len(hrp))
+    bech32String, err = Encode(hrp, data)
+    if err == nil {
+        t.Errorf("Coverage Encode mix case error case : FAIL / bech32String : %v", bech32String)
+    } else {
+        t.Log("Coverage Encode mix case error case : ok / error : ", err)
+    }
+    hrp = string(33) + string(126)
+    data = make([]int, 90-7-len(hrp))
+    bech32String, err = Encode(hrp, data)
+    if err != nil {
+        t.Errorf("Coverage Encode normal case : FAIL / error : %v", err)
+    } else {
+        t.Log("Coverage Encode normal case : ok / bech32String : ", bech32String)
+    }
+    hrp = string(32) + "c"
+    data = make([]int, 90-7-len(hrp))
+    bech32String, err = Encode(hrp, data)
+    if err == nil {
+        t.Errorf("Coverage Encode invalid character human-readable part error case : FAIL / bech32String : %v", bech32String)
+    } else {
+        t.Log("Coverage Encode invalid character human-readable part error case : ok / error : ", err)
+    }
+    hrp = "b" + string(127)
+    data = make([]int, 90-7-len(hrp))
+    bech32String, err = Encode(hrp, data)
+    if err == nil {
+        t.Errorf("Coverage Encode invalid character human-readable part error case : FAIL / bech32String : %v", bech32String)
+    } else {
+        t.Log("Coverage Encode invalid character human-readable part error case : ok / error : ", err)
+    }
+    hrp = "bc"
+    data = []int{0, 31}
+    bech32String, err = Encode(hrp, data)
+    if err != nil {
+        t.Errorf("Coverage Encode normal case : FAIL / error : %v", err)
+    } else {
+        t.Log("Coverage Encode normal case : ok / bech32String : ", bech32String)
+    }
+    hrp = "bc"
+    data = []int{-1}
+    bech32String, err = Encode(hrp, data)
+    if err == nil {
+        t.Errorf("Coverage Encode invalid data error case : FAIL / bech32String : %v", bech32String)
+    } else {
+        t.Log("Coverage Encode invalid data error case : ok / error : ", err)
+    }
+    hrp = "bc"
+    data = []int{32}
+    bech32String, err = Encode(hrp, data)
+    if err == nil {
+        t.Errorf("Coverage Encode invalid data error case : FAIL / bech32String : %v", bech32String)
+    } else {
+        t.Log("Coverage Encode invalid data error case : ok / error : ", err)
+    }
+}
diff --git a/common/big.go b/common/big.go
new file mode 100644 (file)
index 0000000..4ce87ee
--- /dev/null
@@ -0,0 +1,156 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import "math/big"
+
+// Common big integers often used
+var (
+       Big1     = big.NewInt(1)
+       Big2     = big.NewInt(2)
+       Big3     = big.NewInt(3)
+       Big0     = big.NewInt(0)
+       BigTrue  = Big1
+       BigFalse = Big0
+       Big32    = big.NewInt(32)
+       Big36    = big.NewInt(36)
+       Big97    = big.NewInt(97)
+       Big98    = big.NewInt(98)
+       Big256   = big.NewInt(0xff)
+       Big257   = big.NewInt(257)
+       MaxBig   = String2Big("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
+)
+
+// Big pow
+//
+// Returns the power of two big integers
+func BigPow(a, b int) *big.Int {
+       c := new(big.Int)
+       c.Exp(big.NewInt(int64(a)), big.NewInt(int64(b)), big.NewInt(0))
+
+       return c
+}
+
+// Big
+//
+// Shortcut for new(big.Int).SetString(..., 0)
+func Big(num string) *big.Int {
+       n := new(big.Int)
+       n.SetString(num, 0)
+
+       return n
+}
+
+// Bytes2Big
+//
+func BytesToBig(data []byte) *big.Int {
+       n := new(big.Int)
+       n.SetBytes(data)
+
+       return n
+}
+func Bytes2Big(data []byte) *big.Int { return BytesToBig(data) }
+func BigD(data []byte) *big.Int      { return BytesToBig(data) }
+
+func String2Big(num string) *big.Int {
+       n := new(big.Int)
+       n.SetString(num, 0)
+       return n
+}
+
+func BitTest(num *big.Int, i int) bool {
+       return num.Bit(i) > 0
+}
+
+// To256
+//
+// "cast" the big int to a 256 big int (i.e., limit to)
+var tt256 = new(big.Int).Lsh(big.NewInt(1), 256)
+var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1))
+var tt255 = new(big.Int).Lsh(big.NewInt(1), 255)
+
+func U256(x *big.Int) *big.Int {
+       //if x.Cmp(Big0) < 0 {
+       //              return new(big.Int).Add(tt256, x)
+       //      }
+
+       x.And(x, tt256m1)
+
+       return x
+}
+
+func S256(x *big.Int) *big.Int {
+       if x.Cmp(tt255) < 0 {
+               return x
+       } else {
+               // We don't want to modify x, ever
+               return new(big.Int).Sub(x, tt256)
+       }
+}
+
+func FirstBitSet(v *big.Int) int {
+       for i := 0; i < v.BitLen(); i++ {
+               if v.Bit(i) > 0 {
+                       return i
+               }
+       }
+
+       return v.BitLen()
+}
+
+// Big to bytes
+//
+// Returns the bytes of a big integer with the size specified by **base**
+// Attempts to pad the byte array with zeros.
+func BigToBytes(num *big.Int, base int) []byte {
+       ret := make([]byte, base/8)
+
+       if len(num.Bytes()) > base/8 {
+               return num.Bytes()
+       }
+
+       return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...)
+}
+
+// Big copy
+//
+// Creates a copy of the given big integer
+func BigCopy(src *big.Int) *big.Int {
+       return new(big.Int).Set(src)
+}
+
+// Big max
+//
+// Returns the maximum size big integer
+func BigMax(x, y *big.Int) *big.Int {
+       if x.Cmp(y) < 0 {
+               return y
+       }
+
+       return x
+}
+
+// Big min
+//
+// Returns the minimum size big integer
+func BigMin(x, y *big.Int) *big.Int {
+       if x.Cmp(y) > 0 {
+               return y
+       }
+
+       return x
+}
diff --git a/common/big_test.go b/common/big_test.go
new file mode 100644 (file)
index 0000000..1eb0c0c
--- /dev/null
@@ -0,0 +1,89 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import (
+       "bytes"
+       "testing"
+)
+
+func TestMisc(t *testing.T) {
+       a := Big("10")
+       b := Big("57896044618658097711785492504343953926634992332820282019728792003956564819968")
+       c := []byte{1, 2, 3, 4}
+       z := BitTest(a, 1)
+
+       if z != true {
+               t.Error("Expected true got", z)
+       }
+
+       U256(a)
+       S256(a)
+
+       U256(b)
+       S256(b)
+
+       BigD(c)
+}
+
+func TestBigMax(t *testing.T) {
+       a := Big("10")
+       b := Big("5")
+
+       max1 := BigMax(a, b)
+       if max1 != a {
+               t.Errorf("Expected %d got %d", a, max1)
+       }
+
+       max2 := BigMax(b, a)
+       if max2 != a {
+               t.Errorf("Expected %d got %d", a, max2)
+       }
+}
+
+func TestBigMin(t *testing.T) {
+       a := Big("10")
+       b := Big("5")
+
+       min1 := BigMin(a, b)
+       if min1 != b {
+               t.Errorf("Expected %d got %d", b, min1)
+       }
+
+       min2 := BigMin(b, a)
+       if min2 != b {
+               t.Errorf("Expected %d got %d", b, min2)
+       }
+}
+
+func TestBigCopy(t *testing.T) {
+       a := Big("10")
+       b := BigCopy(a)
+       c := Big("1000000000000")
+       y := BigToBytes(b, 16)
+       ybytes := []byte{0, 10}
+       z := BigToBytes(c, 16)
+       zbytes := []byte{232, 212, 165, 16, 0}
+
+       if bytes.Compare(y, ybytes) != 0 {
+               t.Error("Got", ybytes)
+       }
+
+       if bytes.Compare(z, zbytes) != 0 {
+               t.Error("Got", zbytes)
+       }
+}
diff --git a/common/bytes.go b/common/bytes.go
new file mode 100644 (file)
index 0000000..4fb016a
--- /dev/null
@@ -0,0 +1,266 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Package common contains various helper functions.
+package common
+
+import (
+       "bytes"
+       "encoding/binary"
+       "encoding/hex"
+       "fmt"
+       "math/big"
+       "strings"
+)
+
+func ToHex(b []byte) string {
+       hex := Bytes2Hex(b)
+       // Prefer output of "0x0" instead of "0x"
+       if len(hex) == 0 {
+               hex = "0"
+       }
+       return "0x" + hex
+}
+
+func FromHex(s string) []byte {
+       if len(s) > 1 {
+               if s[0:2] == "0x" {
+                       s = s[2:]
+               }
+               if len(s)%2 == 1 {
+                       s = "0" + s
+               }
+               return Hex2Bytes(s)
+       }
+       return nil
+}
+
+// Number to bytes
+//
+// Returns the number in bytes with the specified base
+func NumberToBytes(num interface{}, bits int) []byte {
+       buf := new(bytes.Buffer)
+       err := binary.Write(buf, binary.BigEndian, num)
+       if err != nil {
+               fmt.Println("NumberToBytes failed:", err)
+       }
+
+       return buf.Bytes()[buf.Len()-(bits/8):]
+}
+
+// Bytes to number
+//
+// Attempts to cast a byte slice to a unsigned integer
+func BytesToNumber(b []byte) uint64 {
+       var number uint64
+
+       // Make sure the buffer is 64bits
+       data := make([]byte, 8)
+       data = append(data[:len(b)], b...)
+
+       buf := bytes.NewReader(data)
+       err := binary.Read(buf, binary.BigEndian, &number)
+       if err != nil {
+               fmt.Println("BytesToNumber failed:", err)
+       }
+
+       return number
+}
+
+// Read variable int
+//
+// Read a variable length number in big endian byte order
+func ReadVarInt(buff []byte) (ret uint64) {
+       switch l := len(buff); {
+       case l > 4:
+               d := LeftPadBytes(buff, 8)
+               binary.Read(bytes.NewReader(d), binary.BigEndian, &ret)
+       case l > 2:
+               var num uint32
+               d := LeftPadBytes(buff, 4)
+               binary.Read(bytes.NewReader(d), binary.BigEndian, &num)
+               ret = uint64(num)
+       case l > 1:
+               var num uint16
+               d := LeftPadBytes(buff, 2)
+               binary.Read(bytes.NewReader(d), binary.BigEndian, &num)
+               ret = uint64(num)
+       default:
+               var num uint8
+               binary.Read(bytes.NewReader(buff), binary.BigEndian, &num)
+               ret = uint64(num)
+       }
+
+       return
+}
+
+// Copy bytes
+//
+// Returns an exact copy of the provided bytes
+func CopyBytes(b []byte) (copiedBytes []byte) {
+       copiedBytes = make([]byte, len(b))
+       copy(copiedBytes, b)
+
+       return
+}
+
+func HasHexPrefix(str string) bool {
+       l := len(str)
+       return l >= 2 && str[0:2] == "0x"
+}
+
+func IsHex(str string) bool {
+       l := len(str)
+       return l >= 4 && l%2 == 0 && str[0:2] == "0x"
+}
+
+func Bytes2Hex(d []byte) string {
+       return hex.EncodeToString(d)
+}
+
+func Hex2Bytes(str string) []byte {
+       h, _ := hex.DecodeString(str)
+
+       return h
+}
+
+func Hex2BytesFixed(str string, flen int) []byte {
+       h, _ := hex.DecodeString(str)
+       if len(h) == flen {
+               return h
+       } else {
+               if len(h) > flen {
+                       return h[len(h)-flen : len(h)]
+               } else {
+                       hh := make([]byte, flen)
+                       copy(hh[flen-len(h):flen], h[:])
+                       return hh
+               }
+       }
+}
+
+func StringToByteFunc(str string, cb func(str string) []byte) (ret []byte) {
+       if len(str) > 1 && str[0:2] == "0x" && !strings.Contains(str, "\n") {
+               ret = Hex2Bytes(str[2:])
+       } else {
+               ret = cb(str)
+       }
+
+       return
+}
+
+func FormatData(data string) []byte {
+       if len(data) == 0 {
+               return nil
+       }
+       // Simple stupid
+       d := new(big.Int)
+       if data[0:1] == "\"" && data[len(data)-1:] == "\"" {
+               return RightPadBytes([]byte(data[1:len(data)-1]), 32)
+       } else if len(data) > 1 && data[:2] == "0x" {
+               d.SetBytes(Hex2Bytes(data[2:]))
+       } else {
+               d.SetString(data, 0)
+       }
+
+       return BigToBytes(d, 256)
+}
+
+func ParseData(data ...interface{}) (ret []byte) {
+       for _, item := range data {
+               switch t := item.(type) {
+               case string:
+                       var str []byte
+                       if IsHex(t) {
+                               str = Hex2Bytes(t[2:])
+                       } else {
+                               str = []byte(t)
+                       }
+
+                       ret = append(ret, RightPadBytes(str, 32)...)
+               case []byte:
+                       ret = append(ret, LeftPadBytes(t, 32)...)
+               }
+       }
+
+       return
+}
+
+func RightPadBytes(slice []byte, l int) []byte {
+       if l < len(slice) {
+               return slice
+       }
+
+       padded := make([]byte, l)
+       copy(padded[0:len(slice)], slice)
+
+       return padded
+}
+
+func LeftPadBytes(slice []byte, l int) []byte {
+       if l < len(slice) {
+               return slice
+       }
+
+       padded := make([]byte, l)
+       copy(padded[l-len(slice):], slice)
+
+       return padded
+}
+
+func LeftPadString(str string, l int) string {
+       if l < len(str) {
+               return str
+       }
+
+       zeros := Bytes2Hex(make([]byte, (l-len(str))/2))
+
+       return zeros + str
+
+}
+
+func RightPadString(str string, l int) string {
+       if l < len(str) {
+               return str
+       }
+
+       zeros := Bytes2Hex(make([]byte, (l-len(str))/2))
+
+       return str + zeros
+
+}
+
+func ToAddress(slice []byte) (addr []byte) {
+       if len(slice) < 20 {
+               addr = LeftPadBytes(slice, 20)
+       } else if len(slice) > 20 {
+               addr = slice[len(slice)-20:]
+       } else {
+               addr = slice
+       }
+
+       addr = CopyBytes(addr)
+
+       return
+}
+
+func ByteSliceToInterface(slice [][]byte) (ret []interface{}) {
+       for _, i := range slice {
+               ret = append(ret, i)
+       }
+
+       return
+}
diff --git a/common/bytes_test.go b/common/bytes_test.go
new file mode 100644 (file)
index 0000000..2e52084
--- /dev/null
@@ -0,0 +1,196 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import (
+       "bytes"
+       "testing"
+
+       checker "gopkg.in/check.v1"
+)
+
+type BytesSuite struct{}
+
+var _ = checker.Suite(&BytesSuite{})
+
+func (s *BytesSuite) TestNumberToBytes(c *checker.C) {
+       // data1 := int(1)
+       // res1 := NumberToBytes(data1, 16)
+       // c.Check(res1, checker.Panics)
+
+       var data2 float64 = 3.141592653
+       exp2 := []byte{0xe9, 0x38}
+       res2 := NumberToBytes(data2, 16)
+       c.Assert(res2, checker.DeepEquals, exp2)
+}
+
+func (s *BytesSuite) TestBytesToNumber(c *checker.C) {
+       datasmall := []byte{0xe9, 0x38, 0xe9, 0x38}
+       datalarge := []byte{0xe9, 0x38, 0xe9, 0x38, 0xe9, 0x38, 0xe9, 0x38}
+
+       var expsmall uint64 = 0xe938e938
+       var explarge uint64 = 0x0
+
+       ressmall := BytesToNumber(datasmall)
+       reslarge := BytesToNumber(datalarge)
+
+       c.Assert(ressmall, checker.Equals, expsmall)
+       c.Assert(reslarge, checker.Equals, explarge)
+
+}
+
+func (s *BytesSuite) TestReadVarInt(c *checker.C) {
+       data8 := []byte{1, 2, 3, 4, 5, 6, 7, 8}
+       data4 := []byte{1, 2, 3, 4}
+       data2 := []byte{1, 2}
+       data1 := []byte{1}
+
+       exp8 := uint64(72623859790382856)
+       exp4 := uint64(16909060)
+       exp2 := uint64(258)
+       exp1 := uint64(1)
+
+       res8 := ReadVarInt(data8)
+       res4 := ReadVarInt(data4)
+       res2 := ReadVarInt(data2)
+       res1 := ReadVarInt(data1)
+
+       c.Assert(res8, checker.Equals, exp8)
+       c.Assert(res4, checker.Equals, exp4)
+       c.Assert(res2, checker.Equals, exp2)
+       c.Assert(res1, checker.Equals, exp1)
+}
+
+func (s *BytesSuite) TestCopyBytes(c *checker.C) {
+       data1 := []byte{1, 2, 3, 4}
+       exp1 := []byte{1, 2, 3, 4}
+       res1 := CopyBytes(data1)
+       c.Assert(res1, checker.DeepEquals, exp1)
+}
+
+func (s *BytesSuite) TestIsHex(c *checker.C) {
+       data1 := "a9e67e"
+       exp1 := false
+       res1 := IsHex(data1)
+       c.Assert(res1, checker.DeepEquals, exp1)
+
+       data2 := "0xa9e67e00"
+       exp2 := true
+       res2 := IsHex(data2)
+       c.Assert(res2, checker.DeepEquals, exp2)
+
+}
+
+func (s *BytesSuite) TestParseDataString(c *checker.C) {
+       res1 := ParseData("hello", "world", "0x0106")
+       data := "68656c6c6f000000000000000000000000000000000000000000000000000000776f726c640000000000000000000000000000000000000000000000000000000106000000000000000000000000000000000000000000000000000000000000"
+       exp1 := Hex2Bytes(data)
+       c.Assert(res1, checker.DeepEquals, exp1)
+}
+
+func (s *BytesSuite) TestParseDataBytes(c *checker.C) {
+       data1 := []byte{232, 212, 165, 16, 0}
+       exp1 := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 212, 165, 16, 0}
+
+       res1 := ParseData(data1)
+       c.Assert(res1, checker.DeepEquals, exp1)
+
+}
+
+func (s *BytesSuite) TestLeftPadBytes(c *checker.C) {
+       val1 := []byte{1, 2, 3, 4}
+       exp1 := []byte{0, 0, 0, 0, 1, 2, 3, 4}
+
+       res1 := LeftPadBytes(val1, 8)
+       res2 := LeftPadBytes(val1, 2)
+
+       c.Assert(res1, checker.DeepEquals, exp1)
+       c.Assert(res2, checker.DeepEquals, val1)
+}
+
+func (s *BytesSuite) TestFormatData(c *checker.C) {
+       data1 := ""
+       data2 := "0xa9e67e00"
+       data3 := "a9e67e"
+       data4 := "\"a9e67e00\""
+
+       // exp1 := []byte{}
+       exp2 := []byte{00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0xa9, 0xe6, 0x7e, 00}
+       exp3 := []byte{00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00}
+       exp4 := []byte{0x61, 0x39, 0x65, 0x36, 0x37, 0x65, 0x30, 0x30, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00}
+
+       res1 := FormatData(data1)
+       res2 := FormatData(data2)
+       res3 := FormatData(data3)
+       res4 := FormatData(data4)
+
+       c.Assert(res1, checker.IsNil)
+       c.Assert(res2, checker.DeepEquals, exp2)
+       c.Assert(res3, checker.DeepEquals, exp3)
+       c.Assert(res4, checker.DeepEquals, exp4)
+}
+
+func (s *BytesSuite) TestRightPadBytes(c *checker.C) {
+       val := []byte{1, 2, 3, 4}
+       exp := []byte{1, 2, 3, 4, 0, 0, 0, 0}
+
+       resstd := RightPadBytes(val, 8)
+       resshrt := RightPadBytes(val, 2)
+
+       c.Assert(resstd, checker.DeepEquals, exp)
+       c.Assert(resshrt, checker.DeepEquals, val)
+}
+
+func (s *BytesSuite) TestLeftPadString(c *checker.C) {
+       val := "test"
+       exp := "\x30\x30\x30\x30" + val
+
+       resstd := LeftPadString(val, 8)
+       resshrt := LeftPadString(val, 2)
+
+       c.Assert(resstd, checker.Equals, exp)
+       c.Assert(resshrt, checker.Equals, val)
+}
+
+func (s *BytesSuite) TestRightPadString(c *checker.C) {
+       val := "test"
+       exp := val + "\x30\x30\x30\x30"
+
+       resstd := RightPadString(val, 8)
+       resshrt := RightPadString(val, 2)
+
+       c.Assert(resstd, checker.Equals, exp)
+       c.Assert(resshrt, checker.Equals, val)
+}
+
+func TestFromHex(t *testing.T) {
+       input := "0x01"
+       expected := []byte{1}
+       result := FromHex(input)
+       if bytes.Compare(expected, result) != 0 {
+               t.Errorf("Expected % x got % x", expected, result)
+       }
+}
+
+func TestFromHexOddLength(t *testing.T) {
+       input := "0x1"
+       expected := []byte{1}
+       result := FromHex(input)
+       if bytes.Compare(expected, result) != 0 {
+               t.Errorf("Expected % x got % x", expected, result)
+       }
+}
diff --git a/common/debug.go b/common/debug.go
new file mode 100644 (file)
index 0000000..61acd8c
--- /dev/null
@@ -0,0 +1,52 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import (
+       "fmt"
+       "os"
+       "runtime"
+       "runtime/debug"
+       "strings"
+)
+
+// Report gives off a warning requesting the user to submit an issue to the github tracker.
+func Report(extra ...interface{}) {
+       fmt.Fprintln(os.Stderr, "You've encountered a sought after, hard to reproduce bug. Please report this to the developers <3 https://github.com/ethereum/go-ethereum/issues")
+       fmt.Fprintln(os.Stderr, extra...)
+
+       _, file, line, _ := runtime.Caller(1)
+       fmt.Fprintf(os.Stderr, "%v:%v\n", file, line)
+
+       debug.PrintStack()
+
+       fmt.Fprintln(os.Stderr, "#### BUG! PLEASE REPORT ####")
+}
+
+// PrintDepricationWarning prinst the given string in a box using fmt.Println.
+func PrintDepricationWarning(str string) {
+       line := strings.Repeat("#", len(str)+4)
+       emptyLine := strings.Repeat(" ", len(str))
+       fmt.Printf(`
+%s
+# %s #
+# %s #
+# %s #
+%s
+
+`, line, emptyLine, str, emptyLine, line)
+}
diff --git a/common/defaults.go b/common/defaults.go
new file mode 100644 (file)
index 0000000..8a136fa
--- /dev/null
@@ -0,0 +1,48 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import (
+       "path/filepath"
+       "runtime"
+)
+
+const (
+       DefaultIPCSocket = "geth.ipc"  // Default (relative) name of the IPC RPC socket
+       DefaultHTTPHost  = "localhost" // Default host interface for the HTTP RPC server
+       DefaultHTTPPort  = 8545        // Default TCP port for the HTTP RPC server
+       DefaultWSHost    = "localhost" // Default host interface for the websocket RPC server
+       DefaultWSPort    = 8546        // Default TCP port for the websocket RPC server
+)
+
+// DefaultDataDir is the default data directory to use for the databases and other
+// persistence requirements.
+func DefaultDataDir() string {
+       // Try to place the data folder in the user's home dir
+       home := HomeDir()
+       if home != "" {
+               if runtime.GOOS == "darwin" {
+                       return filepath.Join(home, "Library", "Ethereum")
+               } else if runtime.GOOS == "windows" {
+                       return filepath.Join(home, "AppData", "Roaming", "Ethereum")
+               } else {
+                       return filepath.Join(home, ".ethereum")
+               }
+       }
+       // As we cannot guess a stable location, return empty and handle later
+       return ""
+}
diff --git a/common/icap.go b/common/icap.go
new file mode 100644 (file)
index 0000000..a36e669
--- /dev/null
@@ -0,0 +1,190 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// Spec at https://github.com/ethereum/wiki/wiki/ICAP:-Inter-exchange-Client-Address-Protocol
+
+package common
+
+import (
+       "errors"
+       "math/big"
+       "strconv"
+       "strings"
+)
+
+var (
+       Base36Chars          = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+       ICAPLengthError      = errors.New("Invalid ICAP length")
+       ICAPEncodingError    = errors.New("Invalid ICAP encoding")
+       ICAPChecksumError    = errors.New("Invalid ICAP checksum")
+       ICAPCountryCodeError = errors.New("Invalid ICAP country code")
+       ICAPAssetIdentError  = errors.New("Invalid ICAP asset identifier")
+       ICAPInstCodeError    = errors.New("Invalid ICAP institution code")
+       ICAPClientIdentError = errors.New("Invalid ICAP client identifier")
+)
+
+func ICAPToAddress(s string) (Address, error) {
+       switch len(s) {
+       case 35: // "XE" + 2 digit checksum + 31 base-36 chars of address
+               return parseICAP(s)
+       case 34: // "XE" + 2 digit checksum + 30 base-36 chars of address
+               return parseICAP(s)
+       case 20: // "XE" + 2 digit checksum + 3-char asset identifier +
+               // 4-char institution identifier + 9-char institution client identifier
+               return parseIndirectICAP(s)
+       default:
+               return Address{}, ICAPLengthError
+       }
+}
+
+func parseICAP(s string) (Address, error) {
+       if !strings.HasPrefix(s, "XE") {
+               return Address{}, ICAPCountryCodeError
+       }
+       if err := validCheckSum(s); err != nil {
+               return Address{}, err
+       }
+       // checksum is ISO13616, Ethereum address is base-36
+       bigAddr, _ := new(big.Int).SetString(s[4:], 36)
+       return BigToAddress(bigAddr), nil
+}
+
+func parseIndirectICAP(s string) (Address, error) {
+       if !strings.HasPrefix(s, "XE") {
+               return Address{}, ICAPCountryCodeError
+       }
+       if s[4:7] != "ETH" {
+               return Address{}, ICAPAssetIdentError
+       }
+       if err := validCheckSum(s); err != nil {
+               return Address{}, err
+       }
+       // TODO: integrate with ICAP namereg
+       return Address{}, errors.New("not implemented")
+}
+
+func AddressToICAP(a Address) (string, error) {
+       enc := base36Encode(a.Big())
+       // zero padd encoded address to Direct ICAP length if needed
+       if len(enc) < 30 {
+               enc = join(strings.Repeat("0", 30-len(enc)), enc)
+       }
+       icap := join("XE", checkDigits(enc), enc)
+       return icap, nil
+}
+
+// TODO: integrate with ICAP namereg when it's available
+func AddressToIndirectICAP(a Address, instCode string) (string, error) {
+       // return addressToIndirectICAP(a, instCode)
+       return "", errors.New("not implemented")
+}
+
+func addressToIndirectICAP(a Address, instCode string) (string, error) {
+       // TODO: add addressToClientIdent which grabs client ident from ICAP namereg
+       //clientIdent := addressToClientIdent(a)
+       clientIdent := "todo"
+       return clientIdentToIndirectICAP(instCode, clientIdent)
+}
+
+func clientIdentToIndirectICAP(instCode, clientIdent string) (string, error) {
+       if len(instCode) != 4 || !validBase36(instCode) {
+               return "", ICAPInstCodeError
+       }
+       if len(clientIdent) != 9 || !validBase36(instCode) {
+               return "", ICAPClientIdentError
+       }
+
+       // currently ETH is only valid asset identifier
+       s := join("ETH", instCode, clientIdent)
+       return join("XE", checkDigits(s), s), nil
+}
+
+// https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN
+func validCheckSum(s string) error {
+       s = join(s[4:], s[:4])
+       expanded, err := iso13616Expand(s)
+       if err != nil {
+               return err
+       }
+       checkSumNum, _ := new(big.Int).SetString(expanded, 10)
+       if checkSumNum.Mod(checkSumNum, Big97).Cmp(Big1) != 0 {
+               return ICAPChecksumError
+       }
+       return nil
+}
+
+func checkDigits(s string) string {
+       expanded, _ := iso13616Expand(strings.Join([]string{s, "XE00"}, ""))
+       num, _ := new(big.Int).SetString(expanded, 10)
+       num.Sub(Big98, num.Mod(num, Big97))
+
+       checkDigits := num.String()
+       // zero padd checksum
+       if len(checkDigits) == 1 {
+               checkDigits = join("0", checkDigits)
+       }
+       return checkDigits
+}
+
+// not base-36, but expansion to decimal literal: A = 10, B = 11, ... Z = 35
+func iso13616Expand(s string) (string, error) {
+       var parts []string
+       if !validBase36(s) {
+               return "", ICAPEncodingError
+       }
+       for _, c := range s {
+               i := uint64(c)
+               if i >= 65 {
+                       parts = append(parts, strconv.FormatUint(uint64(c)-55, 10))
+               } else {
+                       parts = append(parts, string(c))
+               }
+       }
+       return join(parts...), nil
+}
+
+func base36Encode(i *big.Int) string {
+       var chars []rune
+       x := new(big.Int)
+       for {
+               x.Mod(i, Big36)
+               chars = append(chars, rune(Base36Chars[x.Uint64()]))
+               i.Div(i, Big36)
+               if i.Cmp(Big0) == 0 {
+                       break
+               }
+       }
+       // reverse slice
+       for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 {
+               chars[i], chars[j] = chars[j], chars[i]
+       }
+       return string(chars)
+}
+
+func validBase36(s string) bool {
+       for _, c := range s {
+               i := uint64(c)
+               // 0-9 or A-Z
+               if i < 48 || (i > 57 && i < 65) || i > 90 {
+                       return false
+               }
+       }
+       return true
+}
+
+func join(s ...string) string {
+       return strings.Join(s, "")
+}
diff --git a/common/icap_test.go b/common/icap_test.go
new file mode 100644 (file)
index 0000000..6306686
--- /dev/null
@@ -0,0 +1,91 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import "testing"
+
+/* More test vectors:
+https://github.com/ethereum/web3.js/blob/master/test/iban.fromAddress.js
+https://github.com/ethereum/web3.js/blob/master/test/iban.toAddress.js
+https://github.com/ethereum/web3.js/blob/master/test/iban.isValid.js
+https://github.com/ethereum/libethereum/blob/develop/test/libethcore/icap.cpp
+*/
+
+type icapTest struct {
+       name string
+       addr string
+       icap string
+}
+
+var icapOKTests = []icapTest{
+       {"Direct1", "0x52dc504a422f0e2a9e7632a34a50f1a82f8224c7", "XE499OG1EH8ZZI0KXC6N83EKGT1BM97P2O7"},
+       {"Direct2", "0x11c5496aee77c1ba1f0854206a26dda82a81d6d8", "XE1222Q908LN1QBBU6XUQSO1OHWJIOS46OO"},
+       {"DirectZeroPrefix", "0x00c5496aee77c1ba1f0854206a26dda82a81d6d8", "XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS"},
+       {"DirectDoubleZeroPrefix", "0x0000a5327eab78357cbf2ae8f3d49fd9d90c7d22", "XE0600DQK33XDTYUCRI0KYM5ELAKXDWWF6"},
+}
+
+var icapInvalidTests = []icapTest{
+       {"DirectInvalidCheckSum", "", "XE7438O073KYGTWWZN0F2WZ0R8PX5ZPPZS"},
+       {"DirectInvalidCountryCode", "", "XD7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS"},
+       {"DirectInvalidLength36", "", "XE499OG1EH8ZZI0KXC6N83EKGT1BM97P2O77"},
+       {"DirectInvalidLength33", "", "XE499OG1EH8ZZI0KXC6N83EKGT1BM97P2"},
+
+       {"IndirectInvalidCheckSum", "", "XE35ETHXREGGOPHERSSS"},
+       {"IndirectInvalidAssetIdentifier", "", "XE34ETHXREGGOPHERSSS"},
+       {"IndirectInvalidLength19", "", "XE34ETHXREGGOPHERSS"},
+       {"IndirectInvalidLength21", "", "XE34ETHXREGGOPHERSSSS"},
+}
+
+func TestICAPOK(t *testing.T) {
+       for _, test := range icapOKTests {
+               decodeEncodeTest(HexToAddress(test.addr), test.icap, t)
+       }
+}
+
+func TestICAPInvalid(t *testing.T) {
+       for _, test := range icapInvalidTests {
+               failedDecodingTest(test.icap, t)
+       }
+}
+
+func decodeEncodeTest(addr0 Address, icap0 string, t *testing.T) {
+       icap1, err := AddressToICAP(addr0)
+       if err != nil {
+               t.Errorf("ICAP encoding failed: %s", err)
+       }
+       if icap1 != icap0 {
+               t.Errorf("ICAP mismatch: have: %s want: %s", icap1, icap0)
+       }
+
+       addr1, err := ICAPToAddress(icap0)
+       if err != nil {
+               t.Errorf("ICAP decoding failed: %s", err)
+       }
+       if addr1 != addr0 {
+               t.Errorf("Address mismatch: have: %x want: %x", addr1, addr0)
+       }
+}
+
+func failedDecodingTest(icap string, t *testing.T) {
+       addr, err := ICAPToAddress(icap)
+       if err == nil {
+               t.Errorf("Expected ICAP decoding to fail.")
+       }
+       if addr != (Address{}) {
+               t.Errorf("Expected empty Address on failed ICAP decoding.")
+       }
+}
diff --git a/common/list.go b/common/list.go
new file mode 100644 (file)
index 0000000..07b2f17
--- /dev/null
@@ -0,0 +1,97 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import (
+       "encoding/json"
+       "reflect"
+       "sync"
+)
+
+// The list type is an anonymous slice handler which can be used
+// for containing any slice type to use in an environment which
+// does not support slice types (e.g., JavaScript, QML)
+type List struct {
+       mut    sync.Mutex
+       val    interface{}
+       list   reflect.Value
+       Length int
+}
+
+// Initialise a new list. Panics if non-slice type is given.
+func NewList(t interface{}) *List {
+       list := reflect.ValueOf(t)
+       if list.Kind() != reflect.Slice {
+               panic("list container initialized with a non-slice type")
+       }
+
+       return &List{sync.Mutex{}, t, list, list.Len()}
+}
+
+func EmptyList() *List {
+       return NewList([]interface{}{})
+}
+
+// Get N element from the embedded slice. Returns nil if OOB.
+func (self *List) Get(i int) interface{} {
+       if self.list.Len() > i {
+               self.mut.Lock()
+               defer self.mut.Unlock()
+
+               i := self.list.Index(i).Interface()
+
+               return i
+       }
+
+       return nil
+}
+
+func (self *List) GetAsJson(i int) interface{} {
+       e := self.Get(i)
+
+       r, _ := json.Marshal(e)
+
+       return string(r)
+}
+
+// Appends value at the end of the slice. Panics when incompatible value
+// is given.
+func (self *List) Append(v interface{}) {
+       self.mut.Lock()
+       defer self.mut.Unlock()
+
+       self.list = reflect.Append(self.list, reflect.ValueOf(v))
+       self.Length = self.list.Len()
+}
+
+// Returns the underlying slice as interface.
+func (self *List) Interface() interface{} {
+       return self.list.Interface()
+}
+
+// For JavaScript <3
+func (self *List) ToJSON() string {
+       // make(T, 0) != nil
+       list := make([]interface{}, 0)
+       for i := 0; i < self.Length; i++ {
+               list = append(list, self.Get(i))
+       }
+
+       data, _ := json.Marshal(list)
+
+       return string(data)
+}
diff --git a/common/main_test.go b/common/main_test.go
new file mode 100644 (file)
index 0000000..149d099
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import (
+       "testing"
+
+       checker "gopkg.in/check.v1"
+)
+
+func Test(t *testing.T) { checker.TestingT(t) }
diff --git a/common/math/dist.go b/common/math/dist.go
new file mode 100644 (file)
index 0000000..488949d
--- /dev/null
@@ -0,0 +1,96 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package math
+
+import (
+       "math/big"
+       "sort"
+
+       "bytom/common"
+)
+
+type Summer interface {
+       Sum(i int) *big.Int
+       Len() int
+}
+
+func Sum(slice Summer) (sum *big.Int) {
+       sum = new(big.Int)
+
+       for i := 0; i < slice.Len(); i++ {
+               sum.Add(sum, slice.Sum(i))
+       }
+       return
+}
+
+type Vector struct {
+       Gas, Price *big.Int
+}
+
+type VectorsBy func(v1, v2 Vector) bool
+
+func (self VectorsBy) Sort(vectors []Vector) {
+       bs := vectorSorter{
+               vectors: vectors,
+               by:      self,
+       }
+       sort.Sort(bs)
+}
+
+type vectorSorter struct {
+       vectors []Vector
+       by      func(v1, v2 Vector) bool
+}
+
+func (v vectorSorter) Len() int           { return len(v.vectors) }
+func (v vectorSorter) Less(i, j int) bool { return v.by(v.vectors[i], v.vectors[j]) }
+func (v vectorSorter) Swap(i, j int)      { v.vectors[i], v.vectors[j] = v.vectors[j], v.vectors[i] }
+
+func PriceSort(v1, v2 Vector) bool { return v1.Price.Cmp(v2.Price) < 0 }
+func GasSort(v1, v2 Vector) bool   { return v1.Gas.Cmp(v2.Gas) < 0 }
+
+type vectorSummer struct {
+       vectors []Vector
+       by      func(v Vector) *big.Int
+}
+
+type VectorSum func(v Vector) *big.Int
+
+func (v VectorSum) Sum(vectors []Vector) *big.Int {
+       vs := vectorSummer{
+               vectors: vectors,
+               by:      v,
+       }
+       return Sum(vs)
+}
+
+func (v vectorSummer) Len() int           { return len(v.vectors) }
+func (v vectorSummer) Sum(i int) *big.Int { return v.by(v.vectors[i]) }
+
+func GasSum(v Vector) *big.Int { return v.Gas }
+
+var etherInWei = new(big.Rat).SetInt(common.String2Big("1000000000000000000"))
+
+func GasPrice(bp, gl, ep *big.Int) *big.Int {
+       BP := new(big.Rat).SetInt(bp)
+       GL := new(big.Rat).SetInt(gl)
+       EP := new(big.Rat).SetInt(ep)
+       GP := new(big.Rat).Quo(BP, GL)
+       GP = GP.Quo(GP, EP)
+
+       return GP.Mul(GP, etherInWei).Num()
+}
diff --git a/common/math/dist_test.go b/common/math/dist_test.go
new file mode 100644 (file)
index 0000000..826faea
--- /dev/null
@@ -0,0 +1,82 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package math
+
+import (
+       "fmt"
+       "math/big"
+       "testing"
+)
+
+type summer struct {
+       numbers []*big.Int
+}
+
+func (s summer) Len() int { return len(s.numbers) }
+func (s summer) Sum(i int) *big.Int {
+       return s.numbers[i]
+}
+
+func TestSum(t *testing.T) {
+       summer := summer{numbers: []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}}
+       sum := Sum(summer)
+       if sum.Cmp(big.NewInt(6)) != 0 {
+               t.Errorf("got sum = %d, want 6", sum)
+       }
+}
+
+func TestDist(t *testing.T) {
+       var vectors = []Vector{
+               Vector{big.NewInt(1000), big.NewInt(1234)},
+               Vector{big.NewInt(500), big.NewInt(10023)},
+               Vector{big.NewInt(1034), big.NewInt(1987)},
+               Vector{big.NewInt(1034), big.NewInt(1987)},
+               Vector{big.NewInt(8983), big.NewInt(1977)},
+               Vector{big.NewInt(98382), big.NewInt(1887)},
+               Vector{big.NewInt(12398), big.NewInt(1287)},
+               Vector{big.NewInt(12398), big.NewInt(1487)},
+               Vector{big.NewInt(12398), big.NewInt(1987)},
+               Vector{big.NewInt(12398), big.NewInt(128)},
+               Vector{big.NewInt(12398), big.NewInt(1987)},
+               Vector{big.NewInt(1398), big.NewInt(187)},
+               Vector{big.NewInt(12328), big.NewInt(1927)},
+               Vector{big.NewInt(12398), big.NewInt(1987)},
+               Vector{big.NewInt(22398), big.NewInt(1287)},
+               Vector{big.NewInt(1370), big.NewInt(1981)},
+               Vector{big.NewInt(12398), big.NewInt(1957)},
+               Vector{big.NewInt(42198), big.NewInt(1987)},
+       }
+
+       VectorsBy(GasSort).Sort(vectors)
+       fmt.Println(vectors)
+
+       BP := big.NewInt(15)
+       GL := big.NewInt(1000000)
+       EP := big.NewInt(100)
+       fmt.Println("BP", BP, "GL", GL, "EP", EP)
+       GP := GasPrice(BP, GL, EP)
+       fmt.Println("GP =", GP, "Wei per GU")
+
+       S := len(vectors) / 4
+       fmt.Println("L", len(vectors), "S", S)
+       for i := 1; i <= S*4; i += S {
+               fmt.Printf("T%d = %v\n", i, vectors[i])
+       }
+
+       g := VectorSum(GasSum).Sum(vectors)
+       fmt.Printf("G = âˆ‘g* (%v)\n", g)
+}
diff --git a/common/number/int.go b/common/number/int.go
new file mode 100644 (file)
index 0000000..15b32b9
--- /dev/null
@@ -0,0 +1,197 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package number
+
+import (
+       "math/big"
+
+       "bytom/common"
+)
+
+var tt256 = new(big.Int).Lsh(big.NewInt(1), 256)
+var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1))
+var tt255 = new(big.Int).Lsh(big.NewInt(1), 255)
+
+func limitUnsigned256(x *Number) *Number {
+       x.num.And(x.num, tt256m1)
+       return x
+}
+
+func limitSigned256(x *Number) *Number {
+       if x.num.Cmp(tt255) < 0 {
+               return x
+       } else {
+               x.num.Sub(x.num, tt256)
+               return x
+       }
+}
+
+// Number function
+type Initialiser func(n int64) *Number
+
+// A Number represents a generic integer with a bounding function limiter. Limit is called after each operations
+// to give "fake" bounded integers. New types of Number can be created through NewInitialiser returning a lambda
+// with the new Initialiser.
+type Number struct {
+       num   *big.Int
+       limit func(n *Number) *Number
+}
+
+// Returns a new initialiser for a new *Number without having to expose certain fields
+func NewInitialiser(limiter func(*Number) *Number) Initialiser {
+       return func(n int64) *Number {
+               return &Number{big.NewInt(n), limiter}
+       }
+}
+
+// Return a Number with a UNSIGNED limiter up to 256 bits
+func Uint256(n int64) *Number {
+       return &Number{big.NewInt(n), limitUnsigned256}
+}
+
+// Return a Number with a SIGNED limiter up to 256 bits
+func Int256(n int64) *Number {
+       return &Number{big.NewInt(n), limitSigned256}
+}
+
+// Returns a Number with a SIGNED unlimited size
+func Big(n int64) *Number {
+       return &Number{big.NewInt(n), func(x *Number) *Number { return x }}
+}
+
+// Sets i to sum of x+y
+func (i *Number) Add(x, y *Number) *Number {
+       i.num.Add(x.num, y.num)
+       return i.limit(i)
+}
+
+// Sets i to difference of x-y
+func (i *Number) Sub(x, y *Number) *Number {
+       i.num.Sub(x.num, y.num)
+       return i.limit(i)
+}
+
+// Sets i to product of x*y
+func (i *Number) Mul(x, y *Number) *Number {
+       i.num.Mul(x.num, y.num)
+       return i.limit(i)
+}
+
+// Sets i to the quotient prodject of x/y
+func (i *Number) Div(x, y *Number) *Number {
+       i.num.Div(x.num, y.num)
+       return i.limit(i)
+}
+
+// Sets i to x % y
+func (i *Number) Mod(x, y *Number) *Number {
+       i.num.Mod(x.num, y.num)
+       return i.limit(i)
+}
+
+// Sets i to x << s
+func (i *Number) Lsh(x *Number, s uint) *Number {
+       i.num.Lsh(x.num, s)
+       return i.limit(i)
+}
+
+// Sets i to x^y
+func (i *Number) Pow(x, y *Number) *Number {
+       i.num.Exp(x.num, y.num, big.NewInt(0))
+       return i.limit(i)
+}
+
+// Setters
+
+// Set x to i
+func (i *Number) Set(x *Number) *Number {
+       i.num.Set(x.num)
+       return i.limit(i)
+}
+
+// Set x bytes to i
+func (i *Number) SetBytes(x []byte) *Number {
+       i.num.SetBytes(x)
+       return i.limit(i)
+}
+
+// Cmp compares x and y and returns:
+//
+//     -1 if x <  y
+//     0 if x == y
+//     +1 if x >  y
+func (i *Number) Cmp(x *Number) int {
+       return i.num.Cmp(x.num)
+}
+
+// Getters
+
+// Returns the string representation of i
+func (i *Number) String() string {
+       return i.num.String()
+}
+
+// Returns the byte representation of i
+func (i *Number) Bytes() []byte {
+       return i.num.Bytes()
+}
+
+// Uint64 returns the Uint64 representation of x. If x cannot be represented in an int64, the result is undefined.
+func (i *Number) Uint64() uint64 {
+       return i.num.Uint64()
+}
+
+// Int64 returns the int64 representation of x. If x cannot be represented in an int64, the result is undefined.
+func (i *Number) Int64() int64 {
+       return i.num.Int64()
+}
+
+// Returns the signed version of i
+func (i *Number) Int256() *Number {
+       return Int(0).Set(i)
+}
+
+// Returns the unsigned version of i
+func (i *Number) Uint256() *Number {
+       return Uint(0).Set(i)
+}
+
+// Returns the index of the first bit that's set to 1
+func (i *Number) FirstBitSet() int {
+       for j := 0; j < i.num.BitLen(); j++ {
+               if i.num.Bit(j) > 0 {
+                       return j
+               }
+       }
+
+       return i.num.BitLen()
+}
+
+// Variables
+
+var (
+       Zero       = Uint(0)
+       One        = Uint(1)
+       Two        = Uint(2)
+       MaxUint256 = Uint(0).SetBytes(common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
+
+       MinOne = Int(-1)
+
+       // "typedefs"
+       Uint = Uint256
+       Int  = Int256
+)
diff --git a/common/number/uint_test.go b/common/number/uint_test.go
new file mode 100644 (file)
index 0000000..4ff0d49
--- /dev/null
@@ -0,0 +1,108 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package number
+
+import (
+       "math/big"
+       "testing"
+
+       "bytom/common"
+)
+
+func TestSet(t *testing.T) {
+       a := Uint(0)
+       b := Uint(10)
+       a.Set(b)
+       if a.num.Cmp(b.num) != 0 {
+               t.Error("didn't compare", a, b)
+       }
+
+       c := Uint(0).SetBytes(common.Hex2Bytes("0a"))
+       if c.num.Cmp(big.NewInt(10)) != 0 {
+               t.Error("c set bytes failed.")
+       }
+}
+
+func TestInitialiser(t *testing.T) {
+       check := false
+       init := NewInitialiser(func(x *Number) *Number {
+               check = true
+               return x
+       })
+       a := init(0).Add(init(1), init(2))
+       if a.Cmp(init(3)) != 0 {
+               t.Error("expected 3. got", a)
+       }
+       if !check {
+               t.Error("expected limiter to be called")
+       }
+}
+
+func TestGet(t *testing.T) {
+       a := Uint(10)
+       if a.Uint64() != 10 {
+               t.Error("expected to get 10. got", a.Uint64())
+       }
+
+       a = Uint(10)
+       if a.Int64() != 10 {
+               t.Error("expected to get 10. got", a.Int64())
+       }
+}
+
+func TestCmp(t *testing.T) {
+       a := Uint(10)
+       b := Uint(10)
+       c := Uint(11)
+
+       if a.Cmp(b) != 0 {
+               t.Error("a b == 0 failed", a, b)
+       }
+
+       if a.Cmp(c) >= 0 {
+               t.Error("a c < 0 failed", a, c)
+       }
+
+       if c.Cmp(b) <= 0 {
+               t.Error("c b > 0 failed", c, b)
+       }
+}
+
+func TestMaxArith(t *testing.T) {
+       a := Uint(0).Add(MaxUint256, One)
+       if a.Cmp(Zero) != 0 {
+               t.Error("expected max256 + 1 = 0 got", a)
+       }
+
+       a = Uint(0).Sub(Uint(0), One)
+       if a.Cmp(MaxUint256) != 0 {
+               t.Error("expected 0 - 1 = max256 got", a)
+       }
+
+       a = Int(0).Sub(Int(0), One)
+       if a.Cmp(MinOne) != 0 {
+               t.Error("expected 0 - 1 = -1 got", a)
+       }
+}
+
+func TestConversion(t *testing.T) {
+       a := Int(-1)
+       b := a.Uint256()
+       if b.Cmp(MaxUint256) != 0 {
+               t.Error("expected -1 => unsigned to return max. got", b)
+       }
+}
diff --git a/common/path.go b/common/path.go
new file mode 100644 (file)
index 0000000..cbcd13c
--- /dev/null
@@ -0,0 +1,74 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import (
+       "fmt"
+       "os"
+       "os/user"
+       "path/filepath"
+       "runtime"
+       "strings"
+)
+
+// MakeName creates a node name that follows the ethereum convention
+// for such names. It adds the operation system name and Go runtime version
+// the name.
+func MakeName(name, version string) string {
+       return fmt.Sprintf("%s/v%s/%s/%s", name, version, runtime.GOOS, runtime.Version())
+}
+
+func ExpandHomePath(p string) (path string) {
+       path = p
+       sep := string(os.PathSeparator)
+
+       // Check in case of paths like "/something/~/something/"
+       if len(p) > 1 && p[:1+len(sep)] == "~"+sep {
+               usr, _ := user.Current()
+               dir := usr.HomeDir
+
+               path = strings.Replace(p, "~", dir, 1)
+       }
+
+       return
+}
+
+func FileExist(filePath string) bool {
+       _, err := os.Stat(filePath)
+       if err != nil && os.IsNotExist(err) {
+               return false
+       }
+
+       return true
+}
+
+func AbsolutePath(Datadir string, filename string) string {
+       if filepath.IsAbs(filename) {
+               return filename
+       }
+       return filepath.Join(Datadir, filename)
+}
+
+func HomeDir() string {
+       if home := os.Getenv("HOME"); home != "" {
+               return home
+       }
+       if usr, err := user.Current(); err == nil {
+               return usr.HomeDir
+       }
+       return ""
+}
diff --git a/common/size.go b/common/size.go
new file mode 100644 (file)
index 0000000..9653b36
--- /dev/null
@@ -0,0 +1,89 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import (
+       "fmt"
+       "math/big"
+)
+
+type StorageSize float64
+
+func (self StorageSize) String() string {
+       if self > 1000000 {
+               return fmt.Sprintf("%.2f mB", self/1000000)
+       } else if self > 1000 {
+               return fmt.Sprintf("%.2f kB", self/1000)
+       } else {
+               return fmt.Sprintf("%.2f B", self)
+       }
+}
+
+func (self StorageSize) Int64() int64 {
+       return int64(self)
+}
+
+// The different number of units
+var (
+       Douglas  = BigPow(10, 42)
+       Einstein = BigPow(10, 21)
+       Ether    = BigPow(10, 18)
+       Finney   = BigPow(10, 15)
+       Szabo    = BigPow(10, 12)
+       Shannon  = BigPow(10, 9)
+       Babbage  = BigPow(10, 6)
+       Ada      = BigPow(10, 3)
+       Wei      = big.NewInt(1)
+)
+
+//
+// Currency to string
+// Returns a string representing a human readable format
+func CurrencyToString(num *big.Int) string {
+       var (
+               fin   *big.Int = num
+               denom string   = "Wei"
+       )
+
+       switch {
+       case num.Cmp(Ether) >= 0:
+               fin = new(big.Int).Div(num, Ether)
+               denom = "Ether"
+       case num.Cmp(Finney) >= 0:
+               fin = new(big.Int).Div(num, Finney)
+               denom = "Finney"
+       case num.Cmp(Szabo) >= 0:
+               fin = new(big.Int).Div(num, Szabo)
+               denom = "Szabo"
+       case num.Cmp(Shannon) >= 0:
+               fin = new(big.Int).Div(num, Shannon)
+               denom = "Shannon"
+       case num.Cmp(Babbage) >= 0:
+               fin = new(big.Int).Div(num, Babbage)
+               denom = "Babbage"
+       case num.Cmp(Ada) >= 0:
+               fin = new(big.Int).Div(num, Ada)
+               denom = "Ada"
+       }
+
+       // TODO add comment clarifying expected behavior
+       if len(fin.String()) > 5 {
+               return fmt.Sprintf("%sE%d %s", fin.String()[0:5], len(fin.String())-5, denom)
+       }
+
+       return fmt.Sprintf("%v %s", fin, denom)
+}
diff --git a/common/size_test.go b/common/size_test.go
new file mode 100644 (file)
index 0000000..ce19cab
--- /dev/null
@@ -0,0 +1,59 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import (
+       "math/big"
+
+       checker "gopkg.in/check.v1"
+)
+
+type SizeSuite struct{}
+
+var _ = checker.Suite(&SizeSuite{})
+
+func (s *SizeSuite) TestStorageSizeString(c *checker.C) {
+       data1 := 2381273
+       data2 := 2192
+       data3 := 12
+
+       exp1 := "2.38 mB"
+       exp2 := "2.19 kB"
+       exp3 := "12.00 B"
+
+       c.Assert(StorageSize(data1).String(), checker.Equals, exp1)
+       c.Assert(StorageSize(data2).String(), checker.Equals, exp2)
+       c.Assert(StorageSize(data3).String(), checker.Equals, exp3)
+}
+
+func (s *SizeSuite) TestCommon(c *checker.C) {
+       ether := CurrencyToString(BigPow(10, 19))
+       finney := CurrencyToString(BigPow(10, 16))
+       szabo := CurrencyToString(BigPow(10, 13))
+       shannon := CurrencyToString(BigPow(10, 10))
+       babbage := CurrencyToString(BigPow(10, 7))
+       ada := CurrencyToString(BigPow(10, 4))
+       wei := CurrencyToString(big.NewInt(10))
+
+       c.Assert(ether, checker.Equals, "10 Ether")
+       c.Assert(finney, checker.Equals, "10 Finney")
+       c.Assert(szabo, checker.Equals, "10 Szabo")
+       c.Assert(shannon, checker.Equals, "10 Shannon")
+       c.Assert(babbage, checker.Equals, "10 Babbage")
+       c.Assert(ada, checker.Equals, "10 Ada")
+       c.Assert(wei, checker.Equals, "10 Wei")
+}
diff --git a/common/test_utils.go b/common/test_utils.go
new file mode 100644 (file)
index 0000000..a848642
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import (
+       "encoding/json"
+       "fmt"
+       "io/ioutil"
+)
+
+// LoadJSON reads the given file and unmarshals its content.
+func LoadJSON(file string, val interface{}) error {
+       content, err := ioutil.ReadFile(file)
+       if err != nil {
+               return err
+       }
+       if err := json.Unmarshal(content, val); err != nil {
+               if syntaxerr, ok := err.(*json.SyntaxError); ok {
+                       line := findLine(content, syntaxerr.Offset)
+                       return fmt.Errorf("JSON syntax error at %v:%v: %v", file, line, err)
+               }
+               return fmt.Errorf("JSON unmarshal error in %v: %v", file, err)
+       }
+       return nil
+}
+
+// findLine returns the line number for the given offset into data.
+func findLine(data []byte, offset int64) (line int) {
+       line = 1
+       for i, r := range string(data) {
+               if int64(i) >= offset {
+                       return
+               }
+               if r == '\n' {
+                       line++
+               }
+       }
+       return
+}
diff --git a/common/types.go b/common/types.go
new file mode 100644 (file)
index 0000000..c4e78c7
--- /dev/null
@@ -0,0 +1,199 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import (
+       _"encoding/hex"
+       "encoding/json"
+       "errors"
+       "fmt"
+       "math/big"
+       "math/rand"
+       "reflect"
+       "strings"
+)
+
+const (
+       HashLength    = 32
+       AddressLength = 42
+       PubkeyHashLength = 20
+)
+
+var hashJsonLengthErr = errors.New("common: unmarshalJSON failed: hash must be exactly 32 bytes")
+
+type (
+       Hash    [HashLength]byte
+       Address [AddressLength]byte
+)
+
+func BytesToHash(b []byte) Hash {
+       var h Hash
+       h.SetBytes(b)
+       return h
+}
+func StringToHash(s string) Hash { return BytesToHash([]byte(s)) }
+func BigToHash(b *big.Int) Hash  { return BytesToHash(b.Bytes()) }
+func HexToHash(s string) Hash    { return BytesToHash(FromHex(s)) }
+
+
+// Don't use the default 'String' method in case we want to overwrite
+
+// Get the string representation of the underlying hash
+func (h Hash) Str() string   { return string(h[:]) }
+func (h Hash) Bytes() []byte { return h[:] }
+func (h Hash) Big() *big.Int { return Bytes2Big(h[:]) }
+func (h Hash) Hex() string   { return "0x" + Bytes2Hex(h[:]) }
+
+// UnmarshalJSON parses a hash in its hex from to a hash.
+func (h *Hash) UnmarshalJSON(input []byte) error {
+       length := len(input)
+       if length >= 2 && input[0] == '"' && input[length-1] == '"' {
+               input = input[1 : length-1]
+       }
+       // strip "0x" for length check
+       if len(input) > 1 && strings.ToLower(string(input[:2])) == "0x" {
+               input = input[2:]
+       }
+
+       // validate the length of the input hash
+       if len(input) != HashLength*2 {
+               return hashJsonLengthErr
+       }
+       h.SetBytes(FromHex(string(input)))
+       return nil
+}
+
+// Serialize given hash to JSON
+func (h Hash) MarshalJSON() ([]byte, error) {
+       return json.Marshal(h.Hex())
+}
+
+// Sets the hash to the value of b. If b is larger than len(h) it will panic
+func (h *Hash) SetBytes(b []byte) {
+       if len(b) > len(h) {
+               b = b[len(b)-HashLength:]
+       }
+
+       copy(h[HashLength-len(b):], b)
+}
+
+// Set string `s` to h. If s is larger than len(h) it will panic
+func (h *Hash) SetString(s string) { h.SetBytes([]byte(s)) }
+
+// Sets h to other
+func (h *Hash) Set(other Hash) {
+       for i, v := range other {
+               h[i] = v
+       }
+}
+
+// Generate implements testing/quick.Generator.
+func (h Hash) Generate(rand *rand.Rand, size int) reflect.Value {
+       m := rand.Intn(len(h))
+       for i := len(h) - 1; i > m; i-- {
+               h[i] = byte(rand.Uint32())
+       }
+       return reflect.ValueOf(h)
+}
+
+func EmptyHash(h Hash) bool {
+       return h == Hash{}
+}
+
+/////////// Address
+func BytesToAddress(b []byte) Address {
+       var a Address
+       a.SetBytes(b)
+       return a
+}
+func StringToAddress(s string) Address { return BytesToAddress([]byte(s)) }
+func BigToAddress(b *big.Int) Address  { return BytesToAddress(b.Bytes()) }
+func HexToAddress(s string) Address    { return BytesToAddress(FromHex(s)) }
+
+// IsHexAddress verifies whether a string can represent a valid hex-encoded
+// Ethereum address or not.
+func IsHexAddress(s string) bool {
+       if len(s) == 2+2*AddressLength && IsHex(s) {
+               return true
+       }
+       if len(s) == 2*AddressLength && IsHex("0x"+s) {
+               return true
+       }
+       return false
+}
+
+// Get the string representation of the underlying address
+func (a Address) Str() string   { return string(a[:]) }
+func (a Address) Bytes() []byte { return a[:] }
+func (a Address) Big() *big.Int { return Bytes2Big(a[:]) }
+func (a Address) Hash() Hash    { return BytesToHash(a[:]) }
+func (a Address) Hex() string   { return "0x" + Bytes2Hex(a[:]) }
+
+// Sets the address to the value of b. If b is larger than len(a) it will panic
+func (a *Address) SetBytes(b []byte) {
+       if len(b) > len(a) {
+               b = b[len(b)-AddressLength:]
+       }
+       copy(a[AddressLength-len(b):], b)
+}
+
+// Set string `s` to a. If s is larger than len(a) it will panic
+func (a *Address) SetString(s string) { a.SetBytes([]byte(s)) }
+
+// Sets a to other
+func (a *Address) Set(other Address) {
+       for i, v := range other {
+               a[i] = v
+       }
+}
+
+// Serialize given address to JSON
+func (a Address) MarshalJSON() ([]byte, error) {
+       return json.Marshal(a.Hex())
+}
+
+// Parse address from raw json data
+func (a *Address) UnmarshalJSON(data []byte) error {
+       if len(data) > 2 && data[0] == '"' && data[len(data)-1] == '"' {
+               data = data[1 : len(data)-1]
+       }
+
+       if len(data) != AddressLength {
+               return fmt.Errorf("Invalid address length, expected %d got %d bytes", AddressLength, len(data))
+       }
+
+       _, res, err := AddressDecode("bm", string(data))
+       if err != nil {
+               return err
+       }
+
+       if len(res) != PubkeyHashLength {
+               return fmt.Errorf("Invalid address")
+       }
+       a.Set(BytesToAddress(data))
+       return nil
+}
+
+// PP Pretty Prints a byte slice in the following format:
+//     hex(value[:4])...(hex[len(value)-4:])
+func PP(value []byte) string {
+       if len(value) <= 8 {
+               return Bytes2Hex(value)
+       }
+
+       return fmt.Sprintf("%x...%x", value[:4], value[len(value)-4])
+}
diff --git a/common/types_template.go b/common/types_template.go
new file mode 100644 (file)
index 0000000..8048f9c
--- /dev/null
@@ -0,0 +1,64 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build none
+//sed -e 's/_N_/Hash/g' -e 's/_S_/32/g' -e '1d' types_template.go | gofmt -w hash.go
+
+package common
+
+import "math/big"
+
+type _N_ [_S_]byte
+
+func BytesTo_N_(b []byte) _N_ {
+       var h _N_
+       h.SetBytes(b)
+       return h
+}
+func StringTo_N_(s string) _N_ { return BytesTo_N_([]byte(s)) }
+func BigTo_N_(b *big.Int) _N_  { return BytesTo_N_(b.Bytes()) }
+func HexTo_N_(s string) _N_    { return BytesTo_N_(FromHex(s)) }
+
+// Don't use the default 'String' method in case we want to overwrite
+
+// Get the string representation of the underlying hash
+func (h _N_) Str() string   { return string(h[:]) }
+func (h _N_) Bytes() []byte { return h[:] }
+func (h _N_) Big() *big.Int { return Bytes2Big(h[:]) }
+func (h _N_) Hex() string   { return "0x" + Bytes2Hex(h[:]) }
+
+// Sets the hash to the value of b. If b is larger than len(h) it will panic
+func (h *_N_) SetBytes(b []byte) {
+       // Use the right most bytes
+       if len(b) > len(h) {
+               b = b[len(b)-_S_:]
+       }
+
+       // Reverse the loop
+       for i := len(b) - 1; i >= 0; i-- {
+               h[_S_-len(b)+i] = b[i]
+       }
+}
+
+// Set string `s` to h. If s is larger than len(h) it will panic
+func (h *_N_) SetString(s string) { h.SetBytes([]byte(s)) }
+
+// Sets h to other
+func (h *_N_) Set(other _N_) {
+       for i, v := range other {
+               h[i] = v
+       }
+}
diff --git a/common/types_test.go b/common/types_test.go
new file mode 100644 (file)
index 0000000..de67cfc
--- /dev/null
@@ -0,0 +1,87 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package common
+
+import (
+       "math/big"
+       "testing"
+)
+
+func TestBytesConversion(t *testing.T) {
+       bytes := []byte{5}
+       hash := BytesToHash(bytes)
+
+       var exp Hash
+       exp[31] = 5
+
+       if hash != exp {
+               t.Errorf("expected %x got %x", exp, hash)
+       }
+}
+
+func TestHashJsonValidation(t *testing.T) {
+       var h Hash
+       var tests = []struct {
+               Prefix string
+               Size   int
+               Error  error
+       }{
+               {"", 2, hashJsonLengthErr},
+               {"", 62, hashJsonLengthErr},
+               {"", 66, hashJsonLengthErr},
+               {"", 65, hashJsonLengthErr},
+               {"0X", 64, nil},
+               {"0x", 64, nil},
+               {"0x", 62, hashJsonLengthErr},
+       }
+       for i, test := range tests {
+               if err := h.UnmarshalJSON(append([]byte(test.Prefix), make([]byte, test.Size)...)); err != test.Error {
+                       t.Errorf("test #%d: error mismatch: have %v, want %v", i, err, test.Error)
+               }
+       }
+}
+
+func TestAddressUnmarshalJSON(t *testing.T) {
+       var a Address
+       var tests = []struct {
+               Input     string
+               ShouldErr bool
+               Output    *big.Int
+       }{
+               {"", true, nil},
+               {`""`, true, nil},
+               {`"0x"`, true, nil},
+               {`"0x00"`, true, nil},
+               {`"0xG000000000000000000000000000000000000000"`, true, nil},
+               {`"0x0000000000000000000000000000000000000000"`, false, big.NewInt(0)},
+               {`"0x0000000000000000000000000000000000000010"`, false, big.NewInt(16)},
+       }
+       for i, test := range tests {
+               err := a.UnmarshalJSON([]byte(test.Input))
+               if err != nil && !test.ShouldErr {
+                       t.Errorf("test #%d: unexpected error: %v", i, err)
+               }
+               if err == nil {
+                       if test.ShouldErr {
+                               t.Errorf("test #%d: expected error, got none", i)
+                       }
+                       if a.Big().Cmp(test.Output) != 0 {
+                               t.Errorf("test #%d: address mismatch: have %v, want %v", i, a.Big(), test.Output)
+                       }
+               }
+       }
+}
diff --git a/crypto/crypto.go b/crypto/crypto.go
new file mode 100644 (file)
index 0000000..8c328d9
--- /dev/null
@@ -0,0 +1,94 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package crypto
+
+import (
+       "fmt"
+       //"io"
+       //"io/ioutil"
+       //"math/big"
+       //"os"
+
+       //"encoding/hex"
+       //"errors"
+
+       "bytom/common"
+       "golang.org/x/crypto/sha3"
+       "golang.org/x/crypto/ripemd160"
+)
+
+
+func Sha256(data ...[]byte) []byte {
+       d := sha3.New256()
+       for _, b := range data {
+               d.Write(b)
+       }
+       return d.Sum(nil)
+}
+
+func Sha256Hash(data ...[]byte) (h common.Hash) {
+       d := sha3.New256()
+       for _, b := range data {
+               d.Write(b)
+       }
+       d.Sum(h[:0])
+       return h
+}
+
+func Sha3(data ...[]byte) []byte          { return Sha256(data...) }
+func Sha3Hash(data ...[]byte) common.Hash { return Sha256Hash(data...) }
+
+
+func Ripemd160(data []byte) []byte {
+       ripemd := ripemd160.New()
+       ripemd.Write(data)
+
+       return ripemd.Sum(nil)
+}
+
+func PubkeyToAddress(pubBytes []byte) common.Address{
+       address, _ := common.AddressEncode("bm", 1, toInt(Ripemd160(Sha3(pubBytes))))
+       fmt.Printf(address)
+       return common.StringToAddress(address)
+}
+
+func AddressToPubkey(addr common.Address) (int, []byte, error) {
+       ver, data, err := common.AddressDecode("bm", addr.Str())
+       return ver, toBytes(data), err
+}
+
+func zeroBytes(bytes []byte) {
+       for i := range bytes {
+               bytes[i] = 0
+       }
+}
+
+func toInt(bytes []byte) []int{
+       ints := make([]int, len(bytes))
+       for i := range bytes {
+               ints[i] = int(bytes[i])
+       }
+       return ints
+}
+
+func toBytes(ints []int) []byte{
+       bytes := make([]byte, len(ints))
+       for i := range ints {
+               bytes[i] = byte(ints[i])
+       }
+       return bytes
+}
diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go
new file mode 100644 (file)
index 0000000..565de94
--- /dev/null
@@ -0,0 +1,266 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package crypto
+
+import (
+       _"bytes"
+       _"crypto/ecdsa"
+       _"encoding/hex"
+       _"fmt"
+       _"io/ioutil"
+       _"math/big"
+       _"os"
+       "testing"
+       _"time"
+       _"bytom/common"
+
+)
+
+var testAddrHex = "970e8128ab834e8eac17ab8e3812f010678cf791"
+var testPrivHex = "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"
+
+/*
+// These tests are sanity checks.
+// They should ensure that we don't e.g. use Sha3-224 instead of Sha3-256
+// and that the sha3 library uses keccak-f permutation.
+func TestSha3(t *testing.T) {
+       msg := []byte("abc")
+       exp, _ := hex.DecodeString("4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45")
+       checkhash(t, "Sha3-256", func(in []byte) []byte { return Sha3(in) }, msg, exp)
+}
+
+func TestSha3Hash(t *testing.T) {
+       msg := []byte("abc")
+       exp, _ := hex.DecodeString("4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45")
+       checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := Sha3Hash(in); return h[:] }, msg, exp)
+}
+
+func TestSha256(t *testing.T) {
+       msg := []byte("abc")
+       exp, _ := hex.DecodeString("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")
+       checkhash(t, "Sha256", Sha256, msg, exp)
+}
+
+func TestRipemd160(t *testing.T) {
+       msg := []byte("abc")
+       exp, _ := hex.DecodeString("8eb208f7e05d987a9b044a8e98c6b087f15a0bfc")
+       checkhash(t, "Ripemd160", Ripemd160, msg, exp)
+}
+
+func BenchmarkSha3(b *testing.B) {
+       a := []byte("hello world")
+       amount := 1000000
+       start := time.Now()
+       for i := 0; i < amount; i++ {
+               Keccak256(a)
+       }
+
+       fmt.Println(amount, ":", time.Since(start))
+}
+*/
+
+func TestAddressFormat(t *testing.T) {
+       addr := PubkeyToAddress([]byte("289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"))
+       t.Log("address:", addr.Str())
+       //addr = common.StringToAddress("bm1pyjkqc458pWy4W4H53a06fpl298260mvualj97g")
+       ver, data, _  := AddressToPubkey(addr)
+       t.Log("address:", ver, data)
+
+}
+
+/*
+func checkhash(t *testing.T, name string, f func([]byte) []byte, msg, exp []byte) {
+       sum := f(msg)
+       if bytes.Compare(exp, sum) != 0 {
+               t.Fatalf("hash %s mismatch: want: %x have: %x", name, exp, sum)
+       }
+}
+*/
+
+/*
+func Test0Key(t *testing.T) {
+       key := common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")
+       _, err := secp256k1.GeneratePubKey(key)
+       if err == nil {
+               t.Errorf("expected error due to zero privkey")
+       }
+}
+
+func TestSign(t *testing.T) {
+       key, _ := HexToECDSA(testPrivHex)
+       addr := common.HexToAddress(testAddrHex)
+
+       msg := Keccak256([]byte("foo"))
+       sig, err := Sign(msg, key)
+       if err != nil {
+               t.Errorf("Sign error: %s", err)
+       }
+       recoveredPub, err := Ecrecover(msg, sig)
+       if err != nil {
+               t.Errorf("ECRecover error: %s", err)
+       }
+       recoveredAddr := PubkeyToAddress(*ToECDSAPub(recoveredPub))
+       if addr != recoveredAddr {
+               t.Errorf("Address mismatch: want: %x have: %x", addr, recoveredAddr)
+       }
+
+       // should be equal to SigToPub
+       recoveredPub2, err := SigToPub(msg, sig)
+       if err != nil {
+               t.Errorf("ECRecover error: %s", err)
+       }
+       recoveredAddr2 := PubkeyToAddress(*recoveredPub2)
+       if addr != recoveredAddr2 {
+               t.Errorf("Address mismatch: want: %x have: %x", addr, recoveredAddr2)
+       }
+
+}
+
+func TestInvalidSign(t *testing.T) {
+       _, err := Sign(make([]byte, 1), nil)
+       if err == nil {
+               t.Errorf("expected sign with hash 1 byte to error")
+       }
+
+       _, err = Sign(make([]byte, 33), nil)
+       if err == nil {
+               t.Errorf("expected sign with hash 33 byte to error")
+       }
+}
+
+func TestNewContractAddress(t *testing.T) {
+       key, _ := HexToECDSA(testPrivHex)
+       addr := common.HexToAddress(testAddrHex)
+       genAddr := PubkeyToAddress(key.PublicKey)
+       // sanity check before using addr to create contract address
+       checkAddr(t, genAddr, addr)
+
+       caddr0 := CreateAddress(addr, 0)
+       caddr1 := CreateAddress(addr, 1)
+       caddr2 := CreateAddress(addr, 2)
+       checkAddr(t, common.HexToAddress("333c3310824b7c685133f2bedb2ca4b8b4df633d"), caddr0)
+       checkAddr(t, common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"), caddr1)
+       checkAddr(t, common.HexToAddress("c9ddedf451bc62ce88bf9292afb13df35b670699"), caddr2)
+}
+
+func TestLoadECDSAFile(t *testing.T) {
+       keyBytes := common.FromHex(testPrivHex)
+       fileName0 := "test_key0"
+       fileName1 := "test_key1"
+       checkKey := func(k *ecdsa.PrivateKey) {
+               checkAddr(t, PubkeyToAddress(k.PublicKey), common.HexToAddress(testAddrHex))
+               loadedKeyBytes := FromECDSA(k)
+               if !bytes.Equal(loadedKeyBytes, keyBytes) {
+                       t.Fatalf("private key mismatch: want: %x have: %x", keyBytes, loadedKeyBytes)
+               }
+       }
+
+       ioutil.WriteFile(fileName0, []byte(testPrivHex), 0600)
+       defer os.Remove(fileName0)
+
+       key0, err := LoadECDSA(fileName0)
+       if err != nil {
+               t.Fatal(err)
+       }
+       checkKey(key0)
+
+       // again, this time with SaveECDSA instead of manual save:
+       err = SaveECDSA(fileName1, key0)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.Remove(fileName1)
+
+       key1, err := LoadECDSA(fileName1)
+       if err != nil {
+               t.Fatal(err)
+       }
+       checkKey(key1)
+}
+
+func TestValidateSignatureValues(t *testing.T) {
+       check := func(expected bool, v byte, r, s *big.Int) {
+               if ValidateSignatureValues(v, r, s, false) != expected {
+                       t.Errorf("mismatch for v: %d r: %d s: %d want: %v", v, r, s, expected)
+               }
+       }
+       minusOne := big.NewInt(-1)
+       one := common.Big1
+       zero := common.Big0
+       secp256k1nMinus1 := new(big.Int).Sub(secp256k1.N, common.Big1)
+
+       // correct v,r,s
+       check(true, 27, one, one)
+       check(true, 28, one, one)
+       // incorrect v, correct r,s,
+       check(false, 30, one, one)
+       check(false, 26, one, one)
+
+       // incorrect v, combinations of incorrect/correct r,s at lower limit
+       check(false, 0, zero, zero)
+       check(false, 0, zero, one)
+       check(false, 0, one, zero)
+       check(false, 0, one, one)
+
+       // correct v for any combination of incorrect r,s
+       check(false, 27, zero, zero)
+       check(false, 27, zero, one)
+       check(false, 27, one, zero)
+
+       check(false, 28, zero, zero)
+       check(false, 28, zero, one)
+       check(false, 28, one, zero)
+
+       // correct sig with max r,s
+       check(true, 27, secp256k1nMinus1, secp256k1nMinus1)
+       // correct v, combinations of incorrect r,s at upper limit
+       check(false, 27, secp256k1.N, secp256k1nMinus1)
+       check(false, 27, secp256k1nMinus1, secp256k1.N)
+       check(false, 27, secp256k1.N, secp256k1.N)
+
+       // current callers ensures r,s cannot be negative, but let's test for that too
+       // as crypto package could be used stand-alone
+       check(false, 27, minusOne, one)
+       check(false, 27, one, minusOne)
+}
+
+
+
+func checkAddr(t *testing.T, addr0, addr1 common.Address) {
+       if addr0 != addr1 {
+               t.Fatalf("address mismatch: want: %x have: %x", addr0, addr1)
+       }
+}
+
+// test to help Python team with integration of libsecp256k1
+// skip but keep it after they are done
+func TestPythonIntegration(t *testing.T) {
+       kh := "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"
+       k0, _ := HexToECDSA(kh)
+       k1 := FromECDSA(k0)
+
+       msg0 := Keccak256([]byte("foo"))
+       sig0, _ := secp256k1.Sign(msg0, k1)
+
+       msg1 := common.FromHex("00000000000000000000000000000000")
+       sig1, _ := secp256k1.Sign(msg0, k1)
+
+       fmt.Printf("msg: %x, privkey: %x sig: %x\n", msg0, k1, sig0)
+       fmt.Printf("msg: %x, privkey: %x sig: %x\n", msg1, k1, sig1)
+}
+
+*/
\ No newline at end of file
index 5066f35..b82a6b6 100644 (file)
@@ -7,8 +7,8 @@ import (
        "hash"
        "io"
 
-       "github.com/bytom/crypto/ed25519"
-       "github.com/bytom/crypto/ed25519/internal/edwards25519"
+       "bytom/crypto/ed25519"
+       "bytom/crypto/ed25519/internal/edwards25519"
 )
 
 type (
@@ -31,7 +31,7 @@ func NewXPrv(r io.Reader) (xprv XPrv, err error) {
                return xprv, err
        }
        hasher := sha512.New()
-       hasher.Write([]byte("Chain seed"))
+       hasher.Write([]byte("Bytom seed"))
        hasher.Write(entropy[:])
        hasher.Sum(xprv[:0])
        modifyScalar(xprv[:32])
index 194b20e..b880a92 100644 (file)
@@ -3,7 +3,7 @@ package chainkd
 import (
        "io"
 
-       "github.com/bytom/crypto/ed25519"
+       "bytom/crypto/ed25519"
 )
 
 // Utility functions
index 9ec0d75..23cfeae 100644 (file)
@@ -3,7 +3,7 @@ package ecmath
 import (
        "crypto/subtle"
 
-       "github.com/bytom/crypto/ed25519/internal/edwards25519"
+       "github.com/blockchain/crypto/ed25519/internal/edwards25519"
 )
 
 // Point is a point on the ed25519 curve.
index 651f2ca..b51be2a 100644 (file)
@@ -3,7 +3,7 @@ package ecmath
 import (
        "crypto/subtle"
 
-       "github.com/bytom/crypto/ed25519/internal/edwards25519"
+       "bytom/crypto/ed25519/internal/edwards25519"
 )
 
 // Scalar is a 256-bit little-endian scalar.
index a25bc57..6e1b786 100644 (file)
@@ -22,7 +22,7 @@ import (
        "io"
        "strconv"
 
-       "github.com/bytom/crypto/ed25519/internal/edwards25519"
+       "bytom/crypto/ed25519/internal/edwards25519"
 )
 
 const (
index 68e14a4..8233b44 100644 (file)
@@ -16,7 +16,7 @@ import (
        "strings"
        "testing"
 
-       "github.com/bytom/crypto/ed25519/internal/edwards25519"
+       "bytom/crypto/ed25519/internal/edwards25519"
 )
 
 type zeroReader struct{}
diff --git a/crypto/randentropy/rand_entropy.go b/crypto/randentropy/rand_entropy.go
new file mode 100644 (file)
index 0000000..539d3ac
--- /dev/null
@@ -0,0 +1,42 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package randentropy
+
+import (
+       crand "crypto/rand"
+       "io"
+)
+
+var Reader io.Reader = &randEntropy{}
+
+type randEntropy struct {
+}
+
+func (*randEntropy) Read(bytes []byte) (n int, err error) {
+       readBytes := GetEntropyCSPRNG(len(bytes))
+       copy(bytes, readBytes)
+       return len(bytes), nil
+}
+
+func GetEntropyCSPRNG(n int) []byte {
+       mainBuff := make([]byte, n)
+       _, err := io.ReadFull(crand.Reader, mainBuff)
+       if err != nil {
+               panic("reading from crypto/rand failed: " + err.Error())
+       }
+       return mainBuff
+}