--- /dev/null
+//+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
--- /dev/null
+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])
+ }
+ }
+}
--- /dev/null
+// 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()
+}
--- /dev/null
+// 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)
+}
--- /dev/null
+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)
+}
--- /dev/null
+// 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
--- /dev/null
+// 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)
+}
--- /dev/null
+// 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
--- /dev/null
+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)
+ }
+}
--- /dev/null
+{"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
--- /dev/null
+{"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
--- /dev/null
+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)
--- /dev/null
+{"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
--- /dev/null
+{"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
--- /dev/null
+{"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
--- /dev/null
+{"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
--- /dev/null
+{"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
--- /dev/null
+{"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
--- /dev/null
+{
+ "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"
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+}
--- /dev/null
+// 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
+ }
+ }
+ }
+}
--- /dev/null
+// 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() {}
--- /dev/null
+//+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))}
+}
--- /dev/null
+//+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))}
+}
--- /dev/null
+# 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)
+```
--- /dev/null
+// 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
--- /dev/null
+// 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)
+ }
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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)
+ }
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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)
+ }
+}
--- /dev/null
+// 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)
+}
--- /dev/null
+// 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 ""
+}
--- /dev/null
+// 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, "")
+}
--- /dev/null
+// 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.")
+ }
+}
--- /dev/null
+// 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)
+}
--- /dev/null
+// 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) }
--- /dev/null
+// 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()
+}
--- /dev/null
+// 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)
+}
--- /dev/null
+// 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
+)
--- /dev/null
+// 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)
+ }
+}
--- /dev/null
+// 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 ""
+}
--- /dev/null
+// 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)
+}
--- /dev/null
+// 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")
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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])
+}
--- /dev/null
+// 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
+ }
+}
--- /dev/null
+// 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)
+ }
+ }
+ }
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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
"hash"
"io"
- "github.com/bytom/crypto/ed25519"
- "github.com/bytom/crypto/ed25519/internal/edwards25519"
+ "bytom/crypto/ed25519"
+ "bytom/crypto/ed25519/internal/edwards25519"
)
type (
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])
import (
"io"
- "github.com/bytom/crypto/ed25519"
+ "bytom/crypto/ed25519"
)
// Utility functions
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.
import (
"crypto/subtle"
- "github.com/bytom/crypto/ed25519/internal/edwards25519"
+ "bytom/crypto/ed25519/internal/edwards25519"
)
// Scalar is a 256-bit little-endian scalar.
"io"
"strconv"
- "github.com/bytom/crypto/ed25519/internal/edwards25519"
+ "bytom/crypto/ed25519/internal/edwards25519"
)
const (
"strings"
"testing"
- "github.com/bytom/crypto/ed25519/internal/edwards25519"
+ "bytom/crypto/ed25519/internal/edwards25519"
)
type zeroReader struct{}
--- /dev/null
+// 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
+}