aboutsummaryrefslogtreecommitdiff
path: root/accounts
diff options
context:
space:
mode:
Diffstat (limited to 'accounts')
-rw-r--r--accounts/abi/abi.go192
-rw-r--r--accounts/abi/argument.go365
-rw-r--r--accounts/abi/bind/auth.go96
-rw-r--r--accounts/abi/bind/backend.go112
-rw-r--r--accounts/abi/bind/backends/simulated.go523
-rw-r--r--accounts/abi/bind/base.go366
-rw-r--r--accounts/abi/bind/bind.go558
-rw-r--r--accounts/abi/bind/template.go616
-rw-r--r--accounts/abi/bind/topics.go241
-rw-r--r--accounts/abi/bind/util.go76
-rw-r--r--accounts/abi/doc.go26
-rw-r--r--accounts/abi/error.go84
-rw-r--r--accounts/abi/event.go77
-rw-r--r--accounts/abi/method.go90
-rw-r--r--accounts/abi/numbers.go44
-rw-r--r--accounts/abi/pack.go81
-rw-r--r--accounts/abi/reflect.go226
-rw-r--r--accounts/abi/type.go348
-rw-r--r--accounts/abi/unpack.go295
-rw-r--r--accounts/accounts.go222
-rw-r--r--accounts/errors.go68
-rw-r--r--accounts/external/backend.go231
-rw-r--r--accounts/hd.go152
-rw-r--r--accounts/keystore/account_cache.go301
-rw-r--r--accounts/keystore/file_cache.go102
-rw-r--r--accounts/keystore/key.go232
-rw-r--r--accounts/keystore/keystore.go495
-rw-r--r--accounts/keystore/passphrase.go356
-rw-r--r--accounts/keystore/plain.go61
-rw-r--r--accounts/keystore/presale.go147
-rw-r--r--accounts/keystore/testdata/dupes/11
-rw-r--r--accounts/keystore/testdata/dupes/21
-rw-r--r--accounts/keystore/testdata/dupes/foo1
-rw-r--r--accounts/keystore/testdata/keystore/.hiddenfile1
-rw-r--r--accounts/keystore/testdata/keystore/README21
-rw-r--r--accounts/keystore/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef81
-rw-r--r--accounts/keystore/testdata/keystore/aaa1
-rw-r--r--accounts/keystore/testdata/keystore/empty0
-rw-r--r--accounts/keystore/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e1
-rw-r--r--accounts/keystore/testdata/keystore/garbagebin0 -> 300 bytes
-rw-r--r--accounts/keystore/testdata/keystore/no-address1
-rw-r--r--accounts/keystore/testdata/keystore/zero1
-rw-r--r--accounts/keystore/testdata/keystore/zzz1
-rw-r--r--accounts/keystore/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e1
-rw-r--r--accounts/keystore/testdata/v1_test_vector.json28
-rw-r--r--accounts/keystore/testdata/v3_test_vector.json97
-rw-r--r--accounts/keystore/testdata/very-light-scrypt.json1
-rw-r--r--accounts/keystore/wallet.go148
-rw-r--r--accounts/keystore/watch.go108
-rw-r--r--accounts/keystore/watch_fallback.go28
-rw-r--r--accounts/manager.go229
-rw-r--r--accounts/scwallet/README.md102
-rw-r--r--accounts/scwallet/apdu.go87
-rw-r--r--accounts/scwallet/hub.go302
-rw-r--r--accounts/scwallet/securechannel.go346
-rw-r--r--accounts/scwallet/wallet.go1082
-rw-r--r--accounts/sort.go31
-rw-r--r--accounts/url.go104
-rw-r--r--accounts/usbwallet/hub.go279
-rw-r--r--accounts/usbwallet/ledger.go465
-rw-r--r--accounts/usbwallet/trezor.go365
-rw-r--r--accounts/usbwallet/trezor/messages-common.pb.go811
-rw-r--r--accounts/usbwallet/trezor/messages-common.proto147
-rw-r--r--accounts/usbwallet/trezor/messages-ethereum.pb.go698
-rw-r--r--accounts/usbwallet/trezor/messages-ethereum.proto131
-rw-r--r--accounts/usbwallet/trezor/messages-management.pb.go1621
-rw-r--r--accounts/usbwallet/trezor/messages-management.proto289
-rw-r--r--accounts/usbwallet/trezor/messages.pb.go889
-rw-r--r--accounts/usbwallet/trezor/messages.proto264
-rw-r--r--accounts/usbwallet/trezor/trezor.go70
-rw-r--r--accounts/usbwallet/wallet.go595
71 files changed, 16132 insertions, 0 deletions
diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
new file mode 100644
index 0000000..7af6685
--- /dev/null
+++ b/accounts/abi/abi.go
@@ -0,0 +1,192 @@
+// 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 abi
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+
+ "github.com/ava-labs/go-ethereum/common"
+)
+
+// The ABI holds information about a contract's context and available
+// invokable methods. It will allow you to type check function calls and
+// packs data accordingly.
+type ABI struct {
+ Constructor Method
+ Methods map[string]Method
+ Events map[string]Event
+}
+
+// JSON returns a parsed ABI interface and error if it failed.
+func JSON(reader io.Reader) (ABI, error) {
+ dec := json.NewDecoder(reader)
+
+ var abi ABI
+ if err := dec.Decode(&abi); err != nil {
+ return ABI{}, err
+ }
+
+ return abi, nil
+}
+
+// Pack the given method name to conform the ABI. Method call's data
+// will consist of method_id, args0, arg1, ... argN. Method id consists
+// of 4 bytes and arguments are all 32 bytes.
+// Method ids are created from the first 4 bytes of the hash of the
+// methods string signature. (signature = baz(uint32,string32))
+func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
+ // Fetch the ABI of the requested method
+ if name == "" {
+ // constructor
+ arguments, err := abi.Constructor.Inputs.Pack(args...)
+ if err != nil {
+ return nil, err
+ }
+ return arguments, nil
+ }
+ method, exist := abi.Methods[name]
+ if !exist {
+ return nil, fmt.Errorf("method '%s' not found", name)
+ }
+ arguments, err := method.Inputs.Pack(args...)
+ if err != nil {
+ return nil, err
+ }
+ // Pack up the method ID too if not a constructor and return
+ return append(method.ID(), arguments...), nil
+}
+
+// Unpack output in v according to the abi specification
+func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) {
+ if len(data) == 0 {
+ return fmt.Errorf("abi: unmarshalling empty output")
+ }
+ // since there can't be naming collisions with contracts and events,
+ // we need to decide whether we're calling a method or an event
+ if method, ok := abi.Methods[name]; ok {
+ if len(data)%32 != 0 {
+ return fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data)
+ }
+ return method.Outputs.Unpack(v, data)
+ }
+ if event, ok := abi.Events[name]; ok {
+ return event.Inputs.Unpack(v, data)
+ }
+ return fmt.Errorf("abi: could not locate named method or event")
+}
+
+// UnpackIntoMap unpacks a log into the provided map[string]interface{}
+func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
+ if len(data) == 0 {
+ return fmt.Errorf("abi: unmarshalling empty output")
+ }
+ // since there can't be naming collisions with contracts and events,
+ // we need to decide whether we're calling a method or an event
+ if method, ok := abi.Methods[name]; ok {
+ if len(data)%32 != 0 {
+ return fmt.Errorf("abi: improperly formatted output")
+ }
+ return method.Outputs.UnpackIntoMap(v, data)
+ }
+ if event, ok := abi.Events[name]; ok {
+ return event.Inputs.UnpackIntoMap(v, data)
+ }
+ return fmt.Errorf("abi: could not locate named method or event")
+}
+
+// UnmarshalJSON implements json.Unmarshaler interface
+func (abi *ABI) UnmarshalJSON(data []byte) error {
+ var fields []struct {
+ Type string
+ Name string
+ Constant bool
+ Anonymous bool
+ Inputs []Argument
+ Outputs []Argument
+ }
+ if err := json.Unmarshal(data, &fields); err != nil {
+ return err
+ }
+ abi.Methods = make(map[string]Method)
+ abi.Events = make(map[string]Event)
+ for _, field := range fields {
+ switch field.Type {
+ case "constructor":
+ abi.Constructor = Method{
+ Inputs: field.Inputs,
+ }
+ // empty defaults to function according to the abi spec
+ case "function", "":
+ name := field.Name
+ _, ok := abi.Methods[name]
+ for idx := 0; ok; idx++ {
+ name = fmt.Sprintf("%s%d", field.Name, idx)
+ _, ok = abi.Methods[name]
+ }
+ abi.Methods[name] = Method{
+ Name: name,
+ RawName: field.Name,
+ Const: field.Constant,
+ Inputs: field.Inputs,
+ Outputs: field.Outputs,
+ }
+ case "event":
+ name := field.Name
+ _, ok := abi.Events[name]
+ for idx := 0; ok; idx++ {
+ name = fmt.Sprintf("%s%d", field.Name, idx)
+ _, ok = abi.Events[name]
+ }
+ abi.Events[name] = Event{
+ Name: name,
+ RawName: field.Name,
+ Anonymous: field.Anonymous,
+ Inputs: field.Inputs,
+ }
+ }
+ }
+
+ return nil
+}
+
+// MethodById looks up a method by the 4-byte id
+// returns nil if none found
+func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
+ if len(sigdata) < 4 {
+ return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
+ }
+ for _, method := range abi.Methods {
+ if bytes.Equal(method.ID(), sigdata[:4]) {
+ return &method, nil
+ }
+ }
+ return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
+}
+
+// EventByID looks an event up by its topic hash in the
+// ABI and returns nil if none found.
+func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
+ for _, event := range abi.Events {
+ if bytes.Equal(event.ID().Bytes(), topic.Bytes()) {
+ return &event, nil
+ }
+ }
+ return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
+}
diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go
new file mode 100644
index 0000000..4dae586
--- /dev/null
+++ b/accounts/abi/argument.go
@@ -0,0 +1,365 @@
+// 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 abi
+
+import (
+ "encoding/json"
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// Argument holds the name of the argument and the corresponding type.
+// Types are used when packing and testing arguments.
+type Argument struct {
+ Name string
+ Type Type
+ Indexed bool // indexed is only used by events
+}
+
+type Arguments []Argument
+
+type ArgumentMarshaling struct {
+ Name string
+ Type string
+ Components []ArgumentMarshaling
+ Indexed bool
+}
+
+// UnmarshalJSON implements json.Unmarshaler interface
+func (argument *Argument) UnmarshalJSON(data []byte) error {
+ var arg ArgumentMarshaling
+ err := json.Unmarshal(data, &arg)
+ if err != nil {
+ return fmt.Errorf("argument json err: %v", err)
+ }
+
+ argument.Type, err = NewType(arg.Type, arg.Components)
+ if err != nil {
+ return err
+ }
+ argument.Name = arg.Name
+ argument.Indexed = arg.Indexed
+
+ return nil
+}
+
+// LengthNonIndexed returns the number of arguments when not counting 'indexed' ones. Only events
+// can ever have 'indexed' arguments, it should always be false on arguments for method input/output
+func (arguments Arguments) LengthNonIndexed() int {
+ out := 0
+ for _, arg := range arguments {
+ if !arg.Indexed {
+ out++
+ }
+ }
+ return out
+}
+
+// NonIndexed returns the arguments with indexed arguments filtered out
+func (arguments Arguments) NonIndexed() Arguments {
+ var ret []Argument
+ for _, arg := range arguments {
+ if !arg.Indexed {
+ ret = append(ret, arg)
+ }
+ }
+ return ret
+}
+
+// isTuple returns true for non-atomic constructs, like (uint,uint) or uint[]
+func (arguments Arguments) isTuple() bool {
+ return len(arguments) > 1
+}
+
+// Unpack performs the operation hexdata -> Go format
+func (arguments Arguments) Unpack(v interface{}, data []byte) error {
+ // make sure the passed value is arguments pointer
+ if reflect.Ptr != reflect.ValueOf(v).Kind() {
+ return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
+ }
+ marshalledValues, err := arguments.UnpackValues(data)
+ if err != nil {
+ return err
+ }
+ if arguments.isTuple() {
+ return arguments.unpackTuple(v, marshalledValues)
+ }
+ return arguments.unpackAtomic(v, marshalledValues[0])
+}
+
+// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value
+func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
+ marshalledValues, err := arguments.UnpackValues(data)
+ if err != nil {
+ return err
+ }
+
+ return arguments.unpackIntoMap(v, marshalledValues)
+}
+
+// unpack sets the unmarshalled value to go format.
+// Note the dst here must be settable.
+func unpack(t *Type, dst interface{}, src interface{}) error {
+ var (
+ dstVal = reflect.ValueOf(dst).Elem()
+ srcVal = reflect.ValueOf(src)
+ )
+ tuple, typ := false, t
+ for {
+ if typ.T == SliceTy || typ.T == ArrayTy {
+ typ = typ.Elem
+ continue
+ }
+ tuple = typ.T == TupleTy
+ break
+ }
+ if !tuple {
+ return set(dstVal, srcVal)
+ }
+
+ // Dereferences interface or pointer wrapper
+ dstVal = indirectInterfaceOrPtr(dstVal)
+
+ switch t.T {
+ case TupleTy:
+ if dstVal.Kind() != reflect.Struct {
+ return fmt.Errorf("abi: invalid dst value for unpack, want struct, got %s", dstVal.Kind())
+ }
+ fieldmap, err := mapArgNamesToStructFields(t.TupleRawNames, dstVal)
+ if err != nil {
+ return err
+ }
+ for i, elem := range t.TupleElems {
+ fname := fieldmap[t.TupleRawNames[i]]
+ field := dstVal.FieldByName(fname)
+ if !field.IsValid() {
+ return fmt.Errorf("abi: field %s can't found in the given value", t.TupleRawNames[i])
+ }
+ if err := unpack(elem, field.Addr().Interface(), srcVal.Field(i).Interface()); err != nil {
+ return err
+ }
+ }
+ return nil
+ case SliceTy:
+ if dstVal.Kind() != reflect.Slice {
+ return fmt.Errorf("abi: invalid dst value for unpack, want slice, got %s", dstVal.Kind())
+ }
+ slice := reflect.MakeSlice(dstVal.Type(), srcVal.Len(), srcVal.Len())
+ for i := 0; i < slice.Len(); i++ {
+ if err := unpack(t.Elem, slice.Index(i).Addr().Interface(), srcVal.Index(i).Interface()); err != nil {
+ return err
+ }
+ }
+ dstVal.Set(slice)
+ case ArrayTy:
+ if dstVal.Kind() != reflect.Array {
+ return fmt.Errorf("abi: invalid dst value for unpack, want array, got %s", dstVal.Kind())
+ }
+ array := reflect.New(dstVal.Type()).Elem()
+ for i := 0; i < array.Len(); i++ {
+ if err := unpack(t.Elem, array.Index(i).Addr().Interface(), srcVal.Index(i).Interface()); err != nil {
+ return err
+ }
+ }
+ dstVal.Set(array)
+ }
+ return nil
+}
+
+// unpackIntoMap unpacks marshalledValues into the provided map[string]interface{}
+func (arguments Arguments) unpackIntoMap(v map[string]interface{}, marshalledValues []interface{}) error {
+ // Make sure map is not nil
+ if v == nil {
+ return fmt.Errorf("abi: cannot unpack into a nil map")
+ }
+
+ for i, arg := range arguments.NonIndexed() {
+ v[arg.Name] = marshalledValues[i]
+ }
+ return nil
+}
+
+// unpackAtomic unpacks ( hexdata -> go ) a single value
+func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error {
+ if arguments.LengthNonIndexed() == 0 {
+ return nil
+ }
+ argument := arguments.NonIndexed()[0]
+ elem := reflect.ValueOf(v).Elem()
+
+ if elem.Kind() == reflect.Struct && argument.Type.T != TupleTy {
+ fieldmap, err := mapArgNamesToStructFields([]string{argument.Name}, elem)
+ if err != nil {
+ return err
+ }
+ field := elem.FieldByName(fieldmap[argument.Name])
+ if !field.IsValid() {
+ return fmt.Errorf("abi: field %s can't be found in the given value", argument.Name)
+ }
+ return unpack(&argument.Type, field.Addr().Interface(), marshalledValues)
+ }
+ return unpack(&argument.Type, elem.Addr().Interface(), marshalledValues)
+}
+
+// unpackTuple unpacks ( hexdata -> go ) a batch of values.
+func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
+ var (
+ value = reflect.ValueOf(v).Elem()
+ typ = value.Type()
+ kind = value.Kind()
+ )
+ if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
+ return err
+ }
+
+ // If the interface is a struct, get of abi->struct_field mapping
+ var abi2struct map[string]string
+ if kind == reflect.Struct {
+ var (
+ argNames []string
+ err error
+ )
+ for _, arg := range arguments.NonIndexed() {
+ argNames = append(argNames, arg.Name)
+ }
+ abi2struct, err = mapArgNamesToStructFields(argNames, value)
+ if err != nil {
+ return err
+ }
+ }
+ for i, arg := range arguments.NonIndexed() {
+ switch kind {
+ case reflect.Struct:
+ field := value.FieldByName(abi2struct[arg.Name])
+ if !field.IsValid() {
+ return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
+ }
+ if err := unpack(&arg.Type, field.Addr().Interface(), marshalledValues[i]); err != nil {
+ return err
+ }
+ case reflect.Slice, reflect.Array:
+ if value.Len() < i {
+ return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
+ }
+ v := value.Index(i)
+ if err := requireAssignable(v, reflect.ValueOf(marshalledValues[i])); err != nil {
+ return err
+ }
+ if err := unpack(&arg.Type, v.Addr().Interface(), marshalledValues[i]); err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", typ)
+ }
+ }
+ return nil
+
+}
+
+// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
+// without supplying a struct to unpack into. Instead, this method returns a list containing the
+// values. An atomic argument will be a list with one element.
+func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
+ retval := make([]interface{}, 0, arguments.LengthNonIndexed())
+ virtualArgs := 0
+ for index, arg := range arguments.NonIndexed() {
+ marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
+ if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
+ // If we have a static array, like [3]uint256, these are coded as
+ // just like uint256,uint256,uint256.
+ // This means that we need to add two 'virtual' arguments when
+ // we count the index from now on.
+ //
+ // Array values nested multiple levels deep are also encoded inline:
+ // [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256
+ //
+ // Calculate the full array size to get the correct offset for the next argument.
+ // Decrement it by 1, as the normal index increment is still applied.
+ virtualArgs += getTypeSize(arg.Type)/32 - 1
+ } else if arg.Type.T == TupleTy && !isDynamicType(arg.Type) {
+ // If we have a static tuple, like (uint256, bool, uint256), these are
+ // coded as just like uint256,bool,uint256
+ virtualArgs += getTypeSize(arg.Type)/32 - 1
+ }
+ if err != nil {
+ return nil, err
+ }
+ retval = append(retval, marshalledValue)
+ }
+ return retval, nil
+}
+
+// PackValues performs the operation Go format -> Hexdata
+// It is the semantic opposite of UnpackValues
+func (arguments Arguments) PackValues(args []interface{}) ([]byte, error) {
+ return arguments.Pack(args...)
+}
+
+// Pack performs the operation Go format -> Hexdata
+func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
+ // Make sure arguments match up and pack them
+ abiArgs := arguments
+ if len(args) != len(abiArgs) {
+ return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(abiArgs))
+ }
+ // variable input is the output appended at the end of packed
+ // output. This is used for strings and bytes types input.
+ var variableInput []byte
+
+ // input offset is the bytes offset for packed output
+ inputOffset := 0
+ for _, abiArg := range abiArgs {
+ inputOffset += getTypeSize(abiArg.Type)
+ }
+ var ret []byte
+ for i, a := range args {
+ input := abiArgs[i]
+ // pack the input
+ packed, err := input.Type.pack(reflect.ValueOf(a))
+ if err != nil {
+ return nil, err
+ }
+ // check for dynamic types
+ if isDynamicType(input.Type) {
+ // set the offset
+ ret = append(ret, packNum(reflect.ValueOf(inputOffset))...)
+ // calculate next offset
+ inputOffset += len(packed)
+ // append to variable input
+ variableInput = append(variableInput, packed...)
+ } else {
+ // append the packed value to the input
+ ret = append(ret, packed...)
+ }
+ }
+ // append the variable input at the end of the packed input
+ ret = append(ret, variableInput...)
+
+ return ret, nil
+}
+
+// ToCamelCase converts an under-score string to a camel-case string
+func ToCamelCase(input string) string {
+ parts := strings.Split(input, "_")
+ for i, s := range parts {
+ if len(s) > 0 {
+ parts[i] = strings.ToUpper(s[:1]) + s[1:]
+ }
+ }
+ return strings.Join(parts, "")
+}
diff --git a/accounts/abi/bind/auth.go b/accounts/abi/bind/auth.go
new file mode 100644
index 0000000..c916cc4
--- /dev/null
+++ b/accounts/abi/bind/auth.go
@@ -0,0 +1,96 @@
+// 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 bind
+
+import (
+ "crypto/ecdsa"
+ "errors"
+ "io"
+ "io/ioutil"
+
+ "github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/coreth/accounts/external"
+ "github.com/ava-labs/coreth/accounts/keystore"
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/crypto"
+)
+
+// NewTransactor is a utility method to easily create a transaction signer from
+// an encrypted json key stream and the associated passphrase.
+func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
+ json, err := ioutil.ReadAll(keyin)
+ if err != nil {
+ return nil, err
+ }
+ key, err := keystore.DecryptKey(json, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ return NewKeyedTransactor(key.PrivateKey), nil
+}
+
+// NewKeyStoreTransactor is a utility method to easily create a transaction signer from
+// an decrypted key from a keystore
+func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) {
+ return &TransactOpts{
+ From: account.Address,
+ Signer: func(signer types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) {
+ if address != account.Address {
+ return nil, errors.New("not authorized to sign this account")
+ }
+ signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes())
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(signer, signature)
+ },
+ }, nil
+}
+
+// NewKeyedTransactor is a utility method to easily create a transaction signer
+// from a single private key.
+func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
+ keyAddr := crypto.PubkeyToAddress(key.PublicKey)
+ return &TransactOpts{
+ From: keyAddr,
+ Signer: func(signer types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) {
+ if address != keyAddr {
+ return nil, errors.New("not authorized to sign this account")
+ }
+ signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key)
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(signer, signature)
+ },
+ }
+}
+
+// NewClefTransactor is a utility method to easily create a transaction signer
+// with a clef backend.
+func NewClefTransactor(clef *external.ExternalSigner, account accounts.Account) *TransactOpts {
+ return &TransactOpts{
+ From: account.Address,
+ Signer: func(signer types.Signer, address common.Address, transaction *types.Transaction) (*types.Transaction, error) {
+ if address != account.Address {
+ return nil, errors.New("not authorized to sign this account")
+ }
+ return clef.SignTx(account, transaction, nil) // Clef enforces its own chain id
+ },
+ }
+}
diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go
new file mode 100644
index 0000000..556394f
--- /dev/null
+++ b/accounts/abi/bind/backend.go
@@ -0,0 +1,112 @@
+// 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 bind
+
+import (
+ "context"
+ "errors"
+ "math/big"
+
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/go-ethereum"
+ "github.com/ava-labs/go-ethereum/common"
+)
+
+var (
+ // ErrNoCode is returned by call and transact operations for which the requested
+ // recipient contract to operate on does not exist in the state db or does not
+ // have any code associated with it (i.e. suicided).
+ ErrNoCode = errors.New("no contract code at given address")
+
+ // This error is raised when attempting to perform a pending state action
+ // on a backend that doesn't implement PendingContractCaller.
+ ErrNoPendingState = errors.New("backend does not support pending state")
+
+ // This error is returned by WaitDeployed if contract creation leaves an
+ // empty contract behind.
+ ErrNoCodeAfterDeploy = errors.New("no contract code after deployment")
+)
+
+// ContractCaller defines the methods needed to allow operating with contract on a read
+// only basis.
+type ContractCaller interface {
+ // CodeAt returns the code of the given account. This is needed to differentiate
+ // between contract internal errors and the local chain being out of sync.
+ CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)
+ // ContractCall executes an Ethereum contract call with the specified data as the
+ // input.
+ CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
+}
+
+// PendingContractCaller defines methods to perform contract calls on the pending state.
+// Call will try to discover this interface when access to the pending state is requested.
+// If the backend does not support the pending state, Call returns ErrNoPendingState.
+type PendingContractCaller interface {
+ // PendingCodeAt returns the code of the given account in the pending state.
+ PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error)
+ // PendingCallContract executes an Ethereum contract call against the pending state.
+ PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error)
+}
+
+// ContractTransactor defines the methods needed to allow operating with contract
+// on a write only basis. Beside the transacting method, the remainder are helpers
+// used when the user does not provide some needed values, but rather leaves it up
+// to the transactor to decide.
+type ContractTransactor interface {
+ // PendingCodeAt returns the code of the given account in the pending state.
+ PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)
+ // PendingNonceAt retrieves the current pending nonce associated with an account.
+ PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
+ // SuggestGasPrice retrieves the currently suggested gas price to allow a timely
+ // execution of a transaction.
+ SuggestGasPrice(ctx context.Context) (*big.Int, error)
+ // EstimateGas tries to estimate the gas needed to execute a specific
+ // transaction based on the current pending state of the backend blockchain.
+ // There is no guarantee that this is the true gas limit requirement as other
+ // transactions may be added or removed by miners, but it should provide a basis
+ // for setting a reasonable default.
+ EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error)
+ // SendTransaction injects the transaction into the pending pool for execution.
+ SendTransaction(ctx context.Context, tx *types.Transaction) error
+}
+
+// ContractFilterer defines the methods needed to access log events using one-off
+// queries or continuous event subscriptions.
+type ContractFilterer interface {
+ // FilterLogs executes a log filter operation, blocking during execution and
+ // returning all the results in one batch.
+ //
+ // TODO(karalabe): Deprecate when the subscription one can return past data too.
+ FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error)
+
+ // SubscribeFilterLogs creates a background log filtering operation, returning
+ // a subscription immediately, which can be used to stream the found events.
+ SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error)
+}
+
+// DeployBackend wraps the operations needed by WaitMined and WaitDeployed.
+type DeployBackend interface {
+ TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
+ CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error)
+}
+
+// ContractBackend defines the methods needed to work with contracts on a read-write basis.
+type ContractBackend interface {
+ ContractCaller
+ ContractTransactor
+ ContractFilterer
+}
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
new file mode 100644
index 0000000..88610a4
--- /dev/null
+++ b/accounts/abi/bind/backends/simulated.go
@@ -0,0 +1,523 @@
+// 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 backends
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math/big"
+ "sync"
+ "time"
+
+ "github.com/ava-labs/coreth/accounts/abi/bind"
+ "github.com/ava-labs/coreth/consensus/ethash"
+ "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/core/bloombits"
+ "github.com/ava-labs/coreth/core/rawdb"
+ "github.com/ava-labs/coreth/core/state"
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/coreth/core/vm"
+ "github.com/ava-labs/coreth/params"
+ "github.com/ava-labs/coreth/rpc"
+ "github.com/ava-labs/go-ethereum"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/common/math"
+ "github.com/ava-labs/go-ethereum/eth/filters"
+ "github.com/ava-labs/go-ethereum/ethdb"
+ "github.com/ava-labs/go-ethereum/event"
+)
+
+// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
+var _ bind.ContractBackend = (*SimulatedBackend)(nil)
+
+var (
+ errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
+ errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
+)
+
+// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
+// the background. Its main purpose is to allow easily testing contract bindings.
+type SimulatedBackend struct {
+ database ethdb.Database // In memory database to store our testing data
+ blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
+
+ mu sync.Mutex
+ pendingBlock *types.Block // Currently pending block that will be imported on request
+ pendingState *state.StateDB // Currently pending state that will be the active on on request
+
+ events *filters.EventSystem // Event system for filtering log events live
+
+ config *params.ChainConfig
+}
+
+// NewSimulatedBackendWithDatabase creates a new binding backend based on the given database
+// and uses a simulated blockchain for testing purposes.
+func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
+ genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
+ genesis.MustCommit(database)
+ blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil)
+
+ backend := &SimulatedBackend{
+ database: database,
+ blockchain: blockchain,
+ config: genesis.Config,
+ events: filters.NewEventSystem(new(event.TypeMux), &filterBackend{database, blockchain}, false),
+ }
+ backend.rollback()
+ return backend
+}
+
+// NewSimulatedBackend creates a new binding backend using a simulated blockchain
+// for testing purposes.
+func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
+ return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit)
+}
+
+// Close terminates the underlying blockchain's update loop.
+func (b *SimulatedBackend) Close() error {
+ b.blockchain.Stop()
+ return nil
+}
+
+// Commit imports all the pending transactions as a single block and starts a
+// fresh new state.
+func (b *SimulatedBackend) Commit() {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil {
+ panic(err) // This cannot happen unless the simulator is wrong, fail in that case
+ }
+ b.rollback()
+}
+
+// Rollback aborts all pending transactions, reverting to the last committed state.
+func (b *SimulatedBackend) Rollback() {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ b.rollback()
+}
+
+func (b *SimulatedBackend) rollback() {
+ blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
+ statedb, _ := b.blockchain.State()
+
+ b.pendingBlock = blocks[0]
+ b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
+}
+
+// CodeAt returns the code associated with a certain account in the blockchain.
+func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
+ return nil, errBlockNumberUnsupported
+ }
+ statedb, _ := b.blockchain.State()
+ return statedb.GetCode(contract), nil
+}
+
+// BalanceAt returns the wei balance of a certain account in the blockchain.
+func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (*big.Int, error) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
+ return nil, errBlockNumberUnsupported
+ }
+ statedb, _ := b.blockchain.State()
+ return statedb.GetBalance(contract), nil
+}
+
+// NonceAt returns the nonce of a certain account in the blockchain.
+func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (uint64, error) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
+ return 0, errBlockNumberUnsupported
+ }
+ statedb, _ := b.blockchain.State()
+ return statedb.GetNonce(contract), nil
+}
+
+// StorageAt returns the value of key in the storage of an account in the blockchain.
+func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
+ return nil, errBlockNumberUnsupported
+ }
+ statedb, _ := b.blockchain.State()
+ val := statedb.GetState(contract, key)
+ return val[:], nil
+}
+
+// TransactionReceipt returns the receipt of a transaction.
+func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
+ receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash, b.config)
+ return receipt, nil
+}
+
+// TransactionByHash checks the pool of pending transactions in addition to the
+// blockchain. The isPending return value indicates whether the transaction has been
+// mined yet. Note that the transaction may not be part of the canonical chain even if
+// it's not pending.
+func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ tx := b.pendingBlock.Transaction(txHash)
+ if tx != nil {
+ return tx, true, nil
+ }
+ tx, _, _, _ = rawdb.ReadTransaction(b.database, txHash)
+ if tx != nil {
+ return tx, false, nil
+ }
+ return nil, false, ethereum.NotFound
+}
+
+// PendingCodeAt returns the code associated with an account in the pending state.
+func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ return b.pendingState.GetCode(contract), nil
+}
+
+// CallContract executes a contract call.
+func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
+ return nil, errBlockNumberUnsupported
+ }
+ state, err := b.blockchain.State()
+ if err != nil {
+ return nil, err
+ }
+ rval, _, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
+ return rval, err
+}
+
+// PendingCallContract executes a contract call on the pending state.
+func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+ defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
+
+ rval, _, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
+ return rval, err
+}
+
+// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
+// the nonce currently pending for the account.
+func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ return b.pendingState.GetOrNewStateObject(account).Nonce(), nil
+}
+
+// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
+// chain doesn't have miners, we just return a gas price of 1 for any call.
+func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
+ return big.NewInt(1), nil
+}
+
+// EstimateGas executes the requested code against the currently pending block/state and
+// returns the used amount of gas.
+func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ // Determine the lowest and highest possible gas limits to binary search in between
+ var (
+ lo uint64 = params.TxGas - 1
+ hi uint64
+ cap uint64
+ )
+ if call.Gas >= params.TxGas {
+ hi = call.Gas
+ } else {
+ hi = b.pendingBlock.GasLimit()
+ }
+ cap = hi
+
+ // Create a helper to check if a gas allowance results in an executable transaction
+ executable := func(gas uint64) bool {
+ call.Gas = gas
+
+ snapshot := b.pendingState.Snapshot()
+ _, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
+ b.pendingState.RevertToSnapshot(snapshot)
+
+ if err != nil || failed {
+ return false
+ }
+ return true
+ }
+ // Execute the binary search and hone in on an executable gas limit
+ for lo+1 < hi {
+ mid := (hi + lo) / 2
+ if !executable(mid) {
+ lo = mid
+ } else {
+ hi = mid
+ }
+ }
+ // Reject the transaction as invalid if it still fails at the highest allowance
+ if hi == cap {
+ if !executable(hi) {
+ return 0, errGasEstimationFailed
+ }
+ }
+ return hi, nil
+}
+
+// callContract implements common code between normal and pending contract calls.
+// state is modified during execution, make sure to copy it if necessary.
+func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, uint64, bool, error) {
+ // Ensure message is initialized properly.
+ if call.GasPrice == nil {
+ call.GasPrice = big.NewInt(1)
+ }
+ if call.Gas == 0 {
+ call.Gas = 50000000
+ }
+ if call.Value == nil {
+ call.Value = new(big.Int)
+ }
+ // Set infinite balance to the fake caller account.
+ from := statedb.GetOrNewStateObject(call.From)
+ from.SetBalance(math.MaxBig256)
+ // Execute the call.
+ msg := callmsg{call}
+
+ evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil)
+ // Create a new environment which holds all relevant information
+ // about the transaction and calling mechanisms.
+ vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
+ gaspool := new(core.GasPool).AddGas(math.MaxUint64)
+
+ return core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
+}
+
+// SendTransaction updates the pending block to include the given transaction.
+// It panics if the transaction is invalid.
+func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ sender, err := types.Sender(types.NewEIP155Signer(b.config.ChainID), tx)
+ if err != nil {
+ panic(fmt.Errorf("invalid transaction: %v", err))
+ }
+ nonce := b.pendingState.GetNonce(sender)
+ if tx.Nonce() != nonce {
+ panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce))
+ }
+
+ blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
+ for _, tx := range b.pendingBlock.Transactions() {
+ block.AddTxWithChain(b.blockchain, tx)
+ }
+ block.AddTxWithChain(b.blockchain, tx)
+ })
+ statedb, _ := b.blockchain.State()
+
+ b.pendingBlock = blocks[0]
+ b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
+ return nil
+}
+
+// FilterLogs executes a log filter operation, blocking during execution and
+// returning all the results in one batch.
+//
+// TODO(karalabe): Deprecate when the subscription one can return past data too.
+func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) {
+ var filter *filters.Filter
+ if query.BlockHash != nil {
+ // Block filter requested, construct a single-shot filter
+ filter = filters.NewBlockFilter(&filterBackend{b.database, b.blockchain}, *query.BlockHash, query.Addresses, query.Topics)
+ } else {
+ // Initialize unset filter boundaried to run from genesis to chain head
+ from := int64(0)
+ if query.FromBlock != nil {
+ from = query.FromBlock.Int64()
+ }
+ to := int64(-1)
+ if query.ToBlock != nil {
+ to = query.ToBlock.Int64()
+ }
+ // Construct the range filter
+ filter = filters.NewRangeFilter(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics)
+ }
+ // Run the filter and return all the logs
+ logs, err := filter.Logs(ctx)
+ if err != nil {
+ return nil, err
+ }
+ res := make([]types.Log, len(logs))
+ for i, log := range logs {
+ res[i] = *log
+ }
+ return res, nil
+}
+
+// SubscribeFilterLogs creates a background log filtering operation, returning a
+// subscription immediately, which can be used to stream the found events.
+func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
+ // Subscribe to contract events
+ sink := make(chan []*types.Log)
+
+ sub, err := b.events.SubscribeLogs(query, sink)
+ if err != nil {
+ return nil, err
+ }
+ // Since we're getting logs in batches, we need to flatten them into a plain stream
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case logs := <-sink:
+ for _, log := range logs {
+ select {
+ case ch <- *log:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+// AdjustTime adds a time shift to the simulated clock.
+func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+ blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
+ for _, tx := range b.pendingBlock.Transactions() {
+ block.AddTx(tx)
+ }
+ block.OffsetTime(int64(adjustment.Seconds()))
+ })
+ statedb, _ := b.blockchain.State()
+
+ b.pendingBlock = blocks[0]
+ b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
+
+ return nil
+}
+
+// Blockchain returns the underlying blockchain.
+func (b *SimulatedBackend) Blockchain() *core.BlockChain {
+ return b.blockchain
+}
+
+// callmsg implements core.Message to allow passing it as a transaction simulator.
+type callmsg struct {
+ ethereum.CallMsg
+}
+
+func (m callmsg) From() common.Address { return m.CallMsg.From }
+func (m callmsg) Nonce() uint64 { return 0 }
+func (m callmsg) CheckNonce() bool { return false }
+func (m callmsg) To() *common.Address { return m.CallMsg.To }
+func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
+func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
+func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
+func (m callmsg) Data() []byte { return m.CallMsg.Data }
+
+// filterBackend implements filters.Backend to support filtering for logs without
+// taking bloom-bits acceleration structures into account.
+type filterBackend struct {
+ db ethdb.Database
+ bc *core.BlockChain
+}
+
+func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db }
+func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
+
+func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) {
+ if block == rpc.LatestBlockNumber {
+ return fb.bc.CurrentHeader(), nil
+ }
+ return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil
+}
+
+func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
+ return fb.bc.GetHeaderByHash(hash), nil
+}
+
+func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
+ number := rawdb.ReadHeaderNumber(fb.db, hash)
+ if number == nil {
+ return nil, nil
+ }
+ return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil
+}
+
+func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
+ number := rawdb.ReadHeaderNumber(fb.db, hash)
+ if number == nil {
+ return nil, nil
+ }
+ receipts := rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config())
+ if receipts == nil {
+ return nil, nil
+ }
+ logs := make([][]*types.Log, len(receipts))
+ for i, receipt := range receipts {
+ logs[i] = receipt.Logs
+ }
+ return logs, nil
+}
+
+func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ <-quit
+ return nil
+ })
+}
+func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
+ return fb.bc.SubscribeChainEvent(ch)
+}
+func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
+ return fb.bc.SubscribeRemovedLogsEvent(ch)
+}
+func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
+ return fb.bc.SubscribeLogsEvent(ch)
+}
+
+func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }
+func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) {
+ panic("not supported")
+}
diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go
new file mode 100644
index 0000000..3d64f85
--- /dev/null
+++ b/accounts/abi/bind/base.go
@@ -0,0 +1,366 @@
+// 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 bind
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math/big"
+
+ "github.com/ava-labs/coreth/accounts/abi"
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/go-ethereum"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/crypto"
+ "github.com/ava-labs/go-ethereum/event"
+)
+
+// SignerFn is a signer function callback when a contract requires a method to
+// sign the transaction before submission.
+type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Transaction, error)
+
+// CallOpts is the collection of options to fine tune a contract call request.
+type CallOpts struct {
+ Pending bool // Whether to operate on the pending state or the last known one
+ From common.Address // Optional the sender address, otherwise the first account is used
+ BlockNumber *big.Int // Optional the block number on which the call should be performed
+ Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+}
+
+// TransactOpts is the collection of authorization data required to create a
+// valid Ethereum transaction.
+type TransactOpts struct {
+ From common.Address // Ethereum account to send the transaction from
+ Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state)
+ Signer SignerFn // Method to use for signing the transaction (mandatory)
+
+ Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds)
+ GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
+ GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
+
+ Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+}
+
+// FilterOpts is the collection of options to fine tune filtering for events
+// within a bound contract.
+type FilterOpts struct {
+ Start uint64 // Start of the queried range
+ End *uint64 // End of the range (nil = latest)
+
+ Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+}
+
+// WatchOpts is the collection of options to fine tune subscribing for events
+// within a bound contract.
+type WatchOpts struct {
+ Start *uint64 // Start of the queried range (nil = latest)
+ Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+}
+
+// BoundContract is the base wrapper object that reflects a contract on the
+// Ethereum network. It contains a collection of methods that are used by the
+// higher level contract bindings to operate.
+type BoundContract struct {
+ address common.Address // Deployment address of the contract on the Ethereum blockchain
+ abi abi.ABI // Reflect based ABI to access the correct Ethereum methods
+ caller ContractCaller // Read interface to interact with the blockchain
+ transactor ContractTransactor // Write interface to interact with the blockchain
+ filterer ContractFilterer // Event filtering to interact with the blockchain
+}
+
+// NewBoundContract creates a low level contract interface through which calls
+// and transactions may be made through.
+func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor, filterer ContractFilterer) *BoundContract {
+ return &BoundContract{
+ address: address,
+ abi: abi,
+ caller: caller,
+ transactor: transactor,
+ filterer: filterer,
+ }
+}
+
+// DeployContract deploys a contract onto the Ethereum blockchain and binds the
+// deployment address with a Go wrapper.
+func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) {
+ // Otherwise try to deploy the contract
+ c := NewBoundContract(common.Address{}, abi, backend, backend, backend)
+
+ input, err := c.abi.Pack("", params...)
+ if err != nil {
+ return common.Address{}, nil, nil, err
+ }
+ tx, err := c.transact(opts, nil, append(bytecode, input...))
+ if err != nil {
+ return common.Address{}, nil, nil, err
+ }
+ c.address = crypto.CreateAddress(opts.From, tx.Nonce())
+ return c.address, tx, c, nil
+}
+
+// Call invokes the (constant) contract method with params as input values and
+// sets the output to result. The result type might be a single field for simple
+// returns, a slice of interfaces for anonymous returns and a struct for named
+// returns.
+func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, params ...interface{}) error {
+ // Don't crash on a lazy user
+ if opts == nil {
+ opts = new(CallOpts)
+ }
+ // Pack the input, call and unpack the results
+ input, err := c.abi.Pack(method, params...)
+ if err != nil {
+ return err
+ }
+ var (
+ msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input}
+ ctx = ensureContext(opts.Context)
+ code []byte
+ output []byte
+ )
+ if opts.Pending {
+ pb, ok := c.caller.(PendingContractCaller)
+ if !ok {
+ return ErrNoPendingState
+ }
+ output, err = pb.PendingCallContract(ctx, msg)
+ if err == nil && len(output) == 0 {
+ // Make sure we have a contract to operate on, and bail out otherwise.
+ if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
+ return err
+ } else if len(code) == 0 {
+ return ErrNoCode
+ }
+ }
+ } else {
+ output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber)
+ if err == nil && len(output) == 0 {
+ // Make sure we have a contract to operate on, and bail out otherwise.
+ if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil {
+ return err
+ } else if len(code) == 0 {
+ return ErrNoCode
+ }
+ }
+ }
+ if err != nil {
+ return err
+ }
+ return c.abi.Unpack(result, method, output)
+}
+
+// Transact invokes the (paid) contract method with params as input values.
+func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ // Otherwise pack up the parameters and invoke the contract
+ input, err := c.abi.Pack(method, params...)
+ if err != nil {
+ return nil, err
+ }
+ return c.transact(opts, &c.address, input)
+}
+
+// Transfer initiates a plain transaction to move funds to the contract, calling
+// its default method if one is available.
+func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) {
+ return c.transact(opts, &c.address, nil)
+}
+
+// transact executes an actual transaction invocation, first deriving any missing
+// authorization fields, and then scheduling the transaction for execution.
+func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
+ var err error
+
+ // Ensure a valid value field and resolve the account nonce
+ value := opts.Value
+ if value == nil {
+ value = new(big.Int)
+ }
+ var nonce uint64
+ if opts.Nonce == nil {
+ nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
+ if err != nil {
+ return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
+ }
+ } else {
+ nonce = opts.Nonce.Uint64()
+ }
+ // Figure out the gas allowance and gas price values
+ gasPrice := opts.GasPrice
+ if gasPrice == nil {
+ gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context))
+ if err != nil {
+ return nil, fmt.Errorf("failed to suggest gas price: %v", err)
+ }
+ }
+ gasLimit := opts.GasLimit
+ if gasLimit == 0 {
+ // Gas estimation cannot succeed without code for method invocations
+ if contract != nil {
+ if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
+ return nil, err
+ } else if len(code) == 0 {
+ return nil, ErrNoCode
+ }
+ }
+ // If the contract surely has code (or code is not needed), estimate the transaction
+ msg := ethereum.CallMsg{From: opts.From, To: contract, Value: value, Data: input}
+ gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
+ if err != nil {
+ return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
+ }
+ }
+ // Create the transaction, sign it and schedule it for execution
+ var rawTx *types.Transaction
+ if contract == nil {
+ rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input)
+ } else {
+ rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input)
+ }
+ if opts.Signer == nil {
+ return nil, errors.New("no signer to authorize the transaction with")
+ }
+ signedTx, err := opts.Signer(types.HomesteadSigner{}, opts.From, rawTx)
+ if err != nil {
+ return nil, err
+ }
+ if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
+ return nil, err
+ }
+ return signedTx, nil
+}
+
+// FilterLogs filters contract logs for past blocks, returning the necessary
+// channels to construct a strongly typed bound iterator on top of them.
+func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
+ // Don't crash on a lazy user
+ if opts == nil {
+ opts = new(FilterOpts)
+ }
+ // Append the event selector to the query parameters and construct the topic set
+ query = append([][]interface{}{{c.abi.Events[name].ID()}}, query...)
+
+ topics, err := makeTopics(query...)
+ if err != nil {
+ return nil, nil, err
+ }
+ // Start the background filtering
+ logs := make(chan types.Log, 128)
+
+ config := ethereum.FilterQuery{
+ Addresses: []common.Address{c.address},
+ Topics: topics,
+ FromBlock: new(big.Int).SetUint64(opts.Start),
+ }
+ if opts.End != nil {
+ config.ToBlock = new(big.Int).SetUint64(*opts.End)
+ }
+ /* TODO(karalabe): Replace the rest of the method below with this when supported
+ sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
+ */
+ buff, err := c.filterer.FilterLogs(ensureContext(opts.Context), config)
+ if err != nil {
+ return nil, nil, err
+ }
+ sub, err := event.NewSubscription(func(quit <-chan struct{}) error {
+ for _, log := range buff {
+ select {
+ case logs <- log:
+ case <-quit:
+ return nil
+ }
+ }
+ return nil
+ }), nil
+
+ if err != nil {
+ return nil, nil, err
+ }
+ return logs, sub, nil
+}
+
+// WatchLogs filters subscribes to contract logs for future blocks, returning a
+// subscription object that can be used to tear down the watcher.
+func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
+ // Don't crash on a lazy user
+ if opts == nil {
+ opts = new(WatchOpts)
+ }
+ // Append the event selector to the query parameters and construct the topic set
+ query = append([][]interface{}{{c.abi.Events[name].ID()}}, query...)
+
+ topics, err := makeTopics(query...)
+ if err != nil {
+ return nil, nil, err
+ }
+ // Start the background filtering
+ logs := make(chan types.Log, 128)
+
+ config := ethereum.FilterQuery{
+ Addresses: []common.Address{c.address},
+ Topics: topics,
+ }
+ if opts.Start != nil {
+ config.FromBlock = new(big.Int).SetUint64(*opts.Start)
+ }
+ sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
+ if err != nil {
+ return nil, nil, err
+ }
+ return logs, sub, nil
+}
+
+// UnpackLog unpacks a retrieved log into the provided output structure.
+func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error {
+ if len(log.Data) > 0 {
+ if err := c.abi.Unpack(out, event, log.Data); err != nil {
+ return err
+ }
+ }
+ var indexed abi.Arguments
+ for _, arg := range c.abi.Events[event].Inputs {
+ if arg.Indexed {
+ indexed = append(indexed, arg)
+ }
+ }
+ return parseTopics(out, indexed, log.Topics[1:])
+}
+
+// UnpackLogIntoMap unpacks a retrieved log into the provided map.
+func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error {
+ if len(log.Data) > 0 {
+ if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
+ return err
+ }
+ }
+ var indexed abi.Arguments
+ for _, arg := range c.abi.Events[event].Inputs {
+ if arg.Indexed {
+ indexed = append(indexed, arg)
+ }
+ }
+ return parseTopicsIntoMap(out, indexed, log.Topics[1:])
+}
+
+// ensureContext is a helper method to ensure a context is not nil, even if the
+// user specified it as such.
+func ensureContext(ctx context.Context) context.Context {
+ if ctx == nil {
+ return context.TODO()
+ }
+ return ctx
+}
diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go
new file mode 100644
index 0000000..e869eef
--- /dev/null
+++ b/accounts/abi/bind/bind.go
@@ -0,0 +1,558 @@
+// 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 bind generates Ethereum contract Go bindings.
+//
+// Detailed usage document and tutorial available on the go-ethereum Wiki page:
+// https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts
+package bind
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "go/format"
+ "regexp"
+ "strings"
+ "text/template"
+ "unicode"
+
+ "github.com/ava-labs/coreth/accounts/abi"
+ "github.com/ava-labs/go-ethereum/log"
+)
+
+// Lang is a target programming language selector to generate bindings for.
+type Lang int
+
+const (
+ LangGo Lang = iota
+ LangJava
+ LangObjC
+)
+
+// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
+// to be used as is in client code, but rather as an intermediate struct which
+// enforces compile time type safety and naming convention opposed to having to
+// manually maintain hard coded strings that break on runtime.
+func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string) (string, error) {
+ // Process each individual contract requested binding
+ contracts := make(map[string]*tmplContract)
+
+ // Map used to flag each encountered library as such
+ isLib := make(map[string]struct{})
+
+ for i := 0; i < len(types); i++ {
+ // Parse the actual ABI to generate the binding for
+ evmABI, err := abi.JSON(strings.NewReader(abis[i]))
+ if err != nil {
+ return "", err
+ }
+ // Strip any whitespace from the JSON ABI
+ strippedABI := strings.Map(func(r rune) rune {
+ if unicode.IsSpace(r) {
+ return -1
+ }
+ return r
+ }, abis[i])
+
+ // Extract the call and transact methods; events, struct definitions; and sort them alphabetically
+ var (
+ calls = make(map[string]*tmplMethod)
+ transacts = make(map[string]*tmplMethod)
+ events = make(map[string]*tmplEvent)
+ structs = make(map[string]*tmplStruct)
+ )
+ for _, original := range evmABI.Methods {
+ // Normalize the method for capital cases and non-anonymous inputs/outputs
+ normalized := original
+ normalized.Name = methodNormalizer[lang](original.Name)
+
+ normalized.Inputs = make([]abi.Argument, len(original.Inputs))
+ copy(normalized.Inputs, original.Inputs)
+ for j, input := range normalized.Inputs {
+ if input.Name == "" {
+ normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
+ }
+ if _, exist := structs[input.Type.String()]; input.Type.T == abi.TupleTy && !exist {
+ bindStructType[lang](input.Type, structs)
+ }
+ }
+ normalized.Outputs = make([]abi.Argument, len(original.Outputs))
+ copy(normalized.Outputs, original.Outputs)
+ for j, output := range normalized.Outputs {
+ if output.Name != "" {
+ normalized.Outputs[j].Name = capitalise(output.Name)
+ }
+ if _, exist := structs[output.Type.String()]; output.Type.T == abi.TupleTy && !exist {
+ bindStructType[lang](output.Type, structs)
+ }
+ }
+ // Append the methods to the call or transact lists
+ if original.Const {
+ calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
+ } else {
+ transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
+ }
+ }
+ for _, original := range evmABI.Events {
+ // Skip anonymous events as they don't support explicit filtering
+ if original.Anonymous {
+ continue
+ }
+ // Normalize the event for capital cases and non-anonymous outputs
+ normalized := original
+ normalized.Name = methodNormalizer[lang](original.Name)
+
+ normalized.Inputs = make([]abi.Argument, len(original.Inputs))
+ copy(normalized.Inputs, original.Inputs)
+ for j, input := range normalized.Inputs {
+ // Indexed fields are input, non-indexed ones are outputs
+ if input.Indexed {
+ if input.Name == "" {
+ normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
+ }
+ if _, exist := structs[input.Type.String()]; input.Type.T == abi.TupleTy && !exist {
+ bindStructType[lang](input.Type, structs)
+ }
+ }
+ }
+ // Append the event to the accumulator list
+ events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
+ }
+
+ // There is no easy way to pass arbitrary java objects to the Go side.
+ if len(structs) > 0 && lang == LangJava {
+ return "", errors.New("java binding for tuple arguments is not supported yet")
+ }
+
+ contracts[types[i]] = &tmplContract{
+ Type: capitalise(types[i]),
+ InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
+ InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
+ Constructor: evmABI.Constructor,
+ Calls: calls,
+ Transacts: transacts,
+ Events: events,
+ Libraries: make(map[string]string),
+ Structs: structs,
+ }
+ // Function 4-byte signatures are stored in the same sequence
+ // as types, if available.
+ if len(fsigs) > i {
+ contracts[types[i]].FuncSigs = fsigs[i]
+ }
+ // Parse library references.
+ for pattern, name := range libs {
+ matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin))
+ if err != nil {
+ log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
+ }
+ if matched {
+ contracts[types[i]].Libraries[pattern] = name
+ // keep track that this type is a library
+ if _, ok := isLib[name]; !ok {
+ isLib[name] = struct{}{}
+ }
+ }
+ }
+ }
+ // Check if that type has already been identified as a library
+ for i := 0; i < len(types); i++ {
+ _, ok := isLib[types[i]]
+ contracts[types[i]].Library = ok
+ }
+ // Generate the contract template data content and render it
+ data := &tmplData{
+ Package: pkg,
+ Contracts: contracts,
+ Libraries: libs,
+ }
+ buffer := new(bytes.Buffer)
+
+ funcs := map[string]interface{}{
+ "bindtype": bindType[lang],
+ "bindtopictype": bindTopicType[lang],
+ "namedtype": namedType[lang],
+ "formatmethod": formatMethod,
+ "formatevent": formatEvent,
+ "capitalise": capitalise,
+ "decapitalise": decapitalise,
+ }
+ tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
+ if err := tmpl.Execute(buffer, data); err != nil {
+ return "", err
+ }
+ // For Go bindings pass the code through gofmt to clean it up
+ if lang == LangGo {
+ code, err := format.Source(buffer.Bytes())
+ if err != nil {
+ return "", fmt.Errorf("%v\n%s", err, buffer)
+ }
+ return string(code), nil
+ }
+ // For all others just return as is for now
+ return buffer.String(), nil
+}
+
+// bindType is a set of type binders that convert Solidity types to some supported
+// programming language types.
+var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
+ LangGo: bindTypeGo,
+ LangJava: bindTypeJava,
+}
+
+// bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go one.
+func bindBasicTypeGo(kind abi.Type) string {
+ switch kind.T {
+ case abi.AddressTy:
+ return "common.Address"
+ case abi.IntTy, abi.UintTy:
+ parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
+ switch parts[2] {
+ case "8", "16", "32", "64":
+ return fmt.Sprintf("%sint%s", parts[1], parts[2])
+ }
+ return "*big.Int"
+ case abi.FixedBytesTy:
+ return fmt.Sprintf("[%d]byte", kind.Size)
+ case abi.BytesTy:
+ return "[]byte"
+ case abi.FunctionTy:
+ return "[24]byte"
+ default:
+ // string, bool types
+ return kind.String()
+ }
+}
+
+// bindTypeGo converts solidity types to Go ones. Since there is no clear mapping
+// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
+// mapped will use an upscaled type (e.g. BigDecimal).
+func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
+ switch kind.T {
+ case abi.TupleTy:
+ return structs[kind.String()].Name
+ case abi.ArrayTy:
+ return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs)
+ case abi.SliceTy:
+ return "[]" + bindTypeGo(*kind.Elem, structs)
+ default:
+ return bindBasicTypeGo(kind)
+ }
+}
+
+// bindBasicTypeJava converts basic solidity types(except array, slice and tuple) to Java one.
+func bindBasicTypeJava(kind abi.Type) string {
+ switch kind.T {
+ case abi.AddressTy:
+ return "Address"
+ case abi.IntTy, abi.UintTy:
+ // Note that uint and int (without digits) are also matched,
+ // these are size 256, and will translate to BigInt (the default).
+ parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
+ if len(parts) != 3 {
+ return kind.String()
+ }
+ // All unsigned integers should be translated to BigInt since gomobile doesn't
+ // support them.
+ if parts[1] == "u" {
+ return "BigInt"
+ }
+
+ namedSize := map[string]string{
+ "8": "byte",
+ "16": "short",
+ "32": "int",
+ "64": "long",
+ }[parts[2]]
+
+ // default to BigInt
+ if namedSize == "" {
+ namedSize = "BigInt"
+ }
+ return namedSize
+ case abi.FixedBytesTy, abi.BytesTy:
+ return "byte[]"
+ case abi.BoolTy:
+ return "boolean"
+ case abi.StringTy:
+ return "String"
+ case abi.FunctionTy:
+ return "byte[24]"
+ default:
+ return kind.String()
+ }
+}
+
+// pluralizeJavaType explicitly converts multidimensional types to predefined
+// type in go side.
+func pluralizeJavaType(typ string) string {
+ switch typ {
+ case "boolean":
+ return "Bools"
+ case "String":
+ return "Strings"
+ case "Address":
+ return "Addresses"
+ case "byte[]":
+ return "Binaries"
+ case "BigInt":
+ return "BigInts"
+ }
+ return typ + "[]"
+}
+
+// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
+// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
+// mapped will use an upscaled type (e.g. BigDecimal).
+func bindTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
+ switch kind.T {
+ case abi.TupleTy:
+ return structs[kind.String()].Name
+ case abi.ArrayTy, abi.SliceTy:
+ return pluralizeJavaType(bindTypeJava(*kind.Elem, structs))
+ default:
+ return bindBasicTypeJava(kind)
+ }
+}
+
+// bindTopicType is a set of type binders that convert Solidity types to some
+// supported programming language topic types.
+var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
+ LangGo: bindTopicTypeGo,
+ LangJava: bindTopicTypeJava,
+}
+
+// bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same
+// funcionality as for simple types, but dynamic types get converted to hashes.
+func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
+ bound := bindTypeGo(kind, structs)
+ if bound == "string" || bound == "[]byte" {
+ bound = "common.Hash"
+ }
+ return bound
+}
+
+// bindTopicTypeJava converts a Solidity topic type to a Java one. It is almost the same
+// funcionality as for simple types, but dynamic types get converted to hashes.
+func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
+ bound := bindTypeJava(kind, structs)
+ if bound == "String" || bound == "byte[]" {
+ bound = "Hash"
+ }
+ return bound
+}
+
+// bindStructType is a set of type binders that convert Solidity tuple types to some supported
+// programming language struct definition.
+var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
+ LangGo: bindStructTypeGo,
+ LangJava: bindStructTypeJava,
+}
+
+// bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping
+// in the given map.
+// Notably, this function will resolve and record nested struct recursively.
+func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
+ switch kind.T {
+ case abi.TupleTy:
+ if s, exist := structs[kind.String()]; exist {
+ return s.Name
+ }
+ var fields []*tmplField
+ for i, elem := range kind.TupleElems {
+ field := bindStructTypeGo(*elem, structs)
+ fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem})
+ }
+ name := fmt.Sprintf("Struct%d", len(structs))
+ structs[kind.String()] = &tmplStruct{
+ Name: name,
+ Fields: fields,
+ }
+ return name
+ case abi.ArrayTy:
+ return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs)
+ case abi.SliceTy:
+ return "[]" + bindStructTypeGo(*kind.Elem, structs)
+ default:
+ return bindBasicTypeGo(kind)
+ }
+}
+
+// bindStructTypeJava converts a Solidity tuple type to a Java one and records the mapping
+// in the given map.
+// Notably, this function will resolve and record nested struct recursively.
+func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
+ switch kind.T {
+ case abi.TupleTy:
+ if s, exist := structs[kind.String()]; exist {
+ return s.Name
+ }
+ var fields []*tmplField
+ for i, elem := range kind.TupleElems {
+ field := bindStructTypeJava(*elem, structs)
+ fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem})
+ }
+ name := fmt.Sprintf("Class%d", len(structs))
+ structs[kind.String()] = &tmplStruct{
+ Name: name,
+ Fields: fields,
+ }
+ return name
+ case abi.ArrayTy, abi.SliceTy:
+ return pluralizeJavaType(bindStructTypeJava(*kind.Elem, structs))
+ default:
+ return bindBasicTypeJava(kind)
+ }
+}
+
+// namedType is a set of functions that transform language specific types to
+// named versions that my be used inside method names.
+var namedType = map[Lang]func(string, abi.Type) string{
+ LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
+ LangJava: namedTypeJava,
+}
+
+// namedTypeJava converts some primitive data types to named variants that can
+// be used as parts of method names.
+func namedTypeJava(javaKind string, solKind abi.Type) string {
+ switch javaKind {
+ case "byte[]":
+ return "Binary"
+ case "boolean":
+ return "Bool"
+ default:
+ parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
+ if len(parts) != 4 {
+ return javaKind
+ }
+ switch parts[2] {
+ case "8", "16", "32", "64":
+ if parts[3] == "" {
+ return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2]))
+ }
+ return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2]))
+
+ default:
+ return javaKind
+ }
+ }
+}
+
+// methodNormalizer is a name transformer that modifies Solidity method names to
+// conform to target language naming concentions.
+var methodNormalizer = map[Lang]func(string) string{
+ LangGo: abi.ToCamelCase,
+ LangJava: decapitalise,
+}
+
+// capitalise makes a camel-case string which starts with an upper case character.
+func capitalise(input string) string {
+ return abi.ToCamelCase(input)
+}
+
+// decapitalise makes a camel-case string which starts with a lower case character.
+func decapitalise(input string) string {
+ if len(input) == 0 {
+ return input
+ }
+
+ goForm := abi.ToCamelCase(input)
+ return strings.ToLower(goForm[:1]) + goForm[1:]
+}
+
+// structured checks whether a list of ABI data types has enough information to
+// operate through a proper Go struct or if flat returns are needed.
+func structured(args abi.Arguments) bool {
+ if len(args) < 2 {
+ return false
+ }
+ exists := make(map[string]bool)
+ for _, out := range args {
+ // If the name is anonymous, we can't organize into a struct
+ if out.Name == "" {
+ return false
+ }
+ // If the field name is empty when normalized or collides (var, Var, _var, _Var),
+ // we can't organize into a struct
+ field := capitalise(out.Name)
+ if field == "" || exists[field] {
+ return false
+ }
+ exists[field] = true
+ }
+ return true
+}
+
+// resolveArgName converts a raw argument representation into a user friendly format.
+func resolveArgName(arg abi.Argument, structs map[string]*tmplStruct) string {
+ var (
+ prefix string
+ embedded string
+ typ = &arg.Type
+ )
+loop:
+ for {
+ switch typ.T {
+ case abi.SliceTy:
+ prefix += "[]"
+ case abi.ArrayTy:
+ prefix += fmt.Sprintf("[%d]", typ.Size)
+ default:
+ embedded = typ.String()
+ break loop
+ }
+ typ = typ.Elem
+ }
+ if s, exist := structs[embedded]; exist {
+ return prefix + s.Name
+ } else {
+ return arg.Type.String()
+ }
+}
+
+// formatMethod transforms raw method representation into a user friendly one.
+func formatMethod(method abi.Method, structs map[string]*tmplStruct) string {
+ inputs := make([]string, len(method.Inputs))
+ for i, input := range method.Inputs {
+ inputs[i] = fmt.Sprintf("%v %v", resolveArgName(input, structs), input.Name)
+ }
+ outputs := make([]string, len(method.Outputs))
+ for i, output := range method.Outputs {
+ outputs[i] = resolveArgName(output, structs)
+ if len(output.Name) > 0 {
+ outputs[i] += fmt.Sprintf(" %v", output.Name)
+ }
+ }
+ constant := ""
+ if method.Const {
+ constant = "constant "
+ }
+ return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.RawName, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", "))
+}
+
+// formatEvent transforms raw event representation into a user friendly one.
+func formatEvent(event abi.Event, structs map[string]*tmplStruct) string {
+ inputs := make([]string, len(event.Inputs))
+ for i, input := range event.Inputs {
+ if input.Indexed {
+ inputs[i] = fmt.Sprintf("%v indexed %v", resolveArgName(input, structs), input.Name)
+ } else {
+ inputs[i] = fmt.Sprintf("%v %v", resolveArgName(input, structs), input.Name)
+ }
+ }
+ return fmt.Sprintf("event %v(%v)", event.RawName, strings.Join(inputs, ", "))
+}
diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go
new file mode 100644
index 0000000..e90d02e
--- /dev/null
+++ b/accounts/abi/bind/template.go
@@ -0,0 +1,616 @@
+// 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 bind
+
+import "github.com/ava-labs/coreth/accounts/abi"
+
+// tmplData is the data structure required to fill the binding template.
+type tmplData struct {
+ Package string // Name of the package to place the generated file in
+ Contracts map[string]*tmplContract // List of contracts to generate into this file
+ Libraries map[string]string // Map the bytecode's link pattern to the library name
+}
+
+// tmplContract contains the data needed to generate an individual contract binding.
+type tmplContract struct {
+ Type string // Type name of the main contract binding
+ InputABI string // JSON ABI used as the input to generate the binding from
+ InputBin string // Optional EVM bytecode used to denetare deploy code from
+ FuncSigs map[string]string // Optional map: string signature -> 4-byte signature
+ Constructor abi.Method // Contract constructor for deploy parametrization
+ Calls map[string]*tmplMethod // Contract calls that only read state data
+ Transacts map[string]*tmplMethod // Contract calls that write state data
+ Events map[string]*tmplEvent // Contract events accessors
+ Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs
+ Structs map[string]*tmplStruct // Contract struct type definitions
+ Library bool
+}
+
+// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
+// and cached data fields.
+type tmplMethod struct {
+ Original abi.Method // Original method as parsed by the abi package
+ Normalized abi.Method // Normalized version of the parsed method (capitalized names, non-anonymous args/returns)
+ Structured bool // Whether the returns should be accumulated into a struct
+}
+
+// tmplEvent is a wrapper around an a
+type tmplEvent struct {
+ Original abi.Event // Original event as parsed by the abi package
+ Normalized abi.Event // Normalized version of the parsed fields
+}
+
+// tmplField is a wrapper around a struct field with binding language
+// struct type definition and relative filed name.
+type tmplField struct {
+ Type string // Field type representation depends on target binding language
+ Name string // Field name converted from the raw user-defined field name
+ SolKind abi.Type // Raw abi type information
+}
+
+// tmplStruct is a wrapper around an abi.tuple contains a auto-generated
+// struct name.
+type tmplStruct struct {
+ Name string // Auto-generated struct name(We can't obtain the raw struct name through abi)
+ Fields []*tmplField // Struct fields definition depends on the binding language.
+}
+
+// tmplSource is language to template mapping containing all the supported
+// programming languages the package can generate to.
+var tmplSource = map[Lang]string{
+ LangGo: tmplSourceGo,
+ LangJava: tmplSourceJava,
+}
+
+// tmplSourceGo is the Go source template use to generate the contract binding
+// based on.
+const tmplSourceGo = `
+// Code generated - DO NOT EDIT.
+// This file is a generated binding and any manual changes will be lost.
+
+package {{.Package}}
+
+import (
+ "math/big"
+ "strings"
+
+ ethereum "github.com/ava-labs/go-ethereum"
+ "github.com/ava-labs/coreth/accounts/abi"
+ "github.com/ava-labs/coreth/accounts/abi/bind"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/go-ethereum/event"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var (
+ _ = big.NewInt
+ _ = strings.NewReader
+ _ = ethereum.NotFound
+ _ = abi.U256
+ _ = bind.Bind
+ _ = common.Big1
+ _ = types.BloomLookup
+ _ = event.NewSubscription
+)
+
+{{range $contract := .Contracts}}
+ {{$structs := $contract.Structs}}
+ // {{.Type}}ABI is the input ABI used to generate the binding from.
+ const {{.Type}}ABI = "{{.InputABI}}"
+
+ {{if $contract.FuncSigs}}
+ // {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
+ var {{.Type}}FuncSigs = map[string]string{
+ {{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}",
+ {{end}}
+ }
+ {{end}}
+
+ {{if .InputBin}}
+ // {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
+ var {{.Type}}Bin = "0x{{.InputBin}}"
+
+ // Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
+ func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
+ parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI))
+ if err != nil {
+ return common.Address{}, nil, nil, err
+ }
+ {{range $pattern, $name := .Libraries}}
+ {{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
+ {{$contract.Type}}Bin = strings.Replace({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:], -1)
+ {{end}}
+ address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
+ if err != nil {
+ return common.Address{}, nil, nil, err
+ }
+ return address, tx, &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
+ }
+ {{end}}
+
+ // {{.Type}} is an auto generated Go binding around an Ethereum contract.
+ type {{.Type}} struct {
+ {{.Type}}Caller // Read-only binding to the contract
+ {{.Type}}Transactor // Write-only binding to the contract
+ {{.Type}}Filterer // Log filterer for contract events
+ }
+
+ // {{.Type}}Caller is an auto generated read-only Go binding around an Ethereum contract.
+ type {{.Type}}Caller struct {
+ contract *bind.BoundContract // Generic contract wrapper for the low level calls
+ }
+
+ // {{.Type}}Transactor is an auto generated write-only Go binding around an Ethereum contract.
+ type {{.Type}}Transactor struct {
+ contract *bind.BoundContract // Generic contract wrapper for the low level calls
+ }
+
+ // {{.Type}}Filterer is an auto generated log filtering Go binding around an Ethereum contract events.
+ type {{.Type}}Filterer struct {
+ contract *bind.BoundContract // Generic contract wrapper for the low level calls
+ }
+
+ // {{.Type}}Session is an auto generated Go binding around an Ethereum contract,
+ // with pre-set call and transact options.
+ type {{.Type}}Session struct {
+ Contract *{{.Type}} // Generic contract binding to set the session for
+ CallOpts bind.CallOpts // Call options to use throughout this session
+ TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
+ }
+
+ // {{.Type}}CallerSession is an auto generated read-only Go binding around an Ethereum contract,
+ // with pre-set call options.
+ type {{.Type}}CallerSession struct {
+ Contract *{{.Type}}Caller // Generic contract caller binding to set the session for
+ CallOpts bind.CallOpts // Call options to use throughout this session
+ }
+
+ // {{.Type}}TransactorSession is an auto generated write-only Go binding around an Ethereum contract,
+ // with pre-set transact options.
+ type {{.Type}}TransactorSession struct {
+ Contract *{{.Type}}Transactor // Generic contract transactor binding to set the session for
+ TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
+ }
+
+ // {{.Type}}Raw is an auto generated low-level Go binding around an Ethereum contract.
+ type {{.Type}}Raw struct {
+ Contract *{{.Type}} // Generic contract binding to access the raw methods on
+ }
+
+ // {{.Type}}CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
+ type {{.Type}}CallerRaw struct {
+ Contract *{{.Type}}Caller // Generic read-only contract binding to access the raw methods on
+ }
+
+ // {{.Type}}TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
+ type {{.Type}}TransactorRaw struct {
+ Contract *{{.Type}}Transactor // Generic write-only contract binding to access the raw methods on
+ }
+
+ // New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract.
+ func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) {
+ contract, err := bind{{.Type}}(address, backend, backend, backend)
+ if err != nil {
+ return nil, err
+ }
+ return &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
+ }
+
+ // New{{.Type}}Caller creates a new read-only instance of {{.Type}}, bound to a specific deployed contract.
+ func New{{.Type}}Caller(address common.Address, caller bind.ContractCaller) (*{{.Type}}Caller, error) {
+ contract, err := bind{{.Type}}(address, caller, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &{{.Type}}Caller{contract: contract}, nil
+ }
+
+ // New{{.Type}}Transactor creates a new write-only instance of {{.Type}}, bound to a specific deployed contract.
+ func New{{.Type}}Transactor(address common.Address, transactor bind.ContractTransactor) (*{{.Type}}Transactor, error) {
+ contract, err := bind{{.Type}}(address, nil, transactor, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &{{.Type}}Transactor{contract: contract}, nil
+ }
+
+ // New{{.Type}}Filterer creates a new log filterer instance of {{.Type}}, bound to a specific deployed contract.
+ func New{{.Type}}Filterer(address common.Address, filterer bind.ContractFilterer) (*{{.Type}}Filterer, error) {
+ contract, err := bind{{.Type}}(address, nil, nil, filterer)
+ if err != nil {
+ return nil, err
+ }
+ return &{{.Type}}Filterer{contract: contract}, nil
+ }
+
+ // bind{{.Type}} binds a generic wrapper to an already deployed contract.
+ func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
+ parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI))
+ if err != nil {
+ return nil, err
+ }
+ return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
+ }
+
+ // Call invokes the (constant) contract method with params as input values and
+ // sets the output to result. The result type might be a single field for simple
+ // returns, a slice of interfaces for anonymous returns and a struct for named
+ // returns.
+ func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
+ return _{{$contract.Type}}.Contract.{{$contract.Type}}Caller.contract.Call(opts, result, method, params...)
+ }
+
+ // Transfer initiates a plain transaction to move funds to the contract, calling
+ // its default method if one is available.
+ func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transfer(opts)
+ }
+
+ // Transact invokes the (paid) contract method with params as input values.
+ func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transact(opts, method, params...)
+ }
+
+ // Call invokes the (constant) contract method with params as input values and
+ // sets the output to result. The result type might be a single field for simple
+ // returns, a slice of interfaces for anonymous returns and a struct for named
+ // returns.
+ func (_{{$contract.Type}} *{{$contract.Type}}CallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
+ return _{{$contract.Type}}.Contract.contract.Call(opts, result, method, params...)
+ }
+
+ // Transfer initiates a plain transaction to move funds to the contract, calling
+ // its default method if one is available.
+ func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.contract.Transfer(opts)
+ }
+
+ // Transact invokes the (paid) contract method with params as input values.
+ func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...)
+ }
+
+ {{range .Structs}}
+ // {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
+ type {{.Name}} struct {
+ {{range $field := .Fields}}
+ {{$field.Name}} {{$field.Type}}{{end}}
+ }
+ {{end}}
+
+ {{range .Calls}}
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{formatmethod .Original $structs}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) {
+ {{if .Structured}}ret := new(struct{
+ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}}
+ {{end}}
+ }){{else}}var (
+ {{range $i, $_ := .Normalized.Outputs}}ret{{$i}} = new({{bindtype .Type $structs}})
+ {{end}}
+ ){{end}}
+ out := {{if .Structured}}ret{{else}}{{if eq (len .Normalized.Outputs) 1}}ret0{{else}}&[]interface{}{
+ {{range $i, $_ := .Normalized.Outputs}}ret{{$i}},
+ {{end}}
+ }{{end}}{{end}}
+ err := _{{$contract.Type}}.contract.Call(opts, out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ return {{if .Structured}}*ret,{{else}}{{range $i, $_ := .Normalized.Outputs}}*ret{{$i}},{{end}}{{end}} err
+ }
+
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{formatmethod .Original $structs}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{formatmethod .Original $structs}}
+ func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+ {{end}}
+
+ {{range .Transacts}}
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{formatmethod .Original $structs}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{formatmethod .Original $structs}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{formatmethod .Original $structs}}
+ func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+ {{end}}
+
+ {{range .Events}}
+ // {{$contract.Type}}{{.Normalized.Name}}Iterator is returned from Filter{{.Normalized.Name}} and is used to iterate over the raw logs and unpacked data for {{.Normalized.Name}} events raised by the {{$contract.Type}} contract.
+ type {{$contract.Type}}{{.Normalized.Name}}Iterator struct {
+ Event *{{$contract.Type}}{{.Normalized.Name}} // Event containing the contract specifics and raw log
+
+ contract *bind.BoundContract // Generic contract to use for unpacking event data
+ event string // Event name to use for unpacking event data
+
+ logs chan types.Log // Log channel receiving the found contract events
+ sub ethereum.Subscription // Subscription for errors, completion and termination
+ done bool // Whether the subscription completed delivering logs
+ fail error // Occurred error to stop iteration
+ }
+ // Next advances the iterator to the subsequent event, returning whether there
+ // are any more events found. In case of a retrieval or parsing error, false is
+ // returned and Error() can be queried for the exact failure.
+ func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Next() bool {
+ // If the iterator failed, stop iterating
+ if (it.fail != nil) {
+ return false
+ }
+ // If the iterator completed, deliver directly whatever's available
+ if (it.done) {
+ select {
+ case log := <-it.logs:
+ it.Event = new({{$contract.Type}}{{.Normalized.Name}})
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+ // Iterator still in progress, wait for either a data or an error event
+ select {
+ case log := <-it.logs:
+ it.Event = new({{$contract.Type}}{{.Normalized.Name}})
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+ }
+ // Error returns any retrieval or parsing error occurred during filtering.
+ func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Error() error {
+ return it.fail
+ }
+ // Close terminates the iteration process, releasing any pending underlying
+ // resources.
+ func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+ }
+
+ // {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} event raised by the {{$contract.Type}} contract.
+ type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}}
+ {{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type $structs}}{{else}}{{bindtype .Type $structs}}{{end}}; {{end}}
+ Raw types.Log // Blockchain specific contextual infos
+ }
+
+ // Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{formatevent .Original $structs}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) {
+ {{range .Normalized.Inputs}}
+ {{if .Indexed}}var {{.Name}}Rule []interface{}
+ for _, {{.Name}}Item := range {{.Name}} {
+ {{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
+ }{{end}}{{end}}
+
+ logs, sub, err := _{{$contract.Type}}.contract.FilterLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
+ if err != nil {
+ return nil, err
+ }
+ return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil
+ }
+
+ // Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{formatevent .Original $structs}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (event.Subscription, error) {
+ {{range .Normalized.Inputs}}
+ {{if .Indexed}}var {{.Name}}Rule []interface{}
+ for _, {{.Name}}Item := range {{.Name}} {
+ {{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
+ }{{end}}{{end}}
+
+ logs, sub, err := _{{$contract.Type}}.contract.WatchLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case log := <-logs:
+ // New log arrived, parse the event and forward to the user
+ event := new({{$contract.Type}}{{.Normalized.Name}})
+ if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+ }
+
+ // Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
+ event := new({{$contract.Type}}{{.Normalized.Name}})
+ if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
+ return nil, err
+ }
+ return event, nil
+ }
+
+ {{end}}
+{{end}}
+`
+
+// tmplSourceJava is the Java source template use to generate the contract binding
+// based on.
+const tmplSourceJava = `
+// This file is an automatically generated Java binding. Do not modify as any
+// change will likely be lost upon the next re-generation!
+
+package {{.Package}};
+
+import org.ethereum.geth.*;
+import java.util.*;
+
+{{range $contract := .Contracts}}
+{{$structs := $contract.Structs}}
+{{if not .Library}}public {{end}}class {{.Type}} {
+ // ABI is the input ABI used to generate the binding from.
+ public final static String ABI = "{{.InputABI}}";
+ {{if $contract.FuncSigs}}
+ // {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
+ public final static Map<String, String> {{.Type}}FuncSigs;
+ static {
+ Hashtable<String, String> temp = new Hashtable<String, String>();
+ {{range $strsig, $binsig := .FuncSigs}}temp.put("{{$binsig}}", "{{$strsig}}");
+ {{end}}
+ {{.Type}}FuncSigs = Collections.unmodifiableMap(temp);
+ }
+ {{end}}
+ {{if .InputBin}}
+ // BYTECODE is the compiled bytecode used for deploying new contracts.
+ public final static String BYTECODE = "0x{{.InputBin}}";
+
+ // deploy deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
+ public static {{.Type}} deploy(TransactOpts auth, EthereumClient client{{range .Constructor.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
+ Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}});
+ String bytecode = BYTECODE;
+ {{if .Libraries}}
+
+ // "link" contract to dependent libraries by deploying them first.
+ {{range $pattern, $name := .Libraries}}
+ {{capitalise $name}} {{decapitalise $name}}Inst = {{capitalise $name}}.deploy(auth, client);
+ bytecode = bytecode.replace("__${{$pattern}}$__", {{decapitalise $name}}Inst.Address.getHex().substring(2));
+ {{end}}
+ {{end}}
+ {{range $index, $element := .Constructor.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
+ {{end}}
+ return new {{.Type}}(Geth.deployContract(auth, ABI, Geth.decodeFromHex(bytecode), client, args));
+ }
+
+ // Internal constructor used by contract deployment.
+ private {{.Type}}(BoundContract deployment) {
+ this.Address = deployment.getAddress();
+ this.Deployer = deployment.getDeployer();
+ this.Contract = deployment;
+ }
+ {{end}}
+
+ // Ethereum address where this contract is located at.
+ public final Address Address;
+
+ // Ethereum transaction in which this contract was deployed (if known!).
+ public final Transaction Deployer;
+
+ // Contract instance bound to a blockchain address.
+ private final BoundContract Contract;
+
+ // Creates a new instance of {{.Type}}, bound to a specific deployed contract.
+ public {{.Type}}(Address address, EthereumClient client) throws Exception {
+ this(Geth.bindContract(address, ABI, client));
+ }
+
+ {{range .Calls}}
+ {{if gt (len .Normalized.Outputs) 1}}
+ // {{capitalise .Normalized.Name}}Results is the output of a call to {{.Normalized.Name}}.
+ public class {{capitalise .Normalized.Name}}Results {
+ {{range $index, $item := .Normalized.Outputs}}public {{bindtype .Type $structs}} {{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}};
+ {{end}}
+ }
+ {{end}}
+
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
+ Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
+ {{range $index, $item := .Normalized.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
+ {{end}}
+
+ Interfaces results = Geth.newInterfaces({{(len .Normalized.Outputs)}});
+ {{range $index, $item := .Normalized.Outputs}}Interface result{{$index}} = Geth.newInterface(); result{{$index}}.setDefault{{namedtype (bindtype .Type $structs) .Type}}(); results.set({{$index}}, result{{$index}});
+ {{end}}
+
+ if (opts == null) {
+ opts = Geth.newCallOpts();
+ }
+ this.Contract.call(opts, results, "{{.Original.Name}}", args);
+ {{if gt (len .Normalized.Outputs) 1}}
+ {{capitalise .Normalized.Name}}Results result = new {{capitalise .Normalized.Name}}Results();
+ {{range $index, $item := .Normalized.Outputs}}result.{{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}} = results.get({{$index}}).get{{namedtype (bindtype .Type $structs) .Type}}();
+ {{end}}
+ return result;
+ {{else}}{{range .Normalized.Outputs}}return results.get(0).get{{namedtype (bindtype .Type $structs) .Type}}();{{end}}
+ {{end}}
+ }
+ {{end}}
+
+ {{range .Transacts}}
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
+ Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
+ {{range $index, $item := .Normalized.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
+ {{end}}
+ return this.Contract.transact(opts, "{{.Original.Name}}" , args);
+ }
+ {{end}}
+}
+{{end}}
+`
diff --git a/accounts/abi/bind/topics.go b/accounts/abi/bind/topics.go
new file mode 100644
index 0000000..58e4ff3
--- /dev/null
+++ b/accounts/abi/bind/topics.go
@@ -0,0 +1,241 @@
+// Copyright 2018 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 bind
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "math/big"
+ "reflect"
+
+ "github.com/ava-labs/coreth/accounts/abi"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/crypto"
+)
+
+// makeTopics converts a filter query argument list into a filter topic set.
+func makeTopics(query ...[]interface{}) ([][]common.Hash, error) {
+ topics := make([][]common.Hash, len(query))
+ for i, filter := range query {
+ for _, rule := range filter {
+ var topic common.Hash
+
+ // Try to generate the topic based on simple types
+ switch rule := rule.(type) {
+ case common.Hash:
+ copy(topic[:], rule[:])
+ case common.Address:
+ copy(topic[common.HashLength-common.AddressLength:], rule[:])
+ case *big.Int:
+ blob := rule.Bytes()
+ copy(topic[common.HashLength-len(blob):], blob)
+ case bool:
+ if rule {
+ topic[common.HashLength-1] = 1
+ }
+ case int8:
+ blob := big.NewInt(int64(rule)).Bytes()
+ copy(topic[common.HashLength-len(blob):], blob)
+ case int16:
+ blob := big.NewInt(int64(rule)).Bytes()
+ copy(topic[common.HashLength-len(blob):], blob)
+ case int32:
+ blob := big.NewInt(int64(rule)).Bytes()
+ copy(topic[common.HashLength-len(blob):], blob)
+ case int64:
+ blob := big.NewInt(rule).Bytes()
+ copy(topic[common.HashLength-len(blob):], blob)
+ case uint8:
+ blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
+ copy(topic[common.HashLength-len(blob):], blob)
+ case uint16:
+ blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
+ copy(topic[common.HashLength-len(blob):], blob)
+ case uint32:
+ blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
+ copy(topic[common.HashLength-len(blob):], blob)
+ case uint64:
+ blob := new(big.Int).SetUint64(rule).Bytes()
+ copy(topic[common.HashLength-len(blob):], blob)
+ case string:
+ hash := crypto.Keccak256Hash([]byte(rule))
+ copy(topic[:], hash[:])
+ case []byte:
+ hash := crypto.Keccak256Hash(rule)
+ copy(topic[:], hash[:])
+
+ default:
+ // Attempt to generate the topic from funky types
+ val := reflect.ValueOf(rule)
+
+ switch {
+
+ // static byte array
+ case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
+ reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val)
+
+ default:
+ return nil, fmt.Errorf("unsupported indexed type: %T", rule)
+ }
+ }
+ topics[i] = append(topics[i], topic)
+ }
+ }
+ return topics, nil
+}
+
+// Big batch of reflect types for topic reconstruction.
+var (
+ reflectHash = reflect.TypeOf(common.Hash{})
+ reflectAddress = reflect.TypeOf(common.Address{})
+ reflectBigInt = reflect.TypeOf(new(big.Int))
+)
+
+// parseTopics converts the indexed topic fields into actual log field values.
+//
+// Note, dynamic types cannot be reconstructed since they get mapped to Keccak256
+// hashes as the topic value!
+func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) error {
+ // Sanity check that the fields and topics match up
+ if len(fields) != len(topics) {
+ return errors.New("topic/field count mismatch")
+ }
+ // Iterate over all the fields and reconstruct them from topics
+ for _, arg := range fields {
+ if !arg.Indexed {
+ return errors.New("non-indexed field in topic reconstruction")
+ }
+ field := reflect.ValueOf(out).Elem().FieldByName(capitalise(arg.Name))
+
+ // Try to parse the topic back into the fields based on primitive types
+ switch field.Kind() {
+ case reflect.Bool:
+ if topics[0][common.HashLength-1] == 1 {
+ field.Set(reflect.ValueOf(true))
+ }
+ case reflect.Int8:
+ num := new(big.Int).SetBytes(topics[0][:])
+ field.Set(reflect.ValueOf(int8(num.Int64())))
+
+ case reflect.Int16:
+ num := new(big.Int).SetBytes(topics[0][:])
+ field.Set(reflect.ValueOf(int16(num.Int64())))
+
+ case reflect.Int32:
+ num := new(big.Int).SetBytes(topics[0][:])
+ field.Set(reflect.ValueOf(int32(num.Int64())))
+
+ case reflect.Int64:
+ num := new(big.Int).SetBytes(topics[0][:])
+ field.Set(reflect.ValueOf(num.Int64()))
+
+ case reflect.Uint8:
+ num := new(big.Int).SetBytes(topics[0][:])
+ field.Set(reflect.ValueOf(uint8(num.Uint64())))
+
+ case reflect.Uint16:
+ num := new(big.Int).SetBytes(topics[0][:])
+ field.Set(reflect.ValueOf(uint16(num.Uint64())))
+
+ case reflect.Uint32:
+ num := new(big.Int).SetBytes(topics[0][:])
+ field.Set(reflect.ValueOf(uint32(num.Uint64())))
+
+ case reflect.Uint64:
+ num := new(big.Int).SetBytes(topics[0][:])
+ field.Set(reflect.ValueOf(num.Uint64()))
+
+ default:
+ // Ran out of plain primitive types, try custom types
+ switch field.Type() {
+ case reflectHash: // Also covers all dynamic types
+ field.Set(reflect.ValueOf(topics[0]))
+
+ case reflectAddress:
+ var addr common.Address
+ copy(addr[:], topics[0][common.HashLength-common.AddressLength:])
+ field.Set(reflect.ValueOf(addr))
+
+ case reflectBigInt:
+ num := new(big.Int).SetBytes(topics[0][:])
+ field.Set(reflect.ValueOf(num))
+
+ default:
+ // Ran out of custom types, try the crazies
+ switch {
+
+ // static byte array
+ case arg.Type.T == abi.FixedBytesTy:
+ reflect.Copy(field, reflect.ValueOf(topics[0][:arg.Type.Size]))
+
+ default:
+ return fmt.Errorf("unsupported indexed type: %v", arg.Type)
+ }
+ }
+ }
+ topics = topics[1:]
+ }
+ return nil
+}
+
+// parseTopicsIntoMap converts the indexed topic field-value pairs into map key-value pairs
+func parseTopicsIntoMap(out map[string]interface{}, fields abi.Arguments, topics []common.Hash) error {
+ // Sanity check that the fields and topics match up
+ if len(fields) != len(topics) {
+ return errors.New("topic/field count mismatch")
+ }
+ // Iterate over all the fields and reconstruct them from topics
+ for _, arg := range fields {
+ if !arg.Indexed {
+ return errors.New("non-indexed field in topic reconstruction")
+ }
+
+ switch arg.Type.T {
+ case abi.BoolTy:
+ out[arg.Name] = topics[0][common.HashLength-1] == 1
+ case abi.IntTy, abi.UintTy:
+ num := new(big.Int).SetBytes(topics[0][:])
+ out[arg.Name] = num
+ case abi.AddressTy:
+ var addr common.Address
+ copy(addr[:], topics[0][common.HashLength-common.AddressLength:])
+ out[arg.Name] = addr
+ case abi.HashTy:
+ out[arg.Name] = topics[0]
+ case abi.FixedBytesTy:
+ out[arg.Name] = topics[0][:]
+ case abi.StringTy, abi.BytesTy, abi.SliceTy, abi.ArrayTy:
+ // Array types (including strings and bytes) have their keccak256 hashes stored in the topic- not a hash
+ // whose bytes can be decoded to the actual value- so the best we can do is retrieve that hash
+ out[arg.Name] = topics[0]
+ case abi.FunctionTy:
+ if garbage := binary.BigEndian.Uint64(topics[0][0:8]); garbage != 0 {
+ return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[0].Bytes())
+ }
+ var tmp [24]byte
+ copy(tmp[:], topics[0][8:32])
+ out[arg.Name] = tmp
+ default: // Not handling tuples
+ return fmt.Errorf("unsupported indexed type: %v", arg.Type)
+ }
+
+ topics = topics[1:]
+ }
+
+ return nil
+}
diff --git a/accounts/abi/bind/util.go b/accounts/abi/bind/util.go
new file mode 100644
index 0000000..b3ec22c
--- /dev/null
+++ b/accounts/abi/bind/util.go
@@ -0,0 +1,76 @@
+// 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 bind
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/log"
+)
+
+// WaitMined waits for tx to be mined on the blockchain.
+// It stops waiting when the context is canceled.
+func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error) {
+ queryTicker := time.NewTicker(time.Second)
+ defer queryTicker.Stop()
+
+ logger := log.New("hash", tx.Hash())
+ for {
+ receipt, err := b.TransactionReceipt(ctx, tx.Hash())
+ if receipt != nil {
+ return receipt, nil
+ }
+ if err != nil {
+ logger.Trace("Receipt retrieval failed", "err", err)
+ } else {
+ logger.Trace("Transaction not yet mined")
+ }
+ // Wait for the next round.
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ case <-queryTicker.C:
+ }
+ }
+}
+
+// WaitDeployed waits for a contract deployment transaction and returns the on-chain
+// contract address when it is mined. It stops waiting when ctx is canceled.
+func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (common.Address, error) {
+ if tx.To() != nil {
+ return common.Address{}, fmt.Errorf("tx is not contract creation")
+ }
+ receipt, err := WaitMined(ctx, b, tx)
+ if err != nil {
+ return common.Address{}, err
+ }
+ if receipt.ContractAddress == (common.Address{}) {
+ return common.Address{}, fmt.Errorf("zero address")
+ }
+ // Check that code has indeed been deployed at the address.
+ // This matters on pre-Homestead chains: OOG in the constructor
+ // could leave an empty account behind.
+ code, err := b.CodeAt(ctx, receipt.ContractAddress, nil)
+ if err == nil && len(code) == 0 {
+ err = ErrNoCodeAfterDeploy
+ }
+ return receipt.ContractAddress, err
+}
diff --git a/accounts/abi/doc.go b/accounts/abi/doc.go
new file mode 100644
index 0000000..8242068
--- /dev/null
+++ b/accounts/abi/doc.go
@@ -0,0 +1,26 @@
+// 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 abi implements the Ethereum ABI (Application Binary
+// Interface).
+//
+// The Ethereum ABI is strongly typed, known at compile time
+// and static. This ABI will handle basic type casting; unsigned
+// to signed and visa versa. It does not handle slice casting such
+// as unsigned slice to signed slice. Bit size type casting is also
+// handled. ints with a bit size of 32 will be properly cast to int256,
+// etc.
+package abi
diff --git a/accounts/abi/error.go b/accounts/abi/error.go
new file mode 100644
index 0000000..9d8674a
--- /dev/null
+++ b/accounts/abi/error.go
@@ -0,0 +1,84 @@
+// 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 abi
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+)
+
+var (
+ errBadBool = errors.New("abi: improperly encoded boolean value")
+)
+
+// formatSliceString formats the reflection kind with the given slice size
+// and returns a formatted string representation.
+func formatSliceString(kind reflect.Kind, sliceSize int) string {
+ if sliceSize == -1 {
+ return fmt.Sprintf("[]%v", kind)
+ }
+ return fmt.Sprintf("[%d]%v", sliceSize, kind)
+}
+
+// sliceTypeCheck checks that the given slice can by assigned to the reflection
+// type in t.
+func sliceTypeCheck(t Type, val reflect.Value) error {
+ if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
+ return typeErr(formatSliceString(t.Kind, t.Size), val.Type())
+ }
+
+ if t.T == ArrayTy && val.Len() != t.Size {
+ return typeErr(formatSliceString(t.Elem.Kind, t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
+ }
+
+ if t.Elem.T == SliceTy {
+ if val.Len() > 0 {
+ return sliceTypeCheck(*t.Elem, val.Index(0))
+ }
+ } else if t.Elem.T == ArrayTy {
+ return sliceTypeCheck(*t.Elem, val.Index(0))
+ }
+
+ if elemKind := val.Type().Elem().Kind(); elemKind != t.Elem.Kind {
+ return typeErr(formatSliceString(t.Elem.Kind, t.Size), val.Type())
+ }
+ return nil
+}
+
+// typeCheck checks that the given reflection value can be assigned to the reflection
+// type in t.
+func typeCheck(t Type, value reflect.Value) error {
+ if t.T == SliceTy || t.T == ArrayTy {
+ return sliceTypeCheck(t, value)
+ }
+
+ // Check base type validity. Element types will be checked later on.
+ if t.Kind != value.Kind() {
+ return typeErr(t.Kind, value.Kind())
+ } else if t.T == FixedBytesTy && t.Size != value.Len() {
+ return typeErr(t.Type, value.Type())
+ } else {
+ return nil
+ }
+
+}
+
+// typeErr returns a formatted type casting error.
+func typeErr(expected, got interface{}) error {
+ return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected)
+}
diff --git a/accounts/abi/event.go b/accounts/abi/event.go
new file mode 100644
index 0000000..7a41baa
--- /dev/null
+++ b/accounts/abi/event.go
@@ -0,0 +1,77 @@
+// 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 abi
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/crypto"
+)
+
+// Event is an event potentially triggered by the EVM's LOG mechanism. The Event
+// holds type information (inputs) about the yielded output. Anonymous events
+// don't get the signature canonical representation as the first LOG topic.
+type Event struct {
+ // Name is the event name used for internal representation. It's derived from
+ // the raw name and a suffix will be added in the case of a event overload.
+ //
+ // e.g.
+ // There are two events have same name:
+ // * foo(int,int)
+ // * foo(uint,uint)
+ // The event name of the first one wll be resolved as foo while the second one
+ // will be resolved as foo0.
+ Name string
+ // RawName is the raw event name parsed from ABI.
+ RawName string
+ Anonymous bool
+ Inputs Arguments
+}
+
+func (e Event) String() string {
+ inputs := make([]string, len(e.Inputs))
+ for i, input := range e.Inputs {
+ inputs[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
+ if input.Indexed {
+ inputs[i] = fmt.Sprintf("%v indexed %v", input.Type, input.Name)
+ }
+ }
+ return fmt.Sprintf("event %v(%v)", e.RawName, strings.Join(inputs, ", "))
+}
+
+// Sig returns the event string signature according to the ABI spec.
+//
+// Example
+//
+// event foo(uint32 a, int b) = "foo(uint32,int256)"
+//
+// Please note that "int" is substitute for its canonical representation "int256"
+func (e Event) Sig() string {
+ types := make([]string, len(e.Inputs))
+ for i, input := range e.Inputs {
+ types[i] = input.Type.String()
+ }
+ return fmt.Sprintf("%v(%v)", e.RawName, strings.Join(types, ","))
+}
+
+// ID returns the canonical representation of the event's signature used by the
+// abi definition to identify event names and types.
+func (e Event) ID() common.Hash {
+ return common.BytesToHash(crypto.Keccak256([]byte(e.Sig())))
+}
diff --git a/accounts/abi/method.go b/accounts/abi/method.go
new file mode 100644
index 0000000..9cceaba
--- /dev/null
+++ b/accounts/abi/method.go
@@ -0,0 +1,90 @@
+// 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 abi
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/ava-labs/go-ethereum/crypto"
+)
+
+// Method represents a callable given a `Name` and whether the method is a constant.
+// If the method is `Const` no transaction needs to be created for this
+// particular Method call. It can easily be simulated using a local VM.
+// For example a `Balance()` method only needs to retrieve something
+// from the storage and therefore requires no Tx to be send to the
+// network. A method such as `Transact` does require a Tx and thus will
+// be flagged `false`.
+// Input specifies the required input parameters for this gives method.
+type Method struct {
+ // Name is the method name used for internal representation. It's derived from
+ // the raw name and a suffix will be added in the case of a function overload.
+ //
+ // e.g.
+ // There are two functions have same name:
+ // * foo(int,int)
+ // * foo(uint,uint)
+ // The method name of the first one will be resolved as foo while the second one
+ // will be resolved as foo0.
+ Name string
+ // RawName is the raw method name parsed from ABI.
+ RawName string
+ Const bool
+ Inputs Arguments
+ Outputs Arguments
+}
+
+// Sig returns the methods string signature according to the ABI spec.
+//
+// Example
+//
+// function foo(uint32 a, int b) = "foo(uint32,int256)"
+//
+// Please note that "int" is substitute for its canonical representation "int256"
+func (method Method) Sig() string {
+ types := make([]string, len(method.Inputs))
+ for i, input := range method.Inputs {
+ types[i] = input.Type.String()
+ }
+ return fmt.Sprintf("%v(%v)", method.RawName, strings.Join(types, ","))
+}
+
+func (method Method) String() string {
+ inputs := make([]string, len(method.Inputs))
+ for i, input := range method.Inputs {
+ inputs[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
+ }
+ outputs := make([]string, len(method.Outputs))
+ for i, output := range method.Outputs {
+ outputs[i] = output.Type.String()
+ if len(output.Name) > 0 {
+ outputs[i] += fmt.Sprintf(" %v", output.Name)
+ }
+ }
+ constant := ""
+ if method.Const {
+ constant = "constant "
+ }
+ return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.RawName, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", "))
+}
+
+// ID returns the canonical representation of the method's signature used by the
+// abi definition to identify method names and types.
+func (method Method) ID() []byte {
+ return crypto.Keccak256([]byte(method.Sig()))[:4]
+}
diff --git a/accounts/abi/numbers.go b/accounts/abi/numbers.go
new file mode 100644
index 0000000..c2e844c
--- /dev/null
+++ b/accounts/abi/numbers.go
@@ -0,0 +1,44 @@
+// 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 abi
+
+import (
+ "math/big"
+ "reflect"
+
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/common/math"
+)
+
+var (
+ bigT = reflect.TypeOf(&big.Int{})
+ derefbigT = reflect.TypeOf(big.Int{})
+ uint8T = reflect.TypeOf(uint8(0))
+ uint16T = reflect.TypeOf(uint16(0))
+ uint32T = reflect.TypeOf(uint32(0))
+ uint64T = reflect.TypeOf(uint64(0))
+ int8T = reflect.TypeOf(int8(0))
+ int16T = reflect.TypeOf(int16(0))
+ int32T = reflect.TypeOf(int32(0))
+ int64T = reflect.TypeOf(int64(0))
+ addressT = reflect.TypeOf(common.Address{})
+)
+
+// U256 converts a big Int into a 256bit EVM number.
+func U256(n *big.Int) []byte {
+ return math.PaddedBigBytes(math.U256(n), 32)
+}
diff --git a/accounts/abi/pack.go b/accounts/abi/pack.go
new file mode 100644
index 0000000..d88dde8
--- /dev/null
+++ b/accounts/abi/pack.go
@@ -0,0 +1,81 @@
+// 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 abi
+
+import (
+ "math/big"
+ "reflect"
+
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/common/math"
+)
+
+// packBytesSlice packs the given bytes as [L, V] as the canonical representation
+// bytes slice
+func packBytesSlice(bytes []byte, l int) []byte {
+ len := packNum(reflect.ValueOf(l))
+ return append(len, common.RightPadBytes(bytes, (l+31)/32*32)...)
+}
+
+// packElement packs the given reflect value according to the abi specification in
+// t.
+func packElement(t Type, reflectValue reflect.Value) []byte {
+ switch t.T {
+ case IntTy, UintTy:
+ return packNum(reflectValue)
+ case StringTy:
+ return packBytesSlice([]byte(reflectValue.String()), reflectValue.Len())
+ case AddressTy:
+ if reflectValue.Kind() == reflect.Array {
+ reflectValue = mustArrayToByteSlice(reflectValue)
+ }
+
+ return common.LeftPadBytes(reflectValue.Bytes(), 32)
+ case BoolTy:
+ if reflectValue.Bool() {
+ return math.PaddedBigBytes(common.Big1, 32)
+ }
+ return math.PaddedBigBytes(common.Big0, 32)
+ case BytesTy:
+ if reflectValue.Kind() == reflect.Array {
+ reflectValue = mustArrayToByteSlice(reflectValue)
+ }
+ return packBytesSlice(reflectValue.Bytes(), reflectValue.Len())
+ case FixedBytesTy, FunctionTy:
+ if reflectValue.Kind() == reflect.Array {
+ reflectValue = mustArrayToByteSlice(reflectValue)
+ }
+ return common.RightPadBytes(reflectValue.Bytes(), 32)
+ default:
+ panic("abi: fatal error")
+ }
+}
+
+// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation
+func packNum(value reflect.Value) []byte {
+ switch kind := value.Kind(); kind {
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return U256(new(big.Int).SetUint64(value.Uint()))
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return U256(big.NewInt(value.Int()))
+ case reflect.Ptr:
+ return U256(value.Interface().(*big.Int))
+ default:
+ panic("abi: fatal error")
+ }
+
+}
diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go
new file mode 100644
index 0000000..73ca8fa
--- /dev/null
+++ b/accounts/abi/reflect.go
@@ -0,0 +1,226 @@
+// 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 abi
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// indirect recursively dereferences the value until it either gets the value
+// or finds a big.Int
+func indirect(v reflect.Value) reflect.Value {
+ if v.Kind() == reflect.Ptr && v.Elem().Type() != derefbigT {
+ return indirect(v.Elem())
+ }
+ return v
+}
+
+// indirectInterfaceOrPtr recursively dereferences the value until value is not interface.
+func indirectInterfaceOrPtr(v reflect.Value) reflect.Value {
+ if (v.Kind() == reflect.Interface || v.Kind() == reflect.Ptr) && v.Elem().IsValid() {
+ return indirect(v.Elem())
+ }
+ return v
+}
+
+// reflectIntKind returns the reflect using the given size and
+// unsignedness.
+func reflectIntKindAndType(unsigned bool, size int) (reflect.Kind, reflect.Type) {
+ switch size {
+ case 8:
+ if unsigned {
+ return reflect.Uint8, uint8T
+ }
+ return reflect.Int8, int8T
+ case 16:
+ if unsigned {
+ return reflect.Uint16, uint16T
+ }
+ return reflect.Int16, int16T
+ case 32:
+ if unsigned {
+ return reflect.Uint32, uint32T
+ }
+ return reflect.Int32, int32T
+ case 64:
+ if unsigned {
+ return reflect.Uint64, uint64T
+ }
+ return reflect.Int64, int64T
+ }
+ return reflect.Ptr, bigT
+}
+
+// mustArrayToBytesSlice creates a new byte slice with the exact same size as value
+// and copies the bytes in value to the new slice.
+func mustArrayToByteSlice(value reflect.Value) reflect.Value {
+ slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len())
+ reflect.Copy(slice, value)
+ return slice
+}
+
+// set attempts to assign src to dst by either setting, copying or otherwise.
+//
+// set is a bit more lenient when it comes to assignment and doesn't force an as
+// strict ruleset as bare `reflect` does.
+func set(dst, src reflect.Value) error {
+ dstType, srcType := dst.Type(), src.Type()
+ switch {
+ case dstType.Kind() == reflect.Interface && dst.Elem().IsValid():
+ return set(dst.Elem(), src)
+ case dstType.Kind() == reflect.Ptr && dstType.Elem() != derefbigT:
+ return set(dst.Elem(), src)
+ case srcType.AssignableTo(dstType) && dst.CanSet():
+ dst.Set(src)
+ case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice:
+ return setSlice(dst, src)
+ default:
+ return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
+ }
+ return nil
+}
+
+// setSlice attempts to assign src to dst when slices are not assignable by default
+// e.g. src: [][]byte -> dst: [][15]byte
+func setSlice(dst, src reflect.Value) error {
+ slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
+ for i := 0; i < src.Len(); i++ {
+ v := src.Index(i)
+ reflect.Copy(slice.Index(i), v)
+ }
+
+ dst.Set(slice)
+ return nil
+}
+
+// requireAssignable assures that `dest` is a pointer and it's not an interface.
+func requireAssignable(dst, src reflect.Value) error {
+ if dst.Kind() != reflect.Ptr && dst.Kind() != reflect.Interface {
+ return fmt.Errorf("abi: cannot unmarshal %v into %v", src.Type(), dst.Type())
+ }
+ return nil
+}
+
+// requireUnpackKind verifies preconditions for unpacking `args` into `kind`
+func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
+ args Arguments) error {
+
+ switch k {
+ case reflect.Struct:
+ case reflect.Slice, reflect.Array:
+ if minLen := args.LengthNonIndexed(); v.Len() < minLen {
+ return fmt.Errorf("abi: insufficient number of elements in the list/array for unpack, want %d, got %d",
+ minLen, v.Len())
+ }
+ default:
+ return fmt.Errorf("abi: cannot unmarshal tuple into %v", t)
+ }
+ return nil
+}
+
+// mapArgNamesToStructFields maps a slice of argument names to struct fields.
+// first round: for each Exportable field that contains a `abi:""` tag
+// and this field name exists in the given argument name list, pair them together.
+// second round: for each argument name that has not been already linked,
+// find what variable is expected to be mapped into, if it exists and has not been
+// used, pair them.
+// Note this function assumes the given value is a struct value.
+func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[string]string, error) {
+ typ := value.Type()
+
+ abi2struct := make(map[string]string)
+ struct2abi := make(map[string]string)
+
+ // first round ~~~
+ for i := 0; i < typ.NumField(); i++ {
+ structFieldName := typ.Field(i).Name
+
+ // skip private struct fields.
+ if structFieldName[:1] != strings.ToUpper(structFieldName[:1]) {
+ continue
+ }
+ // skip fields that have no abi:"" tag.
+ var ok bool
+ var tagName string
+ if tagName, ok = typ.Field(i).Tag.Lookup("abi"); !ok {
+ continue
+ }
+ // check if tag is empty.
+ if tagName == "" {
+ return nil, fmt.Errorf("struct: abi tag in '%s' is empty", structFieldName)
+ }
+ // check which argument field matches with the abi tag.
+ found := false
+ for _, arg := range argNames {
+ if arg == tagName {
+ if abi2struct[arg] != "" {
+ return nil, fmt.Errorf("struct: abi tag in '%s' already mapped", structFieldName)
+ }
+ // pair them
+ abi2struct[arg] = structFieldName
+ struct2abi[structFieldName] = arg
+ found = true
+ }
+ }
+ // check if this tag has been mapped.
+ if !found {
+ return nil, fmt.Errorf("struct: abi tag '%s' defined but not found in abi", tagName)
+ }
+ }
+
+ // second round ~~~
+ for _, argName := range argNames {
+
+ structFieldName := ToCamelCase(argName)
+
+ if structFieldName == "" {
+ return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct")
+ }
+
+ // this abi has already been paired, skip it... unless there exists another, yet unassigned
+ // struct field with the same field name. If so, raise an error:
+ // abi: [ { "name": "value" } ]
+ // struct { Value *big.Int , Value1 *big.Int `abi:"value"`}
+ if abi2struct[argName] != "" {
+ if abi2struct[argName] != structFieldName &&
+ struct2abi[structFieldName] == "" &&
+ value.FieldByName(structFieldName).IsValid() {
+ return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", argName)
+ }
+ continue
+ }
+
+ // return an error if this struct field has already been paired.
+ if struct2abi[structFieldName] != "" {
+ return nil, fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", structFieldName)
+ }
+
+ if value.FieldByName(structFieldName).IsValid() {
+ // pair them
+ abi2struct[argName] = structFieldName
+ struct2abi[structFieldName] = argName
+ } else {
+ // not paired, but annotate as used, to detect cases like
+ // abi : [ { "name": "value" }, { "name": "_value" } ]
+ // struct { Value *big.Int }
+ struct2abi[structFieldName] = argName
+ }
+ }
+ return abi2struct, nil
+}
diff --git a/accounts/abi/type.go b/accounts/abi/type.go
new file mode 100644
index 0000000..597d314
--- /dev/null
+++ b/accounts/abi/type.go
@@ -0,0 +1,348 @@
+// 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 abi
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// Type enumerator
+const (
+ IntTy byte = iota
+ UintTy
+ BoolTy
+ StringTy
+ SliceTy
+ ArrayTy
+ TupleTy
+ AddressTy
+ FixedBytesTy
+ BytesTy
+ HashTy
+ FixedPointTy
+ FunctionTy
+)
+
+// Type is the reflection of the supported argument type
+type Type struct {
+ Elem *Type
+ Kind reflect.Kind
+ Type reflect.Type
+ Size int
+ T byte // Our own type checking
+
+ stringKind string // holds the unparsed string for deriving signatures
+
+ // Tuple relative fields
+ TupleElems []*Type // Type information of all tuple fields
+ TupleRawNames []string // Raw field name of all tuple fields
+}
+
+var (
+ // typeRegex parses the abi sub types
+ typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
+)
+
+// NewType creates a new reflection type of abi type given in t.
+func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
+ // check that array brackets are equal if they exist
+ if strings.Count(t, "[") != strings.Count(t, "]") {
+ return Type{}, fmt.Errorf("invalid arg type in abi")
+ }
+ typ.stringKind = t
+
+ // if there are brackets, get ready to go into slice/array mode and
+ // recursively create the type
+ if strings.Count(t, "[") != 0 {
+ i := strings.LastIndex(t, "[")
+ // recursively embed the type
+ embeddedType, err := NewType(t[:i], components)
+ if err != nil {
+ return Type{}, err
+ }
+ // grab the last cell and create a type from there
+ sliced := t[i:]
+ // grab the slice size with regexp
+ re := regexp.MustCompile("[0-9]+")
+ intz := re.FindAllString(sliced, -1)
+
+ if len(intz) == 0 {
+ // is a slice
+ typ.T = SliceTy
+ typ.Kind = reflect.Slice
+ typ.Elem = &embeddedType
+ typ.Type = reflect.SliceOf(embeddedType.Type)
+ typ.stringKind = embeddedType.stringKind + sliced
+ } else if len(intz) == 1 {
+ // is a array
+ typ.T = ArrayTy
+ typ.Kind = reflect.Array
+ typ.Elem = &embeddedType
+ typ.Size, err = strconv.Atoi(intz[0])
+ if err != nil {
+ return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
+ }
+ typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type)
+ typ.stringKind = embeddedType.stringKind + sliced
+ } else {
+ return Type{}, fmt.Errorf("invalid formatting of array type")
+ }
+ return typ, err
+ }
+ // parse the type and size of the abi-type.
+ matches := typeRegex.FindAllStringSubmatch(t, -1)
+ if len(matches) == 0 {
+ return Type{}, fmt.Errorf("invalid type '%v'", t)
+ }
+ parsedType := matches[0]
+
+ // varSize is the size of the variable
+ var varSize int
+ if len(parsedType[3]) > 0 {
+ var err error
+ varSize, err = strconv.Atoi(parsedType[2])
+ if err != nil {
+ return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
+ }
+ } else {
+ if parsedType[0] == "uint" || parsedType[0] == "int" {
+ // this should fail because it means that there's something wrong with
+ // the abi type (the compiler should always format it to the size...always)
+ return Type{}, fmt.Errorf("unsupported arg type: %s", t)
+ }
+ }
+ // varType is the parsed abi type
+ switch varType := parsedType[1]; varType {
+ case "int":
+ typ.Kind, typ.Type = reflectIntKindAndType(false, varSize)
+ typ.Size = varSize
+ typ.T = IntTy
+ case "uint":
+ typ.Kind, typ.Type = reflectIntKindAndType(true, varSize)
+ typ.Size = varSize
+ typ.T = UintTy
+ case "bool":
+ typ.Kind = reflect.Bool
+ typ.T = BoolTy
+ typ.Type = reflect.TypeOf(bool(false))
+ case "address":
+ typ.Kind = reflect.Array
+ typ.Type = addressT
+ typ.Size = 20
+ typ.T = AddressTy
+ case "string":
+ typ.Kind = reflect.String
+ typ.Type = reflect.TypeOf("")
+ typ.T = StringTy
+ case "bytes":
+ if varSize == 0 {
+ typ.T = BytesTy
+ typ.Kind = reflect.Slice
+ typ.Type = reflect.SliceOf(reflect.TypeOf(byte(0)))
+ } else {
+ typ.T = FixedBytesTy
+ typ.Kind = reflect.Array
+ typ.Size = varSize
+ typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0)))
+ }
+ case "tuple":
+ var (
+ fields []reflect.StructField
+ elems []*Type
+ names []string
+ expression string // canonical parameter expression
+ )
+ expression += "("
+ for idx, c := range components {
+ cType, err := NewType(c.Type, c.Components)
+ if err != nil {
+ return Type{}, err
+ }
+ if ToCamelCase(c.Name) == "" {
+ return Type{}, errors.New("abi: purely anonymous or underscored field is not supported")
+ }
+ fields = append(fields, reflect.StructField{
+ Name: ToCamelCase(c.Name), // reflect.StructOf will panic for any exported field.
+ Type: cType.Type,
+ Tag: reflect.StructTag("json:\"" + c.Name + "\""),
+ })
+ elems = append(elems, &cType)
+ names = append(names, c.Name)
+ expression += cType.stringKind
+ if idx != len(components)-1 {
+ expression += ","
+ }
+ }
+ expression += ")"
+ typ.Kind = reflect.Struct
+ typ.Type = reflect.StructOf(fields)
+ typ.TupleElems = elems
+ typ.TupleRawNames = names
+ typ.T = TupleTy
+ typ.stringKind = expression
+ case "function":
+ typ.Kind = reflect.Array
+ typ.T = FunctionTy
+ typ.Size = 24
+ typ.Type = reflect.ArrayOf(24, reflect.TypeOf(byte(0)))
+ default:
+ return Type{}, fmt.Errorf("unsupported arg type: %s", t)
+ }
+
+ return
+}
+
+// String implements Stringer
+func (t Type) String() (out string) {
+ return t.stringKind
+}
+
+func (t Type) pack(v reflect.Value) ([]byte, error) {
+ // dereference pointer first if it's a pointer
+ v = indirect(v)
+ if err := typeCheck(t, v); err != nil {
+ return nil, err
+ }
+
+ switch t.T {
+ case SliceTy, ArrayTy:
+ var ret []byte
+
+ if t.requiresLengthPrefix() {
+ // append length
+ ret = append(ret, packNum(reflect.ValueOf(v.Len()))...)
+ }
+
+ // calculate offset if any
+ offset := 0
+ offsetReq := isDynamicType(*t.Elem)
+ if offsetReq {
+ offset = getTypeSize(*t.Elem) * v.Len()
+ }
+ var tail []byte
+ for i := 0; i < v.Len(); i++ {
+ val, err := t.Elem.pack(v.Index(i))
+ if err != nil {
+ return nil, err
+ }
+ if !offsetReq {
+ ret = append(ret, val...)
+ continue
+ }
+ ret = append(ret, packNum(reflect.ValueOf(offset))...)
+ offset += len(val)
+ tail = append(tail, val...)
+ }
+ return append(ret, tail...), nil
+ case TupleTy:
+ // (T1,...,Tk) for k >= 0 and any types T1, …, Tk
+ // enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k))
+ // where X = (X(1), ..., X(k)) and head and tail are defined for Ti being a static
+ // type as
+ // head(X(i)) = enc(X(i)) and tail(X(i)) = "" (the empty string)
+ // and as
+ // head(X(i)) = enc(len(head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(i-1))))
+ // tail(X(i)) = enc(X(i))
+ // otherwise, i.e. if Ti is a dynamic type.
+ fieldmap, err := mapArgNamesToStructFields(t.TupleRawNames, v)
+ if err != nil {
+ return nil, err
+ }
+ // Calculate prefix occupied size.
+ offset := 0
+ for _, elem := range t.TupleElems {
+ offset += getTypeSize(*elem)
+ }
+ var ret, tail []byte
+ for i, elem := range t.TupleElems {
+ field := v.FieldByName(fieldmap[t.TupleRawNames[i]])
+ if !field.IsValid() {
+ return nil, fmt.Errorf("field %s for tuple not found in the given struct", t.TupleRawNames[i])
+ }
+ val, err := elem.pack(field)
+ if err != nil {
+ return nil, err
+ }
+ if isDynamicType(*elem) {
+ ret = append(ret, packNum(reflect.ValueOf(offset))...)
+ tail = append(tail, val...)
+ offset += len(val)
+ } else {
+ ret = append(ret, val...)
+ }
+ }
+ return append(ret, tail...), nil
+
+ default:
+ return packElement(t, v), nil
+ }
+}
+
+// requireLengthPrefix returns whether the type requires any sort of length
+// prefixing.
+func (t Type) requiresLengthPrefix() bool {
+ return t.T == StringTy || t.T == BytesTy || t.T == SliceTy
+}
+
+// isDynamicType returns true if the type is dynamic.
+// The following types are called “dynamic”:
+// * bytes
+// * string
+// * T[] for any T
+// * T[k] for any dynamic T and any k >= 0
+// * (T1,...,Tk) if Ti is dynamic for some 1 <= i <= k
+func isDynamicType(t Type) bool {
+ if t.T == TupleTy {
+ for _, elem := range t.TupleElems {
+ if isDynamicType(*elem) {
+ return true
+ }
+ }
+ return false
+ }
+ return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem))
+}
+
+// getTypeSize returns the size that this type needs to occupy.
+// We distinguish static and dynamic types. Static types are encoded in-place
+// and dynamic types are encoded at a separately allocated location after the
+// current block.
+// So for a static variable, the size returned represents the size that the
+// variable actually occupies.
+// For a dynamic variable, the returned size is fixed 32 bytes, which is used
+// to store the location reference for actual value storage.
+func getTypeSize(t Type) int {
+ if t.T == ArrayTy && !isDynamicType(*t.Elem) {
+ // Recursively calculate type size if it is a nested array
+ if t.Elem.T == ArrayTy {
+ return t.Size * getTypeSize(*t.Elem)
+ }
+ return t.Size * 32
+ } else if t.T == TupleTy && !isDynamicType(t) {
+ total := 0
+ for _, elem := range t.TupleElems {
+ total += getTypeSize(*elem)
+ }
+ return total
+ }
+ return 32
+}
diff --git a/accounts/abi/unpack.go b/accounts/abi/unpack.go
new file mode 100644
index 0000000..d3cd310
--- /dev/null
+++ b/accounts/abi/unpack.go
@@ -0,0 +1,295 @@
+// Copyright 2017 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 abi
+
+import (
+ "encoding/binary"
+ "fmt"
+ "math/big"
+ "reflect"
+
+ "github.com/ava-labs/go-ethereum/common"
+)
+
+var (
+ maxUint256 = big.NewInt(0).Add(
+ big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), nil),
+ big.NewInt(-1))
+ maxInt256 = big.NewInt(0).Add(
+ big.NewInt(0).Exp(big.NewInt(2), big.NewInt(255), nil),
+ big.NewInt(-1))
+)
+
+// reads the integer based on its kind
+func readInteger(typ byte, kind reflect.Kind, b []byte) interface{} {
+ switch kind {
+ case reflect.Uint8:
+ return b[len(b)-1]
+ case reflect.Uint16:
+ return binary.BigEndian.Uint16(b[len(b)-2:])
+ case reflect.Uint32:
+ return binary.BigEndian.Uint32(b[len(b)-4:])
+ case reflect.Uint64:
+ return binary.BigEndian.Uint64(b[len(b)-8:])
+ case reflect.Int8:
+ return int8(b[len(b)-1])
+ case reflect.Int16:
+ return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
+ case reflect.Int32:
+ return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
+ case reflect.Int64:
+ return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
+ default:
+ // the only case lefts for integer is int256/uint256.
+ // big.SetBytes can't tell if a number is negative, positive on itself.
+ // On EVM, if the returned number > max int256, it is negative.
+ ret := new(big.Int).SetBytes(b)
+ if typ == UintTy {
+ return ret
+ }
+
+ if ret.Cmp(maxInt256) > 0 {
+ ret.Add(maxUint256, big.NewInt(0).Neg(ret))
+ ret.Add(ret, big.NewInt(1))
+ ret.Neg(ret)
+ }
+ return ret
+ }
+}
+
+// reads a bool
+func readBool(word []byte) (bool, error) {
+ for _, b := range word[:31] {
+ if b != 0 {
+ return false, errBadBool
+ }
+ }
+ switch word[31] {
+ case 0:
+ return false, nil
+ case 1:
+ return true, nil
+ default:
+ return false, errBadBool
+ }
+}
+
+// A function type is simply the address with the function selection signature at the end.
+// This enforces that standard by always presenting it as a 24-array (address + sig = 24 bytes)
+func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
+ if t.T != FunctionTy {
+ return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array")
+ }
+ if garbage := binary.BigEndian.Uint64(word[24:32]); garbage != 0 {
+ err = fmt.Errorf("abi: got improperly encoded function type, got %v", word)
+ } else {
+ copy(funcTy[:], word[0:24])
+ }
+ return
+}
+
+// through reflection, creates a fixed array to be read from
+func readFixedBytes(t Type, word []byte) (interface{}, error) {
+ if t.T != FixedBytesTy {
+ return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array")
+ }
+ // convert
+ array := reflect.New(t.Type).Elem()
+
+ reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
+ return array.Interface(), nil
+
+}
+
+// iteratively unpack elements
+func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
+ if size < 0 {
+ return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size)
+ }
+ if start+32*size > len(output) {
+ return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
+ }
+
+ // this value will become our slice or our array, depending on the type
+ var refSlice reflect.Value
+
+ if t.T == SliceTy {
+ // declare our slice
+ refSlice = reflect.MakeSlice(t.Type, size, size)
+ } else if t.T == ArrayTy {
+ // declare our array
+ refSlice = reflect.New(t.Type).Elem()
+ } else {
+ return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
+ }
+
+ // Arrays have packed elements, resulting in longer unpack steps.
+ // Slices have just 32 bytes per element (pointing to the contents).
+ elemSize := getTypeSize(*t.Elem)
+
+ for i, j := start, 0; j < size; i, j = i+elemSize, j+1 {
+ inter, err := toGoType(i, *t.Elem, output)
+ if err != nil {
+ return nil, err
+ }
+
+ // append the item to our reflect slice
+ refSlice.Index(j).Set(reflect.ValueOf(inter))
+ }
+
+ // return the interface
+ return refSlice.Interface(), nil
+}
+
+func forTupleUnpack(t Type, output []byte) (interface{}, error) {
+ retval := reflect.New(t.Type).Elem()
+ virtualArgs := 0
+ for index, elem := range t.TupleElems {
+ marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
+ if elem.T == ArrayTy && !isDynamicType(*elem) {
+ // If we have a static array, like [3]uint256, these are coded as
+ // just like uint256,uint256,uint256.
+ // This means that we need to add two 'virtual' arguments when
+ // we count the index from now on.
+ //
+ // Array values nested multiple levels deep are also encoded inline:
+ // [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256
+ //
+ // Calculate the full array size to get the correct offset for the next argument.
+ // Decrement it by 1, as the normal index increment is still applied.
+ virtualArgs += getTypeSize(*elem)/32 - 1
+ } else if elem.T == TupleTy && !isDynamicType(*elem) {
+ // If we have a static tuple, like (uint256, bool, uint256), these are
+ // coded as just like uint256,bool,uint256
+ virtualArgs += getTypeSize(*elem)/32 - 1
+ }
+ if err != nil {
+ return nil, err
+ }
+ retval.Field(index).Set(reflect.ValueOf(marshalledValue))
+ }
+ return retval.Interface(), nil
+}
+
+// toGoType parses the output bytes and recursively assigns the value of these bytes
+// into a go type with accordance with the ABI spec.
+func toGoType(index int, t Type, output []byte) (interface{}, error) {
+ if index+32 > len(output) {
+ return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
+ }
+
+ var (
+ returnOutput []byte
+ begin, length int
+ err error
+ )
+
+ // if we require a length prefix, find the beginning word and size returned.
+ if t.requiresLengthPrefix() {
+ begin, length, err = lengthPrefixPointsTo(index, output)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ returnOutput = output[index : index+32]
+ }
+
+ switch t.T {
+ case TupleTy:
+ if isDynamicType(t) {
+ begin, err := tuplePointsTo(index, output)
+ if err != nil {
+ return nil, err
+ }
+ return forTupleUnpack(t, output[begin:])
+ } else {
+ return forTupleUnpack(t, output[index:])
+ }
+ case SliceTy:
+ return forEachUnpack(t, output[begin:], 0, length)
+ case ArrayTy:
+ if isDynamicType(*t.Elem) {
+ offset := int64(binary.BigEndian.Uint64(returnOutput[len(returnOutput)-8:]))
+ return forEachUnpack(t, output[offset:], 0, t.Size)
+ }
+ return forEachUnpack(t, output[index:], 0, t.Size)
+ case StringTy: // variable arrays are written at the end of the return bytes
+ return string(output[begin : begin+length]), nil
+ case IntTy, UintTy:
+ return readInteger(t.T, t.Kind, returnOutput), nil
+ case BoolTy:
+ return readBool(returnOutput)
+ case AddressTy:
+ return common.BytesToAddress(returnOutput), nil
+ case HashTy:
+ return common.BytesToHash(returnOutput), nil
+ case BytesTy:
+ return output[begin : begin+length], nil
+ case FixedBytesTy:
+ return readFixedBytes(t, returnOutput)
+ case FunctionTy:
+ return readFunctionType(t, returnOutput)
+ default:
+ return nil, fmt.Errorf("abi: unknown type %v", t.T)
+ }
+}
+
+// interprets a 32 byte slice as an offset and then determines which indice to look to decode the type.
+func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
+ bigOffsetEnd := big.NewInt(0).SetBytes(output[index : index+32])
+ bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
+ outputLength := big.NewInt(int64(len(output)))
+
+ if bigOffsetEnd.Cmp(outputLength) > 0 {
+ return 0, 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", bigOffsetEnd, outputLength)
+ }
+
+ if bigOffsetEnd.BitLen() > 63 {
+ return 0, 0, fmt.Errorf("abi offset larger than int64: %v", bigOffsetEnd)
+ }
+
+ offsetEnd := int(bigOffsetEnd.Uint64())
+ lengthBig := big.NewInt(0).SetBytes(output[offsetEnd-32 : offsetEnd])
+
+ totalSize := big.NewInt(0)
+ totalSize.Add(totalSize, bigOffsetEnd)
+ totalSize.Add(totalSize, lengthBig)
+ if totalSize.BitLen() > 63 {
+ return 0, 0, fmt.Errorf("abi: length larger than int64: %v", totalSize)
+ }
+
+ if totalSize.Cmp(outputLength) > 0 {
+ return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %v require %v", outputLength, totalSize)
+ }
+ start = int(bigOffsetEnd.Uint64())
+ length = int(lengthBig.Uint64())
+ return
+}
+
+// tuplePointsTo resolves the location reference for dynamic tuple.
+func tuplePointsTo(index int, output []byte) (start int, err error) {
+ offset := big.NewInt(0).SetBytes(output[index : index+32])
+ outputLen := big.NewInt(int64(len(output)))
+
+ if offset.Cmp(big.NewInt(int64(len(output)))) > 0 {
+ return 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", offset, outputLen)
+ }
+ if offset.BitLen() > 63 {
+ return 0, fmt.Errorf("abi offset larger than int64: %v", offset)
+ }
+ return int(offset.Uint64()), nil
+}
diff --git a/accounts/accounts.go b/accounts/accounts.go
new file mode 100644
index 0000000..bca3223
--- /dev/null
+++ b/accounts/accounts.go
@@ -0,0 +1,222 @@
+// Copyright 2017 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 accounts implements high level Ethereum account management.
+package accounts
+
+import (
+ "fmt"
+ "math/big"
+
+ "github.com/ava-labs/coreth/core/types"
+ ethereum "github.com/ava-labs/go-ethereum"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/event"
+ "golang.org/x/crypto/sha3"
+)
+
+// Account represents an Ethereum account located at a specific location defined
+// by the optional URL field.
+type Account struct {
+ Address common.Address `json:"address"` // Ethereum account address derived from the key
+ URL URL `json:"url"` // Optional resource locator within a backend
+}
+
+const (
+ MimetypeDataWithValidator = "data/validator"
+ MimetypeTypedData = "data/typed"
+ MimetypeClique = "application/x-clique-header"
+ MimetypeTextPlain = "text/plain"
+)
+
+// Wallet represents a software or hardware wallet that might contain one or more
+// accounts (derived from the same seed).
+type Wallet interface {
+ // URL retrieves the canonical path under which this wallet is reachable. It is
+ // user by upper layers to define a sorting order over all wallets from multiple
+ // backends.
+ URL() URL
+
+ // Status returns a textual status to aid the user in the current state of the
+ // wallet. It also returns an error indicating any failure the wallet might have
+ // encountered.
+ Status() (string, error)
+
+ // Open initializes access to a wallet instance. It is not meant to unlock or
+ // decrypt account keys, rather simply to establish a connection to hardware
+ // wallets and/or to access derivation seeds.
+ //
+ // The passphrase parameter may or may not be used by the implementation of a
+ // particular wallet instance. The reason there is no passwordless open method
+ // is to strive towards a uniform wallet handling, oblivious to the different
+ // backend providers.
+ //
+ // Please note, if you open a wallet, you must close it to release any allocated
+ // resources (especially important when working with hardware wallets).
+ Open(passphrase string) error
+
+ // Close releases any resources held by an open wallet instance.
+ Close() error
+
+ // Accounts retrieves the list of signing accounts the wallet is currently aware
+ // of. For hierarchical deterministic wallets, the list will not be exhaustive,
+ // rather only contain the accounts explicitly pinned during account derivation.
+ Accounts() []Account
+
+ // Contains returns whether an account is part of this particular wallet or not.
+ Contains(account Account) bool
+
+ // Derive attempts to explicitly derive a hierarchical deterministic account at
+ // the specified derivation path. If requested, the derived account will be added
+ // to the wallet's tracked account list.
+ Derive(path DerivationPath, pin bool) (Account, error)
+
+ // SelfDerive sets a base account derivation path from which the wallet attempts
+ // to discover non zero accounts and automatically add them to list of tracked
+ // accounts.
+ //
+ // Note, self derivaton will increment the last component of the specified path
+ // opposed to decending into a child path to allow discovering accounts starting
+ // from non zero components.
+ //
+ // Some hardware wallets switched derivation paths through their evolution, so
+ // this method supports providing multiple bases to discover old user accounts
+ // too. Only the last base will be used to derive the next empty account.
+ //
+ // You can disable automatic account discovery by calling SelfDerive with a nil
+ // chain state reader.
+ SelfDerive(bases []DerivationPath, chain ethereum.ChainStateReader)
+
+ // SignData requests the wallet to sign the hash of the given data
+ // It looks up the account specified either solely via its address contained within,
+ // or optionally with the aid of any location metadata from the embedded URL field.
+ //
+ // If the wallet requires additional authentication to sign the request (e.g.
+ // a password to decrypt the account, or a PIN code o verify the transaction),
+ // an AuthNeededError instance will be returned, containing infos for the user
+ // about which fields or actions are needed. The user may retry by providing
+ // the needed details via SignDataWithPassphrase, or by other means (e.g. unlock
+ // the account in a keystore).
+ SignData(account Account, mimeType string, data []byte) ([]byte, error)
+
+ // SignDataWithPassphrase is identical to SignData, but also takes a password
+ // NOTE: there's an chance that an erroneous call might mistake the two strings, and
+ // supply password in the mimetype field, or vice versa. Thus, an implementation
+ // should never echo the mimetype or return the mimetype in the error-response
+ SignDataWithPassphrase(account Account, passphrase, mimeType string, data []byte) ([]byte, error)
+
+ // SignText requests the wallet to sign the hash of a given piece of data, prefixed
+ // by the Ethereum prefix scheme
+ // It looks up the account specified either solely via its address contained within,
+ // or optionally with the aid of any location metadata from the embedded URL field.
+ //
+ // If the wallet requires additional authentication to sign the request (e.g.
+ // a password to decrypt the account, or a PIN code o verify the transaction),
+ // an AuthNeededError instance will be returned, containing infos for the user
+ // about which fields or actions are needed. The user may retry by providing
+ // the needed details via SignHashWithPassphrase, or by other means (e.g. unlock
+ // the account in a keystore).
+ SignText(account Account, text []byte) ([]byte, error)
+
+ // SignTextWithPassphrase is identical to Signtext, but also takes a password
+ SignTextWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error)
+
+ // SignTx requests the wallet to sign the given transaction.
+ //
+ // It looks up the account specified either solely via its address contained within,
+ // or optionally with the aid of any location metadata from the embedded URL field.
+ //
+ // If the wallet requires additional authentication to sign the request (e.g.
+ // a password to decrypt the account, or a PIN code to verify the transaction),
+ // an AuthNeededError instance will be returned, containing infos for the user
+ // about which fields or actions are needed. The user may retry by providing
+ // the needed details via SignTxWithPassphrase, or by other means (e.g. unlock
+ // the account in a keystore).
+ SignTx(account Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
+
+ // SignTxWithPassphrase is identical to SignTx, but also takes a password
+ SignTxWithPassphrase(account Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
+}
+
+// Backend is a "wallet provider" that may contain a batch of accounts they can
+// sign transactions with and upon request, do so.
+type Backend interface {
+ // Wallets retrieves the list of wallets the backend is currently aware of.
+ //
+ // The returned wallets are not opened by default. For software HD wallets this
+ // means that no base seeds are decrypted, and for hardware wallets that no actual
+ // connection is established.
+ //
+ // The resulting wallet list will be sorted alphabetically based on its internal
+ // URL assigned by the backend. Since wallets (especially hardware) may come and
+ // go, the same wallet might appear at a different positions in the list during
+ // subsequent retrievals.
+ Wallets() []Wallet
+
+ // Subscribe creates an async subscription to receive notifications when the
+ // backend detects the arrival or departure of a wallet.
+ Subscribe(sink chan<- WalletEvent) event.Subscription
+}
+
+// TextHash is a helper function that calculates a hash for the given message that can be
+// safely used to calculate a signature from.
+//
+// The hash is calulcated as
+// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
+//
+// This gives context to the signed message and prevents signing of transactions.
+func TextHash(data []byte) []byte {
+ hash, _ := TextAndHash(data)
+ return hash
+}
+
+// TextAndHash is a helper function that calculates a hash for the given message that can be
+// safely used to calculate a signature from.
+//
+// The hash is calulcated as
+// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
+//
+// This gives context to the signed message and prevents signing of transactions.
+func TextAndHash(data []byte) ([]byte, string) {
+ msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), string(data))
+ hasher := sha3.NewLegacyKeccak256()
+ hasher.Write([]byte(msg))
+ return hasher.Sum(nil), msg
+}
+
+// WalletEventType represents the different event types that can be fired by
+// the wallet subscription subsystem.
+type WalletEventType int
+
+const (
+ // WalletArrived is fired when a new wallet is detected either via USB or via
+ // a filesystem event in the keystore.
+ WalletArrived WalletEventType = iota
+
+ // WalletOpened is fired when a wallet is successfully opened with the purpose
+ // of starting any background processes such as automatic key derivation.
+ WalletOpened
+
+ // WalletDropped
+ WalletDropped
+)
+
+// WalletEvent is an event fired by an account backend when a wallet arrival or
+// departure is detected.
+type WalletEvent struct {
+ Wallet Wallet // Wallet instance arrived or departed
+ Kind WalletEventType // Event type that happened in the system
+}
diff --git a/accounts/errors.go b/accounts/errors.go
new file mode 100644
index 0000000..2fed35f
--- /dev/null
+++ b/accounts/errors.go
@@ -0,0 +1,68 @@
+// Copyright 2017 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 accounts
+
+import (
+ "errors"
+ "fmt"
+)
+
+// ErrUnknownAccount is returned for any requested operation for which no backend
+// provides the specified account.
+var ErrUnknownAccount = errors.New("unknown account")
+
+// ErrUnknownWallet is returned for any requested operation for which no backend
+// provides the specified wallet.
+var ErrUnknownWallet = errors.New("unknown wallet")
+
+// ErrNotSupported is returned when an operation is requested from an account
+// backend that it does not support.
+var ErrNotSupported = errors.New("not supported")
+
+// ErrInvalidPassphrase is returned when a decryption operation receives a bad
+// passphrase.
+var ErrInvalidPassphrase = errors.New("invalid password")
+
+// ErrWalletAlreadyOpen is returned if a wallet is attempted to be opened the
+// second time.
+var ErrWalletAlreadyOpen = errors.New("wallet already open")
+
+// ErrWalletClosed is returned if a wallet is attempted to be opened the
+// secodn time.
+var ErrWalletClosed = errors.New("wallet closed")
+
+// AuthNeededError is returned by backends for signing requests where the user
+// is required to provide further authentication before signing can succeed.
+//
+// This usually means either that a password needs to be supplied, or perhaps a
+// one time PIN code displayed by some hardware device.
+type AuthNeededError struct {
+ Needed string // Extra authentication the user needs to provide
+}
+
+// NewAuthNeededError creates a new authentication error with the extra details
+// about the needed fields set.
+func NewAuthNeededError(needed string) error {
+ return &AuthNeededError{
+ Needed: needed,
+ }
+}
+
+// Error implements the standard error interface.
+func (err *AuthNeededError) Error() string {
+ return fmt.Sprintf("authentication needed: %s", err.Needed)
+}
diff --git a/accounts/external/backend.go b/accounts/external/backend.go
new file mode 100644
index 0000000..16e201d
--- /dev/null
+++ b/accounts/external/backend.go
@@ -0,0 +1,231 @@
+// Copyright 2019 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 external
+
+import (
+ "fmt"
+ "math/big"
+ "sync"
+
+ "github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/coreth/internal/ethapi"
+ "github.com/ava-labs/coreth/rpc"
+ "github.com/ava-labs/go-ethereum"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/common/hexutil"
+ "github.com/ava-labs/go-ethereum/event"
+ "github.com/ava-labs/go-ethereum/log"
+ "github.com/ava-labs/go-ethereum/signer/core"
+)
+
+type ExternalBackend struct {
+ signers []accounts.Wallet
+}
+
+func (eb *ExternalBackend) Wallets() []accounts.Wallet {
+ return eb.signers
+}
+
+func NewExternalBackend(endpoint string) (*ExternalBackend, error) {
+ signer, err := NewExternalSigner(endpoint)
+ if err != nil {
+ return nil, err
+ }
+ return &ExternalBackend{
+ signers: []accounts.Wallet{signer},
+ }, nil
+}
+
+func (eb *ExternalBackend) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ <-quit
+ return nil
+ })
+}
+
+// ExternalSigner provides an API to interact with an external signer (clef)
+// It proxies request to the external signer while forwarding relevant
+// request headers
+type ExternalSigner struct {
+ client *rpc.Client
+ endpoint string
+ status string
+ cacheMu sync.RWMutex
+ cache []accounts.Account
+}
+
+func NewExternalSigner(endpoint string) (*ExternalSigner, error) {
+ client, err := rpc.Dial(endpoint)
+ if err != nil {
+ return nil, err
+ }
+ extsigner := &ExternalSigner{
+ client: client,
+ endpoint: endpoint,
+ }
+ // Check if reachable
+ version, err := extsigner.pingVersion()
+ if err != nil {
+ return nil, err
+ }
+ extsigner.status = fmt.Sprintf("ok [version=%v]", version)
+ return extsigner, nil
+}
+
+func (api *ExternalSigner) URL() accounts.URL {
+ return accounts.URL{
+ Scheme: "extapi",
+ Path: api.endpoint,
+ }
+}
+
+func (api *ExternalSigner) Status() (string, error) {
+ return api.status, nil
+}
+
+func (api *ExternalSigner) Open(passphrase string) error {
+ return fmt.Errorf("operation not supported on external signers")
+}
+
+func (api *ExternalSigner) Close() error {
+ return fmt.Errorf("operation not supported on external signers")
+}
+
+func (api *ExternalSigner) Accounts() []accounts.Account {
+ var accnts []accounts.Account
+ res, err := api.listAccounts()
+ if err != nil {
+ log.Error("account listing failed", "error", err)
+ return accnts
+ }
+ for _, addr := range res {
+ accnts = append(accnts, accounts.Account{
+ URL: accounts.URL{
+ Scheme: "extapi",
+ Path: api.endpoint,
+ },
+ Address: addr,
+ })
+ }
+ api.cacheMu.Lock()
+ api.cache = accnts
+ api.cacheMu.Unlock()
+ return accnts
+}
+
+func (api *ExternalSigner) Contains(account accounts.Account) bool {
+ api.cacheMu.RLock()
+ defer api.cacheMu.RUnlock()
+ for _, a := range api.cache {
+ if a.Address == account.Address && (account.URL == (accounts.URL{}) || account.URL == api.URL()) {
+ return true
+ }
+ }
+ return false
+}
+
+func (api *ExternalSigner) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
+ return accounts.Account{}, fmt.Errorf("operation not supported on external signers")
+}
+
+func (api *ExternalSigner) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) {
+ log.Error("operation SelfDerive not supported on external signers")
+}
+
+func (api *ExternalSigner) signHash(account accounts.Account, hash []byte) ([]byte, error) {
+ return []byte{}, fmt.Errorf("operation not supported on external signers")
+}
+
+// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
+func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
+ var res hexutil.Bytes
+ var signAddress = common.NewMixedcaseAddress(account.Address)
+ if err := api.client.Call(&res, "account_signData",
+ mimeType,
+ &signAddress, // Need to use the pointer here, because of how MarshalJSON is defined
+ hexutil.Encode(data)); err != nil {
+ return nil, err
+ }
+ // If V is on 27/28-form, convert to to 0/1 for Clique
+ if mimeType == accounts.MimetypeClique && (res[64] == 27 || res[64] == 28) {
+ res[64] -= 27 // Transform V from 27/28 to 0/1 for Clique use
+ }
+ return res, nil
+}
+
+func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) {
+ var res hexutil.Bytes
+ var signAddress = common.NewMixedcaseAddress(account.Address)
+ if err := api.client.Call(&res, "account_signData",
+ accounts.MimetypeTextPlain,
+ &signAddress, // Need to use the pointer here, because of how MarshalJSON is defined
+ hexutil.Encode(text)); err != nil {
+ return nil, err
+ }
+ return res, nil
+}
+
+func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ res := ethapi.SignTransactionResult{}
+ data := hexutil.Bytes(tx.Data())
+ var to *common.MixedcaseAddress
+ if tx.To() != nil {
+ t := common.NewMixedcaseAddress(*tx.To())
+ to = &t
+ }
+ args := &core.SendTxArgs{
+ Data: &data,
+ Nonce: hexutil.Uint64(tx.Nonce()),
+ Value: hexutil.Big(*tx.Value()),
+ Gas: hexutil.Uint64(tx.Gas()),
+ GasPrice: hexutil.Big(*tx.GasPrice()),
+ To: to,
+ From: common.NewMixedcaseAddress(account.Address),
+ }
+ if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
+ return nil, err
+ }
+ return res.Tx, nil
+}
+
+func (api *ExternalSigner) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
+ return []byte{}, fmt.Errorf("password-operations not supported on external signers")
+}
+
+func (api *ExternalSigner) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ return nil, fmt.Errorf("password-operations not supported on external signers")
+}
+func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
+ return nil, fmt.Errorf("password-operations not supported on external signers")
+}
+
+func (api *ExternalSigner) listAccounts() ([]common.Address, error) {
+ var res []common.Address
+ if err := api.client.Call(&res, "account_list"); err != nil {
+ return nil, err
+ }
+ return res, nil
+}
+
+func (api *ExternalSigner) pingVersion() (string, error) {
+ var v string
+ if err := api.client.Call(&v, "account_version"); err != nil {
+ return "", err
+ }
+ return v, nil
+}
diff --git a/accounts/hd.go b/accounts/hd.go
new file mode 100644
index 0000000..75c4761
--- /dev/null
+++ b/accounts/hd.go
@@ -0,0 +1,152 @@
+// Copyright 2017 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 accounts
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math"
+ "math/big"
+ "strings"
+)
+
+// DefaultRootDerivationPath is the root path to which custom derivation endpoints
+// are appended. As such, the first account will be at m/44'/60'/0'/0, the second
+// at m/44'/60'/0'/1, etc.
+var DefaultRootDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
+
+// DefaultBaseDerivationPath is the base path from which custom derivation endpoints
+// are incremented. As such, the first account will be at m/44'/60'/0'/0/0, the second
+// at m/44'/60'/0'/0/1, etc.
+var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0}
+
+// LegacyLedgerBaseDerivationPath is the legacy base path from which custom derivation
+// endpoints are incremented. As such, the first account will be at m/44'/60'/0'/0, the
+// second at m/44'/60'/0'/1, etc.
+var LegacyLedgerBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
+
+// DerivationPath represents the computer friendly version of a hierarchical
+// deterministic wallet account derivaion path.
+//
+// The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
+// defines derivation paths to be of the form:
+//
+// m / purpose' / coin_type' / account' / change / address_index
+//
+// The BIP-44 spec https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
+// defines that the `purpose` be 44' (or 0x8000002C) for crypto currencies, and
+// SLIP-44 https://github.com/satoshilabs/slips/blob/master/slip-0044.md assigns
+// the `coin_type` 60' (or 0x8000003C) to Ethereum.
+//
+// The root path for Ethereum is m/44'/60'/0'/0 according to the specification
+// from https://github.com/ethereum/EIPs/issues/84, albeit it's not set in stone
+// yet whether accounts should increment the last component or the children of
+// that. We will go with the simpler approach of incrementing the last component.
+type DerivationPath []uint32
+
+// ParseDerivationPath converts a user specified derivation path string to the
+// internal binary representation.
+//
+// Full derivation paths need to start with the `m/` prefix, relative derivation
+// paths (which will get appended to the default root path) must not have prefixes
+// in front of the first element. Whitespace is ignored.
+func ParseDerivationPath(path string) (DerivationPath, error) {
+ var result DerivationPath
+
+ // Handle absolute or relative paths
+ components := strings.Split(path, "/")
+ switch {
+ case len(components) == 0:
+ return nil, errors.New("empty derivation path")
+
+ case strings.TrimSpace(components[0]) == "":
+ return nil, errors.New("ambiguous path: use 'm/' prefix for absolute paths, or no leading '/' for relative ones")
+
+ case strings.TrimSpace(components[0]) == "m":
+ components = components[1:]
+
+ default:
+ result = append(result, DefaultRootDerivationPath...)
+ }
+ // All remaining components are relative, append one by one
+ if len(components) == 0 {
+ return nil, errors.New("empty derivation path") // Empty relative paths
+ }
+ for _, component := range components {
+ // Ignore any user added whitespace
+ component = strings.TrimSpace(component)
+ var value uint32
+
+ // Handle hardened paths
+ if strings.HasSuffix(component, "'") {
+ value = 0x80000000
+ component = strings.TrimSpace(strings.TrimSuffix(component, "'"))
+ }
+ // Handle the non hardened component
+ bigval, ok := new(big.Int).SetString(component, 0)
+ if !ok {
+ return nil, fmt.Errorf("invalid component: %s", component)
+ }
+ max := math.MaxUint32 - value
+ if bigval.Sign() < 0 || bigval.Cmp(big.NewInt(int64(max))) > 0 {
+ if value == 0 {
+ return nil, fmt.Errorf("component %v out of allowed range [0, %d]", bigval, max)
+ }
+ return nil, fmt.Errorf("component %v out of allowed hardened range [0, %d]", bigval, max)
+ }
+ value += uint32(bigval.Uint64())
+
+ // Append and repeat
+ result = append(result, value)
+ }
+ return result, nil
+}
+
+// String implements the stringer interface, converting a binary derivation path
+// to its canonical representation.
+func (path DerivationPath) String() string {
+ result := "m"
+ for _, component := range path {
+ var hardened bool
+ if component >= 0x80000000 {
+ component -= 0x80000000
+ hardened = true
+ }
+ result = fmt.Sprintf("%s/%d", result, component)
+ if hardened {
+ result += "'"
+ }
+ }
+ return result
+}
+
+// MarshalJSON turns a derivation path into its json-serialized string
+func (path DerivationPath) MarshalJSON() ([]byte, error) {
+ return json.Marshal(path.String())
+}
+
+// UnmarshalJSON a json-serialized string back into a derivation path
+func (path *DerivationPath) UnmarshalJSON(b []byte) error {
+ var dp string
+ var err error
+ if err = json.Unmarshal(b, &dp); err != nil {
+ return err
+ }
+ *path, err = ParseDerivationPath(dp)
+ return err
+}
diff --git a/accounts/keystore/account_cache.go b/accounts/keystore/account_cache.go
new file mode 100644
index 0000000..2067ccb
--- /dev/null
+++ b/accounts/keystore/account_cache.go
@@ -0,0 +1,301 @@
+// Copyright 2017 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 keystore
+
+import (
+ "bufio"
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/log"
+ mapset "github.com/deckarep/golang-set"
+)
+
+// 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 accountsByURL []accounts.Account
+
+func (s accountsByURL) Len() int { return len(s) }
+func (s accountsByURL) Less(i, j int) bool { return s[i].URL.Cmp(s[j].URL) < 0 }
+func (s accountsByURL) 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 []accounts.Account
+}
+
+func (err *AmbiguousAddrError) Error() string {
+ files := ""
+ for i, a := range err.Matches {
+ files += a.URL.Path
+ if i < len(err.Matches)-1 {
+ files += ", "
+ }
+ }
+ return fmt.Sprintf("multiple keys match address (%s)", files)
+}
+
+// accountCache is a live index of all accounts in the keystore.
+type accountCache struct {
+ keydir string
+ watcher *watcher
+ mu sync.Mutex
+ all accountsByURL
+ byAddr map[common.Address][]accounts.Account
+ throttle *time.Timer
+ notify chan struct{}
+ fileC fileCache
+}
+
+func newAccountCache(keydir string) (*accountCache, chan struct{}) {
+ ac := &accountCache{
+ keydir: keydir,
+ byAddr: make(map[common.Address][]accounts.Account),
+ notify: make(chan struct{}, 1),
+ fileC: fileCache{all: mapset.NewThreadUnsafeSet()},
+ }
+ ac.watcher = newWatcher(ac)
+ return ac, ac.notify
+}
+
+func (ac *accountCache) accounts() []accounts.Account {
+ ac.maybeReload()
+ ac.mu.Lock()
+ defer ac.mu.Unlock()
+ cpy := make([]accounts.Account, len(ac.all))
+ copy(cpy, ac.all)
+ return cpy
+}
+
+func (ac *accountCache) hasAddress(addr common.Address) bool {
+ ac.maybeReload()
+ ac.mu.Lock()
+ defer ac.mu.Unlock()
+ return len(ac.byAddr[addr]) > 0
+}
+
+func (ac *accountCache) add(newAccount accounts.Account) {
+ ac.mu.Lock()
+ defer ac.mu.Unlock()
+
+ i := sort.Search(len(ac.all), func(i int) bool { return ac.all[i].URL.Cmp(newAccount.URL) >= 0 })
+ if i < len(ac.all) && ac.all[i] == newAccount {
+ return
+ }
+ // newAccount is not in the cache.
+ ac.all = append(ac.all, accounts.Account{})
+ copy(ac.all[i+1:], ac.all[i:])
+ ac.all[i] = newAccount
+ ac.byAddr[newAccount.Address] = append(ac.byAddr[newAccount.Address], newAccount)
+}
+
+// note: removed needs to be unique here (i.e. both File and Address must be set).
+func (ac *accountCache) delete(removed accounts.Account) {
+ ac.mu.Lock()
+ defer ac.mu.Unlock()
+
+ ac.all = removeAccount(ac.all, removed)
+ if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 {
+ delete(ac.byAddr, removed.Address)
+ } else {
+ ac.byAddr[removed.Address] = ba
+ }
+}
+
+// deleteByFile removes an account referenced by the given path.
+func (ac *accountCache) deleteByFile(path string) {
+ ac.mu.Lock()
+ defer ac.mu.Unlock()
+ i := sort.Search(len(ac.all), func(i int) bool { return ac.all[i].URL.Path >= path })
+
+ if i < len(ac.all) && ac.all[i].URL.Path == path {
+ removed := ac.all[i]
+ ac.all = append(ac.all[:i], ac.all[i+1:]...)
+ if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 {
+ delete(ac.byAddr, removed.Address)
+ } else {
+ ac.byAddr[removed.Address] = ba
+ }
+ }
+}
+
+func removeAccount(slice []accounts.Account, elem accounts.Account) []accounts.Account {
+ for i := range slice {
+ if slice[i] == elem {
+ return append(slice[:i], slice[i+1:]...)
+ }
+ }
+ return slice
+}
+
+// find returns the cached account for address if there is a unique match.
+// The exact matching rules are explained by the documentation of accounts.Account.
+// Callers must hold ac.mu.
+func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) {
+ // Limit search to address candidates if possible.
+ matches := ac.all
+ if (a.Address != common.Address{}) {
+ matches = ac.byAddr[a.Address]
+ }
+ if a.URL.Path != "" {
+ // If only the basename is specified, complete the path.
+ if !strings.ContainsRune(a.URL.Path, filepath.Separator) {
+ a.URL.Path = filepath.Join(ac.keydir, a.URL.Path)
+ }
+ for i := range matches {
+ if matches[i].URL == a.URL {
+ return matches[i], nil
+ }
+ }
+ if (a.Address == common.Address{}) {
+ return accounts.Account{}, ErrNoMatch
+ }
+ }
+ switch len(matches) {
+ case 1:
+ return matches[0], nil
+ case 0:
+ return accounts.Account{}, ErrNoMatch
+ default:
+ err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))}
+ copy(err.Matches, matches)
+ sort.Sort(accountsByURL(err.Matches))
+ return accounts.Account{}, err
+ }
+}
+
+func (ac *accountCache) maybeReload() {
+ ac.mu.Lock()
+
+ if ac.watcher.running {
+ ac.mu.Unlock()
+ 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:
+ ac.mu.Unlock()
+ return // The cache was reloaded recently.
+ }
+ }
+ // No watcher running, start it.
+ ac.watcher.start()
+ ac.throttle.Reset(minReloadInterval)
+ ac.mu.Unlock()
+ ac.scanAccounts()
+}
+
+func (ac *accountCache) close() {
+ ac.mu.Lock()
+ ac.watcher.close()
+ if ac.throttle != nil {
+ ac.throttle.Stop()
+ }
+ if ac.notify != nil {
+ close(ac.notify)
+ ac.notify = nil
+ }
+ ac.mu.Unlock()
+}
+
+// scanAccounts checks if any changes have occurred on the filesystem, and
+// updates the account cache accordingly
+func (ac *accountCache) scanAccounts() error {
+ // Scan the entire folder metadata for file changes
+ creates, deletes, updates, err := ac.fileC.scan(ac.keydir)
+ if err != nil {
+ log.Debug("Failed to reload keystore contents", "err", err)
+ return err
+ }
+ if creates.Cardinality() == 0 && deletes.Cardinality() == 0 && updates.Cardinality() == 0 {
+ return nil
+ }
+ // Create a helper method to scan the contents of the key files
+ var (
+ buf = new(bufio.Reader)
+ key struct {
+ Address string `json:"address"`
+ }
+ )
+ readAccount := func(path string) *accounts.Account {
+ fd, err := os.Open(path)
+ if err != nil {
+ log.Trace("Failed to open keystore file", "path", path, "err", err)
+ return nil
+ }
+ defer fd.Close()
+ buf.Reset(fd)
+ // Parse the address.
+ key.Address = ""
+ err = json.NewDecoder(buf).Decode(&key)
+ addr := common.HexToAddress(key.Address)
+ switch {
+ case err != nil:
+ log.Debug("Failed to decode keystore key", "path", path, "err", err)
+ case (addr == common.Address{}):
+ log.Debug("Failed to decode keystore key", "path", path, "err", "missing or zero address")
+ default:
+ return &accounts.Account{
+ Address: addr,
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: path},
+ }
+ }
+ return nil
+ }
+ // Process all the file diffs
+ start := time.Now()
+
+ for _, p := range creates.ToSlice() {
+ if a := readAccount(p.(string)); a != nil {
+ ac.add(*a)
+ }
+ }
+ for _, p := range deletes.ToSlice() {
+ ac.deleteByFile(p.(string))
+ }
+ for _, p := range updates.ToSlice() {
+ path := p.(string)
+ ac.deleteByFile(path)
+ if a := readAccount(path); a != nil {
+ ac.add(*a)
+ }
+ }
+ end := time.Now()
+
+ select {
+ case ac.notify <- struct{}{}:
+ default:
+ }
+ log.Trace("Handled keystore changes", "time", end.Sub(start))
+ return nil
+}
diff --git a/accounts/keystore/file_cache.go b/accounts/keystore/file_cache.go
new file mode 100644
index 0000000..ac87f0c
--- /dev/null
+++ b/accounts/keystore/file_cache.go
@@ -0,0 +1,102 @@
+// Copyright 2017 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 keystore
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+ "time"
+
+ mapset "github.com/deckarep/golang-set"
+ "github.com/ava-labs/go-ethereum/log"
+)
+
+// fileCache is a cache of files seen during scan of keystore.
+type fileCache struct {
+ all mapset.Set // Set of all files from the keystore folder
+ lastMod time.Time // Last time instance when a file was modified
+ mu sync.RWMutex
+}
+
+// scan performs a new scan on the given directory, compares against the already
+// cached filenames, and returns file sets: creates, deletes, updates.
+func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, error) {
+ t0 := time.Now()
+
+ // List all the failes from the keystore folder
+ files, err := ioutil.ReadDir(keyDir)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ t1 := time.Now()
+
+ fc.mu.Lock()
+ defer fc.mu.Unlock()
+
+ // Iterate all the files and gather their metadata
+ all := mapset.NewThreadUnsafeSet()
+ mods := mapset.NewThreadUnsafeSet()
+
+ var newLastMod time.Time
+ for _, fi := range files {
+ path := filepath.Join(keyDir, fi.Name())
+ // Skip any non-key files from the folder
+ if nonKeyFile(fi) {
+ log.Trace("Ignoring file on account scan", "path", path)
+ continue
+ }
+ // Gather the set of all and fresly modified files
+ all.Add(path)
+
+ modified := fi.ModTime()
+ if modified.After(fc.lastMod) {
+ mods.Add(path)
+ }
+ if modified.After(newLastMod) {
+ newLastMod = modified
+ }
+ }
+ t2 := time.Now()
+
+ // Update the tracked files and return the three sets
+ deletes := fc.all.Difference(all) // Deletes = previous - current
+ creates := all.Difference(fc.all) // Creates = current - previous
+ updates := mods.Difference(creates) // Updates = modified - creates
+
+ fc.all, fc.lastMod = all, newLastMod
+ t3 := time.Now()
+
+ // Report on the scanning stats and return
+ log.Debug("FS scan times", "list", t1.Sub(t0), "set", t2.Sub(t1), "diff", t3.Sub(t2))
+ return creates, deletes, updates, nil
+}
+
+// nonKeyFile ignores editor backups, hidden files and folders/symlinks.
+func nonKeyFile(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
+}
diff --git a/accounts/keystore/key.go b/accounts/keystore/key.go
new file mode 100644
index 0000000..3654daa
--- /dev/null
+++ b/accounts/keystore/key.go
@@ -0,0 +1,232 @@
+// 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 keystore
+
+import (
+ "bytes"
+ "crypto/ecdsa"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/crypto"
+ "github.com/pborman/uuid"
+)
+
+const (
+ version = 3
+)
+
+type Key struct {
+ Id uuid.UUID // Version 4 "random" for unique id not derived from key data
+ // to simplify lookups we also store the address
+ Address common.Address
+ // we only store privkey as pubkey/address can be derived from it
+ // privkey in this struct is always in plaintext
+ PrivateKey *ecdsa.PrivateKey
+}
+
+type keyStore interface {
+ // Loads and decrypts the key from disk.
+ GetKey(addr common.Address, filename string, auth string) (*Key, error)
+ // Writes and encrypts the key.
+ StoreKey(filename string, k *Key, auth string) error
+ // Joins filename with the key directory unless it is already absolute.
+ JoinPath(filename string) string
+}
+
+type plainKeyJSON struct {
+ Address string `json:"address"`
+ PrivateKey string `json:"privatekey"`
+ Id string `json:"id"`
+ Version int `json:"version"`
+}
+
+type encryptedKeyJSONV3 struct {
+ Address string `json:"address"`
+ Crypto CryptoJSON `json:"crypto"`
+ Id string `json:"id"`
+ Version int `json:"version"`
+}
+
+type encryptedKeyJSONV1 struct {
+ Address string `json:"address"`
+ Crypto CryptoJSON `json:"crypto"`
+ Id string `json:"id"`
+ Version string `json:"version"`
+}
+
+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"`
+}
+
+func (k *Key) MarshalJSON() (j []byte, err error) {
+ jStruct := plainKeyJSON{
+ hex.EncodeToString(k.Address[:]),
+ hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)),
+ k.Id.String(),
+ version,
+ }
+ j, err = json.Marshal(jStruct)
+ return j, err
+}
+
+func (k *Key) 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 := crypto.HexToECDSA(keyJSON.PrivateKey)
+ if err != nil {
+ return err
+ }
+
+ k.Address = common.BytesToAddress(addr)
+ k.PrivateKey = privkey
+
+ return nil
+}
+
+func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
+ id := uuid.NewRandom()
+ key := &Key{
+ Id: id,
+ Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
+ PrivateKey: privateKeyECDSA,
+ }
+ return key
+}
+
+// NewKeyForDirectICAP generates a key whose address fits into < 155 bits so it can fit
+// into the Direct ICAP spec. for simplicity and easier compatibility with other libs, we
+// retry until the first byte is 0.
+func NewKeyForDirectICAP(rand io.Reader) *Key {
+ randBytes := make([]byte, 64)
+ _, err := rand.Read(randBytes)
+ if err != nil {
+ panic("key generation: could not read from random source: " + err.Error())
+ }
+ reader := bytes.NewReader(randBytes)
+ privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader)
+ if err != nil {
+ panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
+ }
+ key := newKeyFromECDSA(privateKeyECDSA)
+ if !strings.HasPrefix(key.Address.Hex(), "0x00") {
+ return NewKeyForDirectICAP(rand)
+ }
+ return key
+}
+
+func newKey(rand io.Reader) (*Key, error) {
+ privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand)
+ if err != nil {
+ return nil, err
+ }
+ return newKeyFromECDSA(privateKeyECDSA), nil
+}
+
+func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) {
+ key, err := newKey(rand)
+ if err != nil {
+ return nil, accounts.Account{}, err
+ }
+ a := accounts.Account{
+ Address: key.Address,
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))},
+ }
+ if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
+ zeroKey(key.PrivateKey)
+ return nil, a, err
+ }
+ return key, a, err
+}
+
+func writeTemporaryKeyFile(file string, content []byte) (string, 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 f.Name(), nil
+}
+
+func writeKeyFile(file string, content []byte) error {
+ name, err := writeTemporaryKeyFile(file, content)
+ if err != nil {
+ return err
+ }
+ return os.Rename(name, file)
+}
+
+// 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), hex.EncodeToString(keyAddr[:]))
+}
+
+func toISO8601(t time.Time) string {
+ var tz string
+ name, offset := t.Zone()
+ if name == "UTC" {
+ tz = "Z"
+ } else {
+ tz = fmt.Sprintf("%03d00", offset/3600)
+ }
+ return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s",
+ t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
+}
diff --git a/accounts/keystore/keystore.go b/accounts/keystore/keystore.go
new file mode 100644
index 0000000..a3ce33f
--- /dev/null
+++ b/accounts/keystore/keystore.go
@@ -0,0 +1,495 @@
+// Copyright 2017 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 keystore implements encrypted storage of secp256k1 private keys.
+//
+// Keys are stored as encrypted JSON files according to the Web3 Secret Storage specification.
+// See https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition for more information.
+package keystore
+
+import (
+ "crypto/ecdsa"
+ crand "crypto/rand"
+ "errors"
+ "fmt"
+ "math/big"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "sync"
+ "time"
+
+ "github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/crypto"
+ "github.com/ava-labs/go-ethereum/event"
+)
+
+var (
+ ErrLocked = accounts.NewAuthNeededError("password or unlock")
+ ErrNoMatch = errors.New("no key for given address or file")
+ ErrDecrypt = errors.New("could not decrypt key with given password")
+)
+
+// KeyStoreType is the reflect type of a keystore backend.
+var KeyStoreType = reflect.TypeOf(&KeyStore{})
+
+// KeyStoreScheme is the protocol scheme prefixing account and wallet URLs.
+const KeyStoreScheme = "keystore"
+
+// Maximum time between wallet refreshes (if filesystem notifications don't work).
+const walletRefreshCycle = 3 * time.Second
+
+// KeyStore manages a key storage directory on disk.
+type KeyStore struct {
+ storage keyStore // Storage backend, might be cleartext or encrypted
+ cache *accountCache // In-memory account cache over the filesystem storage
+ changes chan struct{} // Channel receiving change notifications from the cache
+ unlocked map[common.Address]*unlocked // Currently unlocked account (decrypted private keys)
+
+ wallets []accounts.Wallet // Wallet wrappers around the individual key files
+ updateFeed event.Feed // Event feed to notify wallet additions/removals
+ updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
+ updating bool // Whether the event notification loop is running
+
+ mu sync.RWMutex
+}
+
+type unlocked struct {
+ *Key
+ abort chan struct{}
+}
+
+// NewKeyStore creates a keystore for the given directory.
+func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore {
+ keydir, _ = filepath.Abs(keydir)
+ ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP, false}}
+ ks.init(keydir)
+ return ks
+}
+
+// NewPlaintextKeyStore creates a keystore for the given directory.
+// Deprecated: Use NewKeyStore.
+func NewPlaintextKeyStore(keydir string) *KeyStore {
+ keydir, _ = filepath.Abs(keydir)
+ ks := &KeyStore{storage: &keyStorePlain{keydir}}
+ ks.init(keydir)
+ return ks
+}
+
+func (ks *KeyStore) init(keydir string) {
+ // Lock the mutex since the account cache might call back with events
+ ks.mu.Lock()
+ defer ks.mu.Unlock()
+
+ // Initialize the set of unlocked keys and the account cache
+ ks.unlocked = make(map[common.Address]*unlocked)
+ ks.cache, ks.changes = newAccountCache(keydir)
+
+ // TODO: In order for this finalizer to work, there must be no references
+ // to ks. addressCache doesn't keep a reference but unlocked keys do,
+ // so the finalizer will not trigger until all timed unlocks have expired.
+ runtime.SetFinalizer(ks, func(m *KeyStore) {
+ m.cache.close()
+ })
+ // Create the initial list of wallets from the cache
+ accs := ks.cache.accounts()
+ ks.wallets = make([]accounts.Wallet, len(accs))
+ for i := 0; i < len(accs); i++ {
+ ks.wallets[i] = &keystoreWallet{account: accs[i], keystore: ks}
+ }
+}
+
+// Wallets implements accounts.Backend, returning all single-key wallets from the
+// keystore directory.
+func (ks *KeyStore) Wallets() []accounts.Wallet {
+ // Make sure the list of wallets is in sync with the account cache
+ ks.refreshWallets()
+
+ ks.mu.RLock()
+ defer ks.mu.RUnlock()
+
+ cpy := make([]accounts.Wallet, len(ks.wallets))
+ copy(cpy, ks.wallets)
+ return cpy
+}
+
+// refreshWallets retrieves the current account list and based on that does any
+// necessary wallet refreshes.
+func (ks *KeyStore) refreshWallets() {
+ // Retrieve the current list of accounts
+ ks.mu.Lock()
+ accs := ks.cache.accounts()
+
+ // Transform the current list of wallets into the new one
+ var (
+ wallets = make([]accounts.Wallet, 0, len(accs))
+ events []accounts.WalletEvent
+ )
+
+ for _, account := range accs {
+ // Drop wallets while they were in front of the next account
+ for len(ks.wallets) > 0 && ks.wallets[0].URL().Cmp(account.URL) < 0 {
+ events = append(events, accounts.WalletEvent{Wallet: ks.wallets[0], Kind: accounts.WalletDropped})
+ ks.wallets = ks.wallets[1:]
+ }
+ // If there are no more wallets or the account is before the next, wrap new wallet
+ if len(ks.wallets) == 0 || ks.wallets[0].URL().Cmp(account.URL) > 0 {
+ wallet := &keystoreWallet{account: account, keystore: ks}
+
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
+ wallets = append(wallets, wallet)
+ continue
+ }
+ // If the account is the same as the first wallet, keep it
+ if ks.wallets[0].Accounts()[0] == account {
+ wallets = append(wallets, ks.wallets[0])
+ ks.wallets = ks.wallets[1:]
+ continue
+ }
+ }
+ // Drop any leftover wallets and set the new batch
+ for _, wallet := range ks.wallets {
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
+ }
+ ks.wallets = wallets
+ ks.mu.Unlock()
+
+ // Fire all wallet events and return
+ for _, event := range events {
+ ks.updateFeed.Send(event)
+ }
+}
+
+// Subscribe implements accounts.Backend, creating an async subscription to
+// receive notifications on the addition or removal of keystore wallets.
+func (ks *KeyStore) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
+ // We need the mutex to reliably start/stop the update loop
+ ks.mu.Lock()
+ defer ks.mu.Unlock()
+
+ // Subscribe the caller and track the subscriber count
+ sub := ks.updateScope.Track(ks.updateFeed.Subscribe(sink))
+
+ // Subscribers require an active notification loop, start it
+ if !ks.updating {
+ ks.updating = true
+ go ks.updater()
+ }
+ return sub
+}
+
+// updater is responsible for maintaining an up-to-date list of wallets stored in
+// the keystore, and for firing wallet addition/removal events. It listens for
+// account change events from the underlying account cache, and also periodically
+// forces a manual refresh (only triggers for systems where the filesystem notifier
+// is not running).
+func (ks *KeyStore) updater() {
+ for {
+ // Wait for an account update or a refresh timeout
+ select {
+ case <-ks.changes:
+ case <-time.After(walletRefreshCycle):
+ }
+ // Run the wallet refresher
+ ks.refreshWallets()
+
+ // If all our subscribers left, stop the updater
+ ks.mu.Lock()
+ if ks.updateScope.Count() == 0 {
+ ks.updating = false
+ ks.mu.Unlock()
+ return
+ }
+ ks.mu.Unlock()
+ }
+}
+
+// HasAddress reports whether a key with the given address is present.
+func (ks *KeyStore) HasAddress(addr common.Address) bool {
+ return ks.cache.hasAddress(addr)
+}
+
+// Accounts returns all key files present in the directory.
+func (ks *KeyStore) Accounts() []accounts.Account {
+ return ks.cache.accounts()
+}
+
+// Delete deletes the key matched by account if the passphrase is correct.
+// If the account contains no filename, the address must match a unique key.
+func (ks *KeyStore) Delete(a accounts.Account, passphrase 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.
+ a, key, err := ks.getDecryptedKey(a, passphrase)
+ if key != nil {
+ zeroKey(key.PrivateKey)
+ }
+ 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(a.URL.Path)
+ if err == nil {
+ ks.cache.delete(a)
+ ks.refreshWallets()
+ }
+ return err
+}
+
+// SignHash calculates a ECDSA signature for the given hash. The produced
+// signature is in the [R || S || V] format where V is 0 or 1.
+func (ks *KeyStore) SignHash(a accounts.Account, hash []byte) ([]byte, error) {
+ // Look up the key to sign with and abort if it cannot be found
+ ks.mu.RLock()
+ defer ks.mu.RUnlock()
+
+ unlockedKey, found := ks.unlocked[a.Address]
+ if !found {
+ return nil, ErrLocked
+ }
+ // Sign the hash using plain ECDSA operations
+ return crypto.Sign(hash, unlockedKey.PrivateKey)
+}
+
+// SignTx signs the given transaction with the requested account.
+func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ // Look up the key to sign with and abort if it cannot be found
+ ks.mu.RLock()
+ defer ks.mu.RUnlock()
+
+ unlockedKey, found := ks.unlocked[a.Address]
+ if !found {
+ return nil, ErrLocked
+ }
+ // Depending on the presence of the chain ID, sign with EIP155 or homestead
+ if chainID != nil {
+ return types.SignTx(tx, types.NewEIP155Signer(chainID), unlockedKey.PrivateKey)
+ }
+ return types.SignTx(tx, types.HomesteadSigner{}, unlockedKey.PrivateKey)
+}
+
+// SignHashWithPassphrase signs hash if the private key matching the given address
+// can be decrypted with the given passphrase. The produced signature is in the
+// [R || S || V] format where V is 0 or 1.
+func (ks *KeyStore) SignHashWithPassphrase(a accounts.Account, passphrase string, hash []byte) (signature []byte, err error) {
+ _, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ defer zeroKey(key.PrivateKey)
+ return crypto.Sign(hash, key.PrivateKey)
+}
+
+// SignTxWithPassphrase signs the transaction if the private key matching the
+// given address can be decrypted with the given passphrase.
+func (ks *KeyStore) SignTxWithPassphrase(a accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ _, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ defer zeroKey(key.PrivateKey)
+
+ // Depending on the presence of the chain ID, sign with EIP155 or homestead
+ if chainID != nil {
+ return types.SignTx(tx, types.NewEIP155Signer(chainID), key.PrivateKey)
+ }
+ return types.SignTx(tx, types.HomesteadSigner{}, key.PrivateKey)
+}
+
+// Unlock unlocks the given account indefinitely.
+func (ks *KeyStore) Unlock(a accounts.Account, passphrase string) error {
+ return ks.TimedUnlock(a, passphrase, 0)
+}
+
+// Lock removes the private key with the given address from memory.
+func (ks *KeyStore) Lock(addr common.Address) error {
+ ks.mu.Lock()
+ if unl, found := ks.unlocked[addr]; found {
+ ks.mu.Unlock()
+ ks.expire(addr, unl, time.Duration(0)*time.Nanosecond)
+ } else {
+ ks.mu.Unlock()
+ }
+ return nil
+}
+
+// TimedUnlock unlocks the given account with the passphrase. The account
+// stays unlocked for the duration of timeout. A timeout of 0 unlocks the account
+// until the program exits. The account must match a unique key file.
+//
+// If the account address is already unlocked for a duration, TimedUnlock extends or
+// shortens the active unlock timeout. If the address was previously unlocked
+// indefinitely the timeout is not altered.
+func (ks *KeyStore) TimedUnlock(a accounts.Account, passphrase string, timeout time.Duration) error {
+ a, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return err
+ }
+
+ ks.mu.Lock()
+ defer ks.mu.Unlock()
+ u, found := ks.unlocked[a.Address]
+ if found {
+ if u.abort == nil {
+ // The address was unlocked indefinitely, so unlocking
+ // it with a timeout would be confusing.
+ zeroKey(key.PrivateKey)
+ return nil
+ }
+ // Terminate the expire goroutine and replace it below.
+ close(u.abort)
+ }
+ if timeout > 0 {
+ u = &unlocked{Key: key, abort: make(chan struct{})}
+ go ks.expire(a.Address, u, timeout)
+ } else {
+ u = &unlocked{Key: key}
+ }
+ ks.unlocked[a.Address] = u
+ return nil
+}
+
+// Find resolves the given account into a unique entry in the keystore.
+func (ks *KeyStore) Find(a accounts.Account) (accounts.Account, error) {
+ ks.cache.maybeReload()
+ ks.cache.mu.Lock()
+ a, err := ks.cache.find(a)
+ ks.cache.mu.Unlock()
+ return a, err
+}
+
+func (ks *KeyStore) getDecryptedKey(a accounts.Account, auth string) (accounts.Account, *Key, error) {
+ a, err := ks.Find(a)
+ if err != nil {
+ return a, nil, err
+ }
+ key, err := ks.storage.GetKey(a.Address, a.URL.Path, auth)
+ return a, key, err
+}
+
+func (ks *KeyStore) expire(addr common.Address, u *unlocked, timeout time.Duration) {
+ t := time.NewTimer(timeout)
+ defer t.Stop()
+ select {
+ case <-u.abort:
+ // just quit
+ case <-t.C:
+ ks.mu.Lock()
+ // only drop if it's still the same key instance that dropLater
+ // was launched with. we can check that using pointer equality
+ // because the map stores a new pointer every time the key is
+ // unlocked.
+ if ks.unlocked[addr] == u {
+ zeroKey(u.PrivateKey)
+ delete(ks.unlocked, addr)
+ }
+ ks.mu.Unlock()
+ }
+}
+
+// NewAccount generates a new key and stores it into the key directory,
+// encrypting it with the passphrase.
+func (ks *KeyStore) NewAccount(passphrase string) (accounts.Account, error) {
+ _, account, err := storeNewKey(ks.storage, crand.Reader, passphrase)
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ // Add the account to the cache immediately rather
+ // than waiting for file system notifications to pick it up.
+ ks.cache.add(account)
+ ks.refreshWallets()
+ return account, nil
+}
+
+// Export exports as a JSON key, encrypted with newPassphrase.
+func (ks *KeyStore) Export(a accounts.Account, passphrase, newPassphrase string) (keyJSON []byte, err error) {
+ _, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ var N, P int
+ if store, ok := ks.storage.(*keyStorePassphrase); ok {
+ N, P = store.scryptN, store.scryptP
+ } else {
+ N, P = StandardScryptN, StandardScryptP
+ }
+ return EncryptKey(key, newPassphrase, N, P)
+}
+
+// Import stores the given encrypted JSON key into the key directory.
+func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (accounts.Account, error) {
+ key, err := DecryptKey(keyJSON, passphrase)
+ if key != nil && key.PrivateKey != nil {
+ defer zeroKey(key.PrivateKey)
+ }
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ return ks.importKey(key, newPassphrase)
+}
+
+// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase.
+func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (accounts.Account, error) {
+ key := newKeyFromECDSA(priv)
+ if ks.cache.hasAddress(key.Address) {
+ return accounts.Account{}, fmt.Errorf("account already exists")
+ }
+ return ks.importKey(key, passphrase)
+}
+
+func (ks *KeyStore) importKey(key *Key, passphrase string) (accounts.Account, error) {
+ a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.storage.JoinPath(keyFileName(key.Address))}}
+ if err := ks.storage.StoreKey(a.URL.Path, key, passphrase); err != nil {
+ return accounts.Account{}, err
+ }
+ ks.cache.add(a)
+ ks.refreshWallets()
+ return a, nil
+}
+
+// Update changes the passphrase of an existing account.
+func (ks *KeyStore) Update(a accounts.Account, passphrase, newPassphrase string) error {
+ a, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return err
+ }
+ return ks.storage.StoreKey(a.URL.Path, key, newPassphrase)
+}
+
+// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores
+// a key file in the key directory. The key file is encrypted with the same passphrase.
+func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (accounts.Account, error) {
+ a, _, err := importPreSaleKey(ks.storage, keyJSON, passphrase)
+ if err != nil {
+ return a, err
+ }
+ ks.cache.add(a)
+ ks.refreshWallets()
+ return a, nil
+}
+
+// zeroKey zeroes a private key in memory.
+func zeroKey(k *ecdsa.PrivateKey) {
+ b := k.D.Bits()
+ for i := range b {
+ b[i] = 0
+ }
+}
diff --git a/accounts/keystore/passphrase.go b/accounts/keystore/passphrase.go
new file mode 100644
index 0000000..fa5a09d
--- /dev/null
+++ b/accounts/keystore/passphrase.go
@@ -0,0 +1,356 @@
+// 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 keystore
+
+import (
+ "bytes"
+ "crypto/aes"
+ "crypto/rand"
+ "crypto/sha256"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/common/math"
+ "github.com/ava-labs/go-ethereum/crypto"
+ "github.com/pborman/uuid"
+ "golang.org/x/crypto/pbkdf2"
+ "golang.org/x/crypto/scrypt"
+)
+
+const (
+ keyHeaderKDF = "scrypt"
+
+ // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB
+ // memory and taking approximately 1s CPU time on a modern processor.
+ StandardScryptN = 1 << 18
+
+ // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB
+ // memory and taking approximately 1s CPU time on a modern processor.
+ StandardScryptP = 1
+
+ // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB
+ // memory and taking approximately 100ms CPU time on a modern processor.
+ LightScryptN = 1 << 12
+
+ // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB
+ // memory and taking approximately 100ms CPU time on a modern processor.
+ LightScryptP = 6
+
+ scryptR = 8
+ scryptDKLen = 32
+)
+
+type keyStorePassphrase struct {
+ keysDirPath string
+ scryptN int
+ scryptP int
+ // skipKeyFileVerification disables the security-feature which does
+ // reads and decrypts any newly created keyfiles. This should be 'false' in all
+ // cases except tests -- setting this to 'true' is not recommended.
+ skipKeyFileVerification bool
+}
+
+func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, 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
+}
+
+// StoreKey generates a key, encrypts with 'auth' and stores in the given directory
+func StoreKey(dir, auth string, scryptN, scryptP int) (accounts.Account, error) {
+ _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth)
+ return a, err
+}
+
+func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
+ keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
+ if err != nil {
+ return err
+ }
+ // Write into temporary file
+ tmpName, err := writeTemporaryKeyFile(filename, keyjson)
+ if err != nil {
+ return err
+ }
+ if !ks.skipKeyFileVerification {
+ // Verify that we can decrypt the file with the given password.
+ _, err = ks.GetKey(key.Address, tmpName, auth)
+ if err != nil {
+ msg := "An error was encountered when saving and verifying the keystore file. \n" +
+ "This indicates that the keystore is corrupted. \n" +
+ "The corrupted file is stored at \n%v\n" +
+ "Please file a ticket at:\n\n" +
+ "https://github.com/ethereum/go-ethereum/issues." +
+ "The error was : %s"
+ return fmt.Errorf(msg, tmpName, err)
+ }
+ }
+ return os.Rename(tmpName, filename)
+}
+
+func (ks keyStorePassphrase) JoinPath(filename string) string {
+ if filepath.IsAbs(filename) {
+ return filename
+ }
+ return filepath.Join(ks.keysDirPath, filename)
+}
+
+// Encryptdata encrypts the data given as 'data' with the password 'auth'.
+func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
+
+ salt := make([]byte, 32)
+ if _, err := io.ReadFull(rand.Reader, salt); err != nil {
+ panic("reading from crypto/rand failed: " + err.Error())
+ }
+ derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen)
+ if err != nil {
+ return CryptoJSON{}, err
+ }
+ encryptKey := derivedKey[:16]
+
+ iv := make([]byte, aes.BlockSize) // 16
+ if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+ panic("reading from crypto/rand failed: " + err.Error())
+ }
+ cipherText, err := aesCTRXOR(encryptKey, data, iv)
+ if err != nil {
+ return CryptoJSON{}, err
+ }
+ mac := crypto.Keccak256(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: keyHeaderKDF,
+ KDFParams: scryptParamsJSON,
+ MAC: hex.EncodeToString(mac),
+ }
+ return cryptoStruct, nil
+}
+
+// EncryptKey encrypts a key using the specified scrypt parameters into a json
+// blob that can be decrypted later on.
+func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
+ keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
+ cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP)
+ if err != nil {
+ return nil, err
+ }
+ encryptedKeyJSONV3 := encryptedKeyJSONV3{
+ hex.EncodeToString(key.Address[:]),
+ cryptoStruct,
+ key.Id.String(),
+ version,
+ }
+ return json.Marshal(encryptedKeyJSONV3)
+}
+
+// DecryptKey decrypts a key from a json blob, returning the private key itself.
+func DecryptKey(keyjson []byte, auth string) (*Key, 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
+ )
+ if version, ok := m["version"].(string); ok && version == "1" {
+ k := new(encryptedKeyJSONV1)
+ if err := json.Unmarshal(keyjson, k); err != nil {
+ return nil, err
+ }
+ keyBytes, keyId, err = decryptKeyV1(k, auth)
+ } else {
+ k := new(encryptedKeyJSONV3)
+ if err := json.Unmarshal(keyjson, k); err != nil {
+ return nil, err
+ }
+ keyBytes, keyId, err = decryptKeyV3(k, auth)
+ }
+ // Handle any decryption errors and return the key
+ if err != nil {
+ return nil, err
+ }
+ key := crypto.ToECDSAUnsafe(keyBytes)
+
+ return &Key{
+ Id: uuid.UUID(keyId),
+ Address: crypto.PubkeyToAddress(key.PublicKey),
+ PrivateKey: key,
+ }, nil
+}
+
+func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
+ if cryptoJson.Cipher != "aes-128-ctr" {
+ return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher)
+ }
+ mac, err := hex.DecodeString(cryptoJson.MAC)
+ if err != nil {
+ return nil, err
+ }
+
+ iv, err := hex.DecodeString(cryptoJson.CipherParams.IV)
+ if err != nil {
+ return nil, err
+ }
+
+ cipherText, err := hex.DecodeString(cryptoJson.CipherText)
+ if err != nil {
+ return nil, err
+ }
+
+ derivedKey, err := getKDFKey(cryptoJson, auth)
+ if err != nil {
+ return nil, err
+ }
+
+ calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
+ if !bytes.Equal(calculatedMAC, mac) {
+ return nil, ErrDecrypt
+ }
+
+ plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
+ if err != nil {
+ return nil, err
+ }
+ return plainText, err
+}
+
+func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
+ if keyProtected.Version != version {
+ return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
+ }
+ keyId = uuid.Parse(keyProtected.Id)
+ plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
+ if err != nil {
+ return nil, nil, err
+ }
+ return plainText, keyId, err
+}
+
+func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
+ 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.Keccak256(derivedKey[16:32], cipherText)
+ if !bytes.Equal(calculatedMAC, mac) {
+ return nil, nil, ErrDecrypt
+ }
+
+ plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[: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 == keyHeaderKDF {
+ 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
+}
diff --git a/accounts/keystore/plain.go b/accounts/keystore/plain.go
new file mode 100644
index 0000000..bc5b377
--- /dev/null
+++ b/accounts/keystore/plain.go
@@ -0,0 +1,61 @@
+// 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 keystore
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/ava-labs/go-ethereum/common"
+)
+
+type keyStorePlain struct {
+ keysDirPath string
+}
+
+func (ks keyStorePlain) GetKey(addr common.Address, filename, auth string) (*Key, error) {
+ fd, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer fd.Close()
+ key := new(Key)
+ if err := json.NewDecoder(fd).Decode(key); err != nil {
+ return nil, err
+ }
+ if key.Address != addr {
+ return nil, fmt.Errorf("key content mismatch: have address %x, want %x", key.Address, addr)
+ }
+ return key, nil
+}
+
+func (ks keyStorePlain) StoreKey(filename string, key *Key, auth string) error {
+ content, err := json.Marshal(key)
+ if err != nil {
+ return err
+ }
+ return writeKeyFile(filename, content)
+}
+
+func (ks keyStorePlain) JoinPath(filename string) string {
+ if filepath.IsAbs(filename) {
+ return filename
+ }
+ return filepath.Join(ks.keysDirPath, filename)
+}
diff --git a/accounts/keystore/presale.go b/accounts/keystore/presale.go
new file mode 100644
index 0000000..5122b0d
--- /dev/null
+++ b/accounts/keystore/presale.go
@@ -0,0 +1,147 @@
+// 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 keystore
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/sha256"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "fmt"
+
+ "github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/go-ethereum/crypto"
+ "github.com/pborman/uuid"
+ "golang.org/x/crypto/pbkdf2"
+)
+
+// creates a Key and stores that in the given KeyStore by decrypting a presale key JSON
+func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accounts.Account, *Key, error) {
+ key, err := decryptPreSaleKey(keyJSON, password)
+ if err != nil {
+ return accounts.Account{}, nil, err
+ }
+ key.Id = uuid.NewRandom()
+ a := accounts.Account{
+ Address: key.Address,
+ URL: accounts.URL{
+ Scheme: KeyStoreScheme,
+ Path: keyStore.JoinPath(keyFileName(key.Address)),
+ },
+ }
+ err = keyStore.StoreKey(a.URL.Path, key, password)
+ return a, key, err
+}
+
+func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error) {
+ preSaleKeyStruct := struct {
+ EncSeed string
+ EthAddr string
+ Email string
+ BtcAddr string
+ }{}
+ err = json.Unmarshal(fileContent, &preSaleKeyStruct)
+ if err != nil {
+ return nil, err
+ }
+ encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
+ if err != nil {
+ return nil, errors.New("invalid hex in encSeed")
+ }
+ if len(encSeedBytes) < 16 {
+ return nil, errors.New("invalid encSeed, too short")
+ }
+ iv := encSeedBytes[:16]
+ cipherText := encSeedBytes[16:]
+ /*
+ See https://github.com/ethereum/pyethsaletool
+
+ pyethsaletool generates the encryption key from password by
+ 2000 rounds of PBKDF2 with HMAC-SHA-256 using password as salt (:().
+ 16 byte key length within PBKDF2 and resulting key is used as AES key
+ */
+ passBytes := []byte(password)
+ derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
+ plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
+ if err != nil {
+ return nil, err
+ }
+ ethPriv := crypto.Keccak256(plainText)
+ ecKey := crypto.ToECDSAUnsafe(ethPriv)
+
+ key = &Key{
+ Id: nil,
+ Address: crypto.PubkeyToAddress(ecKey.PublicKey),
+ PrivateKey: ecKey,
+ }
+ derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x"
+ expectedAddr := preSaleKeyStruct.EthAddr
+ if derivedAddr != expectedAddr {
+ err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr)
+ }
+ return key, err
+}
+
+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)]
+}
diff --git a/accounts/keystore/testdata/dupes/1 b/accounts/keystore/testdata/dupes/1
new file mode 100644
index 0000000..a3868ec
--- /dev/null
+++ b/accounts/keystore/testdata/dupes/1
@@ -0,0 +1 @@
+{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} \ No newline at end of file
diff --git a/accounts/keystore/testdata/dupes/2 b/accounts/keystore/testdata/dupes/2
new file mode 100644
index 0000000..a3868ec
--- /dev/null
+++ b/accounts/keystore/testdata/dupes/2
@@ -0,0 +1 @@
+{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} \ No newline at end of file
diff --git a/accounts/keystore/testdata/dupes/foo b/accounts/keystore/testdata/dupes/foo
new file mode 100644
index 0000000..c57060a
--- /dev/null
+++ b/accounts/keystore/testdata/dupes/foo
@@ -0,0 +1 @@
+{"address":"7ef5a6135f1fd6a02593eedc869c6d41d934aef8","crypto":{"cipher":"aes-128-ctr","ciphertext":"1d0839166e7a15b9c1333fc865d69858b22df26815ccf601b28219b6192974e1","cipherparams":{"iv":"8df6caa7ff1b00c4e871f002cb7921ed"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"e5e6ef3f4ea695f496b643ebd3f75c0aa58ef4070e90c80c5d3fb0241bf1595c"},"mac":"6d16dfde774845e4585357f24bce530528bc69f4f84e1e22880d34fa45c273e5"},"id":"950077c7-71e3-4c44-a4a1-143919141ed4","version":3} \ No newline at end of file
diff --git a/accounts/keystore/testdata/keystore/.hiddenfile b/accounts/keystore/testdata/keystore/.hiddenfile
new file mode 100644
index 0000000..d91facc
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/.hiddenfile
@@ -0,0 +1 @@
+{"address":"f466859ead1932d743d622cb74fc058882e8648a","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}
diff --git a/accounts/keystore/testdata/keystore/README b/accounts/keystore/testdata/keystore/README
new file mode 100644
index 0000000..6af9ac3
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/README
@@ -0,0 +1,21 @@
+This directory contains accounts for testing.
+The password that unlocks them is "foobar".
+
+The "good" key files which are supposed to be loadable are:
+
+- File: UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
+ Address: 0x7ef5a6135f1fd6a02593eedc869c6d41d934aef8
+- File: aaa
+ Address: 0xf466859ead1932d743d622cb74fc058882e8648a
+- File: zzz
+ Address: 0x289d485d9771714cce91d3393d764e1311907acc
+
+The other files (including this README) are broken in various ways
+and should not be picked up by package accounts:
+
+- File: no-address (missing address field, otherwise same as "aaa")
+- File: garbage (file with random data)
+- File: empty (file with no content)
+- File: swapfile~ (should be skipped)
+- File: .hiddenfile (should be skipped)
+- File: foo/... (should be skipped because it is a directory)
diff --git a/accounts/keystore/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 b/accounts/keystore/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
new file mode 100644
index 0000000..c57060a
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
@@ -0,0 +1 @@
+{"address":"7ef5a6135f1fd6a02593eedc869c6d41d934aef8","crypto":{"cipher":"aes-128-ctr","ciphertext":"1d0839166e7a15b9c1333fc865d69858b22df26815ccf601b28219b6192974e1","cipherparams":{"iv":"8df6caa7ff1b00c4e871f002cb7921ed"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"e5e6ef3f4ea695f496b643ebd3f75c0aa58ef4070e90c80c5d3fb0241bf1595c"},"mac":"6d16dfde774845e4585357f24bce530528bc69f4f84e1e22880d34fa45c273e5"},"id":"950077c7-71e3-4c44-a4a1-143919141ed4","version":3} \ No newline at end of file
diff --git a/accounts/keystore/testdata/keystore/aaa b/accounts/keystore/testdata/keystore/aaa
new file mode 100644
index 0000000..a3868ec
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/aaa
@@ -0,0 +1 @@
+{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} \ No newline at end of file
diff --git a/accounts/keystore/testdata/keystore/empty b/accounts/keystore/testdata/keystore/empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/empty
diff --git a/accounts/keystore/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e b/accounts/keystore/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e
new file mode 100644
index 0000000..309841e
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e
@@ -0,0 +1 @@
+{"address":"fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e","crypto":{"cipher":"aes-128-ctr","ciphertext":"8124d5134aa4a927c79fd852989e4b5419397566f04b0936a1eb1d168c7c68a5","cipherparams":{"iv":"e2febe17176414dd2cda28287947eb2f"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":4096,"p":6,"r":8,"salt":"44b415ede89f3bdd6830390a21b78965f571b347a589d1d943029f016c5e8bd5"},"mac":"5e149ff25bfd9dd45746a84bb2bcd2f015f2cbca2b6d25c5de8c29617f71fe5b"},"id":"d6ac5452-2b2c-4d3c-ad80-4bf0327d971c","version":3} \ No newline at end of file
diff --git a/accounts/keystore/testdata/keystore/garbage b/accounts/keystore/testdata/keystore/garbage
new file mode 100644
index 0000000..ff45091
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/garbage
Binary files differ
diff --git a/accounts/keystore/testdata/keystore/no-address b/accounts/keystore/testdata/keystore/no-address
new file mode 100644
index 0000000..ad51269
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/no-address
@@ -0,0 +1 @@
+{"crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} \ No newline at end of file
diff --git a/accounts/keystore/testdata/keystore/zero b/accounts/keystore/testdata/keystore/zero
new file mode 100644
index 0000000..b52617f
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/zero
@@ -0,0 +1 @@
+{"address":"0000000000000000000000000000000000000000","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} \ No newline at end of file
diff --git a/accounts/keystore/testdata/keystore/zzz b/accounts/keystore/testdata/keystore/zzz
new file mode 100644
index 0000000..cfd8a47
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/zzz
@@ -0,0 +1 @@
+{"address":"289d485d9771714cce91d3393d764e1311907acc","crypto":{"cipher":"aes-128-ctr","ciphertext":"faf32ca89d286b107f5e6d842802e05263c49b78d46eac74e6109e9a963378ab","cipherparams":{"iv":"558833eec4a665a8c55608d7d503407d"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"d571fff447ffb24314f9513f5160246f09997b857ac71348b73e785aab40dc04"},"mac":"21edb85ff7d0dab1767b9bf498f2c3cb7be7609490756bd32300bb213b59effe"},"id":"3279afcf-55ba-43ff-8997-02dcc46a6525","version":3} \ No newline at end of file
diff --git a/accounts/keystore/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e b/accounts/keystore/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e
new file mode 100644
index 0000000..498d813
--- /dev/null
+++ b/accounts/keystore/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e
@@ -0,0 +1 @@
+{"address":"cb61d5a9c4896fb9658090b597ef0e7be6f7b67e","Crypto":{"cipher":"aes-128-cbc","ciphertext":"6143d3192db8b66eabd693d9c4e414dcfaee52abda451af79ccf474dafb35f1bfc7ea013aa9d2ee35969a1a2e8d752d0","cipherparams":{"iv":"35337770fc2117994ecdcad026bccff4"},"kdf":"scrypt","kdfparams":{"n":262144,"r":8,"p":1,"dklen":32,"salt":"9afcddebca541253a2f4053391c673ff9fe23097cd8555d149d929e4ccf1257f"},"mac":"3f3d5af884b17a100b0b3232c0636c230a54dc2ac8d986227219b0dd89197644","version":"1"},"id":"e25f7c1f-d318-4f29-b62c-687190d4d299","version":"1"} \ No newline at end of file
diff --git a/accounts/keystore/testdata/v1_test_vector.json b/accounts/keystore/testdata/v1_test_vector.json
new file mode 100644
index 0000000..3d09b55
--- /dev/null
+++ b/accounts/keystore/testdata/v1_test_vector.json
@@ -0,0 +1,28 @@
+{
+ "test1": {
+ "json": {
+ "Crypto": {
+ "cipher": "aes-128-cbc",
+ "cipherparams": {
+ "iv": "35337770fc2117994ecdcad026bccff4"
+ },
+ "ciphertext": "6143d3192db8b66eabd693d9c4e414dcfaee52abda451af79ccf474dafb35f1bfc7ea013aa9d2ee35969a1a2e8d752d0",
+ "kdf": "scrypt",
+ "kdfparams": {
+ "dklen": 32,
+ "n": 262144,
+ "p": 1,
+ "r": 8,
+ "salt": "9afcddebca541253a2f4053391c673ff9fe23097cd8555d149d929e4ccf1257f"
+ },
+ "mac": "3f3d5af884b17a100b0b3232c0636c230a54dc2ac8d986227219b0dd89197644",
+ "version": "1"
+ },
+ "address": "cb61d5a9c4896fb9658090b597ef0e7be6f7b67e",
+ "id": "e25f7c1f-d318-4f29-b62c-687190d4d299",
+ "version": "1"
+ },
+ "password": "g",
+ "priv": "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d"
+ }
+}
diff --git a/accounts/keystore/testdata/v3_test_vector.json b/accounts/keystore/testdata/v3_test_vector.json
new file mode 100644
index 0000000..1e7f790
--- /dev/null
+++ b/accounts/keystore/testdata/v3_test_vector.json
@@ -0,0 +1,97 @@
+{
+ "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"
+ },
+ "31_byte_key": {
+ "json": {
+ "crypto" : {
+ "cipher" : "aes-128-ctr",
+ "cipherparams" : {
+ "iv" : "e0c41130a323adc1446fc82f724bca2f"
+ },
+ "ciphertext" : "9517cd5bdbe69076f9bf5057248c6c050141e970efa36ce53692d5d59a3984",
+ "kdf" : "scrypt",
+ "kdfparams" : {
+ "dklen" : 32,
+ "n" : 2,
+ "r" : 8,
+ "p" : 1,
+ "salt" : "711f816911c92d649fb4c84b047915679933555030b3552c1212609b38208c63"
+ },
+ "mac" : "d5e116151c6aa71470e67a7d42c9620c75c4d23229847dcc127794f0732b0db5"
+ },
+ "id" : "fecfc4ce-e956-48fd-953b-30f8b52ed66c",
+ "version" : 3
+ },
+ "password": "foo",
+ "priv": "fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35"
+ },
+ "30_byte_key": {
+ "json": {
+ "crypto" : {
+ "cipher" : "aes-128-ctr",
+ "cipherparams" : {
+ "iv" : "3ca92af36ad7c2cd92454c59cea5ef00"
+ },
+ "ciphertext" : "108b7d34f3442fc26ab1ab90ca91476ba6bfa8c00975a49ef9051dc675aa",
+ "kdf" : "scrypt",
+ "kdfparams" : {
+ "dklen" : 32,
+ "n" : 2,
+ "r" : 8,
+ "p" : 1,
+ "salt" : "d0769e608fb86cda848065642a9c6fa046845c928175662b8e356c77f914cd3b"
+ },
+ "mac" : "75d0e6759f7b3cefa319c3be41680ab6beea7d8328653474bd06706d4cc67420"
+ },
+ "id" : "a37e1559-5955-450d-8075-7b8931b392b2",
+ "version" : 3
+ },
+ "password": "foo",
+ "priv": "81c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018"
+ }
+}
diff --git a/accounts/keystore/testdata/very-light-scrypt.json b/accounts/keystore/testdata/very-light-scrypt.json
new file mode 100644
index 0000000..d23b9b2
--- /dev/null
+++ b/accounts/keystore/testdata/very-light-scrypt.json
@@ -0,0 +1 @@
+{"address":"45dea0fb0bba44f4fcf290bba71fd57d7117cbb8","crypto":{"cipher":"aes-128-ctr","ciphertext":"b87781948a1befd247bff51ef4063f716cf6c2d3481163e9a8f42e1f9bb74145","cipherparams":{"iv":"dc4926b48a105133d2f16b96833abf1e"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":2,"p":1,"r":8,"salt":"004244bbdc51cadda545b1cfa43cff9ed2ae88e08c61f1479dbb45410722f8f0"},"mac":"39990c1684557447940d4c69e06b1b82b2aceacb43f284df65c956daf3046b85"},"id":"ce541d8d-c79b-40f8-9f8c-20f59616faba","version":3}
diff --git a/accounts/keystore/wallet.go b/accounts/keystore/wallet.go
new file mode 100644
index 0000000..3be901b
--- /dev/null
+++ b/accounts/keystore/wallet.go
@@ -0,0 +1,148 @@
+// Copyright 2017 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 keystore
+
+import (
+ "math/big"
+
+ "github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/coreth/core/types"
+ ethereum "github.com/ava-labs/go-ethereum"
+ "github.com/ava-labs/go-ethereum/crypto"
+)
+
+// keystoreWallet implements the accounts.Wallet interface for the original
+// keystore.
+type keystoreWallet struct {
+ account accounts.Account // Single account contained in this wallet
+ keystore *KeyStore // Keystore where the account originates from
+}
+
+// URL implements accounts.Wallet, returning the URL of the account within.
+func (w *keystoreWallet) URL() accounts.URL {
+ return w.account.URL
+}
+
+// Status implements accounts.Wallet, returning whether the account held by the
+// keystore wallet is unlocked or not.
+func (w *keystoreWallet) Status() (string, error) {
+ w.keystore.mu.RLock()
+ defer w.keystore.mu.RUnlock()
+
+ if _, ok := w.keystore.unlocked[w.account.Address]; ok {
+ return "Unlocked", nil
+ }
+ return "Locked", nil
+}
+
+// Open implements accounts.Wallet, but is a noop for plain wallets since there
+// is no connection or decryption step necessary to access the list of accounts.
+func (w *keystoreWallet) Open(passphrase string) error { return nil }
+
+// Close implements accounts.Wallet, but is a noop for plain wallets since there
+// is no meaningful open operation.
+func (w *keystoreWallet) Close() error { return nil }
+
+// Accounts implements accounts.Wallet, returning an account list consisting of
+// a single account that the plain kestore wallet contains.
+func (w *keystoreWallet) Accounts() []accounts.Account {
+ return []accounts.Account{w.account}
+}
+
+// Contains implements accounts.Wallet, returning whether a particular account is
+// or is not wrapped by this wallet instance.
+func (w *keystoreWallet) Contains(account accounts.Account) bool {
+ return account.Address == w.account.Address && (account.URL == (accounts.URL{}) || account.URL == w.account.URL)
+}
+
+// Derive implements accounts.Wallet, but is a noop for plain wallets since there
+// is no notion of hierarchical account derivation for plain keystore accounts.
+func (w *keystoreWallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
+ return accounts.Account{}, accounts.ErrNotSupported
+}
+
+// SelfDerive implements accounts.Wallet, but is a noop for plain wallets since
+// there is no notion of hierarchical account derivation for plain keystore accounts.
+func (w *keystoreWallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) {
+}
+
+// signHash attempts to sign the given hash with
+// the given account. If the wallet does not wrap this particular account, an
+// error is returned to avoid account leakage (even though in theory we may be
+// able to sign via our shared keystore backend).
+func (w *keystoreWallet) signHash(account accounts.Account, hash []byte) ([]byte, error) {
+ // Make sure the requested account is contained within
+ if !w.Contains(account) {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignHash(account, hash)
+}
+
+// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
+func (w *keystoreWallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
+ return w.signHash(account, crypto.Keccak256(data))
+}
+
+// SignDataWithPassphrase signs keccak256(data). The mimetype parameter describes the type of data being signed
+func (w *keystoreWallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
+ // Make sure the requested account is contained within
+ if !w.Contains(account) {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignHashWithPassphrase(account, passphrase, crypto.Keccak256(data))
+}
+
+func (w *keystoreWallet) SignText(account accounts.Account, text []byte) ([]byte, error) {
+ return w.signHash(account, accounts.TextHash(text))
+}
+
+// SignTextWithPassphrase implements accounts.Wallet, attempting to sign the
+// given hash with the given account using passphrase as extra authentication.
+func (w *keystoreWallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
+ // Make sure the requested account is contained within
+ if !w.Contains(account) {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignHashWithPassphrase(account, passphrase, accounts.TextHash(text))
+}
+
+// SignTx implements accounts.Wallet, attempting to sign the given transaction
+// with the given account. If the wallet does not wrap this particular account,
+// an error is returned to avoid account leakage (even though in theory we may
+// be able to sign via our shared keystore backend).
+func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ // Make sure the requested account is contained within
+ if !w.Contains(account) {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignTx(account, tx, chainID)
+}
+
+// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
+// transaction with the given account using passphrase as extra authentication.
+func (w *keystoreWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ // Make sure the requested account is contained within
+ if !w.Contains(account) {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignTxWithPassphrase(account, passphrase, tx, chainID)
+}
diff --git a/accounts/keystore/watch.go b/accounts/keystore/watch.go
new file mode 100644
index 0000000..7fa5b3c
--- /dev/null
+++ b/accounts/keystore/watch.go
@@ -0,0 +1,108 @@
+// 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,cgo freebsd linux,!arm64 netbsd solaris
+
+package keystore
+
+import (
+ "time"
+
+ "github.com/ava-labs/go-ethereum/log"
+ "github.com/rjeczalik/notify"
+)
+
+type watcher struct {
+ ac *accountCache
+ starting bool
+ running bool
+ ev chan notify.EventInfo
+ quit chan struct{}
+}
+
+func newWatcher(ac *accountCache) *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()
+ }()
+ logger := log.New("path", w.ac.keydir)
+
+ if err := notify.Watch(w.ac.keydir, w.ev, notify.All); err != nil {
+ logger.Trace("Failed to watch keystore folder", "err", err)
+ return
+ }
+ defer notify.Stop(w.ev)
+ logger.Trace("Started watching keystore folder")
+ defer logger.Trace("Stopped watching keystore folder")
+
+ 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 (
+ debounceDuration = 500 * time.Millisecond
+ rescanTriggered = false
+ debounce = time.NewTimer(0)
+ )
+ // Ignore initial trigger
+ if !debounce.Stop() {
+ <-debounce.C
+ }
+ defer debounce.Stop()
+ for {
+ select {
+ case <-w.quit:
+ return
+ case <-w.ev:
+ // Trigger the scan (with delay), if not already triggered
+ if !rescanTriggered {
+ debounce.Reset(debounceDuration)
+ rescanTriggered = true
+ }
+ case <-debounce.C:
+ w.ac.scanAccounts()
+ rescanTriggered = false
+ }
+ }
+}
diff --git a/accounts/keystore/watch_fallback.go b/accounts/keystore/watch_fallback.go
new file mode 100644
index 0000000..de0e87f
--- /dev/null
+++ b/accounts/keystore/watch_fallback.go
@@ -0,0 +1,28 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build darwin,!cgo ios linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris
+
+// This is the fallback implementation of directory watching.
+// It is used on unsupported platforms.
+
+package keystore
+
+type watcher struct{ running bool }
+
+func newWatcher(*accountCache) *watcher { return new(watcher) }
+func (*watcher) start() {}
+func (*watcher) close() {}
diff --git a/accounts/manager.go b/accounts/manager.go
new file mode 100644
index 0000000..39211da
--- /dev/null
+++ b/accounts/manager.go
@@ -0,0 +1,229 @@
+// Copyright 2017 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 accounts
+
+import (
+ "reflect"
+ "sort"
+ "sync"
+
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/event"
+)
+
+// Config contains the settings of the global account manager.
+//
+// TODO(rjl493456442, karalabe, holiman): Get rid of this when account management
+// is removed in favor of Clef.
+type Config struct {
+ InsecureUnlockAllowed bool // Whether account unlocking in insecure environment is allowed
+}
+
+// Manager is an overarching account manager that can communicate with various
+// backends for signing transactions.
+type Manager struct {
+ config *Config // Global account manager configurations
+ backends map[reflect.Type][]Backend // Index of backends currently registered
+ updaters []event.Subscription // Wallet update subscriptions for all backends
+ updates chan WalletEvent // Subscription sink for backend wallet changes
+ wallets []Wallet // Cache of all wallets from all registered backends
+
+ feed event.Feed // Wallet feed notifying of arrivals/departures
+
+ quit chan chan error
+ lock sync.RWMutex
+}
+
+// NewManager creates a generic account manager to sign transaction via various
+// supported backends.
+func NewManager(config *Config, backends ...Backend) *Manager {
+ // Retrieve the initial list of wallets from the backends and sort by URL
+ var wallets []Wallet
+ for _, backend := range backends {
+ wallets = merge(wallets, backend.Wallets()...)
+ }
+ // Subscribe to wallet notifications from all backends
+ updates := make(chan WalletEvent, 4*len(backends))
+
+ subs := make([]event.Subscription, len(backends))
+ for i, backend := range backends {
+ subs[i] = backend.Subscribe(updates)
+ }
+ // Assemble the account manager and return
+ am := &Manager{
+ config: config,
+ backends: make(map[reflect.Type][]Backend),
+ updaters: subs,
+ updates: updates,
+ wallets: wallets,
+ quit: make(chan chan error),
+ }
+ for _, backend := range backends {
+ kind := reflect.TypeOf(backend)
+ am.backends[kind] = append(am.backends[kind], backend)
+ }
+ go am.update()
+
+ return am
+}
+
+// Close terminates the account manager's internal notification processes.
+func (am *Manager) Close() error {
+ errc := make(chan error)
+ am.quit <- errc
+ return <-errc
+}
+
+// Config returns the configuration of account manager.
+func (am *Manager) Config() *Config {
+ return am.config
+}
+
+// update is the wallet event loop listening for notifications from the backends
+// and updating the cache of wallets.
+func (am *Manager) update() {
+ // Close all subscriptions when the manager terminates
+ defer func() {
+ am.lock.Lock()
+ for _, sub := range am.updaters {
+ sub.Unsubscribe()
+ }
+ am.updaters = nil
+ am.lock.Unlock()
+ }()
+
+ // Loop until termination
+ for {
+ select {
+ case event := <-am.updates:
+ // Wallet event arrived, update local cache
+ am.lock.Lock()
+ switch event.Kind {
+ case WalletArrived:
+ am.wallets = merge(am.wallets, event.Wallet)
+ case WalletDropped:
+ am.wallets = drop(am.wallets, event.Wallet)
+ }
+ am.lock.Unlock()
+
+ // Notify any listeners of the event
+ am.feed.Send(event)
+
+ case errc := <-am.quit:
+ // Manager terminating, return
+ errc <- nil
+ return
+ }
+ }
+}
+
+// Backends retrieves the backend(s) with the given type from the account manager.
+func (am *Manager) Backends(kind reflect.Type) []Backend {
+ return am.backends[kind]
+}
+
+// Wallets returns all signer accounts registered under this account manager.
+func (am *Manager) Wallets() []Wallet {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ cpy := make([]Wallet, len(am.wallets))
+ copy(cpy, am.wallets)
+ return cpy
+}
+
+// Wallet retrieves the wallet associated with a particular URL.
+func (am *Manager) Wallet(url string) (Wallet, error) {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ parsed, err := parseURL(url)
+ if err != nil {
+ return nil, err
+ }
+ for _, wallet := range am.Wallets() {
+ if wallet.URL() == parsed {
+ return wallet, nil
+ }
+ }
+ return nil, ErrUnknownWallet
+}
+
+// Accounts returns all account addresses of all wallets within the account manager
+func (am *Manager) Accounts() []common.Address {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ addresses := make([]common.Address, 0) // return [] instead of nil if empty
+ for _, wallet := range am.wallets {
+ for _, account := range wallet.Accounts() {
+ addresses = append(addresses, account.Address)
+ }
+ }
+ return addresses
+}
+
+// Find attempts to locate the wallet corresponding to a specific account. Since
+// accounts can be dynamically added to and removed from wallets, this method has
+// a linear runtime in the number of wallets.
+func (am *Manager) Find(account Account) (Wallet, error) {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ for _, wallet := range am.wallets {
+ if wallet.Contains(account) {
+ return wallet, nil
+ }
+ }
+ return nil, ErrUnknownAccount
+}
+
+// Subscribe creates an async subscription to receive notifications when the
+// manager detects the arrival or departure of a wallet from any of its backends.
+func (am *Manager) Subscribe(sink chan<- WalletEvent) event.Subscription {
+ return am.feed.Subscribe(sink)
+}
+
+// merge is a sorted analogue of append for wallets, where the ordering of the
+// origin list is preserved by inserting new wallets at the correct position.
+//
+// The original slice is assumed to be already sorted by URL.
+func merge(slice []Wallet, wallets ...Wallet) []Wallet {
+ for _, wallet := range wallets {
+ n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 })
+ if n == len(slice) {
+ slice = append(slice, wallet)
+ continue
+ }
+ slice = append(slice[:n], append([]Wallet{wallet}, slice[n:]...)...)
+ }
+ return slice
+}
+
+// drop is the couterpart of merge, which looks up wallets from within the sorted
+// cache and removes the ones specified.
+func drop(slice []Wallet, wallets ...Wallet) []Wallet {
+ for _, wallet := range wallets {
+ n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 })
+ if n == len(slice) {
+ // Wallet not found, may happen during startup
+ continue
+ }
+ slice = append(slice[:n], slice[n+1:]...)
+ }
+ return slice
+}
diff --git a/accounts/scwallet/README.md b/accounts/scwallet/README.md
new file mode 100644
index 0000000..cfca916
--- /dev/null
+++ b/accounts/scwallet/README.md
@@ -0,0 +1,102 @@
+# Using the smartcard wallet
+
+## Requirements
+
+ * A USB smartcard reader
+ * A keycard that supports the status app
+ * PCSCD version 4.3 running on your system **Only version 4.3 is currently supported**
+
+## Preparing the smartcard
+
+ **WARNING: FOILLOWING THESE INSTRUCTIONS WILL DESTROY THE MASTER KEY ON YOUR CARD. ONLY PROCEED IF NO FUNDS ARE ASSOCIATED WITH THESE ACCOUNTS**
+
+ You can use status' [keycard-cli](https://github.com/status-im/keycard-cli) and you should get _at least_ version 2.1.1 of their [smartcard application](https://github.com/status-im/status-keycard/releases/download/2.2.1/keycard_v2.2.1.cap)
+
+ You also need to make sure that the PCSC daemon is running on your system.
+
+ Then, you can install the application to the card by typing:
+
+ ```
+ keycard install -a keycard_v2.2.1.cap && keycard init
+ ```
+
+ At the end of this process, you will be provided with a PIN, a PUK and a pairing password. Write them down, you'll need them shortly.
+
+ Start `geth` with the `console` command. You will notice the following warning:
+
+ ```
+ WARN [04-09|16:58:38.898] Failed to open wallet url=keycard://044def09 err="smartcard: pairing password needed"
+ ```
+
+ Write down the URL (`keycard://044def09` in this example). Then ask `geth` to open the wallet:
+
+ ```
+ > personal.openWallet("keycard://044def09")
+ Please enter the pairing password:
+ ```
+
+ Enter the pairing password that you have received during card initialization. Same with the PIN that you will subsequently be
+ asked for.
+
+ If everything goes well, you should see your new account when typing `personal` on the console:
+
+ ```
+ > personal
+ WARN [04-09|17:02:07.330] Smartcard wallet account derivation failed url=keycard://044def09 err="Unexpected response status Cla=0x80, Ins=0xd1, Sw=0x6985"
+ {
+ listAccounts: [],
+ listWallets: [{
+ status: "Empty, waiting for initialization",
+ url: "keycard://044def09"
+ }],
+ ...
+ }
+ ```
+
+ So the communication with the card is working, but there is no key associated with this wallet. Let's create it:
+
+ ```
+ > personal.initializeWallet("keycard://044def09")
+ "tilt ... impact"
+ ```
+
+ You should get a list of words, this is your seed so write them down. Your wallet should now be initialized:
+
+ ```
+ > personal.listWallets
+ [{
+ accounts: [{
+ address: "0x678b7cd55c61917defb23546a41803c5bfefbc7a",
+ url: "keycard://044d/m/44'/60'/0'/0/0"
+ }],
+ status: "Online",
+ url: "keycard://044def09"
+ }]
+ ```
+
+ You're all set!
+
+## Usage
+
+ 1. Start `geth` with the `console` command
+ 2. Check the card's URL by checking `personal.listWallets`:
+
+```
+ listWallets: [{
+ status: "Online, can derive public keys",
+ url: "keycard://a4d73015"
+ }]
+```
+
+ 3. Open the wallet, you will be prompted for your pairing password, then PIN:
+
+```
+personal.openWallet("keycard://a4d73015")
+```
+
+ 4. Check that creation was successful by typing e.g. `personal`. Then use it like a regular wallet.
+
+## Known issues
+
+ * Starting geth with a valid card seems to make firefox crash.
+ * PCSC version 4.4 should work, but is currently untested
diff --git a/accounts/scwallet/apdu.go b/accounts/scwallet/apdu.go
new file mode 100644
index 0000000..bd36606
--- /dev/null
+++ b/accounts/scwallet/apdu.go
@@ -0,0 +1,87 @@
+// Copyright 2018 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 scwallet
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+)
+
+// commandAPDU represents an application data unit sent to a smartcard.
+type commandAPDU struct {
+ Cla, Ins, P1, P2 uint8 // Class, Instruction, Parameter 1, Parameter 2
+ Data []byte // Command data
+ Le uint8 // Command data length
+}
+
+// serialize serializes a command APDU.
+func (ca commandAPDU) serialize() ([]byte, error) {
+ buf := new(bytes.Buffer)
+
+ if err := binary.Write(buf, binary.BigEndian, ca.Cla); err != nil {
+ return nil, err
+ }
+ if err := binary.Write(buf, binary.BigEndian, ca.Ins); err != nil {
+ return nil, err
+ }
+ if err := binary.Write(buf, binary.BigEndian, ca.P1); err != nil {
+ return nil, err
+ }
+ if err := binary.Write(buf, binary.BigEndian, ca.P2); err != nil {
+ return nil, err
+ }
+ if len(ca.Data) > 0 {
+ if err := binary.Write(buf, binary.BigEndian, uint8(len(ca.Data))); err != nil {
+ return nil, err
+ }
+ if err := binary.Write(buf, binary.BigEndian, ca.Data); err != nil {
+ return nil, err
+ }
+ }
+ if err := binary.Write(buf, binary.BigEndian, ca.Le); err != nil {
+ return nil, err
+ }
+ return buf.Bytes(), nil
+}
+
+// responseAPDU represents an application data unit received from a smart card.
+type responseAPDU struct {
+ Data []byte // response data
+ Sw1, Sw2 uint8 // status words 1 and 2
+}
+
+// deserialize deserializes a response APDU.
+func (ra *responseAPDU) deserialize(data []byte) error {
+ if len(data) < 2 {
+ return fmt.Errorf("can not deserialize data: payload too short (%d < 2)", len(data))
+ }
+
+ ra.Data = make([]byte, len(data)-2)
+
+ buf := bytes.NewReader(data)
+ if err := binary.Read(buf, binary.BigEndian, &ra.Data); err != nil {
+ return err
+ }
+ if err := binary.Read(buf, binary.BigEndian, &ra.Sw1); err != nil {
+ return err
+ }
+ if err := binary.Read(buf, binary.BigEndian, &ra.Sw2); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/accounts/scwallet/hub.go b/accounts/scwallet/hub.go
new file mode 100644
index 0000000..ceb422c
--- /dev/null
+++ b/accounts/scwallet/hub.go
@@ -0,0 +1,302 @@
+// Copyright 2018 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 package implements support for smartcard-based hardware wallets such as
+// the one written by Status: https://github.com/status-im/hardware-wallet
+//
+// This implementation of smartcard wallets have a different interaction process
+// to other types of hardware wallet. The process works like this:
+//
+// 1. (First use with a given client) Establish a pairing between hardware
+// wallet and client. This requires a secret value called a 'pairing password'.
+// You can pair with an unpaired wallet with `personal.openWallet(URI, pairing password)`.
+// 2. (First use only) Initialize the wallet, which generates a keypair, stores
+// it on the wallet, and returns it so the user can back it up. You can
+// initialize a wallet with `personal.initializeWallet(URI)`.
+// 3. Connect to the wallet using the pairing information established in step 1.
+// You can connect to a paired wallet with `personal.openWallet(URI, PIN)`.
+// 4. Interact with the wallet as normal.
+
+package scwallet
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sort"
+ "sync"
+ "time"
+
+ "github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/event"
+ "github.com/ava-labs/go-ethereum/log"
+ pcsc "github.com/gballet/go-libpcsclite"
+)
+
+// Scheme is the URI prefix for smartcard wallets.
+const Scheme = "keycard"
+
+// refreshCycle is the maximum time between wallet refreshes (if USB hotplug
+// notifications don't work).
+const refreshCycle = time.Second
+
+// refreshThrottling is the minimum time between wallet refreshes to avoid thrashing.
+const refreshThrottling = 500 * time.Millisecond
+
+// smartcardPairing contains information about a smart card we have paired with
+// or might pair with the hub.
+type smartcardPairing struct {
+ PublicKey []byte `json:"publicKey"`
+ PairingIndex uint8 `json:"pairingIndex"`
+ PairingKey []byte `json:"pairingKey"`
+ Accounts map[common.Address]accounts.DerivationPath `json:"accounts"`
+}
+
+// Hub is a accounts.Backend that can find and handle generic PC/SC hardware wallets.
+type Hub struct {
+ scheme string // Protocol scheme prefixing account and wallet URLs.
+
+ context *pcsc.Client
+ datadir string
+ pairings map[string]smartcardPairing
+
+ refreshed time.Time // Time instance when the list of wallets was last refreshed
+ wallets map[string]*Wallet // Mapping from reader names to wallet instances
+ updateFeed event.Feed // Event feed to notify wallet additions/removals
+ updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
+ updating bool // Whether the event notification loop is running
+
+ quit chan chan error
+
+ stateLock sync.RWMutex // Protects the internals of the hub from racey access
+}
+
+func (hub *Hub) readPairings() error {
+ hub.pairings = make(map[string]smartcardPairing)
+ pairingFile, err := os.Open(filepath.Join(hub.datadir, "smartcards.json"))
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ return err
+ }
+
+ pairingData, err := ioutil.ReadAll(pairingFile)
+ if err != nil {
+ return err
+ }
+ var pairings []smartcardPairing
+ if err := json.Unmarshal(pairingData, &pairings); err != nil {
+ return err
+ }
+
+ for _, pairing := range pairings {
+ hub.pairings[string(pairing.PublicKey)] = pairing
+ }
+ return nil
+}
+
+func (hub *Hub) writePairings() error {
+ pairingFile, err := os.OpenFile(filepath.Join(hub.datadir, "smartcards.json"), os.O_RDWR|os.O_CREATE, 0755)
+ if err != nil {
+ return err
+ }
+ defer pairingFile.Close()
+
+ pairings := make([]smartcardPairing, 0, len(hub.pairings))
+ for _, pairing := range hub.pairings {
+ pairings = append(pairings, pairing)
+ }
+
+ pairingData, err := json.Marshal(pairings)
+ if err != nil {
+ return err
+ }
+
+ if _, err := pairingFile.Write(pairingData); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing {
+ if pairing, ok := hub.pairings[string(wallet.PublicKey)]; ok {
+ return &pairing
+ }
+ return nil
+}
+
+func (hub *Hub) setPairing(wallet *Wallet, pairing *smartcardPairing) error {
+ if pairing == nil {
+ delete(hub.pairings, string(wallet.PublicKey))
+ } else {
+ hub.pairings[string(wallet.PublicKey)] = *pairing
+ }
+ return hub.writePairings()
+}
+
+// NewHub creates a new hardware wallet manager for smartcards.
+func NewHub(daemonPath string, scheme string, datadir string) (*Hub, error) {
+ context, err := pcsc.EstablishContext(daemonPath, pcsc.ScopeSystem)
+ if err != nil {
+ return nil, err
+ }
+ hub := &Hub{
+ scheme: scheme,
+ context: context,
+ datadir: datadir,
+ wallets: make(map[string]*Wallet),
+ quit: make(chan chan error),
+ }
+ if err := hub.readPairings(); err != nil {
+ return nil, err
+ }
+ hub.refreshWallets()
+ return hub, nil
+}
+
+// Wallets implements accounts.Backend, returning all the currently tracked smart
+// cards that appear to be hardware wallets.
+func (hub *Hub) Wallets() []accounts.Wallet {
+ // Make sure the list of wallets is up to date
+ hub.refreshWallets()
+
+ hub.stateLock.RLock()
+ defer hub.stateLock.RUnlock()
+
+ cpy := make([]accounts.Wallet, 0, len(hub.wallets))
+ for _, wallet := range hub.wallets {
+ cpy = append(cpy, wallet)
+ }
+ sort.Sort(accounts.WalletsByURL(cpy))
+ return cpy
+}
+
+// refreshWallets scans the devices attached to the machine and updates the
+// list of wallets based on the found devices.
+func (hub *Hub) refreshWallets() {
+ // Don't scan the USB like crazy it the user fetches wallets in a loop
+ hub.stateLock.RLock()
+ elapsed := time.Since(hub.refreshed)
+ hub.stateLock.RUnlock()
+
+ if elapsed < refreshThrottling {
+ return
+ }
+ // Retrieve all the smart card reader to check for cards
+ readers, err := hub.context.ListReaders()
+ if err != nil {
+ // This is a perverted hack, the scard library returns an error if no card
+ // readers are present instead of simply returning an empty list. We don't
+ // want to fill the user's log with errors, so filter those out.
+ if err.Error() != "scard: Cannot find a smart card reader." {
+ log.Error("Failed to enumerate smart card readers", "err", err)
+ return
+ }
+ }
+ // Transform the current list of wallets into the new one
+ hub.stateLock.Lock()
+
+ events := []accounts.WalletEvent{}
+ seen := make(map[string]struct{})
+
+ for _, reader := range readers {
+ // Mark the reader as present
+ seen[reader] = struct{}{}
+
+ // If we alreay know about this card, skip to the next reader, otherwise clean up
+ if wallet, ok := hub.wallets[reader]; ok {
+ if err := wallet.ping(); err == nil {
+ continue
+ }
+ wallet.Close()
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
+ delete(hub.wallets, reader)
+ }
+ // New card detected, try to connect to it
+ card, err := hub.context.Connect(reader, pcsc.ShareShared, pcsc.ProtocolAny)
+ if err != nil {
+ log.Debug("Failed to open smart card", "reader", reader, "err", err)
+ continue
+ }
+ wallet := NewWallet(hub, card)
+ if err = wallet.connect(); err != nil {
+ log.Debug("Failed to connect to smart card", "reader", reader, "err", err)
+ card.Disconnect(pcsc.LeaveCard)
+ continue
+ }
+ // Card connected, start tracking in amongs the wallets
+ hub.wallets[reader] = wallet
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
+ }
+ // Remove any wallets no longer present
+ for reader, wallet := range hub.wallets {
+ if _, ok := seen[reader]; !ok {
+ wallet.Close()
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
+ delete(hub.wallets, reader)
+ }
+ }
+ hub.refreshed = time.Now()
+ hub.stateLock.Unlock()
+
+ for _, event := range events {
+ hub.updateFeed.Send(event)
+ }
+}
+
+// Subscribe implements accounts.Backend, creating an async subscription to
+// receive notifications on the addition or removal of smart card wallets.
+func (hub *Hub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
+ // We need the mutex to reliably start/stop the update loop
+ hub.stateLock.Lock()
+ defer hub.stateLock.Unlock()
+
+ // Subscribe the caller and track the subscriber count
+ sub := hub.updateScope.Track(hub.updateFeed.Subscribe(sink))
+
+ // Subscribers require an active notification loop, start it
+ if !hub.updating {
+ hub.updating = true
+ go hub.updater()
+ }
+ return sub
+}
+
+// updater is responsible for maintaining an up-to-date list of wallets managed
+// by the smart card hub, and for firing wallet addition/removal events.
+func (hub *Hub) updater() {
+ for {
+ // TODO: Wait for a USB hotplug event (not supported yet) or a refresh timeout
+ // <-hub.changes
+ time.Sleep(refreshCycle)
+
+ // Run the wallet refresher
+ hub.refreshWallets()
+
+ // If all our subscribers left, stop the updater
+ hub.stateLock.Lock()
+ if hub.updateScope.Count() == 0 {
+ hub.updating = false
+ hub.stateLock.Unlock()
+ return
+ }
+ hub.stateLock.Unlock()
+ }
+}
diff --git a/accounts/scwallet/securechannel.go b/accounts/scwallet/securechannel.go
new file mode 100644
index 0000000..c1b7cff
--- /dev/null
+++ b/accounts/scwallet/securechannel.go
@@ -0,0 +1,346 @@
+// Copyright 2018 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 scwallet
+
+import (
+ "bytes"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/rand"
+ "crypto/sha256"
+ "crypto/sha512"
+ "fmt"
+
+ "github.com/ava-labs/go-ethereum/crypto"
+ pcsc "github.com/gballet/go-libpcsclite"
+ "github.com/wsddn/go-ecdh"
+ "golang.org/x/crypto/pbkdf2"
+ "golang.org/x/text/unicode/norm"
+)
+
+const (
+ maxPayloadSize = 223
+ pairP1FirstStep = 0
+ pairP1LastStep = 1
+
+ scSecretLength = 32
+ scBlockSize = 16
+
+ insOpenSecureChannel = 0x10
+ insMutuallyAuthenticate = 0x11
+ insPair = 0x12
+ insUnpair = 0x13
+
+ pairingSalt = "Keycard Pairing Password Salt"
+)
+
+// SecureChannelSession enables secure communication with a hardware wallet.
+type SecureChannelSession struct {
+ card *pcsc.Card // A handle to the smartcard for communication
+ secret []byte // A shared secret generated from our ECDSA keys
+ publicKey []byte // Our own ephemeral public key
+ PairingKey []byte // A permanent shared secret for a pairing, if present
+ sessionEncKey []byte // The current session encryption key
+ sessionMacKey []byte // The current session MAC key
+ iv []byte // The current IV
+ PairingIndex uint8 // The pairing index
+}
+
+// NewSecureChannelSession creates a new secure channel for the given card and public key.
+func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSession, error) {
+ // Generate an ECDSA keypair for ourselves
+ gen := ecdh.NewEllipticECDH(crypto.S256())
+ private, public, err := gen.GenerateKey(rand.Reader)
+ if err != nil {
+ return nil, err
+ }
+
+ cardPublic, ok := gen.Unmarshal(keyData)
+ if !ok {
+ return nil, fmt.Errorf("Could not unmarshal public key from card")
+ }
+
+ secret, err := gen.GenerateSharedSecret(private, cardPublic)
+ if err != nil {
+ return nil, err
+ }
+
+ return &SecureChannelSession{
+ card: card,
+ secret: secret,
+ publicKey: gen.Marshal(public),
+ }, nil
+}
+
+// Pair establishes a new pairing with the smartcard.
+func (s *SecureChannelSession) Pair(pairingPassword []byte) error {
+ secretHash := pbkdf2.Key(norm.NFKD.Bytes(pairingPassword), norm.NFKD.Bytes([]byte(pairingSalt)), 50000, 32, sha256.New)
+
+ challenge := make([]byte, 32)
+ if _, err := rand.Read(challenge); err != nil {
+ return err
+ }
+
+ response, err := s.pair(pairP1FirstStep, challenge)
+ if err != nil {
+ return err
+ }
+
+ md := sha256.New()
+ md.Write(secretHash[:])
+ md.Write(challenge)
+
+ expectedCryptogram := md.Sum(nil)
+ cardCryptogram := response.Data[:32]
+ cardChallenge := response.Data[32:64]
+
+ if !bytes.Equal(expectedCryptogram, cardCryptogram) {
+ return fmt.Errorf("Invalid card cryptogram %v != %v", expectedCryptogram, cardCryptogram)
+ }
+
+ md.Reset()
+ md.Write(secretHash[:])
+ md.Write(cardChallenge)
+ response, err = s.pair(pairP1LastStep, md.Sum(nil))
+ if err != nil {
+ return err
+ }
+
+ md.Reset()
+ md.Write(secretHash[:])
+ md.Write(response.Data[1:])
+ s.PairingKey = md.Sum(nil)
+ s.PairingIndex = response.Data[0]
+
+ return nil
+}
+
+// Unpair disestablishes an existing pairing.
+func (s *SecureChannelSession) Unpair() error {
+ if s.PairingKey == nil {
+ return fmt.Errorf("Cannot unpair: not paired")
+ }
+
+ _, err := s.transmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{})
+ if err != nil {
+ return err
+ }
+ s.PairingKey = nil
+ // Close channel
+ s.iv = nil
+ return nil
+}
+
+// Open initializes the secure channel.
+func (s *SecureChannelSession) Open() error {
+ if s.iv != nil {
+ return fmt.Errorf("Session already opened")
+ }
+
+ response, err := s.open()
+ if err != nil {
+ return err
+ }
+
+ // Generate the encryption/mac key by hashing our shared secret,
+ // pairing key, and the first bytes returned from the Open APDU.
+ md := sha512.New()
+ md.Write(s.secret)
+ md.Write(s.PairingKey)
+ md.Write(response.Data[:scSecretLength])
+ keyData := md.Sum(nil)
+ s.sessionEncKey = keyData[:scSecretLength]
+ s.sessionMacKey = keyData[scSecretLength : scSecretLength*2]
+
+ // The IV is the last bytes returned from the Open APDU.
+ s.iv = response.Data[scSecretLength:]
+
+ return s.mutuallyAuthenticate()
+}
+
+// mutuallyAuthenticate is an internal method to authenticate both ends of the
+// connection.
+func (s *SecureChannelSession) mutuallyAuthenticate() error {
+ data := make([]byte, scSecretLength)
+ if _, err := rand.Read(data); err != nil {
+ return err
+ }
+
+ response, err := s.transmitEncrypted(claSCWallet, insMutuallyAuthenticate, 0, 0, data)
+ if err != nil {
+ return err
+ }
+ if response.Sw1 != 0x90 || response.Sw2 != 0x00 {
+ return fmt.Errorf("Got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2)
+ }
+
+ if len(response.Data) != scSecretLength {
+ return fmt.Errorf("Response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), scSecretLength)
+ }
+
+ return nil
+}
+
+// open is an internal method that sends an open APDU.
+func (s *SecureChannelSession) open() (*responseAPDU, error) {
+ return transmit(s.card, &commandAPDU{
+ Cla: claSCWallet,
+ Ins: insOpenSecureChannel,
+ P1: s.PairingIndex,
+ P2: 0,
+ Data: s.publicKey,
+ Le: 0,
+ })
+}
+
+// pair is an internal method that sends a pair APDU.
+func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*responseAPDU, error) {
+ return transmit(s.card, &commandAPDU{
+ Cla: claSCWallet,
+ Ins: insPair,
+ P1: p1,
+ P2: 0,
+ Data: data,
+ Le: 0,
+ })
+}
+
+// transmitEncrypted sends an encrypted message, and decrypts and returns the response.
+func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*responseAPDU, error) {
+ if s.iv == nil {
+ return nil, fmt.Errorf("Channel not open")
+ }
+
+ data, err := s.encryptAPDU(data)
+ if err != nil {
+ return nil, err
+ }
+ meta := [16]byte{cla, ins, p1, p2, byte(len(data) + scBlockSize)}
+ if err = s.updateIV(meta[:], data); err != nil {
+ return nil, err
+ }
+
+ fulldata := make([]byte, len(s.iv)+len(data))
+ copy(fulldata, s.iv)
+ copy(fulldata[len(s.iv):], data)
+
+ response, err := transmit(s.card, &commandAPDU{
+ Cla: cla,
+ Ins: ins,
+ P1: p1,
+ P2: p2,
+ Data: fulldata,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ rmeta := [16]byte{byte(len(response.Data))}
+ rmac := response.Data[:len(s.iv)]
+ rdata := response.Data[len(s.iv):]
+ plainData, err := s.decryptAPDU(rdata)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = s.updateIV(rmeta[:], rdata); err != nil {
+ return nil, err
+ }
+ if !bytes.Equal(s.iv, rmac) {
+ return nil, fmt.Errorf("Invalid MAC in response")
+ }
+
+ rapdu := &responseAPDU{}
+ rapdu.deserialize(plainData)
+
+ if rapdu.Sw1 != sw1Ok {
+ return nil, fmt.Errorf("Unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
+ }
+
+ return rapdu, nil
+}
+
+// encryptAPDU is an internal method that serializes and encrypts an APDU.
+func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
+ if len(data) > maxPayloadSize {
+ return nil, fmt.Errorf("Payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize)
+ }
+ data = pad(data, 0x80)
+
+ ret := make([]byte, len(data))
+
+ a, err := aes.NewCipher(s.sessionEncKey)
+ if err != nil {
+ return nil, err
+ }
+ crypter := cipher.NewCBCEncrypter(a, s.iv)
+ crypter.CryptBlocks(ret, data)
+ return ret, nil
+}
+
+// pad applies message padding to a 16 byte boundary.
+func pad(data []byte, terminator byte) []byte {
+ padded := make([]byte, (len(data)/16+1)*16)
+ copy(padded, data)
+ padded[len(data)] = terminator
+ return padded
+}
+
+// decryptAPDU is an internal method that decrypts and deserializes an APDU.
+func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) {
+ a, err := aes.NewCipher(s.sessionEncKey)
+ if err != nil {
+ return nil, err
+ }
+
+ ret := make([]byte, len(data))
+
+ crypter := cipher.NewCBCDecrypter(a, s.iv)
+ crypter.CryptBlocks(ret, data)
+ return unpad(ret, 0x80)
+}
+
+// unpad strips padding from a message.
+func unpad(data []byte, terminator byte) ([]byte, error) {
+ for i := 1; i <= 16; i++ {
+ switch data[len(data)-i] {
+ case 0:
+ continue
+ case terminator:
+ return data[:len(data)-i], nil
+ default:
+ return nil, fmt.Errorf("Expected end of padding, got %d", data[len(data)-i])
+ }
+ }
+ return nil, fmt.Errorf("Expected end of padding, got 0")
+}
+
+// updateIV is an internal method that updates the initialization vector after
+// each message exchanged.
+func (s *SecureChannelSession) updateIV(meta, data []byte) error {
+ data = pad(data, 0)
+ a, err := aes.NewCipher(s.sessionMacKey)
+ if err != nil {
+ return err
+ }
+ crypter := cipher.NewCBCEncrypter(a, make([]byte, 16))
+ crypter.CryptBlocks(meta, meta)
+ crypter.CryptBlocks(data, data)
+ // The first 16 bytes of the last block is the MAC
+ s.iv = data[len(data)-32 : len(data)-16]
+ return nil
+}
diff --git a/accounts/scwallet/wallet.go b/accounts/scwallet/wallet.go
new file mode 100644
index 0000000..f72e685
--- /dev/null
+++ b/accounts/scwallet/wallet.go
@@ -0,0 +1,1082 @@
+// Copyright 2018 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 scwallet
+
+import (
+ "bytes"
+ "context"
+ "crypto/hmac"
+ "crypto/sha256"
+ "crypto/sha512"
+ "encoding/asn1"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "math/big"
+ "regexp"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/coreth/core/types"
+ ethereum "github.com/ava-labs/go-ethereum"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/crypto"
+ "github.com/ava-labs/go-ethereum/log"
+ pcsc "github.com/gballet/go-libpcsclite"
+ "github.com/status-im/keycard-go/derivationpath"
+)
+
+// ErrPairingPasswordNeeded is returned if opening the smart card requires pairing with a pairing
+// password. In this case, the calling application should request user input to enter
+// the pairing password and send it back.
+var ErrPairingPasswordNeeded = errors.New("smartcard: pairing password needed")
+
+// ErrPINNeeded is returned if opening the smart card requires a PIN code. In
+// this case, the calling application should request user input to enter the PIN
+// and send it back.
+var ErrPINNeeded = errors.New("smartcard: pin needed")
+
+// ErrPINUnblockNeeded is returned if opening the smart card requires a PIN code,
+// but all PIN attempts have already been exhausted. In this case the calling
+// application should request user input for the PUK and a new PIN code to set
+// fo the card.
+var ErrPINUnblockNeeded = errors.New("smartcard: pin unblock needed")
+
+// ErrAlreadyOpen is returned if the smart card is attempted to be opened, but
+// there is already a paired and unlocked session.
+var ErrAlreadyOpen = errors.New("smartcard: already open")
+
+// ErrPubkeyMismatch is returned if the public key recovered from a signature
+// does not match the one expected by the user.
+var ErrPubkeyMismatch = errors.New("smartcard: recovered public key mismatch")
+
+var (
+ appletAID = []byte{0xA0, 0x00, 0x00, 0x08, 0x04, 0x00, 0x01, 0x01, 0x01}
+ // DerivationSignatureHash is used to derive the public key from the signature of this hash
+ DerivationSignatureHash = sha256.Sum256(common.Hash{}.Bytes())
+)
+
+// List of APDU command-related constants
+const (
+ claISO7816 = 0
+ claSCWallet = 0x80
+
+ insSelect = 0xA4
+ insGetResponse = 0xC0
+ sw1GetResponse = 0x61
+ sw1Ok = 0x90
+
+ insVerifyPin = 0x20
+ insUnblockPin = 0x22
+ insExportKey = 0xC2
+ insSign = 0xC0
+ insLoadKey = 0xD0
+ insDeriveKey = 0xD1
+ insStatus = 0xF2
+)
+
+// List of ADPU command parameters
+const (
+ P1DeriveKeyFromMaster = uint8(0x00)
+ P1DeriveKeyFromParent = uint8(0x01)
+ P1DeriveKeyFromCurrent = uint8(0x10)
+ statusP1WalletStatus = uint8(0x00)
+ statusP1Path = uint8(0x01)
+ signP1PrecomputedHash = uint8(0x01)
+ signP2OnlyBlock = uint8(0x81)
+ exportP1Any = uint8(0x00)
+ exportP2Pubkey = uint8(0x01)
+)
+
+// Minimum time to wait between self derivation attempts, even it the user is
+// requesting accounts like crazy.
+const selfDeriveThrottling = time.Second
+
+// Wallet represents a smartcard wallet instance.
+type Wallet struct {
+ Hub *Hub // A handle to the Hub that instantiated this wallet.
+ PublicKey []byte // The wallet's public key (used for communication and identification, not signing!)
+
+ lock sync.Mutex // Lock that gates access to struct fields and communication with the card
+ card *pcsc.Card // A handle to the smartcard interface for the wallet.
+ session *Session // The secure communication session with the card
+ log log.Logger // Contextual logger to tag the base with its id
+
+ deriveNextPaths []accounts.DerivationPath // Next derivation paths for account auto-discovery (multiple bases supported)
+ deriveNextAddrs []common.Address // Next derived account addresses for auto-discovery (multiple bases supported)
+ deriveChain ethereum.ChainStateReader // Blockchain state reader to discover used account with
+ deriveReq chan chan struct{} // Channel to request a self-derivation on
+ deriveQuit chan chan error // Channel to terminate the self-deriver with
+}
+
+// NewWallet constructs and returns a new Wallet instance.
+func NewWallet(hub *Hub, card *pcsc.Card) *Wallet {
+ wallet := &Wallet{
+ Hub: hub,
+ card: card,
+ }
+ return wallet
+}
+
+// transmit sends an APDU to the smartcard and receives and decodes the response.
+// It automatically handles requests by the card to fetch the return data separately,
+// and returns an error if the response status code is not success.
+func transmit(card *pcsc.Card, command *commandAPDU) (*responseAPDU, error) {
+ data, err := command.serialize()
+ if err != nil {
+ return nil, err
+ }
+
+ responseData, _, err := card.Transmit(data)
+ if err != nil {
+ return nil, err
+ }
+
+ response := new(responseAPDU)
+ if err = response.deserialize(responseData); err != nil {
+ return nil, err
+ }
+
+ // Are we being asked to fetch the response separately?
+ if response.Sw1 == sw1GetResponse && (command.Cla != claISO7816 || command.Ins != insGetResponse) {
+ return transmit(card, &commandAPDU{
+ Cla: claISO7816,
+ Ins: insGetResponse,
+ P1: 0,
+ P2: 0,
+ Data: nil,
+ Le: response.Sw2,
+ })
+ }
+
+ if response.Sw1 != sw1Ok {
+ return nil, fmt.Errorf("Unexpected insecure response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
+ }
+
+ return response, nil
+}
+
+// applicationInfo encodes information about the smartcard application - its
+// instance UID and public key.
+type applicationInfo struct {
+ InstanceUID []byte `asn1:"tag:15"`
+ PublicKey []byte `asn1:"tag:0"`
+}
+
+// connect connects to the wallet application and establishes a secure channel with it.
+// must be called before any other interaction with the wallet.
+func (w *Wallet) connect() error {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ appinfo, err := w.doselect()
+ if err != nil {
+ return err
+ }
+
+ channel, err := NewSecureChannelSession(w.card, appinfo.PublicKey)
+ if err != nil {
+ return err
+ }
+
+ w.PublicKey = appinfo.PublicKey
+ w.log = log.New("url", w.URL())
+ w.session = &Session{
+ Wallet: w,
+ Channel: channel,
+ }
+ return nil
+}
+
+// doselect is an internal (unlocked) function to send a SELECT APDU to the card.
+func (w *Wallet) doselect() (*applicationInfo, error) {
+ response, err := transmit(w.card, &commandAPDU{
+ Cla: claISO7816,
+ Ins: insSelect,
+ P1: 4,
+ P2: 0,
+ Data: appletAID,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ appinfo := new(applicationInfo)
+ if _, err := asn1.UnmarshalWithParams(response.Data, appinfo, "tag:4"); err != nil {
+ return nil, err
+ }
+ return appinfo, nil
+}
+
+// ping checks the card's status and returns an error if unsuccessful.
+func (w *Wallet) ping() error {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ // We can't ping if not paired
+ if !w.session.paired() {
+ return nil
+ }
+ if _, err := w.session.walletStatus(); err != nil {
+ return err
+ }
+ return nil
+}
+
+// release releases any resources held by an open wallet instance.
+func (w *Wallet) release() error {
+ if w.session != nil {
+ return w.session.release()
+ }
+ return nil
+}
+
+// pair is an internal (unlocked) function for establishing a new pairing
+// with the wallet.
+func (w *Wallet) pair(puk []byte) error {
+ if w.session.paired() {
+ return fmt.Errorf("Wallet already paired")
+ }
+ pairing, err := w.session.pair(puk)
+ if err != nil {
+ return err
+ }
+ if err = w.Hub.setPairing(w, &pairing); err != nil {
+ return err
+ }
+ return w.session.authenticate(pairing)
+}
+
+// Unpair deletes an existing wallet pairing.
+func (w *Wallet) Unpair(pin []byte) error {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ if !w.session.paired() {
+ return fmt.Errorf("wallet %x not paired", w.PublicKey)
+ }
+ if err := w.session.verifyPin(pin); err != nil {
+ return fmt.Errorf("failed to verify pin: %s", err)
+ }
+ if err := w.session.unpair(); err != nil {
+ return fmt.Errorf("failed to unpair: %s", err)
+ }
+ if err := w.Hub.setPairing(w, nil); err != nil {
+ return err
+ }
+ return nil
+}
+
+// URL retrieves the canonical path under which this wallet is reachable. It is
+// user by upper layers to define a sorting order over all wallets from multiple
+// backends.
+func (w *Wallet) URL() accounts.URL {
+ return accounts.URL{
+ Scheme: w.Hub.scheme,
+ Path: fmt.Sprintf("%x", w.PublicKey[1:5]), // Byte #0 isn't unique; 1:5 covers << 64K cards, bump to 1:9 for << 4M
+ }
+}
+
+// Status returns a textual status to aid the user in the current state of the
+// wallet. It also returns an error indicating any failure the wallet might have
+// encountered.
+func (w *Wallet) Status() (string, error) {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ // If the card is not paired, we can only wait
+ if !w.session.paired() {
+ return "Unpaired, waiting for pairing password", nil
+ }
+ // Yay, we have an encrypted session, retrieve the actual status
+ status, err := w.session.walletStatus()
+ if err != nil {
+ return fmt.Sprintf("Failed: %v", err), err
+ }
+ switch {
+ case !w.session.verified && status.PinRetryCount == 0 && status.PukRetryCount == 0:
+ return fmt.Sprintf("Bricked, waiting for full wipe"), nil
+ case !w.session.verified && status.PinRetryCount == 0:
+ return fmt.Sprintf("Blocked, waiting for PUK (%d attempts left) and new PIN", status.PukRetryCount), nil
+ case !w.session.verified:
+ return fmt.Sprintf("Locked, waiting for PIN (%d attempts left)", status.PinRetryCount), nil
+ case !status.Initialized:
+ return fmt.Sprintf("Empty, waiting for initialization"), nil
+ default:
+ return fmt.Sprintf("Online"), nil
+ }
+}
+
+// Open initializes access to a wallet instance. It is not meant to unlock or
+// decrypt account keys, rather simply to establish a connection to hardware
+// wallets and/or to access derivation seeds.
+//
+// The passphrase parameter may or may not be used by the implementation of a
+// particular wallet instance. The reason there is no passwordless open method
+// is to strive towards a uniform wallet handling, oblivious to the different
+// backend providers.
+//
+// Please note, if you open a wallet, you must close it to release any allocated
+// resources (especially important when working with hardware wallets).
+func (w *Wallet) Open(passphrase string) error {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ // If the session is already open, bail out
+ if w.session.verified {
+ return ErrAlreadyOpen
+ }
+ // If the smart card is not yet paired, attempt to do so either from a previous
+ // pairing key or form the supplied PUK code.
+ if !w.session.paired() {
+ // If a previous pairing exists, only ever try to use that
+ if pairing := w.Hub.pairing(w); pairing != nil {
+ if err := w.session.authenticate(*pairing); err != nil {
+ return fmt.Errorf("failed to authenticate card %x: %s", w.PublicKey[:4], err)
+ }
+ // Pairing still ok, fall through to PIN checks
+ } else {
+ // If no passphrase was supplied, request the PUK from the user
+ if passphrase == "" {
+ return ErrPairingPasswordNeeded
+ }
+ // Attempt to pair the smart card with the user supplied PUK
+ if err := w.pair([]byte(passphrase)); err != nil {
+ return err
+ }
+ // Pairing succeeded, fall through to PIN checks. This will of course fail,
+ // but we can't return ErrPINNeeded directly here becase we don't know whether
+ // a PIN check or a PIN reset is needed.
+ passphrase = ""
+ }
+ }
+ // The smart card was successfully paired, retrieve its status to check whether
+ // PIN verification or unblocking is needed.
+ status, err := w.session.walletStatus()
+ if err != nil {
+ return err
+ }
+ // Request the appropriate next authentication data, or use the one supplied
+ switch {
+ case passphrase == "" && status.PinRetryCount > 0:
+ return ErrPINNeeded
+ case passphrase == "":
+ return ErrPINUnblockNeeded
+ case status.PinRetryCount > 0:
+ if !regexp.MustCompile(`^[0-9]{6,}$`).MatchString(passphrase) {
+ w.log.Error("PIN needs to be at least 6 digits")
+ return ErrPINNeeded
+ }
+ if err := w.session.verifyPin([]byte(passphrase)); err != nil {
+ return err
+ }
+ default:
+ if !regexp.MustCompile(`^[0-9]{12,}$`).MatchString(passphrase) {
+ w.log.Error("PUK needs to be at least 12 digits")
+ return ErrPINUnblockNeeded
+ }
+ if err := w.session.unblockPin([]byte(passphrase)); err != nil {
+ return err
+ }
+ }
+ // Smart card paired and unlocked, initialize and register
+ w.deriveReq = make(chan chan struct{})
+ w.deriveQuit = make(chan chan error)
+
+ go w.selfDerive()
+
+ // Notify anyone listening for wallet events that a new device is accessible
+ go w.Hub.updateFeed.Send(accounts.WalletEvent{Wallet: w, Kind: accounts.WalletOpened})
+
+ return nil
+}
+
+// Close stops and closes the wallet, freeing any resources.
+func (w *Wallet) Close() error {
+ // Ensure the wallet was opened
+ w.lock.Lock()
+ dQuit := w.deriveQuit
+ w.lock.Unlock()
+
+ // Terminate the self-derivations
+ var derr error
+ if dQuit != nil {
+ errc := make(chan error)
+ dQuit <- errc
+ derr = <-errc // Save for later, we *must* close the USB
+ }
+ // Terminate the device connection
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ w.deriveQuit = nil
+ w.deriveReq = nil
+
+ if err := w.release(); err != nil {
+ return err
+ }
+ return derr
+}
+
+// selfDerive is an account derivation loop that upon request attempts to find
+// new non-zero accounts.
+func (w *Wallet) selfDerive() {
+ w.log.Debug("Smart card wallet self-derivation started")
+ defer w.log.Debug("Smart card wallet self-derivation stopped")
+
+ // Execute self-derivations until termination or error
+ var (
+ reqc chan struct{}
+ errc chan error
+ err error
+ )
+ for errc == nil && err == nil {
+ // Wait until either derivation or termination is requested
+ select {
+ case errc = <-w.deriveQuit:
+ // Termination requested
+ continue
+ case reqc = <-w.deriveReq:
+ // Account discovery requested
+ }
+ // Derivation needs a chain and device access, skip if either unavailable
+ w.lock.Lock()
+ if w.session == nil || w.deriveChain == nil {
+ w.lock.Unlock()
+ reqc <- struct{}{}
+ continue
+ }
+ pairing := w.Hub.pairing(w)
+
+ // Device lock obtained, derive the next batch of accounts
+ var (
+ paths []accounts.DerivationPath
+ nextAcc accounts.Account
+
+ nextPaths = append([]accounts.DerivationPath{}, w.deriveNextPaths...)
+ nextAddrs = append([]common.Address{}, w.deriveNextAddrs...)
+
+ context = context.Background()
+ )
+ for i := 0; i < len(nextAddrs); i++ {
+ for empty := false; !empty; {
+ // Retrieve the next derived Ethereum account
+ if nextAddrs[i] == (common.Address{}) {
+ if nextAcc, err = w.session.derive(nextPaths[i]); err != nil {
+ w.log.Warn("Smartcard wallet account derivation failed", "err", err)
+ break
+ }
+ nextAddrs[i] = nextAcc.Address
+ }
+ // Check the account's status against the current chain state
+ var (
+ balance *big.Int
+ nonce uint64
+ )
+ balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil)
+ if err != nil {
+ w.log.Warn("Smartcard wallet balance retrieval failed", "err", err)
+ break
+ }
+ nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil)
+ if err != nil {
+ w.log.Warn("Smartcard wallet nonce retrieval failed", "err", err)
+ break
+ }
+ // If the next account is empty, stop self-derivation, but add for the last base path
+ if balance.Sign() == 0 && nonce == 0 {
+ empty = true
+ if i < len(nextAddrs)-1 {
+ break
+ }
+ }
+ // We've just self-derived a new account, start tracking it locally
+ path := make(accounts.DerivationPath, len(nextPaths[i]))
+ copy(path[:], nextPaths[i][:])
+ paths = append(paths, path)
+
+ // Display a log message to the user for new (or previously empty accounts)
+ if _, known := pairing.Accounts[nextAddrs[i]]; !known || !empty || nextAddrs[i] != w.deriveNextAddrs[i] {
+ w.log.Info("Smartcard wallet discovered new account", "address", nextAddrs[i], "path", path, "balance", balance, "nonce", nonce)
+ }
+ pairing.Accounts[nextAddrs[i]] = path
+
+ // Fetch the next potential account
+ if !empty {
+ nextAddrs[i] = common.Address{}
+ nextPaths[i][len(nextPaths[i])-1]++
+ }
+ }
+ }
+ // If there are new accounts, write them out
+ if len(paths) > 0 {
+ err = w.Hub.setPairing(w, pairing)
+ }
+ // Shift the self-derivation forward
+ w.deriveNextAddrs = nextAddrs
+ w.deriveNextPaths = nextPaths
+
+ // Self derivation complete, release device lock
+ w.lock.Unlock()
+
+ // Notify the user of termination and loop after a bit of time (to avoid trashing)
+ reqc <- struct{}{}
+ if err == nil {
+ select {
+ case errc = <-w.deriveQuit:
+ // Termination requested, abort
+ case <-time.After(selfDeriveThrottling):
+ // Waited enough, willing to self-derive again
+ }
+ }
+ }
+ // In case of error, wait for termination
+ if err != nil {
+ w.log.Debug("Smartcard wallet self-derivation failed", "err", err)
+ errc = <-w.deriveQuit
+ }
+ errc <- err
+}
+
+// Accounts retrieves the list of signing accounts the wallet is currently aware
+// of. For hierarchical deterministic wallets, the list will not be exhaustive,
+// rather only contain the accounts explicitly pinned during account derivation.
+func (w *Wallet) Accounts() []accounts.Account {
+ // Attempt self-derivation if it's running
+ reqc := make(chan struct{}, 1)
+ select {
+ case w.deriveReq <- reqc:
+ // Self-derivation request accepted, wait for it
+ <-reqc
+ default:
+ // Self-derivation offline, throttled or busy, skip
+ }
+
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ if pairing := w.Hub.pairing(w); pairing != nil {
+ ret := make([]accounts.Account, 0, len(pairing.Accounts))
+ for address, path := range pairing.Accounts {
+ ret = append(ret, w.makeAccount(address, path))
+ }
+ sort.Sort(accounts.AccountsByURL(ret))
+ return ret
+ }
+ return nil
+}
+
+func (w *Wallet) makeAccount(address common.Address, path accounts.DerivationPath) accounts.Account {
+ return accounts.Account{
+ Address: address,
+ URL: accounts.URL{
+ Scheme: w.Hub.scheme,
+ Path: fmt.Sprintf("%x/%s", w.PublicKey[1:3], path.String()),
+ },
+ }
+}
+
+// Contains returns whether an account is part of this particular wallet or not.
+func (w *Wallet) Contains(account accounts.Account) bool {
+ if pairing := w.Hub.pairing(w); pairing != nil {
+ _, ok := pairing.Accounts[account.Address]
+ return ok
+ }
+ return false
+}
+
+// Initialize installs a keypair generated from the provided key into the wallet.
+func (w *Wallet) Initialize(seed []byte) error {
+ go w.selfDerive()
+ // DO NOT lock at this stage, as the initialize
+ // function relies on Status()
+ return w.session.initialize(seed)
+}
+
+// Derive attempts to explicitly derive a hierarchical deterministic account at
+// the specified derivation path. If requested, the derived account will be added
+// to the wallet's tracked account list.
+func (w *Wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ account, err := w.session.derive(path)
+ if err != nil {
+ return accounts.Account{}, err
+ }
+
+ if pin {
+ pairing := w.Hub.pairing(w)
+ pairing.Accounts[account.Address] = path
+ if err := w.Hub.setPairing(w, pairing); err != nil {
+ return accounts.Account{}, err
+ }
+ }
+
+ return account, nil
+}
+
+// SelfDerive sets a base account derivation path from which the wallet attempts
+// to discover non zero accounts and automatically add them to list of tracked
+// accounts.
+//
+// Note, self derivaton will increment the last component of the specified path
+// opposed to decending into a child path to allow discovering accounts starting
+// from non zero components.
+//
+// Some hardware wallets switched derivation paths through their evolution, so
+// this method supports providing multiple bases to discover old user accounts
+// too. Only the last base will be used to derive the next empty account.
+//
+// You can disable automatic account discovery by calling SelfDerive with a nil
+// chain state reader.
+func (w *Wallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ w.deriveNextPaths = make([]accounts.DerivationPath, len(bases))
+ for i, base := range bases {
+ w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base))
+ copy(w.deriveNextPaths[i][:], base[:])
+ }
+ w.deriveNextAddrs = make([]common.Address, len(bases))
+ w.deriveChain = chain
+}
+
+// SignData requests the wallet to sign the hash of the given data.
+//
+// It looks up the account specified either solely via its address contained within,
+// or optionally with the aid of any location metadata from the embedded URL field.
+//
+// If the wallet requires additional authentication to sign the request (e.g.
+// a password to decrypt the account, or a PIN code o verify the transaction),
+// an AuthNeededError instance will be returned, containing infos for the user
+// about which fields or actions are needed. The user may retry by providing
+// the needed details via SignDataWithPassphrase, or by other means (e.g. unlock
+// the account in a keystore).
+func (w *Wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
+ return w.signHash(account, crypto.Keccak256(data))
+}
+
+func (w *Wallet) signHash(account accounts.Account, hash []byte) ([]byte, error) {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ path, err := w.findAccountPath(account)
+ if err != nil {
+ return nil, err
+ }
+
+ return w.session.sign(path, hash)
+}
+
+// SignTx requests the wallet to sign the given transaction.
+//
+// It looks up the account specified either solely via its address contained within,
+// or optionally with the aid of any location metadata from the embedded URL field.
+//
+// If the wallet requires additional authentication to sign the request (e.g.
+// a password to decrypt the account, or a PIN code o verify the transaction),
+// an AuthNeededError instance will be returned, containing infos for the user
+// about which fields or actions are needed. The user may retry by providing
+// the needed details via SignTxWithPassphrase, or by other means (e.g. unlock
+// the account in a keystore).
+func (w *Wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ signer := types.NewEIP155Signer(chainID)
+ hash := signer.Hash(tx)
+ sig, err := w.signHash(account, hash[:])
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(signer, sig)
+}
+
+// SignDataWithPassphrase requests the wallet to sign the given hash with the
+// given passphrase as extra authentication information.
+//
+// It looks up the account specified either solely via its address contained within,
+// or optionally with the aid of any location metadata from the embedded URL field.
+func (w *Wallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
+ return w.signHashWithPassphrase(account, passphrase, crypto.Keccak256(data))
+}
+
+func (w *Wallet) signHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) {
+ if !w.session.verified {
+ if err := w.Open(passphrase); err != nil {
+ return nil, err
+ }
+ }
+
+ return w.signHash(account, hash)
+}
+
+// SignText requests the wallet to sign the hash of a given piece of data, prefixed
+// by the Ethereum prefix scheme
+// It looks up the account specified either solely via its address contained within,
+// or optionally with the aid of any location metadata from the embedded URL field.
+//
+// If the wallet requires additional authentication to sign the request (e.g.
+// a password to decrypt the account, or a PIN code o verify the transaction),
+// an AuthNeededError instance will be returned, containing infos for the user
+// about which fields or actions are needed. The user may retry by providing
+// the needed details via SignHashWithPassphrase, or by other means (e.g. unlock
+// the account in a keystore).
+func (w *Wallet) SignText(account accounts.Account, text []byte) ([]byte, error) {
+ return w.signHash(account, accounts.TextHash(text))
+}
+
+// SignTextWithPassphrase implements accounts.Wallet, attempting to sign the
+// given hash with the given account using passphrase as extra authentication
+func (w *Wallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
+ return w.signHashWithPassphrase(account, passphrase, crypto.Keccak256(accounts.TextHash(text)))
+}
+
+// SignTxWithPassphrase requests the wallet to sign the given transaction, with the
+// given passphrase as extra authentication information.
+//
+// It looks up the account specified either solely via its address contained within,
+// or optionally with the aid of any location metadata from the embedded URL field.
+func (w *Wallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ if !w.session.verified {
+ if err := w.Open(passphrase); err != nil {
+ return nil, err
+ }
+ }
+ return w.SignTx(account, tx, chainID)
+}
+
+// findAccountPath returns the derivation path for the provided account.
+// It first checks for the address in the list of pinned accounts, and if it is
+// not found, attempts to parse the derivation path from the account's URL.
+func (w *Wallet) findAccountPath(account accounts.Account) (accounts.DerivationPath, error) {
+ pairing := w.Hub.pairing(w)
+ if path, ok := pairing.Accounts[account.Address]; ok {
+ return path, nil
+ }
+
+ // Look for the path in the URL
+ if account.URL.Scheme != w.Hub.scheme {
+ return nil, fmt.Errorf("Scheme %s does not match wallet scheme %s", account.URL.Scheme, w.Hub.scheme)
+ }
+
+ parts := strings.SplitN(account.URL.Path, "/", 2)
+ if len(parts) != 2 {
+ return nil, fmt.Errorf("Invalid URL format: %s", account.URL)
+ }
+
+ if parts[0] != fmt.Sprintf("%x", w.PublicKey[1:3]) {
+ return nil, fmt.Errorf("URL %s is not for this wallet", account.URL)
+ }
+
+ return accounts.ParseDerivationPath(parts[1])
+}
+
+// Session represents a secured communication session with the wallet.
+type Session struct {
+ Wallet *Wallet // A handle to the wallet that opened the session
+ Channel *SecureChannelSession // A secure channel for encrypted messages
+ verified bool // Whether the pin has been verified in this session.
+}
+
+// pair establishes a new pairing over this channel, using the provided secret.
+func (s *Session) pair(secret []byte) (smartcardPairing, error) {
+ err := s.Channel.Pair(secret)
+ if err != nil {
+ return smartcardPairing{}, err
+ }
+
+ return smartcardPairing{
+ PublicKey: s.Wallet.PublicKey,
+ PairingIndex: s.Channel.PairingIndex,
+ PairingKey: s.Channel.PairingKey,
+ Accounts: make(map[common.Address]accounts.DerivationPath),
+ }, nil
+}
+
+// unpair deletes an existing pairing.
+func (s *Session) unpair() error {
+ if !s.verified {
+ return fmt.Errorf("Unpair requires that the PIN be verified")
+ }
+ return s.Channel.Unpair()
+}
+
+// verifyPin unlocks a wallet with the provided pin.
+func (s *Session) verifyPin(pin []byte) error {
+ if _, err := s.Channel.transmitEncrypted(claSCWallet, insVerifyPin, 0, 0, pin); err != nil {
+ return err
+ }
+ s.verified = true
+ return nil
+}
+
+// unblockPin unblocks a wallet with the provided puk and resets the pin to the
+// new one specified.
+func (s *Session) unblockPin(pukpin []byte) error {
+ if _, err := s.Channel.transmitEncrypted(claSCWallet, insUnblockPin, 0, 0, pukpin); err != nil {
+ return err
+ }
+ s.verified = true
+ return nil
+}
+
+// release releases resources associated with the channel.
+func (s *Session) release() error {
+ return s.Wallet.card.Disconnect(pcsc.LeaveCard)
+}
+
+// paired returns true if a valid pairing exists.
+func (s *Session) paired() bool {
+ return s.Channel.PairingKey != nil
+}
+
+// authenticate uses an existing pairing to establish a secure channel.
+func (s *Session) authenticate(pairing smartcardPairing) error {
+ if !bytes.Equal(s.Wallet.PublicKey, pairing.PublicKey) {
+ return fmt.Errorf("Cannot pair using another wallet's pairing; %x != %x", s.Wallet.PublicKey, pairing.PublicKey)
+ }
+ s.Channel.PairingKey = pairing.PairingKey
+ s.Channel.PairingIndex = pairing.PairingIndex
+ return s.Channel.Open()
+}
+
+// walletStatus describes a smartcard wallet's status information.
+type walletStatus struct {
+ PinRetryCount int // Number of remaining PIN retries
+ PukRetryCount int // Number of remaining PUK retries
+ Initialized bool // Whether the card has been initialized with a private key
+}
+
+// walletStatus fetches the wallet's status from the card.
+func (s *Session) walletStatus() (*walletStatus, error) {
+ response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1WalletStatus, 0, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ status := new(walletStatus)
+ if _, err := asn1.UnmarshalWithParams(response.Data, status, "tag:3"); err != nil {
+ return nil, err
+ }
+ return status, nil
+}
+
+// derivationPath fetches the wallet's current derivation path from the card.
+func (s *Session) derivationPath() (accounts.DerivationPath, error) {
+ response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1Path, 0, nil)
+ if err != nil {
+ return nil, err
+ }
+ buf := bytes.NewReader(response.Data)
+ path := make(accounts.DerivationPath, len(response.Data)/4)
+ return path, binary.Read(buf, binary.BigEndian, &path)
+}
+
+// initializeData contains data needed to initialize the smartcard wallet.
+type initializeData struct {
+ PublicKey []byte `asn1:"tag:0"`
+ PrivateKey []byte `asn1:"tag:1"`
+ ChainCode []byte `asn1:"tag:2"`
+}
+
+// initialize initializes the card with new key data.
+func (s *Session) initialize(seed []byte) error {
+ // Check that the wallet isn't currently initialized,
+ // otherwise the key would be overwritten.
+ status, err := s.Wallet.Status()
+ if err != nil {
+ return err
+ }
+ if status == "Online" {
+ return fmt.Errorf("card is already initialized, cowardly refusing to proceed")
+ }
+
+ s.Wallet.lock.Lock()
+ defer s.Wallet.lock.Unlock()
+
+ // HMAC the seed to produce the private key and chain code
+ mac := hmac.New(sha512.New, []byte("Bitcoin seed"))
+ mac.Write(seed)
+ seed = mac.Sum(nil)
+
+ key, err := crypto.ToECDSA(seed[:32])
+ if err != nil {
+ return err
+ }
+
+ id := initializeData{}
+ id.PublicKey = crypto.FromECDSAPub(&key.PublicKey)
+ id.PrivateKey = seed[:32]
+ id.ChainCode = seed[32:]
+ data, err := asn1.Marshal(id)
+ if err != nil {
+ return err
+ }
+
+ // Nasty hack to force the top-level struct tag to be context-specific
+ data[0] = 0xA1
+
+ _, err = s.Channel.transmitEncrypted(claSCWallet, insLoadKey, 0x02, 0, data)
+ return err
+}
+
+// derive derives a new HD key path on the card.
+func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error) {
+ startingPoint, path, err := derivationpath.Decode(path.String())
+ if err != nil {
+ return accounts.Account{}, err
+ }
+
+ var p1 uint8
+ switch startingPoint {
+ case derivationpath.StartingPointMaster:
+ p1 = P1DeriveKeyFromMaster
+ case derivationpath.StartingPointParent:
+ p1 = P1DeriveKeyFromParent
+ case derivationpath.StartingPointCurrent:
+ p1 = P1DeriveKeyFromCurrent
+ default:
+ return accounts.Account{}, fmt.Errorf("invalid startingPoint %d", startingPoint)
+ }
+
+ data := new(bytes.Buffer)
+ for _, segment := range path {
+ if err := binary.Write(data, binary.BigEndian, segment); err != nil {
+ return accounts.Account{}, err
+ }
+ }
+
+ _, err = s.Channel.transmitEncrypted(claSCWallet, insDeriveKey, p1, 0, data.Bytes())
+ if err != nil {
+ return accounts.Account{}, err
+ }
+
+ response, err := s.Channel.transmitEncrypted(claSCWallet, insSign, 0, 0, DerivationSignatureHash[:])
+ if err != nil {
+ return accounts.Account{}, err
+ }
+
+ sigdata := new(signatureData)
+ if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil {
+ return accounts.Account{}, err
+ }
+ rbytes, sbytes := sigdata.Signature.R.Bytes(), sigdata.Signature.S.Bytes()
+ sig := make([]byte, 65)
+ copy(sig[32-len(rbytes):32], rbytes)
+ copy(sig[64-len(sbytes):64], sbytes)
+
+ if err := confirmPublicKey(sig, sigdata.PublicKey); err != nil {
+ return accounts.Account{}, err
+ }
+ pub, err := crypto.UnmarshalPubkey(sigdata.PublicKey)
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ return s.Wallet.makeAccount(crypto.PubkeyToAddress(*pub), path), nil
+}
+
+// keyExport contains information on an exported keypair.
+type keyExport struct {
+ PublicKey []byte `asn1:"tag:0"`
+ PrivateKey []byte `asn1:"tag:1,optional"`
+}
+
+// publicKey returns the public key for the current derivation path.
+func (s *Session) publicKey() ([]byte, error) {
+ response, err := s.Channel.transmitEncrypted(claSCWallet, insExportKey, exportP1Any, exportP2Pubkey, nil)
+ if err != nil {
+ return nil, err
+ }
+ keys := new(keyExport)
+ if _, err := asn1.UnmarshalWithParams(response.Data, keys, "tag:1"); err != nil {
+ return nil, err
+ }
+ return keys.PublicKey, nil
+}
+
+// signatureData contains information on a signature - the signature itself and
+// the corresponding public key.
+type signatureData struct {
+ PublicKey []byte `asn1:"tag:0"`
+ Signature struct {
+ R *big.Int
+ S *big.Int
+ }
+}
+
+// sign asks the card to sign a message, and returns a valid signature after
+// recovering the v value.
+func (s *Session) sign(path accounts.DerivationPath, hash []byte) ([]byte, error) {
+ startTime := time.Now()
+ _, err := s.derive(path)
+ if err != nil {
+ return nil, err
+ }
+ deriveTime := time.Now()
+
+ response, err := s.Channel.transmitEncrypted(claSCWallet, insSign, signP1PrecomputedHash, signP2OnlyBlock, hash)
+ if err != nil {
+ return nil, err
+ }
+ sigdata := new(signatureData)
+ if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil {
+ return nil, err
+ }
+ // Serialize the signature
+ rbytes, sbytes := sigdata.Signature.R.Bytes(), sigdata.Signature.S.Bytes()
+ sig := make([]byte, 65)
+ copy(sig[32-len(rbytes):32], rbytes)
+ copy(sig[64-len(sbytes):64], sbytes)
+
+ // Recover the V value.
+ sig, err = makeRecoverableSignature(hash, sig, sigdata.PublicKey)
+ if err != nil {
+ return nil, err
+ }
+ log.Debug("Signed using smartcard", "deriveTime", deriveTime.Sub(startTime), "signingTime", time.Since(deriveTime))
+
+ return sig, nil
+}
+
+// confirmPublicKey confirms that the given signature belongs to the specified key.
+func confirmPublicKey(sig, pubkey []byte) error {
+ _, err := makeRecoverableSignature(DerivationSignatureHash[:], sig, pubkey)
+ return err
+}
+
+// makeRecoverableSignature uses a signature and an expected public key to
+// recover the v value and produce a recoverable signature.
+func makeRecoverableSignature(hash, sig, expectedPubkey []byte) ([]byte, error) {
+ var libraryError error
+ for v := 0; v < 2; v++ {
+ sig[64] = byte(v)
+ if pubkey, err := crypto.Ecrecover(hash, sig); err == nil {
+ if bytes.Equal(pubkey, expectedPubkey) {
+ return sig, nil
+ }
+ } else {
+ libraryError = err
+ }
+ }
+ if libraryError != nil {
+ return nil, libraryError
+ }
+ return nil, ErrPubkeyMismatch
+}
diff --git a/accounts/sort.go b/accounts/sort.go
new file mode 100644
index 0000000..f467621
--- /dev/null
+++ b/accounts/sort.go
@@ -0,0 +1,31 @@
+// Copyright 2018 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 accounts
+
+// AccountsByURL implements sort.Interface for []Account based on the URL field.
+type AccountsByURL []Account
+
+func (a AccountsByURL) Len() int { return len(a) }
+func (a AccountsByURL) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a AccountsByURL) Less(i, j int) bool { return a[i].URL.Cmp(a[j].URL) < 0 }
+
+// WalletsByURL implements sort.Interface for []Wallet based on the URL field.
+type WalletsByURL []Wallet
+
+func (w WalletsByURL) Len() int { return len(w) }
+func (w WalletsByURL) Swap(i, j int) { w[i], w[j] = w[j], w[i] }
+func (w WalletsByURL) Less(i, j int) bool { return w[i].URL().Cmp(w[j].URL()) < 0 }
diff --git a/accounts/url.go b/accounts/url.go
new file mode 100644
index 0000000..a5add10
--- /dev/null
+++ b/accounts/url.go
@@ -0,0 +1,104 @@
+// Copyright 2017 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 accounts
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "strings"
+)
+
+// URL represents the canonical identification URL of a wallet or account.
+//
+// It is a simplified version of url.URL, with the important limitations (which
+// are considered features here) that it contains value-copyable components only,
+// as well as that it doesn't do any URL encoding/decoding of special characters.
+//
+// The former is important to allow an account to be copied without leaving live
+// references to the original version, whereas the latter is important to ensure
+// one single canonical form opposed to many allowed ones by the RFC 3986 spec.
+//
+// As such, these URLs should not be used outside of the scope of an Ethereum
+// wallet or account.
+type URL struct {
+ Scheme string // Protocol scheme to identify a capable account backend
+ Path string // Path for the backend to identify a unique entity
+}
+
+// parseURL converts a user supplied URL into the accounts specific structure.
+func parseURL(url string) (URL, error) {
+ parts := strings.Split(url, "://")
+ if len(parts) != 2 || parts[0] == "" {
+ return URL{}, errors.New("protocol scheme missing")
+ }
+ return URL{
+ Scheme: parts[0],
+ Path: parts[1],
+ }, nil
+}
+
+// String implements the stringer interface.
+func (u URL) String() string {
+ if u.Scheme != "" {
+ return fmt.Sprintf("%s://%s", u.Scheme, u.Path)
+ }
+ return u.Path
+}
+
+// TerminalString implements the log.TerminalStringer interface.
+func (u URL) TerminalString() string {
+ url := u.String()
+ if len(url) > 32 {
+ return url[:31] + "…"
+ }
+ return url
+}
+
+// MarshalJSON implements the json.Marshaller interface.
+func (u URL) MarshalJSON() ([]byte, error) {
+ return json.Marshal(u.String())
+}
+
+// UnmarshalJSON parses url.
+func (u *URL) UnmarshalJSON(input []byte) error {
+ var textURL string
+ err := json.Unmarshal(input, &textURL)
+ if err != nil {
+ return err
+ }
+ url, err := parseURL(textURL)
+ if err != nil {
+ return err
+ }
+ u.Scheme = url.Scheme
+ u.Path = url.Path
+ return nil
+}
+
+// Cmp compares x and y and returns:
+//
+// -1 if x < y
+// 0 if x == y
+// +1 if x > y
+//
+func (u URL) Cmp(url URL) int {
+ if u.Scheme == url.Scheme {
+ return strings.Compare(u.Path, url.Path)
+ }
+ return strings.Compare(u.Scheme, url.Scheme)
+}
diff --git a/accounts/usbwallet/hub.go b/accounts/usbwallet/hub.go
new file mode 100644
index 0000000..b851ffc
--- /dev/null
+++ b/accounts/usbwallet/hub.go
@@ -0,0 +1,279 @@
+// Copyright 2017 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 usbwallet
+
+import (
+ "errors"
+ "runtime"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/go-ethereum/event"
+ "github.com/ava-labs/go-ethereum/log"
+ "github.com/karalabe/usb"
+)
+
+// LedgerScheme is the protocol scheme prefixing account and wallet URLs.
+const LedgerScheme = "ledger"
+
+// TrezorScheme is the protocol scheme prefixing account and wallet URLs.
+const TrezorScheme = "trezor"
+
+// refreshCycle is the maximum time between wallet refreshes (if USB hotplug
+// notifications don't work).
+const refreshCycle = time.Second
+
+// refreshThrottling is the minimum time between wallet refreshes to avoid USB
+// trashing.
+const refreshThrottling = 500 * time.Millisecond
+
+// Hub is a accounts.Backend that can find and handle generic USB hardware wallets.
+type Hub struct {
+ scheme string // Protocol scheme prefixing account and wallet URLs.
+ vendorID uint16 // USB vendor identifier used for device discovery
+ productIDs []uint16 // USB product identifiers used for device discovery
+ usageID uint16 // USB usage page identifier used for macOS device discovery
+ endpointID int // USB endpoint identifier used for non-macOS device discovery
+ makeDriver func(log.Logger) driver // Factory method to construct a vendor specific driver
+
+ refreshed time.Time // Time instance when the list of wallets was last refreshed
+ wallets []accounts.Wallet // List of USB wallet devices currently tracking
+ updateFeed event.Feed // Event feed to notify wallet additions/removals
+ updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
+ updating bool // Whether the event notification loop is running
+
+ quit chan chan error
+
+ stateLock sync.RWMutex // Protects the internals of the hub from racey access
+
+ // TODO(karalabe): remove if hotplug lands on Windows
+ commsPend int // Number of operations blocking enumeration
+ commsLock sync.Mutex // Lock protecting the pending counter and enumeration
+ enumFails uint32 // Number of times enumeration has failed
+}
+
+// NewLedgerHub creates a new hardware wallet manager for Ledger devices.
+func NewLedgerHub() (*Hub, error) {
+ return newHub(LedgerScheme, 0x2c97, []uint16{
+ // Original product IDs
+ 0x0000, /* Ledger Blue */
+ 0x0001, /* Ledger Nano S */
+ 0x0004, /* Ledger Nano X */
+
+ // Upcoming product IDs: https://www.ledger.com/2019/05/17/windows-10-update-sunsetting-u2f-tunnel-transport-for-ledger-devices/
+ 0x0015, /* HID + U2F + WebUSB Ledger Blue */
+ 0x1015, /* HID + U2F + WebUSB Ledger Nano S */
+ 0x4015, /* HID + U2F + WebUSB Ledger Nano X */
+ 0x0011, /* HID + WebUSB Ledger Blue */
+ 0x1011, /* HID + WebUSB Ledger Nano S */
+ 0x4011, /* HID + WebUSB Ledger Nano X */
+ }, 0xffa0, 0, newLedgerDriver)
+}
+
+// NewTrezorHubWithHID creates a new hardware wallet manager for Trezor devices.
+func NewTrezorHubWithHID() (*Hub, error) {
+ return newHub(TrezorScheme, 0x534c, []uint16{0x0001 /* Trezor HID */}, 0xff00, 0, newTrezorDriver)
+}
+
+// NewTrezorHubWithWebUSB creates a new hardware wallet manager for Trezor devices with
+// firmware version > 1.8.0
+func NewTrezorHubWithWebUSB() (*Hub, error) {
+ return newHub(TrezorScheme, 0x1209, []uint16{0x53c1 /* Trezor WebUSB */}, 0xffff /* No usage id on webusb, don't match unset (0) */, 0, newTrezorDriver)
+}
+
+// newHub creates a new hardware wallet manager for generic USB devices.
+func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16, endpointID int, makeDriver func(log.Logger) driver) (*Hub, error) {
+ if !usb.Supported() {
+ return nil, errors.New("unsupported platform")
+ }
+ hub := &Hub{
+ scheme: scheme,
+ vendorID: vendorID,
+ productIDs: productIDs,
+ usageID: usageID,
+ endpointID: endpointID,
+ makeDriver: makeDriver,
+ quit: make(chan chan error),
+ }
+ hub.refreshWallets()
+ return hub, nil
+}
+
+// Wallets implements accounts.Backend, returning all the currently tracked USB
+// devices that appear to be hardware wallets.
+func (hub *Hub) Wallets() []accounts.Wallet {
+ // Make sure the list of wallets is up to date
+ hub.refreshWallets()
+
+ hub.stateLock.RLock()
+ defer hub.stateLock.RUnlock()
+
+ cpy := make([]accounts.Wallet, len(hub.wallets))
+ copy(cpy, hub.wallets)
+ return cpy
+}
+
+// refreshWallets scans the USB devices attached to the machine and updates the
+// list of wallets based on the found devices.
+func (hub *Hub) refreshWallets() {
+ // Don't scan the USB like crazy it the user fetches wallets in a loop
+ hub.stateLock.RLock()
+ elapsed := time.Since(hub.refreshed)
+ hub.stateLock.RUnlock()
+
+ if elapsed < refreshThrottling {
+ return
+ }
+ // If USB enumeration is continually failing, don't keep trying indefinitely
+ if atomic.LoadUint32(&hub.enumFails) > 2 {
+ return
+ }
+ // Retrieve the current list of USB wallet devices
+ var devices []usb.DeviceInfo
+
+ if runtime.GOOS == "linux" {
+ // hidapi on Linux opens the device during enumeration to retrieve some infos,
+ // breaking the Ledger protocol if that is waiting for user confirmation. This
+ // is a bug acknowledged at Ledger, but it won't be fixed on old devices so we
+ // need to prevent concurrent comms ourselves. The more elegant solution would
+ // be to ditch enumeration in favor of hotplug events, but that don't work yet
+ // on Windows so if we need to hack it anyway, this is more elegant for now.
+ hub.commsLock.Lock()
+ if hub.commsPend > 0 { // A confirmation is pending, don't refresh
+ hub.commsLock.Unlock()
+ return
+ }
+ }
+ infos, err := usb.Enumerate(hub.vendorID, 0)
+ if err != nil {
+ failcount := atomic.AddUint32(&hub.enumFails, 1)
+ if runtime.GOOS == "linux" {
+ // See rationale before the enumeration why this is needed and only on Linux.
+ hub.commsLock.Unlock()
+ }
+ log.Error("Failed to enumerate USB devices", "hub", hub.scheme,
+ "vendor", hub.vendorID, "failcount", failcount, "err", err)
+ return
+ }
+ atomic.StoreUint32(&hub.enumFails, 0)
+
+ for _, info := range infos {
+ for _, id := range hub.productIDs {
+ // Windows and Macos use UsageID matching, Linux uses Interface matching
+ if info.ProductID == id && (info.UsagePage == hub.usageID || info.Interface == hub.endpointID) {
+ devices = append(devices, info)
+ break
+ }
+ }
+ }
+ if runtime.GOOS == "linux" {
+ // See rationale before the enumeration why this is needed and only on Linux.
+ hub.commsLock.Unlock()
+ }
+ // Transform the current list of wallets into the new one
+ hub.stateLock.Lock()
+
+ var (
+ wallets = make([]accounts.Wallet, 0, len(devices))
+ events []accounts.WalletEvent
+ )
+
+ for _, device := range devices {
+ url := accounts.URL{Scheme: hub.scheme, Path: device.Path}
+
+ // Drop wallets in front of the next device or those that failed for some reason
+ for len(hub.wallets) > 0 {
+ // Abort if we're past the current device and found an operational one
+ _, failure := hub.wallets[0].Status()
+ if hub.wallets[0].URL().Cmp(url) >= 0 || failure == nil {
+ break
+ }
+ // Drop the stale and failed devices
+ events = append(events, accounts.WalletEvent{Wallet: hub.wallets[0], Kind: accounts.WalletDropped})
+ hub.wallets = hub.wallets[1:]
+ }
+ // If there are no more wallets or the device is before the next, wrap new wallet
+ if len(hub.wallets) == 0 || hub.wallets[0].URL().Cmp(url) > 0 {
+ logger := log.New("url", url)
+ wallet := &wallet{hub: hub, driver: hub.makeDriver(logger), url: &url, info: device, log: logger}
+
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
+ wallets = append(wallets, wallet)
+ continue
+ }
+ // If the device is the same as the first wallet, keep it
+ if hub.wallets[0].URL().Cmp(url) == 0 {
+ wallets = append(wallets, hub.wallets[0])
+ hub.wallets = hub.wallets[1:]
+ continue
+ }
+ }
+ // Drop any leftover wallets and set the new batch
+ for _, wallet := range hub.wallets {
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
+ }
+ hub.refreshed = time.Now()
+ hub.wallets = wallets
+ hub.stateLock.Unlock()
+
+ // Fire all wallet events and return
+ for _, event := range events {
+ hub.updateFeed.Send(event)
+ }
+}
+
+// Subscribe implements accounts.Backend, creating an async subscription to
+// receive notifications on the addition or removal of USB wallets.
+func (hub *Hub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
+ // We need the mutex to reliably start/stop the update loop
+ hub.stateLock.Lock()
+ defer hub.stateLock.Unlock()
+
+ // Subscribe the caller and track the subscriber count
+ sub := hub.updateScope.Track(hub.updateFeed.Subscribe(sink))
+
+ // Subscribers require an active notification loop, start it
+ if !hub.updating {
+ hub.updating = true
+ go hub.updater()
+ }
+ return sub
+}
+
+// updater is responsible for maintaining an up-to-date list of wallets managed
+// by the USB hub, and for firing wallet addition/removal events.
+func (hub *Hub) updater() {
+ for {
+ // TODO: Wait for a USB hotplug event (not supported yet) or a refresh timeout
+ // <-hub.changes
+ time.Sleep(refreshCycle)
+
+ // Run the wallet refresher
+ hub.refreshWallets()
+
+ // If all our subscribers left, stop the updater
+ hub.stateLock.Lock()
+ if hub.updateScope.Count() == 0 {
+ hub.updating = false
+ hub.stateLock.Unlock()
+ return
+ }
+ hub.stateLock.Unlock()
+ }
+}
diff --git a/accounts/usbwallet/ledger.go b/accounts/usbwallet/ledger.go
new file mode 100644
index 0000000..528e8da
--- /dev/null
+++ b/accounts/usbwallet/ledger.go
@@ -0,0 +1,465 @@
+// Copyright 2017 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 file contains the implementation for interacting with the Ledger hardware
+// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo:
+// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc
+
+package usbwallet
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "io"
+ "math/big"
+
+ "github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/common/hexutil"
+ "github.com/ava-labs/go-ethereum/crypto"
+ "github.com/ava-labs/go-ethereum/log"
+ "github.com/ava-labs/go-ethereum/rlp"
+)
+
+// ledgerOpcode is an enumeration encoding the supported Ledger opcodes.
+type ledgerOpcode byte
+
+// ledgerParam1 is an enumeration encoding the supported Ledger parameters for
+// specific opcodes. The same parameter values may be reused between opcodes.
+type ledgerParam1 byte
+
+// ledgerParam2 is an enumeration encoding the supported Ledger parameters for
+// specific opcodes. The same parameter values may be reused between opcodes.
+type ledgerParam2 byte
+
+const (
+ ledgerOpRetrieveAddress ledgerOpcode = 0x02 // Returns the public key and Ethereum address for a given BIP 32 path
+ ledgerOpSignTransaction ledgerOpcode = 0x04 // Signs an Ethereum transaction after having the user validate the parameters
+ ledgerOpGetConfiguration ledgerOpcode = 0x06 // Returns specific wallet application configuration
+
+ ledgerP1DirectlyFetchAddress ledgerParam1 = 0x00 // Return address directly from the wallet
+ ledgerP1InitTransactionData ledgerParam1 = 0x00 // First transaction data block for signing
+ ledgerP1ContTransactionData ledgerParam1 = 0x80 // Subsequent transaction data block for signing
+ ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 // Do not return the chain code along with the address
+)
+
+// errLedgerReplyInvalidHeader is the error message returned by a Ledger data exchange
+// if the device replies with a mismatching header. This usually means the device
+// is in browser mode.
+var errLedgerReplyInvalidHeader = errors.New("ledger: invalid reply header")
+
+// errLedgerInvalidVersionReply is the error message returned by a Ledger version retrieval
+// when a response does arrive, but it does not contain the expected data.
+var errLedgerInvalidVersionReply = errors.New("ledger: invalid version reply")
+
+// ledgerDriver implements the communication with a Ledger hardware wallet.
+type ledgerDriver struct {
+ device io.ReadWriter // USB device connection to communicate through
+ version [3]byte // Current version of the Ledger firmware (zero if app is offline)
+ browser bool // Flag whether the Ledger is in browser mode (reply channel mismatch)
+ failure error // Any failure that would make the device unusable
+ log log.Logger // Contextual logger to tag the ledger with its id
+}
+
+// newLedgerDriver creates a new instance of a Ledger USB protocol driver.
+func newLedgerDriver(logger log.Logger) driver {
+ return &ledgerDriver{
+ log: logger,
+ }
+}
+
+// Status implements usbwallet.driver, returning various states the Ledger can
+// currently be in.
+func (w *ledgerDriver) Status() (string, error) {
+ if w.failure != nil {
+ return fmt.Sprintf("Failed: %v", w.failure), w.failure
+ }
+ if w.browser {
+ return "Ethereum app in browser mode", w.failure
+ }
+ if w.offline() {
+ return "Ethereum app offline", w.failure
+ }
+ return fmt.Sprintf("Ethereum app v%d.%d.%d online", w.version[0], w.version[1], w.version[2]), w.failure
+}
+
+// offline returns whether the wallet and the Ethereum app is offline or not.
+//
+// The method assumes that the state lock is held!
+func (w *ledgerDriver) offline() bool {
+ return w.version == [3]byte{0, 0, 0}
+}
+
+// Open implements usbwallet.driver, attempting to initialize the connection to the
+// Ledger hardware wallet. The Ledger does not require a user passphrase, so that
+// parameter is silently discarded.
+func (w *ledgerDriver) Open(device io.ReadWriter, passphrase string) error {
+ w.device, w.failure = device, nil
+
+ _, err := w.ledgerDerive(accounts.DefaultBaseDerivationPath)
+ if err != nil {
+ // Ethereum app is not running or in browser mode, nothing more to do, return
+ if err == errLedgerReplyInvalidHeader {
+ w.browser = true
+ }
+ return nil
+ }
+ // Try to resolve the Ethereum app's version, will fail prior to v1.0.2
+ if w.version, err = w.ledgerVersion(); err != nil {
+ w.version = [3]byte{1, 0, 0} // Assume worst case, can't verify if v1.0.0 or v1.0.1
+ }
+ return nil
+}
+
+// Close implements usbwallet.driver, cleaning up and metadata maintained within
+// the Ledger driver.
+func (w *ledgerDriver) Close() error {
+ w.browser, w.version = false, [3]byte{}
+ return nil
+}
+
+// Heartbeat implements usbwallet.driver, performing a sanity check against the
+// Ledger to see if it's still online.
+func (w *ledgerDriver) Heartbeat() error {
+ if _, err := w.ledgerVersion(); err != nil && err != errLedgerInvalidVersionReply {
+ w.failure = err
+ return err
+ }
+ return nil
+}
+
+// Derive implements usbwallet.driver, sending a derivation request to the Ledger
+// and returning the Ethereum address located on that derivation path.
+func (w *ledgerDriver) Derive(path accounts.DerivationPath) (common.Address, error) {
+ return w.ledgerDerive(path)
+}
+
+// SignTx implements usbwallet.driver, sending the transaction to the Ledger and
+// waiting for the user to confirm or deny the transaction.
+//
+// Note, if the version of the Ethereum application running on the Ledger wallet is
+// too old to sign EIP-155 transactions, but such is requested nonetheless, an error
+// will be returned opposed to silently signing in Homestead mode.
+func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) {
+ // If the Ethereum app doesn't run, abort
+ if w.offline() {
+ return common.Address{}, nil, accounts.ErrWalletClosed
+ }
+ // Ensure the wallet is capable of signing the given transaction
+ if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 {
+ return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
+ }
+ // All infos gathered and metadata checks out, request signing
+ return w.ledgerSign(path, tx, chainID)
+}
+
+// ledgerVersion retrieves the current version of the Ethereum wallet app running
+// on the Ledger wallet.
+//
+// The version retrieval protocol is defined as follows:
+//
+// CLA | INS | P1 | P2 | Lc | Le
+// ----+-----+----+----+----+---
+// E0 | 06 | 00 | 00 | 00 | 04
+//
+// With no input data, and the output data being:
+//
+// Description | Length
+// ---------------------------------------------------+--------
+// Flags 01: arbitrary data signature enabled by user | 1 byte
+// Application major version | 1 byte
+// Application minor version | 1 byte
+// Application patch version | 1 byte
+func (w *ledgerDriver) ledgerVersion() ([3]byte, error) {
+ // Send the request and wait for the response
+ reply, err := w.ledgerExchange(ledgerOpGetConfiguration, 0, 0, nil)
+ if err != nil {
+ return [3]byte{}, err
+ }
+ if len(reply) != 4 {
+ return [3]byte{}, errLedgerInvalidVersionReply
+ }
+ // Cache the version for future reference
+ var version [3]byte
+ copy(version[:], reply[1:])
+ return version, nil
+}
+
+// ledgerDerive retrieves the currently active Ethereum address from a Ledger
+// wallet at the specified derivation path.
+//
+// The address derivation protocol is defined as follows:
+//
+// CLA | INS | P1 | P2 | Lc | Le
+// ----+-----+----+----+-----+---
+// E0 | 02 | 00 return address
+// 01 display address and confirm before returning
+// | 00: do not return the chain code
+// | 01: return the chain code
+// | var | 00
+//
+// Where the input data is:
+//
+// Description | Length
+// -------------------------------------------------+--------
+// Number of BIP 32 derivations to perform (max 10) | 1 byte
+// First derivation index (big endian) | 4 bytes
+// ... | 4 bytes
+// Last derivation index (big endian) | 4 bytes
+//
+// And the output data is:
+//
+// Description | Length
+// ------------------------+-------------------
+// Public Key length | 1 byte
+// Uncompressed Public Key | arbitrary
+// Ethereum address length | 1 byte
+// Ethereum address | 40 bytes hex ascii
+// Chain code if requested | 32 bytes
+func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, error) {
+ // Flatten the derivation path into the Ledger request
+ path := make([]byte, 1+4*len(derivationPath))
+ path[0] = byte(len(derivationPath))
+ for i, component := range derivationPath {
+ binary.BigEndian.PutUint32(path[1+4*i:], component)
+ }
+ // Send the request and wait for the response
+ reply, err := w.ledgerExchange(ledgerOpRetrieveAddress, ledgerP1DirectlyFetchAddress, ledgerP2DiscardAddressChainCode, path)
+ if err != nil {
+ return common.Address{}, err
+ }
+ // Discard the public key, we don't need that for now
+ if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
+ return common.Address{}, errors.New("reply lacks public key entry")
+ }
+ reply = reply[1+int(reply[0]):]
+
+ // Extract the Ethereum hex address string
+ if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
+ return common.Address{}, errors.New("reply lacks address entry")
+ }
+ hexstr := reply[1 : 1+int(reply[0])]
+
+ // Decode the hex sting into an Ethereum address and return
+ var address common.Address
+ if _, err = hex.Decode(address[:], hexstr); err != nil {
+ return common.Address{}, err
+ }
+ return address, nil
+}
+
+// ledgerSign sends the transaction to the Ledger wallet, and waits for the user
+// to confirm or deny the transaction.
+//
+// The transaction signing protocol is defined as follows:
+//
+// CLA | INS | P1 | P2 | Lc | Le
+// ----+-----+----+----+-----+---
+// E0 | 04 | 00: first transaction data block
+// 80: subsequent transaction data block
+// | 00 | variable | variable
+//
+// Where the input for the first transaction block (first 255 bytes) is:
+//
+// Description | Length
+// -------------------------------------------------+----------
+// Number of BIP 32 derivations to perform (max 10) | 1 byte
+// First derivation index (big endian) | 4 bytes
+// ... | 4 bytes
+// Last derivation index (big endian) | 4 bytes
+// RLP transaction chunk | arbitrary
+//
+// And the input for subsequent transaction blocks (first 255 bytes) are:
+//
+// Description | Length
+// ----------------------+----------
+// RLP transaction chunk | arbitrary
+//
+// And the output data is:
+//
+// Description | Length
+// ------------+---------
+// signature V | 1 byte
+// signature R | 32 bytes
+// signature S | 32 bytes
+func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) {
+ // Flatten the derivation path into the Ledger request
+ path := make([]byte, 1+4*len(derivationPath))
+ path[0] = byte(len(derivationPath))
+ for i, component := range derivationPath {
+ binary.BigEndian.PutUint32(path[1+4*i:], component)
+ }
+ // Create the transaction RLP based on whether legacy or EIP155 signing was requested
+ var (
+ txrlp []byte
+ err error
+ )
+ if chainID == nil {
+ if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data()}); err != nil {
+ return common.Address{}, nil, err
+ }
+ } else {
+ if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data(), chainID, big.NewInt(0), big.NewInt(0)}); err != nil {
+ return common.Address{}, nil, err
+ }
+ }
+ payload := append(path, txrlp...)
+
+ // Send the request and wait for the response
+ var (
+ op = ledgerP1InitTransactionData
+ reply []byte
+ )
+ for len(payload) > 0 {
+ // Calculate the size of the next data chunk
+ chunk := 255
+ if chunk > len(payload) {
+ chunk = len(payload)
+ }
+ // Send the chunk over, ensuring it's processed correctly
+ reply, err = w.ledgerExchange(ledgerOpSignTransaction, op, 0, payload[:chunk])
+ if err != nil {
+ return common.Address{}, nil, err
+ }
+ // Shift the payload and ensure subsequent chunks are marked as such
+ payload = payload[chunk:]
+ op = ledgerP1ContTransactionData
+ }
+ // Extract the Ethereum signature and do a sanity validation
+ if len(reply) != crypto.SignatureLength {
+ return common.Address{}, nil, errors.New("reply lacks signature")
+ }
+ signature := append(reply[1:], reply[0])
+
+ // Create the correct signer and signature transform based on the chain ID
+ var signer types.Signer
+ if chainID == nil {
+ signer = new(types.HomesteadSigner)
+ } else {
+ signer = types.NewEIP155Signer(chainID)
+ signature[64] -= byte(chainID.Uint64()*2 + 35)
+ }
+ signed, err := tx.WithSignature(signer, signature)
+ if err != nil {
+ return common.Address{}, nil, err
+ }
+ sender, err := types.Sender(signer, signed)
+ if err != nil {
+ return common.Address{}, nil, err
+ }
+ return sender, signed, nil
+}
+
+// ledgerExchange performs a data exchange with the Ledger wallet, sending it a
+// message and retrieving the response.
+//
+// The common transport header is defined as follows:
+//
+// Description | Length
+// --------------------------------------+----------
+// Communication channel ID (big endian) | 2 bytes
+// Command tag | 1 byte
+// Packet sequence index (big endian) | 2 bytes
+// Payload | arbitrary
+//
+// The Communication channel ID allows commands multiplexing over the same
+// physical link. It is not used for the time being, and should be set to 0101
+// to avoid compatibility issues with implementations ignoring a leading 00 byte.
+//
+// The Command tag describes the message content. Use TAG_APDU (0x05) for standard
+// APDU payloads, or TAG_PING (0x02) for a simple link test.
+//
+// The Packet sequence index describes the current sequence for fragmented payloads.
+// The first fragment index is 0x00.
+//
+// APDU Command payloads are encoded as follows:
+//
+// Description | Length
+// -----------------------------------
+// APDU length (big endian) | 2 bytes
+// APDU CLA | 1 byte
+// APDU INS | 1 byte
+// APDU P1 | 1 byte
+// APDU P2 | 1 byte
+// APDU length | 1 byte
+// Optional APDU data | arbitrary
+func (w *ledgerDriver) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 ledgerParam2, data []byte) ([]byte, error) {
+ // Construct the message payload, possibly split into multiple chunks
+ apdu := make([]byte, 2, 7+len(data))
+
+ binary.BigEndian.PutUint16(apdu, uint16(5+len(data)))
+ apdu = append(apdu, []byte{0xe0, byte(opcode), byte(p1), byte(p2), byte(len(data))}...)
+ apdu = append(apdu, data...)
+
+ // Stream all the chunks to the device
+ header := []byte{0x01, 0x01, 0x05, 0x00, 0x00} // Channel ID and command tag appended
+ chunk := make([]byte, 64)
+ space := len(chunk) - len(header)
+
+ for i := 0; len(apdu) > 0; i++ {
+ // Construct the new message to stream
+ chunk = append(chunk[:0], header...)
+ binary.BigEndian.PutUint16(chunk[3:], uint16(i))
+
+ if len(apdu) > space {
+ chunk = append(chunk, apdu[:space]...)
+ apdu = apdu[space:]
+ } else {
+ chunk = append(chunk, apdu...)
+ apdu = nil
+ }
+ // Send over to the device
+ w.log.Trace("Data chunk sent to the Ledger", "chunk", hexutil.Bytes(chunk))
+ if _, err := w.device.Write(chunk); err != nil {
+ return nil, err
+ }
+ }
+ // Stream the reply back from the wallet in 64 byte chunks
+ var reply []byte
+ chunk = chunk[:64] // Yeah, we surely have enough space
+ for {
+ // Read the next chunk from the Ledger wallet
+ if _, err := io.ReadFull(w.device, chunk); err != nil {
+ return nil, err
+ }
+ w.log.Trace("Data chunk received from the Ledger", "chunk", hexutil.Bytes(chunk))
+
+ // Make sure the transport header matches
+ if chunk[0] != 0x01 || chunk[1] != 0x01 || chunk[2] != 0x05 {
+ return nil, errLedgerReplyInvalidHeader
+ }
+ // If it's the first chunk, retrieve the total message length
+ var payload []byte
+
+ if chunk[3] == 0x00 && chunk[4] == 0x00 {
+ reply = make([]byte, 0, int(binary.BigEndian.Uint16(chunk[5:7])))
+ payload = chunk[7:]
+ } else {
+ payload = chunk[5:]
+ }
+ // Append to the reply and stop when filled up
+ if left := cap(reply) - len(reply); left > len(payload) {
+ reply = append(reply, payload...)
+ } else {
+ reply = append(reply, payload[:left]...)
+ break
+ }
+ }
+ return reply[:len(reply)-2], nil
+}
diff --git a/accounts/usbwallet/trezor.go b/accounts/usbwallet/trezor.go
new file mode 100644
index 0000000..833c7a9
--- /dev/null
+++ b/accounts/usbwallet/trezor.go
@@ -0,0 +1,365 @@
+// Copyright 2017 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 file contains the implementation for interacting with the Trezor hardware
+// wallets. The wire protocol spec can be found on the SatoshiLabs website:
+// https://doc.satoshilabs.com/trezor-tech/api-protobuf.html
+
+package usbwallet
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "math/big"
+
+ "github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/coreth/accounts/usbwallet/trezor"
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/common/hexutil"
+ "github.com/ava-labs/go-ethereum/log"
+ "github.com/golang/protobuf/proto"
+)
+
+// ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In
+// this case, the calling application should display a pinpad and send back the
+// encoded passphrase.
+var ErrTrezorPINNeeded = errors.New("trezor: pin needed")
+
+// ErrTrezorPassphraseNeeded is returned if opening the trezor requires a passphrase
+var ErrTrezorPassphraseNeeded = errors.New("trezor: passphrase needed")
+
+// errTrezorReplyInvalidHeader is the error message returned by a Trezor data exchange
+// if the device replies with a mismatching header. This usually means the device
+// is in browser mode.
+var errTrezorReplyInvalidHeader = errors.New("trezor: invalid reply header")
+
+// trezorDriver implements the communication with a Trezor hardware wallet.
+type trezorDriver struct {
+ device io.ReadWriter // USB device connection to communicate through
+ version [3]uint32 // Current version of the Trezor firmware
+ label string // Current textual label of the Trezor device
+ pinwait bool // Flags whether the device is waiting for PIN entry
+ passphrasewait bool // Flags whether the device is waiting for passphrase entry
+ failure error // Any failure that would make the device unusable
+ log log.Logger // Contextual logger to tag the trezor with its id
+}
+
+// newTrezorDriver creates a new instance of a Trezor USB protocol driver.
+func newTrezorDriver(logger log.Logger) driver {
+ return &trezorDriver{
+ log: logger,
+ }
+}
+
+// Status implements accounts.Wallet, always whether the Trezor is opened, closed
+// or whether the Ethereum app was not started on it.
+func (w *trezorDriver) Status() (string, error) {
+ if w.failure != nil {
+ return fmt.Sprintf("Failed: %v", w.failure), w.failure
+ }
+ if w.device == nil {
+ return "Closed", w.failure
+ }
+ if w.pinwait {
+ return fmt.Sprintf("Trezor v%d.%d.%d '%s' waiting for PIN", w.version[0], w.version[1], w.version[2], w.label), w.failure
+ }
+ return fmt.Sprintf("Trezor v%d.%d.%d '%s' online", w.version[0], w.version[1], w.version[2], w.label), w.failure
+}
+
+// Open implements usbwallet.driver, attempting to initialize the connection to
+// the Trezor hardware wallet. Initializing the Trezor is a two or three phase operation:
+// * The first phase is to initialize the connection and read the wallet's
+// features. This phase is invoked if the provided passphrase is empty. The
+// device will display the pinpad as a result and will return an appropriate
+// error to notify the user that a second open phase is needed.
+// * The second phase is to unlock access to the Trezor, which is done by the
+// user actually providing a passphrase mapping a keyboard keypad to the pin
+// number of the user (shuffled according to the pinpad displayed).
+// * If needed the device will ask for passphrase which will require calling
+// open again with the actual passphrase (3rd phase)
+func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
+ w.device, w.failure = device, nil
+
+ // If phase 1 is requested, init the connection and wait for user callback
+ if passphrase == "" && !w.passphrasewait {
+ // If we're already waiting for a PIN entry, insta-return
+ if w.pinwait {
+ return ErrTrezorPINNeeded
+ }
+ // Initialize a connection to the device
+ features := new(trezor.Features)
+ if _, err := w.trezorExchange(&trezor.Initialize{}, features); err != nil {
+ return err
+ }
+ w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
+ w.label = features.GetLabel()
+
+ // Do a manual ping, forcing the device to ask for its PIN and Passphrase
+ askPin := true
+ askPassphrase := true
+ res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin, PassphraseProtection: &askPassphrase}, new(trezor.PinMatrixRequest), new(trezor.PassphraseRequest), new(trezor.Success))
+ if err != nil {
+ return err
+ }
+ // Only return the PIN request if the device wasn't unlocked until now
+ switch res {
+ case 0:
+ w.pinwait = true
+ return ErrTrezorPINNeeded
+ case 1:
+ w.pinwait = false
+ w.passphrasewait = true
+ return ErrTrezorPassphraseNeeded
+ case 2:
+ return nil // responded with trezor.Success
+ }
+ }
+ // Phase 2 requested with actual PIN entry
+ if w.pinwait {
+ w.pinwait = false
+ res, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success), new(trezor.PassphraseRequest))
+ if err != nil {
+ w.failure = err
+ return err
+ }
+ if res == 1 {
+ w.passphrasewait = true
+ return ErrTrezorPassphraseNeeded
+ }
+ } else if w.passphrasewait {
+ w.passphrasewait = false
+ if _, err := w.trezorExchange(&trezor.PassphraseAck{Passphrase: &passphrase}, new(trezor.Success)); err != nil {
+ w.failure = err
+ return err
+ }
+ }
+
+ return nil
+}
+
+// Close implements usbwallet.driver, cleaning up and metadata maintained within
+// the Trezor driver.
+func (w *trezorDriver) Close() error {
+ w.version, w.label, w.pinwait = [3]uint32{}, "", false
+ return nil
+}
+
+// Heartbeat implements usbwallet.driver, performing a sanity check against the
+// Trezor to see if it's still online.
+func (w *trezorDriver) Heartbeat() error {
+ if _, err := w.trezorExchange(&trezor.Ping{}, new(trezor.Success)); err != nil {
+ w.failure = err
+ return err
+ }
+ return nil
+}
+
+// Derive implements usbwallet.driver, sending a derivation request to the Trezor
+// and returning the Ethereum address located on that derivation path.
+func (w *trezorDriver) Derive(path accounts.DerivationPath) (common.Address, error) {
+ return w.trezorDerive(path)
+}
+
+// SignTx implements usbwallet.driver, sending the transaction to the Trezor and
+// waiting for the user to confirm or deny the transaction.
+func (w *trezorDriver) SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) {
+ if w.device == nil {
+ return common.Address{}, nil, accounts.ErrWalletClosed
+ }
+ return w.trezorSign(path, tx, chainID)
+}
+
+// trezorDerive sends a derivation request to the Trezor device and returns the
+// Ethereum address located on that path.
+func (w *trezorDriver) trezorDerive(derivationPath []uint32) (common.Address, error) {
+ address := new(trezor.EthereumAddress)
+ if _, err := w.trezorExchange(&trezor.EthereumGetAddress{AddressN: derivationPath}, address); err != nil {
+ return common.Address{}, err
+ }
+ if addr := address.GetAddressBin(); len(addr) > 0 { // Older firmwares use binary fomats
+ return common.BytesToAddress(addr), nil
+ }
+ if addr := address.GetAddressHex(); len(addr) > 0 { // Newer firmwares use hexadecimal fomats
+ return common.HexToAddress(addr), nil
+ }
+ return common.Address{}, errors.New("missing derived address")
+}
+
+// trezorSign sends the transaction to the Trezor wallet, and waits for the user
+// to confirm or deny the transaction.
+func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) {
+ // Create the transaction initiation message
+ data := tx.Data()
+ length := uint32(len(data))
+
+ request := &trezor.EthereumSignTx{
+ AddressN: derivationPath,
+ Nonce: new(big.Int).SetUint64(tx.Nonce()).Bytes(),
+ GasPrice: tx.GasPrice().Bytes(),
+ GasLimit: new(big.Int).SetUint64(tx.Gas()).Bytes(),
+ Value: tx.Value().Bytes(),
+ DataLength: &length,
+ }
+ if to := tx.To(); to != nil {
+ // Non contract deploy, set recipient explicitly
+ hex := to.Hex()
+ request.ToHex = &hex // Newer firmwares (old will ignore)
+ request.ToBin = (*to)[:] // Older firmwares (new will ignore)
+ }
+ if length > 1024 { // Send the data chunked if that was requested
+ request.DataInitialChunk, data = data[:1024], data[1024:]
+ } else {
+ request.DataInitialChunk, data = data, nil
+ }
+ if chainID != nil { // EIP-155 transaction, set chain ID explicitly (only 32 bit is supported!?)
+ id := uint32(chainID.Int64())
+ request.ChainId = &id
+ }
+ // Send the initiation message and stream content until a signature is returned
+ response := new(trezor.EthereumTxRequest)
+ if _, err := w.trezorExchange(request, response); err != nil {
+ return common.Address{}, nil, err
+ }
+ for response.DataLength != nil && int(*response.DataLength) <= len(data) {
+ chunk := data[:*response.DataLength]
+ data = data[*response.DataLength:]
+
+ if _, err := w.trezorExchange(&trezor.EthereumTxAck{DataChunk: chunk}, response); err != nil {
+ return common.Address{}, nil, err
+ }
+ }
+ // Extract the Ethereum signature and do a sanity validation
+ if len(response.GetSignatureR()) == 0 || len(response.GetSignatureS()) == 0 || response.GetSignatureV() == 0 {
+ return common.Address{}, nil, errors.New("reply lacks signature")
+ }
+ signature := append(append(response.GetSignatureR(), response.GetSignatureS()...), byte(response.GetSignatureV()))
+
+ // Create the correct signer and signature transform based on the chain ID
+ var signer types.Signer
+ if chainID == nil {
+ signer = new(types.HomesteadSigner)
+ } else {
+ signer = types.NewEIP155Signer(chainID)
+ signature[64] -= byte(chainID.Uint64()*2 + 35)
+ }
+ // Inject the final signature into the transaction and sanity check the sender
+ signed, err := tx.WithSignature(signer, signature)
+ if err != nil {
+ return common.Address{}, nil, err
+ }
+ sender, err := types.Sender(signer, signed)
+ if err != nil {
+ return common.Address{}, nil, err
+ }
+ return sender, signed, nil
+}
+
+// trezorExchange performs a data exchange with the Trezor wallet, sending it a
+// message and retrieving the response. If multiple responses are possible, the
+// method will also return the index of the destination object used.
+func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Message) (int, error) {
+ // Construct the original message payload to chunk up
+ data, err := proto.Marshal(req)
+ if err != nil {
+ return 0, err
+ }
+ payload := make([]byte, 8+len(data))
+ copy(payload, []byte{0x23, 0x23})
+ binary.BigEndian.PutUint16(payload[2:], trezor.Type(req))
+ binary.BigEndian.PutUint32(payload[4:], uint32(len(data)))
+ copy(payload[8:], data)
+
+ // Stream all the chunks to the device
+ chunk := make([]byte, 64)
+ chunk[0] = 0x3f // Report ID magic number
+
+ for len(payload) > 0 {
+ // Construct the new message to stream, padding with zeroes if needed
+ if len(payload) > 63 {
+ copy(chunk[1:], payload[:63])
+ payload = payload[63:]
+ } else {
+ copy(chunk[1:], payload)
+ copy(chunk[1+len(payload):], make([]byte, 63-len(payload)))
+ payload = nil
+ }
+ // Send over to the device
+ w.log.Trace("Data chunk sent to the Trezor", "chunk", hexutil.Bytes(chunk))
+ if _, err := w.device.Write(chunk); err != nil {
+ return 0, err
+ }
+ }
+ // Stream the reply back from the wallet in 64 byte chunks
+ var (
+ kind uint16
+ reply []byte
+ )
+ for {
+ // Read the next chunk from the Trezor wallet
+ if _, err := io.ReadFull(w.device, chunk); err != nil {
+ return 0, err
+ }
+ w.log.Trace("Data chunk received from the Trezor", "chunk", hexutil.Bytes(chunk))
+
+ // Make sure the transport header matches
+ if chunk[0] != 0x3f || (len(reply) == 0 && (chunk[1] != 0x23 || chunk[2] != 0x23)) {
+ return 0, errTrezorReplyInvalidHeader
+ }
+ // If it's the first chunk, retrieve the reply message type and total message length
+ var payload []byte
+
+ if len(reply) == 0 {
+ kind = binary.BigEndian.Uint16(chunk[3:5])
+ reply = make([]byte, 0, int(binary.BigEndian.Uint32(chunk[5:9])))
+ payload = chunk[9:]
+ } else {
+ payload = chunk[1:]
+ }
+ // Append to the reply and stop when filled up
+ if left := cap(reply) - len(reply); left > len(payload) {
+ reply = append(reply, payload...)
+ } else {
+ reply = append(reply, payload[:left]...)
+ break
+ }
+ }
+ // Try to parse the reply into the requested reply message
+ if kind == uint16(trezor.MessageType_MessageType_Failure) {
+ // Trezor returned a failure, extract and return the message
+ failure := new(trezor.Failure)
+ if err := proto.Unmarshal(reply, failure); err != nil {
+ return 0, err
+ }
+ return 0, errors.New("trezor: " + failure.GetMessage())
+ }
+ if kind == uint16(trezor.MessageType_MessageType_ButtonRequest) {
+ // Trezor is waiting for user confirmation, ack and wait for the next message
+ return w.trezorExchange(&trezor.ButtonAck{}, results...)
+ }
+ for i, res := range results {
+ if trezor.Type(res) == kind {
+ return i, proto.Unmarshal(reply, res)
+ }
+ }
+ expected := make([]string, len(results))
+ for i, res := range results {
+ expected[i] = trezor.Name(trezor.Type(res))
+ }
+ return 0, fmt.Errorf("trezor: expected reply types %s, got %s", expected, trezor.Name(kind))
+}
diff --git a/accounts/usbwallet/trezor/messages-common.pb.go b/accounts/usbwallet/trezor/messages-common.pb.go
new file mode 100644
index 0000000..304bec0
--- /dev/null
+++ b/accounts/usbwallet/trezor/messages-common.pb.go
@@ -0,0 +1,811 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: messages-common.proto
+
+package trezor
+
+import (
+ fmt "fmt"
+ math "math"
+
+ proto "github.com/golang/protobuf/proto"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type Failure_FailureType int32
+
+const (
+ Failure_Failure_UnexpectedMessage Failure_FailureType = 1
+ Failure_Failure_ButtonExpected Failure_FailureType = 2
+ Failure_Failure_DataError Failure_FailureType = 3
+ Failure_Failure_ActionCancelled Failure_FailureType = 4
+ Failure_Failure_PinExpected Failure_FailureType = 5
+ Failure_Failure_PinCancelled Failure_FailureType = 6
+ Failure_Failure_PinInvalid Failure_FailureType = 7
+ Failure_Failure_InvalidSignature Failure_FailureType = 8
+ Failure_Failure_ProcessError Failure_FailureType = 9
+ Failure_Failure_NotEnoughFunds Failure_FailureType = 10
+ Failure_Failure_NotInitialized Failure_FailureType = 11
+ Failure_Failure_PinMismatch Failure_FailureType = 12
+ Failure_Failure_FirmwareError Failure_FailureType = 99
+)
+
+var Failure_FailureType_name = map[int32]string{
+ 1: "Failure_UnexpectedMessage",
+ 2: "Failure_ButtonExpected",
+ 3: "Failure_DataError",
+ 4: "Failure_ActionCancelled",
+ 5: "Failure_PinExpected",
+ 6: "Failure_PinCancelled",
+ 7: "Failure_PinInvalid",
+ 8: "Failure_InvalidSignature",
+ 9: "Failure_ProcessError",
+ 10: "Failure_NotEnoughFunds",
+ 11: "Failure_NotInitialized",
+ 12: "Failure_PinMismatch",
+ 99: "Failure_FirmwareError",
+}
+
+var Failure_FailureType_value = map[string]int32{
+ "Failure_UnexpectedMessage": 1,
+ "Failure_ButtonExpected": 2,
+ "Failure_DataError": 3,
+ "Failure_ActionCancelled": 4,
+ "Failure_PinExpected": 5,
+ "Failure_PinCancelled": 6,
+ "Failure_PinInvalid": 7,
+ "Failure_InvalidSignature": 8,
+ "Failure_ProcessError": 9,
+ "Failure_NotEnoughFunds": 10,
+ "Failure_NotInitialized": 11,
+ "Failure_PinMismatch": 12,
+ "Failure_FirmwareError": 99,
+}
+
+func (x Failure_FailureType) Enum() *Failure_FailureType {
+ p := new(Failure_FailureType)
+ *p = x
+ return p
+}
+
+func (x Failure_FailureType) String() string {
+ return proto.EnumName(Failure_FailureType_name, int32(x))
+}
+
+func (x *Failure_FailureType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(Failure_FailureType_value, data, "Failure_FailureType")
+ if err != nil {
+ return err
+ }
+ *x = Failure_FailureType(value)
+ return nil
+}
+
+func (Failure_FailureType) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_aaf30d059fdbc38d, []int{1, 0}
+}
+
+//*
+// Type of button request
+type ButtonRequest_ButtonRequestType int32
+
+const (
+ ButtonRequest_ButtonRequest_Other ButtonRequest_ButtonRequestType = 1
+ ButtonRequest_ButtonRequest_FeeOverThreshold ButtonRequest_ButtonRequestType = 2
+ ButtonRequest_ButtonRequest_ConfirmOutput ButtonRequest_ButtonRequestType = 3
+ ButtonRequest_ButtonRequest_ResetDevice ButtonRequest_ButtonRequestType = 4
+ ButtonRequest_ButtonRequest_ConfirmWord ButtonRequest_ButtonRequestType = 5
+ ButtonRequest_ButtonRequest_WipeDevice ButtonRequest_ButtonRequestType = 6
+ ButtonRequest_ButtonRequest_ProtectCall ButtonRequest_ButtonRequestType = 7
+ ButtonRequest_ButtonRequest_SignTx ButtonRequest_ButtonRequestType = 8
+ ButtonRequest_ButtonRequest_FirmwareCheck ButtonRequest_ButtonRequestType = 9
+ ButtonRequest_ButtonRequest_Address ButtonRequest_ButtonRequestType = 10
+ ButtonRequest_ButtonRequest_PublicKey ButtonRequest_ButtonRequestType = 11
+ ButtonRequest_ButtonRequest_MnemonicWordCount ButtonRequest_ButtonRequestType = 12
+ ButtonRequest_ButtonRequest_MnemonicInput ButtonRequest_ButtonRequestType = 13
+ ButtonRequest_ButtonRequest_PassphraseType ButtonRequest_ButtonRequestType = 14
+ ButtonRequest_ButtonRequest_UnknownDerivationPath ButtonRequest_ButtonRequestType = 15
+)
+
+var ButtonRequest_ButtonRequestType_name = map[int32]string{
+ 1: "ButtonRequest_Other",
+ 2: "ButtonRequest_FeeOverThreshold",
+ 3: "ButtonRequest_ConfirmOutput",
+ 4: "ButtonRequest_ResetDevice",
+ 5: "ButtonRequest_ConfirmWord",
+ 6: "ButtonRequest_WipeDevice",
+ 7: "ButtonRequest_ProtectCall",
+ 8: "ButtonRequest_SignTx",
+ 9: "ButtonRequest_FirmwareCheck",
+ 10: "ButtonRequest_Address",
+ 11: "ButtonRequest_PublicKey",
+ 12: "ButtonRequest_MnemonicWordCount",
+ 13: "ButtonRequest_MnemonicInput",
+ 14: "ButtonRequest_PassphraseType",
+ 15: "ButtonRequest_UnknownDerivationPath",
+}
+
+var ButtonRequest_ButtonRequestType_value = map[string]int32{
+ "ButtonRequest_Other": 1,
+ "ButtonRequest_FeeOverThreshold": 2,
+ "ButtonRequest_ConfirmOutput": 3,
+ "ButtonRequest_ResetDevice": 4,
+ "ButtonRequest_ConfirmWord": 5,
+ "ButtonRequest_WipeDevice": 6,
+ "ButtonRequest_ProtectCall": 7,
+ "ButtonRequest_SignTx": 8,
+ "ButtonRequest_FirmwareCheck": 9,
+ "ButtonRequest_Address": 10,
+ "ButtonRequest_PublicKey": 11,
+ "ButtonRequest_MnemonicWordCount": 12,
+ "ButtonRequest_MnemonicInput": 13,
+ "ButtonRequest_PassphraseType": 14,
+ "ButtonRequest_UnknownDerivationPath": 15,
+}
+
+func (x ButtonRequest_ButtonRequestType) Enum() *ButtonRequest_ButtonRequestType {
+ p := new(ButtonRequest_ButtonRequestType)
+ *p = x
+ return p
+}
+
+func (x ButtonRequest_ButtonRequestType) String() string {
+ return proto.EnumName(ButtonRequest_ButtonRequestType_name, int32(x))
+}
+
+func (x *ButtonRequest_ButtonRequestType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(ButtonRequest_ButtonRequestType_value, data, "ButtonRequest_ButtonRequestType")
+ if err != nil {
+ return err
+ }
+ *x = ButtonRequest_ButtonRequestType(value)
+ return nil
+}
+
+func (ButtonRequest_ButtonRequestType) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_aaf30d059fdbc38d, []int{2, 0}
+}
+
+//*
+// Type of PIN request
+type PinMatrixRequest_PinMatrixRequestType int32
+
+const (
+ PinMatrixRequest_PinMatrixRequestType_Current PinMatrixRequest_PinMatrixRequestType = 1
+ PinMatrixRequest_PinMatrixRequestType_NewFirst PinMatrixRequest_PinMatrixRequestType = 2
+ PinMatrixRequest_PinMatrixRequestType_NewSecond PinMatrixRequest_PinMatrixRequestType = 3
+)
+
+var PinMatrixRequest_PinMatrixRequestType_name = map[int32]string{
+ 1: "PinMatrixRequestType_Current",
+ 2: "PinMatrixRequestType_NewFirst",
+ 3: "PinMatrixRequestType_NewSecond",
+}
+
+var PinMatrixRequest_PinMatrixRequestType_value = map[string]int32{
+ "PinMatrixRequestType_Current": 1,
+ "PinMatrixRequestType_NewFirst": 2,
+ "PinMatrixRequestType_NewSecond": 3,
+}
+
+func (x PinMatrixRequest_PinMatrixRequestType) Enum() *PinMatrixRequest_PinMatrixRequestType {
+ p := new(PinMatrixRequest_PinMatrixRequestType)
+ *p = x
+ return p
+}
+
+func (x PinMatrixRequest_PinMatrixRequestType) String() string {
+ return proto.EnumName(PinMatrixRequest_PinMatrixRequestType_name, int32(x))
+}
+
+func (x *PinMatrixRequest_PinMatrixRequestType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(PinMatrixRequest_PinMatrixRequestType_value, data, "PinMatrixRequest_PinMatrixRequestType")
+ if err != nil {
+ return err
+ }
+ *x = PinMatrixRequest_PinMatrixRequestType(value)
+ return nil
+}
+
+func (PinMatrixRequest_PinMatrixRequestType) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_aaf30d059fdbc38d, []int{4, 0}
+}
+
+//*
+// Response: Success of the previous request
+// @end
+type Success struct {
+ Message *string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Success) Reset() { *m = Success{} }
+func (m *Success) String() string { return proto.CompactTextString(m) }
+func (*Success) ProtoMessage() {}
+func (*Success) Descriptor() ([]byte, []int) {
+ return fileDescriptor_aaf30d059fdbc38d, []int{0}
+}
+
+func (m *Success) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Success.Unmarshal(m, b)
+}
+func (m *Success) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Success.Marshal(b, m, deterministic)
+}
+func (m *Success) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Success.Merge(m, src)
+}
+func (m *Success) XXX_Size() int {
+ return xxx_messageInfo_Success.Size(m)
+}
+func (m *Success) XXX_DiscardUnknown() {
+ xxx_messageInfo_Success.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Success proto.InternalMessageInfo
+
+func (m *Success) GetMessage() string {
+ if m != nil && m.Message != nil {
+ return *m.Message
+ }
+ return ""
+}
+
+//*
+// Response: Failure of the previous request
+// @end
+type Failure struct {
+ Code *Failure_FailureType `protobuf:"varint,1,opt,name=code,enum=hw.trezor.messages.common.Failure_FailureType" json:"code,omitempty"`
+ Message *string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Failure) Reset() { *m = Failure{} }
+func (m *Failure) String() string { return proto.CompactTextString(m) }
+func (*Failure) ProtoMessage() {}
+func (*Failure) Descriptor() ([]byte, []int) {
+ return fileDescriptor_aaf30d059fdbc38d, []int{1}
+}
+
+func (m *Failure) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Failure.Unmarshal(m, b)
+}
+func (m *Failure) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Failure.Marshal(b, m, deterministic)
+}
+func (m *Failure) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Failure.Merge(m, src)
+}
+func (m *Failure) XXX_Size() int {
+ return xxx_messageInfo_Failure.Size(m)
+}
+func (m *Failure) XXX_DiscardUnknown() {
+ xxx_messageInfo_Failure.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Failure proto.InternalMessageInfo
+
+func (m *Failure) GetCode() Failure_FailureType {
+ if m != nil && m.Code != nil {
+ return *m.Code
+ }
+ return Failure_Failure_UnexpectedMessage
+}
+
+func (m *Failure) GetMessage() string {
+ if m != nil && m.Message != nil {
+ return *m.Message
+ }
+ return ""
+}
+
+//*
+// Response: Device is waiting for HW button press.
+// @auxstart
+// @next ButtonAck
+type ButtonRequest struct {
+ Code *ButtonRequest_ButtonRequestType `protobuf:"varint,1,opt,name=code,enum=hw.trezor.messages.common.ButtonRequest_ButtonRequestType" json:"code,omitempty"`
+ Data *string `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ButtonRequest) Reset() { *m = ButtonRequest{} }
+func (m *ButtonRequest) String() string { return proto.CompactTextString(m) }
+func (*ButtonRequest) ProtoMessage() {}
+func (*ButtonRequest) Descriptor() ([]byte, []int) {
+ return fileDescriptor_aaf30d059fdbc38d, []int{2}
+}
+
+func (m *ButtonRequest) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ButtonRequest.Unmarshal(m, b)
+}
+func (m *ButtonRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ButtonRequest.Marshal(b, m, deterministic)
+}
+func (m *ButtonRequest) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ButtonRequest.Merge(m, src)
+}
+func (m *ButtonRequest) XXX_Size() int {
+ return xxx_messageInfo_ButtonRequest.Size(m)
+}
+func (m *ButtonRequest) XXX_DiscardUnknown() {
+ xxx_messageInfo_ButtonRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ButtonRequest proto.InternalMessageInfo
+
+func (m *ButtonRequest) GetCode() ButtonRequest_ButtonRequestType {
+ if m != nil && m.Code != nil {
+ return *m.Code
+ }
+ return ButtonRequest_ButtonRequest_Other
+}
+
+func (m *ButtonRequest) GetData() string {
+ if m != nil && m.Data != nil {
+ return *m.Data
+ }
+ return ""
+}
+
+//*
+// Request: Computer agrees to wait for HW button press
+// @auxend
+type ButtonAck struct {
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ButtonAck) Reset() { *m = ButtonAck{} }
+func (m *ButtonAck) String() string { return proto.CompactTextString(m) }
+func (*ButtonAck) ProtoMessage() {}
+func (*ButtonAck) Descriptor() ([]byte, []int) {
+ return fileDescriptor_aaf30d059fdbc38d, []int{3}
+}
+
+func (m *ButtonAck) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ButtonAck.Unmarshal(m, b)
+}
+func (m *ButtonAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ButtonAck.Marshal(b, m, deterministic)
+}
+func (m *ButtonAck) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ButtonAck.Merge(m, src)
+}
+func (m *ButtonAck) XXX_Size() int {
+ return xxx_messageInfo_ButtonAck.Size(m)
+}
+func (m *ButtonAck) XXX_DiscardUnknown() {
+ xxx_messageInfo_ButtonAck.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ButtonAck proto.InternalMessageInfo
+
+//*
+// Response: Device is asking computer to show PIN matrix and awaits PIN encoded using this matrix scheme
+// @auxstart
+// @next PinMatrixAck
+type PinMatrixRequest struct {
+ Type *PinMatrixRequest_PinMatrixRequestType `protobuf:"varint,1,opt,name=type,enum=hw.trezor.messages.common.PinMatrixRequest_PinMatrixRequestType" json:"type,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *PinMatrixRequest) Reset() { *m = PinMatrixRequest{} }
+func (m *PinMatrixRequest) String() string { return proto.CompactTextString(m) }
+func (*PinMatrixRequest) ProtoMessage() {}
+func (*PinMatrixRequest) Descriptor() ([]byte, []int) {
+ return fileDescriptor_aaf30d059fdbc38d, []int{4}
+}
+
+func (m *PinMatrixRequest) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_PinMatrixRequest.Unmarshal(m, b)
+}
+func (m *PinMatrixRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_PinMatrixRequest.Marshal(b, m, deterministic)
+}
+func (m *PinMatrixRequest) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PinMatrixRequest.Merge(m, src)
+}
+func (m *PinMatrixRequest) XXX_Size() int {
+ return xxx_messageInfo_PinMatrixRequest.Size(m)
+}
+func (m *PinMatrixRequest) XXX_DiscardUnknown() {
+ xxx_messageInfo_PinMatrixRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PinMatrixRequest proto.InternalMessageInfo
+
+func (m *PinMatrixRequest) GetType() PinMatrixRequest_PinMatrixRequestType {
+ if m != nil && m.Type != nil {
+ return *m.Type
+ }
+ return PinMatrixRequest_PinMatrixRequestType_Current
+}
+
+//*
+// Request: Computer responds with encoded PIN
+// @auxend
+type PinMatrixAck struct {
+ Pin *string `protobuf:"bytes,1,req,name=pin" json:"pin,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *PinMatrixAck) Reset() { *m = PinMatrixAck{} }
+func (m *PinMatrixAck) String() string { return proto.CompactTextString(m) }
+func (*PinMatrixAck) ProtoMessage() {}
+func (*PinMatrixAck) Descriptor() ([]byte, []int) {
+ return fileDescriptor_aaf30d059fdbc38d, []int{5}
+}
+
+func (m *PinMatrixAck) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_PinMatrixAck.Unmarshal(m, b)
+}
+func (m *PinMatrixAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_PinMatrixAck.Marshal(b, m, deterministic)
+}
+func (m *PinMatrixAck) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PinMatrixAck.Merge(m, src)
+}
+func (m *PinMatrixAck) XXX_Size() int {
+ return xxx_messageInfo_PinMatrixAck.Size(m)
+}
+func (m *PinMatrixAck) XXX_DiscardUnknown() {
+ xxx_messageInfo_PinMatrixAck.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PinMatrixAck proto.InternalMessageInfo
+
+func (m *PinMatrixAck) GetPin() string {
+ if m != nil && m.Pin != nil {
+ return *m.Pin
+ }
+ return ""
+}
+
+//*
+// Response: Device awaits encryption passphrase
+// @auxstart
+// @next PassphraseAck
+type PassphraseRequest struct {
+ OnDevice *bool `protobuf:"varint,1,opt,name=on_device,json=onDevice" json:"on_device,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *PassphraseRequest) Reset() { *m = PassphraseRequest{} }
+func (m *PassphraseRequest) String() string { return proto.CompactTextString(m) }
+func (*PassphraseRequest) ProtoMessage() {}
+func (*PassphraseRequest) Descriptor() ([]byte, []int) {
+ return fileDescriptor_aaf30d059fdbc38d, []int{6}
+}
+
+func (m *PassphraseRequest) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_PassphraseRequest.Unmarshal(m, b)
+}
+func (m *PassphraseRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_PassphraseRequest.Marshal(b, m, deterministic)
+}
+func (m *PassphraseRequest) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PassphraseRequest.Merge(m, src)
+}
+func (m *PassphraseRequest) XXX_Size() int {
+ return xxx_messageInfo_PassphraseRequest.Size(m)
+}
+func (m *PassphraseRequest) XXX_DiscardUnknown() {
+ xxx_messageInfo_PassphraseRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PassphraseRequest proto.InternalMessageInfo
+
+func (m *PassphraseRequest) GetOnDevice() bool {
+ if m != nil && m.OnDevice != nil {
+ return *m.OnDevice
+ }
+ return false
+}
+
+//*
+// Request: Send passphrase back
+// @next PassphraseStateRequest
+type PassphraseAck struct {
+ Passphrase *string `protobuf:"bytes,1,opt,name=passphrase" json:"passphrase,omitempty"`
+ State []byte `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *PassphraseAck) Reset() { *m = PassphraseAck{} }
+func (m *PassphraseAck) String() string { return proto.CompactTextString(m) }
+func (*PassphraseAck) ProtoMessage() {}
+func (*PassphraseAck) Descriptor() ([]byte, []int) {
+ return fileDescriptor_aaf30d059fdbc38d, []int{7}
+}
+
+func (m *PassphraseAck) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_PassphraseAck.Unmarshal(m, b)
+}
+func (m *PassphraseAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_PassphraseAck.Marshal(b, m, deterministic)
+}
+func (m *PassphraseAck) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PassphraseAck.Merge(m, src)
+}
+func (m *PassphraseAck) XXX_Size() int {
+ return xxx_messageInfo_PassphraseAck.Size(m)
+}
+func (m *PassphraseAck) XXX_DiscardUnknown() {
+ xxx_messageInfo_PassphraseAck.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PassphraseAck proto.InternalMessageInfo
+
+func (m *PassphraseAck) GetPassphrase() string {
+ if m != nil && m.Passphrase != nil {
+ return *m.Passphrase
+ }
+ return ""
+}
+
+func (m *PassphraseAck) GetState() []byte {
+ if m != nil {
+ return m.State
+ }
+ return nil
+}
+
+//*
+// Response: Device awaits passphrase state
+// @next PassphraseStateAck
+type PassphraseStateRequest struct {
+ State []byte `protobuf:"bytes,1,opt,name=state" json:"state,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *PassphraseStateRequest) Reset() { *m = PassphraseStateRequest{} }
+func (m *PassphraseStateRequest) String() string { return proto.CompactTextString(m) }
+func (*PassphraseStateRequest) ProtoMessage() {}
+func (*PassphraseStateRequest) Descriptor() ([]byte, []int) {
+ return fileDescriptor_aaf30d059fdbc38d, []int{8}
+}
+
+func (m *PassphraseStateRequest) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_PassphraseStateRequest.Unmarshal(m, b)
+}
+func (m *PassphraseStateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_PassphraseStateRequest.Marshal(b, m, deterministic)
+}
+func (m *PassphraseStateRequest) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PassphraseStateRequest.Merge(m, src)
+}
+func (m *PassphraseStateRequest) XXX_Size() int {
+ return xxx_messageInfo_PassphraseStateRequest.Size(m)
+}
+func (m *PassphraseStateRequest) XXX_DiscardUnknown() {
+ xxx_messageInfo_PassphraseStateRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PassphraseStateRequest proto.InternalMessageInfo
+
+func (m *PassphraseStateRequest) GetState() []byte {
+ if m != nil {
+ return m.State
+ }
+ return nil
+}
+
+//*
+// Request: Send passphrase state back
+// @auxend
+type PassphraseStateAck struct {
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *PassphraseStateAck) Reset() { *m = PassphraseStateAck{} }
+func (m *PassphraseStateAck) String() string { return proto.CompactTextString(m) }
+func (*PassphraseStateAck) ProtoMessage() {}
+func (*PassphraseStateAck) Descriptor() ([]byte, []int) {
+ return fileDescriptor_aaf30d059fdbc38d, []int{9}
+}
+
+func (m *PassphraseStateAck) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_PassphraseStateAck.Unmarshal(m, b)
+}
+func (m *PassphraseStateAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_PassphraseStateAck.Marshal(b, m, deterministic)
+}
+func (m *PassphraseStateAck) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PassphraseStateAck.Merge(m, src)
+}
+func (m *PassphraseStateAck) XXX_Size() int {
+ return xxx_messageInfo_PassphraseStateAck.Size(m)
+}
+func (m *PassphraseStateAck) XXX_DiscardUnknown() {
+ xxx_messageInfo_PassphraseStateAck.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PassphraseStateAck proto.InternalMessageInfo
+
+//*
+// Structure representing BIP32 (hierarchical deterministic) node
+// Used for imports of private key into the device and exporting public key out of device
+// @embed
+type HDNodeType struct {
+ Depth *uint32 `protobuf:"varint,1,req,name=depth" json:"depth,omitempty"`
+ Fingerprint *uint32 `protobuf:"varint,2,req,name=fingerprint" json:"fingerprint,omitempty"`
+ ChildNum *uint32 `protobuf:"varint,3,req,name=child_num,json=childNum" json:"child_num,omitempty"`
+ ChainCode []byte `protobuf:"bytes,4,req,name=chain_code,json=chainCode" json:"chain_code,omitempty"`
+ PrivateKey []byte `protobuf:"bytes,5,opt,name=private_key,json=privateKey" json:"private_key,omitempty"`
+ PublicKey []byte `protobuf:"bytes,6,opt,name=public_key,json=publicKey" json:"public_key,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *HDNodeType) Reset() { *m = HDNodeType{} }
+func (m *HDNodeType) String() string { return proto.CompactTextString(m) }
+func (*HDNodeType) ProtoMessage() {}
+func (*HDNodeType) Descriptor() ([]byte, []int) {
+ return fileDescriptor_aaf30d059fdbc38d, []int{10}
+}
+
+func (m *HDNodeType) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_HDNodeType.Unmarshal(m, b)
+}
+func (m *HDNodeType) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_HDNodeType.Marshal(b, m, deterministic)
+}
+func (m *HDNodeType) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_HDNodeType.Merge(m, src)
+}
+func (m *HDNodeType) XXX_Size() int {
+ return xxx_messageInfo_HDNodeType.Size(m)
+}
+func (m *HDNodeType) XXX_DiscardUnknown() {
+ xxx_messageInfo_HDNodeType.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_HDNodeType proto.InternalMessageInfo
+
+func (m *HDNodeType) GetDepth() uint32 {
+ if m != nil && m.Depth != nil {
+ return *m.Depth
+ }
+ return 0
+}
+
+func (m *HDNodeType) GetFingerprint() uint32 {
+ if m != nil && m.Fingerprint != nil {
+ return *m.Fingerprint
+ }
+ return 0
+}
+
+func (m *HDNodeType) GetChildNum() uint32 {
+ if m != nil && m.ChildNum != nil {
+ return *m.ChildNum
+ }
+ return 0
+}
+
+func (m *HDNodeType) GetChainCode() []byte {
+ if m != nil {
+ return m.ChainCode
+ }
+ return nil
+}
+
+func (m *HDNodeType) GetPrivateKey() []byte {
+ if m != nil {
+ return m.PrivateKey
+ }
+ return nil
+}
+
+func (m *HDNodeType) GetPublicKey() []byte {
+ if m != nil {
+ return m.PublicKey
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterEnum("hw.trezor.messages.common.Failure_FailureType", Failure_FailureType_name, Failure_FailureType_value)
+ proto.RegisterEnum("hw.trezor.messages.common.ButtonRequest_ButtonRequestType", ButtonRequest_ButtonRequestType_name, ButtonRequest_ButtonRequestType_value)
+ proto.RegisterEnum("hw.trezor.messages.common.PinMatrixRequest_PinMatrixRequestType", PinMatrixRequest_PinMatrixRequestType_name, PinMatrixRequest_PinMatrixRequestType_value)
+ proto.RegisterType((*Success)(nil), "hw.trezor.messages.common.Success")
+ proto.RegisterType((*Failure)(nil), "hw.trezor.messages.common.Failure")
+ proto.RegisterType((*ButtonRequest)(nil), "hw.trezor.messages.common.ButtonRequest")
+ proto.RegisterType((*ButtonAck)(nil), "hw.trezor.messages.common.ButtonAck")
+ proto.RegisterType((*PinMatrixRequest)(nil), "hw.trezor.messages.common.PinMatrixRequest")
+ proto.RegisterType((*PinMatrixAck)(nil), "hw.trezor.messages.common.PinMatrixAck")
+ proto.RegisterType((*PassphraseRequest)(nil), "hw.trezor.messages.common.PassphraseRequest")
+ proto.RegisterType((*PassphraseAck)(nil), "hw.trezor.messages.common.PassphraseAck")
+ proto.RegisterType((*PassphraseStateRequest)(nil), "hw.trezor.messages.common.PassphraseStateRequest")
+ proto.RegisterType((*PassphraseStateAck)(nil), "hw.trezor.messages.common.PassphraseStateAck")
+ proto.RegisterType((*HDNodeType)(nil), "hw.trezor.messages.common.HDNodeType")
+}
+
+func init() { proto.RegisterFile("messages-common.proto", fileDescriptor_aaf30d059fdbc38d) }
+
+var fileDescriptor_aaf30d059fdbc38d = []byte{
+ // 846 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x54, 0xcd, 0x52, 0x23, 0x37,
+ 0x10, 0x2e, 0xff, 0x80, 0xed, 0xb6, 0xd9, 0x08, 0xc5, 0x80, 0x09, 0xb0, 0x38, 0xc3, 0x21, 0x5c,
+ 0xe2, 0x4a, 0xe5, 0x98, 0x53, 0x58, 0x83, 0x2b, 0xd4, 0x16, 0x86, 0x1a, 0xd8, 0xda, 0xa3, 0x4b,
+ 0xd1, 0xf4, 0x32, 0x2a, 0xcf, 0x48, 0x13, 0x8d, 0x06, 0xf0, 0x5e, 0xf2, 0x6a, 0x79, 0x89, 0xbc,
+ 0x42, 0xaa, 0x52, 0xb9, 0xe4, 0x11, 0xb6, 0x34, 0x3f, 0x78, 0xc6, 0x66, 0x39, 0xcd, 0xe8, 0xfb,
+ 0xbe, 0xee, 0x96, 0xba, 0x3f, 0x09, 0x76, 0x42, 0x8c, 0x63, 0x76, 0x8f, 0xf1, 0x8f, 0x5c, 0x85,
+ 0xa1, 0x92, 0xa3, 0x48, 0x2b, 0xa3, 0xe8, 0xbe, 0xff, 0x38, 0x32, 0x1a, 0x3f, 0x2b, 0x3d, 0x2a,
+ 0x04, 0xa3, 0x4c, 0xe0, 0x9c, 0x40, 0xeb, 0x36, 0xe1, 0x1c, 0xe3, 0x98, 0x0e, 0xa0, 0x95, 0xb3,
+ 0x83, 0xda, 0xb0, 0x76, 0xda, 0x71, 0x8b, 0xa5, 0xf3, 0x77, 0x03, 0x5a, 0x13, 0x26, 0x82, 0x44,
+ 0x23, 0x7d, 0x07, 0x4d, 0xae, 0xbc, 0x4c, 0xf2, 0xe6, 0xe7, 0xd1, 0xe8, 0xab, 0xa9, 0x47, 0x79,
+ 0x44, 0xf1, 0xbd, 0x5b, 0x44, 0xe8, 0xa6, 0xb1, 0xe5, 0x4a, 0xf5, 0x6a, 0xa5, 0xff, 0xea, 0xd0,
+ 0x2d, 0xe9, 0xe9, 0x11, 0xec, 0xe7, 0xcb, 0xd9, 0x07, 0x89, 0x4f, 0x11, 0x72, 0x83, 0xde, 0x55,
+ 0x26, 0x26, 0x35, 0xfa, 0x1d, 0xec, 0x16, 0xf4, 0xbb, 0xc4, 0x18, 0x25, 0x2f, 0x72, 0x09, 0xa9,
+ 0xd3, 0x1d, 0xd8, 0x2e, 0xb8, 0x73, 0x66, 0xd8, 0x85, 0xd6, 0x4a, 0x93, 0x06, 0x3d, 0x80, 0xbd,
+ 0x02, 0x3e, 0xe3, 0x46, 0x28, 0x39, 0x66, 0x92, 0x63, 0x10, 0xa0, 0x47, 0x9a, 0x74, 0x0f, 0xbe,
+ 0x2d, 0xc8, 0x1b, 0xb1, 0x4c, 0xb6, 0x41, 0x07, 0xd0, 0x2f, 0x11, 0xcb, 0x90, 0x4d, 0xba, 0x0b,
+ 0xb4, 0xc4, 0x5c, 0xca, 0x07, 0x16, 0x08, 0x8f, 0xb4, 0xe8, 0x21, 0x0c, 0x0a, 0x3c, 0x07, 0x6f,
+ 0xc5, 0xbd, 0x64, 0x26, 0xd1, 0x48, 0xda, 0x95, 0x7c, 0x5a, 0xd9, 0xf6, 0x67, 0xfb, 0xeb, 0x94,
+ 0x8f, 0x34, 0x55, 0xe6, 0x42, 0xaa, 0xe4, 0xde, 0x9f, 0x24, 0xd2, 0x8b, 0x09, 0xac, 0x70, 0x97,
+ 0x52, 0x18, 0xc1, 0x02, 0xf1, 0x19, 0x3d, 0xd2, 0x5d, 0xd9, 0xfa, 0x95, 0x88, 0x43, 0x66, 0xb8,
+ 0x4f, 0x7a, 0x74, 0x1f, 0x76, 0x0a, 0x62, 0x22, 0x74, 0xf8, 0xc8, 0x34, 0x66, 0xb5, 0xb8, 0xf3,
+ 0x4f, 0x13, 0xb6, 0xb2, 0xbe, 0xb9, 0xf8, 0x47, 0x82, 0xb1, 0xa1, 0xd3, 0xca, 0x74, 0x7f, 0x79,
+ 0x65, 0xba, 0x95, 0xb8, 0xea, 0xaa, 0x34, 0x69, 0x0a, 0x4d, 0x8f, 0x19, 0x96, 0x8f, 0x39, 0xfd,
+ 0x77, 0xfe, 0x6f, 0xc0, 0xf6, 0x9a, 0xde, 0xee, 0xbf, 0x02, 0xce, 0xae, 0x8d, 0x8f, 0x9a, 0xd4,
+ 0xa8, 0x03, 0x6f, 0xab, 0xc4, 0x04, 0xf1, 0xfa, 0x01, 0xf5, 0x9d, 0xaf, 0x31, 0xf6, 0x55, 0x60,
+ 0x67, 0x7d, 0x0c, 0x07, 0x55, 0xcd, 0x58, 0xc9, 0x4f, 0x42, 0x87, 0xd7, 0x89, 0x89, 0x12, 0x43,
+ 0x1a, 0xd6, 0x47, 0x55, 0x81, 0x8b, 0x31, 0x9a, 0x73, 0x7c, 0x10, 0x1c, 0x49, 0x73, 0x9d, 0xce,
+ 0xe3, 0x3f, 0x2a, 0x6d, 0xa7, 0x7f, 0x08, 0x83, 0x2a, 0xfd, 0x51, 0x44, 0x98, 0x07, 0x6f, 0xae,
+ 0x07, 0xdf, 0x68, 0x65, 0x90, 0x9b, 0x31, 0x0b, 0x02, 0xd2, 0xb2, 0xa3, 0xae, 0xd2, 0xd6, 0x07,
+ 0x77, 0x4f, 0xa4, 0xbd, 0xbe, 0xeb, 0x62, 0x3e, 0x63, 0x1f, 0xf9, 0x9c, 0x74, 0xec, 0xe8, 0xaa,
+ 0x82, 0x33, 0xcf, 0xd3, 0x18, 0x5b, 0x2b, 0x1c, 0xc0, 0xde, 0x4a, 0xd1, 0xe4, 0xf7, 0x40, 0xf0,
+ 0xf7, 0xb8, 0x20, 0x5d, 0x7a, 0x02, 0xc7, 0x55, 0xf2, 0x4a, 0x62, 0xa8, 0xa4, 0xe0, 0xf6, 0x3c,
+ 0x63, 0x95, 0x48, 0x43, 0x7a, 0xeb, 0xd5, 0x0b, 0xd1, 0xa5, 0xb4, 0x3d, 0xdb, 0xa2, 0x43, 0x38,
+ 0x5c, 0x29, 0xc1, 0xe2, 0x38, 0xf2, 0x35, 0x8b, 0xd3, 0xbb, 0x49, 0xde, 0xd0, 0x1f, 0xe0, 0xa4,
+ 0xaa, 0xf8, 0x20, 0xe7, 0x52, 0x3d, 0xca, 0x73, 0xd4, 0xe2, 0x81, 0xd9, 0xcb, 0x75, 0xc3, 0x8c,
+ 0x4f, 0xbe, 0x71, 0xba, 0xd0, 0xc9, 0x84, 0x67, 0x7c, 0xee, 0xfc, 0x5b, 0x03, 0x62, 0x2d, 0xca,
+ 0x8c, 0x16, 0x4f, 0x85, 0xf1, 0xee, 0xa0, 0x69, 0x16, 0x51, 0x61, 0xbc, 0x5f, 0x5f, 0x31, 0xde,
+ 0x6a, 0xe8, 0x1a, 0x90, 0xd9, 0xcf, 0x66, 0x73, 0xfe, 0x84, 0xfe, 0x4b, 0xac, 0x3d, 0xda, 0x4b,
+ 0xf8, 0x6c, 0x9c, 0x68, 0x8d, 0xd2, 0x90, 0x1a, 0xfd, 0x1e, 0x8e, 0x5e, 0x54, 0x4c, 0xf1, 0x71,
+ 0x22, 0x74, 0x6c, 0x48, 0xdd, 0x1a, 0xf3, 0x6b, 0x92, 0x5b, 0xe4, 0x4a, 0x7a, 0xa4, 0xe1, 0x0c,
+ 0xa1, 0xf7, 0xac, 0x39, 0xe3, 0x73, 0x4a, 0xa0, 0x11, 0x09, 0x39, 0xa8, 0x0d, 0xeb, 0xa7, 0x1d,
+ 0xd7, 0xfe, 0x3a, 0x3f, 0xc1, 0xf6, 0xb2, 0xaf, 0x45, 0x37, 0x0e, 0xa0, 0xa3, 0xe4, 0xcc, 0x4b,
+ 0x1d, 0x96, 0xb6, 0xa4, 0xed, 0xb6, 0x95, 0xcc, 0x1c, 0xe7, 0x5c, 0xc0, 0xd6, 0x32, 0xc2, 0x26,
+ 0x7d, 0x0b, 0x10, 0x3d, 0x03, 0xf9, 0xdb, 0x5d, 0x42, 0x68, 0x1f, 0x36, 0x62, 0xc3, 0x4c, 0xf6,
+ 0xd8, 0xf6, 0xdc, 0x6c, 0xe1, 0x8c, 0x60, 0x77, 0x99, 0xe6, 0xd6, 0x42, 0x45, 0xf5, 0x67, 0x7d,
+ 0xad, 0xac, 0xef, 0x03, 0x5d, 0xd1, 0xdb, 0x61, 0xfe, 0x55, 0x03, 0xf8, 0xed, 0x7c, 0xaa, 0xbc,
+ 0xec, 0xbd, 0xee, 0xc3, 0x86, 0x87, 0x91, 0xf1, 0xd3, 0x13, 0x6e, 0xb9, 0xd9, 0x82, 0x0e, 0xa1,
+ 0xfb, 0x49, 0xc8, 0x7b, 0xd4, 0x91, 0x16, 0xd2, 0x0c, 0xea, 0x29, 0x57, 0x86, 0xec, 0x81, 0xb9,
+ 0x2f, 0x02, 0x6f, 0x26, 0x93, 0x70, 0xd0, 0x48, 0xf9, 0x76, 0x0a, 0x4c, 0x93, 0x90, 0x1e, 0x01,
+ 0x70, 0x9f, 0x09, 0x39, 0x4b, 0x9f, 0xa6, 0xe6, 0xb0, 0x7e, 0xda, 0x73, 0x3b, 0x29, 0x32, 0xb6,
+ 0x6f, 0xcc, 0x31, 0x74, 0xa3, 0xd4, 0x6f, 0x38, 0x9b, 0xe3, 0x62, 0xb0, 0x91, 0x6e, 0x1a, 0x72,
+ 0xe8, 0x3d, 0x2e, 0x6c, 0x7c, 0x94, 0xde, 0x8e, 0x94, 0xdf, 0x4c, 0xf9, 0x4e, 0x54, 0xdc, 0x97,
+ 0x2f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb2, 0x7d, 0x20, 0xa6, 0x35, 0x07, 0x00, 0x00,
+}
diff --git a/accounts/usbwallet/trezor/messages-common.proto b/accounts/usbwallet/trezor/messages-common.proto
new file mode 100644
index 0000000..75a983b
--- /dev/null
+++ b/accounts/usbwallet/trezor/messages-common.proto
@@ -0,0 +1,147 @@
+// This file originates from the SatoshiLabs Trezor `common` repository at:
+// https://github.com/trezor/trezor-common/blob/master/protob/messages-common.proto
+// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9.
+
+syntax = "proto2";
+package hw.trezor.messages.common;
+
+/**
+ * Response: Success of the previous request
+ * @end
+ */
+message Success {
+ optional string message = 1; // human readable description of action or request-specific payload
+}
+
+/**
+ * Response: Failure of the previous request
+ * @end
+ */
+message Failure {
+ optional FailureType code = 1; // computer-readable definition of the error state
+ optional string message = 2; // human-readable message of the error state
+ enum FailureType {
+ Failure_UnexpectedMessage = 1;
+ Failure_ButtonExpected = 2;
+ Failure_DataError = 3;
+ Failure_ActionCancelled = 4;
+ Failure_PinExpected = 5;
+ Failure_PinCancelled = 6;
+ Failure_PinInvalid = 7;
+ Failure_InvalidSignature = 8;
+ Failure_ProcessError = 9;
+ Failure_NotEnoughFunds = 10;
+ Failure_NotInitialized = 11;
+ Failure_PinMismatch = 12;
+ Failure_FirmwareError = 99;
+ }
+}
+
+/**
+ * Response: Device is waiting for HW button press.
+ * @auxstart
+ * @next ButtonAck
+ */
+message ButtonRequest {
+ optional ButtonRequestType code = 1;
+ optional string data = 2;
+ /**
+ * Type of button request
+ */
+ enum ButtonRequestType {
+ ButtonRequest_Other = 1;
+ ButtonRequest_FeeOverThreshold = 2;
+ ButtonRequest_ConfirmOutput = 3;
+ ButtonRequest_ResetDevice = 4;
+ ButtonRequest_ConfirmWord = 5;
+ ButtonRequest_WipeDevice = 6;
+ ButtonRequest_ProtectCall = 7;
+ ButtonRequest_SignTx = 8;
+ ButtonRequest_FirmwareCheck = 9;
+ ButtonRequest_Address = 10;
+ ButtonRequest_PublicKey = 11;
+ ButtonRequest_MnemonicWordCount = 12;
+ ButtonRequest_MnemonicInput = 13;
+ ButtonRequest_PassphraseType = 14;
+ ButtonRequest_UnknownDerivationPath = 15;
+ }
+}
+
+/**
+ * Request: Computer agrees to wait for HW button press
+ * @auxend
+ */
+message ButtonAck {
+}
+
+/**
+ * Response: Device is asking computer to show PIN matrix and awaits PIN encoded using this matrix scheme
+ * @auxstart
+ * @next PinMatrixAck
+ */
+message PinMatrixRequest {
+ optional PinMatrixRequestType type = 1;
+ /**
+ * Type of PIN request
+ */
+ enum PinMatrixRequestType {
+ PinMatrixRequestType_Current = 1;
+ PinMatrixRequestType_NewFirst = 2;
+ PinMatrixRequestType_NewSecond = 3;
+ }
+}
+
+/**
+ * Request: Computer responds with encoded PIN
+ * @auxend
+ */
+message PinMatrixAck {
+ required string pin = 1; // matrix encoded PIN entered by user
+}
+
+/**
+ * Response: Device awaits encryption passphrase
+ * @auxstart
+ * @next PassphraseAck
+ */
+message PassphraseRequest {
+ optional bool on_device = 1; // passphrase is being entered on the device
+}
+
+/**
+ * Request: Send passphrase back
+ * @next PassphraseStateRequest
+ */
+message PassphraseAck {
+ optional string passphrase = 1;
+ optional bytes state = 2; // expected device state
+}
+
+/**
+ * Response: Device awaits passphrase state
+ * @next PassphraseStateAck
+ */
+message PassphraseStateRequest {
+ optional bytes state = 1; // actual device state
+}
+
+/**
+ * Request: Send passphrase state back
+ * @auxend
+ */
+message PassphraseStateAck {
+}
+
+/**
+ * Structure representing BIP32 (hierarchical deterministic) node
+ * Used for imports of private key into the device and exporting public key out of device
+ * @embed
+ */
+message HDNodeType {
+ required uint32 depth = 1;
+ required uint32 fingerprint = 2;
+ required uint32 child_num = 3;
+ required bytes chain_code = 4;
+ optional bytes private_key = 5;
+ optional bytes public_key = 6;
+}
diff --git a/accounts/usbwallet/trezor/messages-ethereum.pb.go b/accounts/usbwallet/trezor/messages-ethereum.pb.go
new file mode 100644
index 0000000..5d664f5
--- /dev/null
+++ b/accounts/usbwallet/trezor/messages-ethereum.pb.go
@@ -0,0 +1,698 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: messages-ethereum.proto
+
+package trezor
+
+import (
+ fmt "fmt"
+ math "math"
+
+ proto "github.com/golang/protobuf/proto"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+//*
+// Request: Ask device for public key corresponding to address_n path
+// @start
+// @next EthereumPublicKey
+// @next Failure
+type EthereumGetPublicKey struct {
+ AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"`
+ ShowDisplay *bool `protobuf:"varint,2,opt,name=show_display,json=showDisplay" json:"show_display,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *EthereumGetPublicKey) Reset() { *m = EthereumGetPublicKey{} }
+func (m *EthereumGetPublicKey) String() string { return proto.CompactTextString(m) }
+func (*EthereumGetPublicKey) ProtoMessage() {}
+func (*EthereumGetPublicKey) Descriptor() ([]byte, []int) {
+ return fileDescriptor_cb33f46ba915f15c, []int{0}
+}
+
+func (m *EthereumGetPublicKey) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_EthereumGetPublicKey.Unmarshal(m, b)
+}
+func (m *EthereumGetPublicKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_EthereumGetPublicKey.Marshal(b, m, deterministic)
+}
+func (m *EthereumGetPublicKey) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_EthereumGetPublicKey.Merge(m, src)
+}
+func (m *EthereumGetPublicKey) XXX_Size() int {
+ return xxx_messageInfo_EthereumGetPublicKey.Size(m)
+}
+func (m *EthereumGetPublicKey) XXX_DiscardUnknown() {
+ xxx_messageInfo_EthereumGetPublicKey.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EthereumGetPublicKey proto.InternalMessageInfo
+
+func (m *EthereumGetPublicKey) GetAddressN() []uint32 {
+ if m != nil {
+ return m.AddressN
+ }
+ return nil
+}
+
+func (m *EthereumGetPublicKey) GetShowDisplay() bool {
+ if m != nil && m.ShowDisplay != nil {
+ return *m.ShowDisplay
+ }
+ return false
+}
+
+//*
+// Response: Contains public key derived from device private seed
+// @end
+type EthereumPublicKey struct {
+ Node *HDNodeType `protobuf:"bytes,1,opt,name=node" json:"node,omitempty"`
+ Xpub *string `protobuf:"bytes,2,opt,name=xpub" json:"xpub,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *EthereumPublicKey) Reset() { *m = EthereumPublicKey{} }
+func (m *EthereumPublicKey) String() string { return proto.CompactTextString(m) }
+func (*EthereumPublicKey) ProtoMessage() {}
+func (*EthereumPublicKey) Descriptor() ([]byte, []int) {
+ return fileDescriptor_cb33f46ba915f15c, []int{1}
+}
+
+func (m *EthereumPublicKey) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_EthereumPublicKey.Unmarshal(m, b)
+}
+func (m *EthereumPublicKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_EthereumPublicKey.Marshal(b, m, deterministic)
+}
+func (m *EthereumPublicKey) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_EthereumPublicKey.Merge(m, src)
+}
+func (m *EthereumPublicKey) XXX_Size() int {
+ return xxx_messageInfo_EthereumPublicKey.Size(m)
+}
+func (m *EthereumPublicKey) XXX_DiscardUnknown() {
+ xxx_messageInfo_EthereumPublicKey.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EthereumPublicKey proto.InternalMessageInfo
+
+func (m *EthereumPublicKey) GetNode() *HDNodeType {
+ if m != nil {
+ return m.Node
+ }
+ return nil
+}
+
+func (m *EthereumPublicKey) GetXpub() string {
+ if m != nil && m.Xpub != nil {
+ return *m.Xpub
+ }
+ return ""
+}
+
+//*
+// Request: Ask device for Ethereum address corresponding to address_n path
+// @start
+// @next EthereumAddress
+// @next Failure
+type EthereumGetAddress struct {
+ AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"`
+ ShowDisplay *bool `protobuf:"varint,2,opt,name=show_display,json=showDisplay" json:"show_display,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *EthereumGetAddress) Reset() { *m = EthereumGetAddress{} }
+func (m *EthereumGetAddress) String() string { return proto.CompactTextString(m) }
+func (*EthereumGetAddress) ProtoMessage() {}
+func (*EthereumGetAddress) Descriptor() ([]byte, []int) {
+ return fileDescriptor_cb33f46ba915f15c, []int{2}
+}
+
+func (m *EthereumGetAddress) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_EthereumGetAddress.Unmarshal(m, b)
+}
+func (m *EthereumGetAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_EthereumGetAddress.Marshal(b, m, deterministic)
+}
+func (m *EthereumGetAddress) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_EthereumGetAddress.Merge(m, src)
+}
+func (m *EthereumGetAddress) XXX_Size() int {
+ return xxx_messageInfo_EthereumGetAddress.Size(m)
+}
+func (m *EthereumGetAddress) XXX_DiscardUnknown() {
+ xxx_messageInfo_EthereumGetAddress.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EthereumGetAddress proto.InternalMessageInfo
+
+func (m *EthereumGetAddress) GetAddressN() []uint32 {
+ if m != nil {
+ return m.AddressN
+ }
+ return nil
+}
+
+func (m *EthereumGetAddress) GetShowDisplay() bool {
+ if m != nil && m.ShowDisplay != nil {
+ return *m.ShowDisplay
+ }
+ return false
+}
+
+//*
+// Response: Contains an Ethereum address derived from device private seed
+// @end
+type EthereumAddress struct {
+ AddressBin []byte `protobuf:"bytes,1,opt,name=addressBin" json:"addressBin,omitempty"`
+ AddressHex *string `protobuf:"bytes,2,opt,name=addressHex" json:"addressHex,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *EthereumAddress) Reset() { *m = EthereumAddress{} }
+func (m *EthereumAddress) String() string { return proto.CompactTextString(m) }
+func (*EthereumAddress) ProtoMessage() {}
+func (*EthereumAddress) Descriptor() ([]byte, []int) {
+ return fileDescriptor_cb33f46ba915f15c, []int{3}
+}
+
+func (m *EthereumAddress) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_EthereumAddress.Unmarshal(m, b)
+}
+func (m *EthereumAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_EthereumAddress.Marshal(b, m, deterministic)
+}
+func (m *EthereumAddress) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_EthereumAddress.Merge(m, src)
+}
+func (m *EthereumAddress) XXX_Size() int {
+ return xxx_messageInfo_EthereumAddress.Size(m)
+}
+func (m *EthereumAddress) XXX_DiscardUnknown() {
+ xxx_messageInfo_EthereumAddress.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EthereumAddress proto.InternalMessageInfo
+
+func (m *EthereumAddress) GetAddressBin() []byte {
+ if m != nil {
+ return m.AddressBin
+ }
+ return nil
+}
+
+func (m *EthereumAddress) GetAddressHex() string {
+ if m != nil && m.AddressHex != nil {
+ return *m.AddressHex
+ }
+ return ""
+}
+
+//*
+// Request: Ask device to sign transaction
+// All fields are optional from the protocol's point of view. Each field defaults to value `0` if missing.
+// Note: the first at most 1024 bytes of data MUST be transmitted as part of this message.
+// @start
+// @next EthereumTxRequest
+// @next Failure
+type EthereumSignTx struct {
+ AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"`
+ Nonce []byte `protobuf:"bytes,2,opt,name=nonce" json:"nonce,omitempty"`
+ GasPrice []byte `protobuf:"bytes,3,opt,name=gas_price,json=gasPrice" json:"gas_price,omitempty"`
+ GasLimit []byte `protobuf:"bytes,4,opt,name=gas_limit,json=gasLimit" json:"gas_limit,omitempty"`
+ ToBin []byte `protobuf:"bytes,5,opt,name=toBin" json:"toBin,omitempty"`
+ ToHex *string `protobuf:"bytes,11,opt,name=toHex" json:"toHex,omitempty"`
+ Value []byte `protobuf:"bytes,6,opt,name=value" json:"value,omitempty"`
+ DataInitialChunk []byte `protobuf:"bytes,7,opt,name=data_initial_chunk,json=dataInitialChunk" json:"data_initial_chunk,omitempty"`
+ DataLength *uint32 `protobuf:"varint,8,opt,name=data_length,json=dataLength" json:"data_length,omitempty"`
+ ChainId *uint32 `protobuf:"varint,9,opt,name=chain_id,json=chainId" json:"chain_id,omitempty"`
+ TxType *uint32 `protobuf:"varint,10,opt,name=tx_type,json=txType" json:"tx_type,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *EthereumSignTx) Reset() { *m = EthereumSignTx{} }
+func (m *EthereumSignTx) String() string { return proto.CompactTextString(m) }
+func (*EthereumSignTx) ProtoMessage() {}
+func (*EthereumSignTx) Descriptor() ([]byte, []int) {
+ return fileDescriptor_cb33f46ba915f15c, []int{4}
+}
+
+func (m *EthereumSignTx) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_EthereumSignTx.Unmarshal(m, b)
+}
+func (m *EthereumSignTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_EthereumSignTx.Marshal(b, m, deterministic)
+}
+func (m *EthereumSignTx) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_EthereumSignTx.Merge(m, src)
+}
+func (m *EthereumSignTx) XXX_Size() int {
+ return xxx_messageInfo_EthereumSignTx.Size(m)
+}
+func (m *EthereumSignTx) XXX_DiscardUnknown() {
+ xxx_messageInfo_EthereumSignTx.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EthereumSignTx proto.InternalMessageInfo
+
+func (m *EthereumSignTx) GetAddressN() []uint32 {
+ if m != nil {
+ return m.AddressN
+ }
+ return nil
+}
+
+func (m *EthereumSignTx) GetNonce() []byte {
+ if m != nil {
+ return m.Nonce
+ }
+ return nil
+}
+
+func (m *EthereumSignTx) GetGasPrice() []byte {
+ if m != nil {
+ return m.GasPrice
+ }
+ return nil
+}
+
+func (m *EthereumSignTx) GetGasLimit() []byte {
+ if m != nil {
+ return m.GasLimit
+ }
+ return nil
+}
+
+func (m *EthereumSignTx) GetToBin() []byte {
+ if m != nil {
+ return m.ToBin
+ }
+ return nil
+}
+
+func (m *EthereumSignTx) GetToHex() string {
+ if m != nil && m.ToHex != nil {
+ return *m.ToHex
+ }
+ return ""
+}
+
+func (m *EthereumSignTx) GetValue() []byte {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func (m *EthereumSignTx) GetDataInitialChunk() []byte {
+ if m != nil {
+ return m.DataInitialChunk
+ }
+ return nil
+}
+
+func (m *EthereumSignTx) GetDataLength() uint32 {
+ if m != nil && m.DataLength != nil {
+ return *m.DataLength
+ }
+ return 0
+}
+
+func (m *EthereumSignTx) GetChainId() uint32 {
+ if m != nil && m.ChainId != nil {
+ return *m.ChainId
+ }
+ return 0
+}
+
+func (m *EthereumSignTx) GetTxType() uint32 {
+ if m != nil && m.TxType != nil {
+ return *m.TxType
+ }
+ return 0
+}
+
+//*
+// Response: Device asks for more data from transaction payload, or returns the signature.
+// If data_length is set, device awaits that many more bytes of payload.
+// Otherwise, the signature_* fields contain the computed transaction signature. All three fields will be present.
+// @end
+// @next EthereumTxAck
+type EthereumTxRequest struct {
+ DataLength *uint32 `protobuf:"varint,1,opt,name=data_length,json=dataLength" json:"data_length,omitempty"`
+ SignatureV *uint32 `protobuf:"varint,2,opt,name=signature_v,json=signatureV" json:"signature_v,omitempty"`
+ SignatureR []byte `protobuf:"bytes,3,opt,name=signature_r,json=signatureR" json:"signature_r,omitempty"`
+ SignatureS []byte `protobuf:"bytes,4,opt,name=signature_s,json=signatureS" json:"signature_s,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *EthereumTxRequest) Reset() { *m = EthereumTxRequest{} }
+func (m *EthereumTxRequest) String() string { return proto.CompactTextString(m) }
+func (*EthereumTxRequest) ProtoMessage() {}
+func (*EthereumTxRequest) Descriptor() ([]byte, []int) {
+ return fileDescriptor_cb33f46ba915f15c, []int{5}
+}
+
+func (m *EthereumTxRequest) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_EthereumTxRequest.Unmarshal(m, b)
+}
+func (m *EthereumTxRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_EthereumTxRequest.Marshal(b, m, deterministic)
+}
+func (m *EthereumTxRequest) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_EthereumTxRequest.Merge(m, src)
+}
+func (m *EthereumTxRequest) XXX_Size() int {
+ return xxx_messageInfo_EthereumTxRequest.Size(m)
+}
+func (m *EthereumTxRequest) XXX_DiscardUnknown() {
+ xxx_messageInfo_EthereumTxRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EthereumTxRequest proto.InternalMessageInfo
+
+func (m *EthereumTxRequest) GetDataLength() uint32 {
+ if m != nil && m.DataLength != nil {
+ return *m.DataLength
+ }
+ return 0
+}
+
+func (m *EthereumTxRequest) GetSignatureV() uint32 {
+ if m != nil && m.SignatureV != nil {
+ return *m.SignatureV
+ }
+ return 0
+}
+
+func (m *EthereumTxRequest) GetSignatureR() []byte {
+ if m != nil {
+ return m.SignatureR
+ }
+ return nil
+}
+
+func (m *EthereumTxRequest) GetSignatureS() []byte {
+ if m != nil {
+ return m.SignatureS
+ }
+ return nil
+}
+
+//*
+// Request: Transaction payload data.
+// @next EthereumTxRequest
+type EthereumTxAck struct {
+ DataChunk []byte `protobuf:"bytes,1,opt,name=data_chunk,json=dataChunk" json:"data_chunk,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *EthereumTxAck) Reset() { *m = EthereumTxAck{} }
+func (m *EthereumTxAck) String() string { return proto.CompactTextString(m) }
+func (*EthereumTxAck) ProtoMessage() {}
+func (*EthereumTxAck) Descriptor() ([]byte, []int) {
+ return fileDescriptor_cb33f46ba915f15c, []int{6}
+}
+
+func (m *EthereumTxAck) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_EthereumTxAck.Unmarshal(m, b)
+}
+func (m *EthereumTxAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_EthereumTxAck.Marshal(b, m, deterministic)
+}
+func (m *EthereumTxAck) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_EthereumTxAck.Merge(m, src)
+}
+func (m *EthereumTxAck) XXX_Size() int {
+ return xxx_messageInfo_EthereumTxAck.Size(m)
+}
+func (m *EthereumTxAck) XXX_DiscardUnknown() {
+ xxx_messageInfo_EthereumTxAck.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EthereumTxAck proto.InternalMessageInfo
+
+func (m *EthereumTxAck) GetDataChunk() []byte {
+ if m != nil {
+ return m.DataChunk
+ }
+ return nil
+}
+
+//*
+// Request: Ask device to sign message
+// @start
+// @next EthereumMessageSignature
+// @next Failure
+type EthereumSignMessage struct {
+ AddressN []uint32 `protobuf:"varint,1,rep,name=address_n,json=addressN" json:"address_n,omitempty"`
+ Message []byte `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *EthereumSignMessage) Reset() { *m = EthereumSignMessage{} }
+func (m *EthereumSignMessage) String() string { return proto.CompactTextString(m) }
+func (*EthereumSignMessage) ProtoMessage() {}
+func (*EthereumSignMessage) Descriptor() ([]byte, []int) {
+ return fileDescriptor_cb33f46ba915f15c, []int{7}
+}
+
+func (m *EthereumSignMessage) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_EthereumSignMessage.Unmarshal(m, b)
+}
+func (m *EthereumSignMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_EthereumSignMessage.Marshal(b, m, deterministic)
+}
+func (m *EthereumSignMessage) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_EthereumSignMessage.Merge(m, src)
+}
+func (m *EthereumSignMessage) XXX_Size() int {
+ return xxx_messageInfo_EthereumSignMessage.Size(m)
+}
+func (m *EthereumSignMessage) XXX_DiscardUnknown() {
+ xxx_messageInfo_EthereumSignMessage.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EthereumSignMessage proto.InternalMessageInfo
+
+func (m *EthereumSignMessage) GetAddressN() []uint32 {
+ if m != nil {
+ return m.AddressN
+ }
+ return nil
+}
+
+func (m *EthereumSignMessage) GetMessage() []byte {
+ if m != nil {
+ return m.Message
+ }
+ return nil
+}
+
+//*
+// Response: Signed message
+// @end
+type EthereumMessageSignature struct {
+ AddressBin []byte `protobuf:"bytes,1,opt,name=addressBin" json:"addressBin,omitempty"`
+ Signature []byte `protobuf:"bytes,2,opt,name=signature" json:"signature,omitempty"`
+ AddressHex *string `protobuf:"bytes,3,opt,name=addressHex" json:"addressHex,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *EthereumMessageSignature) Reset() { *m = EthereumMessageSignature{} }
+func (m *EthereumMessageSignature) String() string { return proto.CompactTextString(m) }
+func (*EthereumMessageSignature) ProtoMessage() {}
+func (*EthereumMessageSignature) Descriptor() ([]byte, []int) {
+ return fileDescriptor_cb33f46ba915f15c, []int{8}
+}
+
+func (m *EthereumMessageSignature) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_EthereumMessageSignature.Unmarshal(m, b)
+}
+func (m *EthereumMessageSignature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_EthereumMessageSignature.Marshal(b, m, deterministic)
+}
+func (m *EthereumMessageSignature) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_EthereumMessageSignature.Merge(m, src)
+}
+func (m *EthereumMessageSignature) XXX_Size() int {
+ return xxx_messageInfo_EthereumMessageSignature.Size(m)
+}
+func (m *EthereumMessageSignature) XXX_DiscardUnknown() {
+ xxx_messageInfo_EthereumMessageSignature.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EthereumMessageSignature proto.InternalMessageInfo
+
+func (m *EthereumMessageSignature) GetAddressBin() []byte {
+ if m != nil {
+ return m.AddressBin
+ }
+ return nil
+}
+
+func (m *EthereumMessageSignature) GetSignature() []byte {
+ if m != nil {
+ return m.Signature
+ }
+ return nil
+}
+
+func (m *EthereumMessageSignature) GetAddressHex() string {
+ if m != nil && m.AddressHex != nil {
+ return *m.AddressHex
+ }
+ return ""
+}
+
+//*
+// Request: Ask device to verify message
+// @start
+// @next Success
+// @next Failure
+type EthereumVerifyMessage struct {
+ AddressBin []byte `protobuf:"bytes,1,opt,name=addressBin" json:"addressBin,omitempty"`
+ Signature []byte `protobuf:"bytes,2,opt,name=signature" json:"signature,omitempty"`
+ Message []byte `protobuf:"bytes,3,opt,name=message" json:"message,omitempty"`
+ AddressHex *string `protobuf:"bytes,4,opt,name=addressHex" json:"addressHex,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *EthereumVerifyMessage) Reset() { *m = EthereumVerifyMessage{} }
+func (m *EthereumVerifyMessage) String() string { return proto.CompactTextString(m) }
+func (*EthereumVerifyMessage) ProtoMessage() {}
+func (*EthereumVerifyMessage) Descriptor() ([]byte, []int) {
+ return fileDescriptor_cb33f46ba915f15c, []int{9}
+}
+
+func (m *EthereumVerifyMessage) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_EthereumVerifyMessage.Unmarshal(m, b)
+}
+func (m *EthereumVerifyMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_EthereumVerifyMessage.Marshal(b, m, deterministic)
+}
+func (m *EthereumVerifyMessage) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_EthereumVerifyMessage.Merge(m, src)
+}
+func (m *EthereumVerifyMessage) XXX_Size() int {
+ return xxx_messageInfo_EthereumVerifyMessage.Size(m)
+}
+func (m *EthereumVerifyMessage) XXX_DiscardUnknown() {
+ xxx_messageInfo_EthereumVerifyMessage.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EthereumVerifyMessage proto.InternalMessageInfo
+
+func (m *EthereumVerifyMessage) GetAddressBin() []byte {
+ if m != nil {
+ return m.AddressBin
+ }
+ return nil
+}
+
+func (m *EthereumVerifyMessage) GetSignature() []byte {
+ if m != nil {
+ return m.Signature
+ }
+ return nil
+}
+
+func (m *EthereumVerifyMessage) GetMessage() []byte {
+ if m != nil {
+ return m.Message
+ }
+ return nil
+}
+
+func (m *EthereumVerifyMessage) GetAddressHex() string {
+ if m != nil && m.AddressHex != nil {
+ return *m.AddressHex
+ }
+ return ""
+}
+
+func init() {
+ proto.RegisterType((*EthereumGetPublicKey)(nil), "hw.trezor.messages.ethereum.EthereumGetPublicKey")
+ proto.RegisterType((*EthereumPublicKey)(nil), "hw.trezor.messages.ethereum.EthereumPublicKey")
+ proto.RegisterType((*EthereumGetAddress)(nil), "hw.trezor.messages.ethereum.EthereumGetAddress")
+ proto.RegisterType((*EthereumAddress)(nil), "hw.trezor.messages.ethereum.EthereumAddress")
+ proto.RegisterType((*EthereumSignTx)(nil), "hw.trezor.messages.ethereum.EthereumSignTx")
+ proto.RegisterType((*EthereumTxRequest)(nil), "hw.trezor.messages.ethereum.EthereumTxRequest")
+ proto.RegisterType((*EthereumTxAck)(nil), "hw.trezor.messages.ethereum.EthereumTxAck")
+ proto.RegisterType((*EthereumSignMessage)(nil), "hw.trezor.messages.ethereum.EthereumSignMessage")
+ proto.RegisterType((*EthereumMessageSignature)(nil), "hw.trezor.messages.ethereum.EthereumMessageSignature")
+ proto.RegisterType((*EthereumVerifyMessage)(nil), "hw.trezor.messages.ethereum.EthereumVerifyMessage")
+}
+
+func init() { proto.RegisterFile("messages-ethereum.proto", fileDescriptor_cb33f46ba915f15c) }
+
+var fileDescriptor_cb33f46ba915f15c = []byte{
+ // 593 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0x4d, 0x6f, 0xd3, 0x40,
+ 0x10, 0x95, 0x9b, 0xb4, 0x49, 0x26, 0x0d, 0x1f, 0xa6, 0x55, 0x17, 0x0a, 0x34, 0x18, 0x21, 0xe5,
+ 0x00, 0x3e, 0x70, 0x43, 0xe2, 0xd2, 0x52, 0x44, 0x2b, 0x4a, 0x55, 0xdc, 0xa8, 0x57, 0x6b, 0x63,
+ 0x6f, 0xe3, 0x55, 0x9d, 0xdd, 0xe0, 0x5d, 0xb7, 0x0e, 0x7f, 0x82, 0x23, 0xff, 0x87, 0x5f, 0x86,
+ 0xf6, 0x2b, 0x71, 0x52, 0x54, 0x0e, 0xbd, 0x65, 0xde, 0xbc, 0x7d, 0xf3, 0x66, 0xf4, 0x62, 0xd8,
+ 0x99, 0x10, 0x21, 0xf0, 0x98, 0x88, 0x77, 0x44, 0x66, 0xa4, 0x20, 0xe5, 0x24, 0x9c, 0x16, 0x5c,
+ 0x72, 0x7f, 0x37, 0xbb, 0x09, 0x65, 0x41, 0x7e, 0xf2, 0x22, 0x74, 0x94, 0xd0, 0x51, 0x9e, 0x6d,
+ 0xcf, 0x5f, 0x25, 0x7c, 0x32, 0xe1, 0xcc, 0xbc, 0x09, 0x2e, 0x60, 0xeb, 0xb3, 0xa5, 0x7c, 0x21,
+ 0xf2, 0xac, 0x1c, 0xe5, 0x34, 0xf9, 0x4a, 0x66, 0xfe, 0x2e, 0x74, 0x70, 0x9a, 0x16, 0x44, 0x88,
+ 0x98, 0x21, 0xaf, 0xdf, 0x18, 0xf4, 0xa2, 0xb6, 0x05, 0x4e, 0xfd, 0x57, 0xb0, 0x29, 0x32, 0x7e,
+ 0x13, 0xa7, 0x54, 0x4c, 0x73, 0x3c, 0x43, 0x6b, 0x7d, 0x6f, 0xd0, 0x8e, 0xba, 0x0a, 0x3b, 0x34,
+ 0x50, 0x30, 0x82, 0xc7, 0x4e, 0x77, 0x21, 0xfa, 0x01, 0x9a, 0x8c, 0xa7, 0x04, 0x79, 0x7d, 0x6f,
+ 0xd0, 0x7d, 0xff, 0x26, 0xfc, 0x87, 0x5f, 0x6b, 0xee, 0xe8, 0xf0, 0x94, 0xa7, 0x64, 0x38, 0x9b,
+ 0x92, 0x48, 0x3f, 0xf1, 0x7d, 0x68, 0x56, 0xd3, 0x72, 0xa4, 0x47, 0x75, 0x22, 0xfd, 0x3b, 0x18,
+ 0x82, 0x5f, 0xf3, 0xbe, 0x6f, 0xdc, 0xdd, 0xdb, 0xf9, 0x77, 0x78, 0xe8, 0x54, 0x9d, 0xe4, 0x4b,
+ 0x00, 0xab, 0x70, 0x40, 0x99, 0x76, 0xbf, 0x19, 0xd5, 0x90, 0x5a, 0xff, 0x88, 0x54, 0xd6, 0x62,
+ 0x0d, 0x09, 0xfe, 0xac, 0xc1, 0x03, 0xa7, 0x79, 0x4e, 0xc7, 0x6c, 0x58, 0xdd, 0xed, 0x72, 0x0b,
+ 0xd6, 0x19, 0x67, 0x09, 0xd1, 0x52, 0x9b, 0x91, 0x29, 0xd4, 0x93, 0x31, 0x16, 0xf1, 0xb4, 0xa0,
+ 0x09, 0x41, 0x0d, 0xdd, 0x69, 0x8f, 0xb1, 0x38, 0x53, 0xb5, 0x6b, 0xe6, 0x74, 0x42, 0x25, 0x6a,
+ 0xce, 0x9b, 0x27, 0xaa, 0x56, 0x7a, 0x92, 0x2b, 0xeb, 0xeb, 0x46, 0x4f, 0x17, 0x06, 0x55, 0x86,
+ 0xbb, 0xda, 0xb0, 0x29, 0x14, 0x7a, 0x8d, 0xf3, 0x92, 0xa0, 0x0d, 0xc3, 0xd5, 0x85, 0xff, 0x16,
+ 0xfc, 0x14, 0x4b, 0x1c, 0x53, 0x46, 0x25, 0xc5, 0x79, 0x9c, 0x64, 0x25, 0xbb, 0x42, 0x2d, 0x4d,
+ 0x79, 0xa4, 0x3a, 0xc7, 0xa6, 0xf1, 0x49, 0xe1, 0xfe, 0x1e, 0x74, 0x35, 0x3b, 0x27, 0x6c, 0x2c,
+ 0x33, 0xd4, 0xee, 0x7b, 0x83, 0x5e, 0x04, 0x0a, 0x3a, 0xd1, 0x88, 0xff, 0x14, 0xda, 0x49, 0x86,
+ 0x29, 0x8b, 0x69, 0x8a, 0x3a, 0xba, 0xdb, 0xd2, 0xf5, 0x71, 0xea, 0xef, 0x40, 0x4b, 0x56, 0xb1,
+ 0x9c, 0x4d, 0x09, 0x02, 0xdd, 0xd9, 0x90, 0x95, 0xca, 0x41, 0xf0, 0xdb, 0x5b, 0x44, 0x6a, 0x58,
+ 0x45, 0xe4, 0x47, 0x49, 0x84, 0x5c, 0x1d, 0xe5, 0xdd, 0x1a, 0xb5, 0x07, 0x5d, 0x41, 0xc7, 0x0c,
+ 0xcb, 0xb2, 0x20, 0xf1, 0xb5, 0xbe, 0x68, 0x2f, 0x82, 0x39, 0x74, 0xb1, 0x4c, 0x28, 0xec, 0x61,
+ 0x17, 0x84, 0x68, 0x99, 0x20, 0xec, 0x71, 0x17, 0x84, 0xf3, 0x20, 0x84, 0xde, 0xc2, 0xd8, 0x7e,
+ 0x72, 0xe5, 0xbf, 0x00, 0xed, 0xc0, 0x5e, 0xc9, 0xe4, 0xa5, 0xa3, 0x10, 0x7d, 0x9e, 0xe0, 0x04,
+ 0x9e, 0xd4, 0xd3, 0xf0, 0xcd, 0x64, 0xff, 0xee, 0x48, 0x20, 0x68, 0xd9, 0xff, 0x88, 0x0d, 0x85,
+ 0x2b, 0x83, 0x0a, 0x90, 0x53, 0xb3, 0x4a, 0xe7, 0xce, 0xda, 0x7f, 0x83, 0xfb, 0x1c, 0x3a, 0xf3,
+ 0x3d, 0xac, 0xee, 0x02, 0x58, 0x89, 0x75, 0xe3, 0x56, 0xac, 0x7f, 0x79, 0xb0, 0xed, 0x46, 0x5f,
+ 0x90, 0x82, 0x5e, 0xce, 0xdc, 0x2a, 0xf7, 0x9b, 0x5b, 0xdb, 0xb5, 0xb1, 0xb4, 0xeb, 0x8a, 0xa3,
+ 0xe6, 0xaa, 0xa3, 0x83, 0x8f, 0xf0, 0x3a, 0xe1, 0x93, 0x50, 0x60, 0xc9, 0x45, 0x46, 0x73, 0x3c,
+ 0x12, 0xee, 0x03, 0x93, 0xd3, 0x91, 0xf9, 0xe2, 0x8d, 0xca, 0xcb, 0x83, 0xed, 0xa1, 0x06, 0xad,
+ 0x5b, 0xb7, 0xc2, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8a, 0xce, 0x81, 0xc8, 0x59, 0x05, 0x00,
+ 0x00,
+}
diff --git a/accounts/usbwallet/trezor/messages-ethereum.proto b/accounts/usbwallet/trezor/messages-ethereum.proto
new file mode 100644
index 0000000..096bed2
--- /dev/null
+++ b/accounts/usbwallet/trezor/messages-ethereum.proto
@@ -0,0 +1,131 @@
+// This file originates from the SatoshiLabs Trezor `common` repository at:
+// https://github.com/trezor/trezor-common/blob/master/protob/messages-ethereum.proto
+// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9.
+
+syntax = "proto2";
+package hw.trezor.messages.ethereum;
+
+// Sugar for easier handling in Java
+option java_package = "com.satoshilabs.trezor.lib.protobuf";
+option java_outer_classname = "TrezorMessageEthereum";
+
+import "messages-common.proto";
+
+
+/**
+ * Request: Ask device for public key corresponding to address_n path
+ * @start
+ * @next EthereumPublicKey
+ * @next Failure
+ */
+message EthereumGetPublicKey {
+ repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
+ optional bool show_display = 2; // optionally show on display before sending the result
+}
+
+/**
+ * Response: Contains public key derived from device private seed
+ * @end
+ */
+message EthereumPublicKey {
+ optional hw.trezor.messages.common.HDNodeType node = 1; // BIP32 public node
+ optional string xpub = 2; // serialized form of public node
+}
+
+/**
+ * Request: Ask device for Ethereum address corresponding to address_n path
+ * @start
+ * @next EthereumAddress
+ * @next Failure
+ */
+message EthereumGetAddress {
+ repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
+ optional bool show_display = 2; // optionally show on display before sending the result
+}
+
+/**
+ * Response: Contains an Ethereum address derived from device private seed
+ * @end
+ */
+message EthereumAddress {
+ optional bytes addressBin = 1; // Ethereum address as 20 bytes (legacy firmwares)
+ optional string addressHex = 2; // Ethereum address as hex string (newer firmwares)
+}
+
+/**
+ * Request: Ask device to sign transaction
+ * All fields are optional from the protocol's point of view. Each field defaults to value `0` if missing.
+ * Note: the first at most 1024 bytes of data MUST be transmitted as part of this message.
+ * @start
+ * @next EthereumTxRequest
+ * @next Failure
+ */
+message EthereumSignTx {
+ repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
+ optional bytes nonce = 2; // <=256 bit unsigned big endian
+ optional bytes gas_price = 3; // <=256 bit unsigned big endian (in wei)
+ optional bytes gas_limit = 4; // <=256 bit unsigned big endian
+ optional bytes toBin = 5; // recipient address (20 bytes, legacy firmware)
+ optional string toHex = 11; // recipient address (hex string, newer firmware)
+ optional bytes value = 6; // <=256 bit unsigned big endian (in wei)
+ optional bytes data_initial_chunk = 7; // The initial data chunk (<= 1024 bytes)
+ optional uint32 data_length = 8; // Length of transaction payload
+ optional uint32 chain_id = 9; // Chain Id for EIP 155
+ optional uint32 tx_type = 10; // (only for Wanchain)
+}
+
+/**
+ * Response: Device asks for more data from transaction payload, or returns the signature.
+ * If data_length is set, device awaits that many more bytes of payload.
+ * Otherwise, the signature_* fields contain the computed transaction signature. All three fields will be present.
+ * @end
+ * @next EthereumTxAck
+ */
+message EthereumTxRequest {
+ optional uint32 data_length = 1; // Number of bytes being requested (<= 1024)
+ optional uint32 signature_v = 2; // Computed signature (recovery parameter, limited to 27 or 28)
+ optional bytes signature_r = 3; // Computed signature R component (256 bit)
+ optional bytes signature_s = 4; // Computed signature S component (256 bit)
+}
+
+/**
+ * Request: Transaction payload data.
+ * @next EthereumTxRequest
+ */
+message EthereumTxAck {
+ optional bytes data_chunk = 1; // Bytes from transaction payload (<= 1024 bytes)
+}
+
+/**
+ * Request: Ask device to sign message
+ * @start
+ * @next EthereumMessageSignature
+ * @next Failure
+ */
+message EthereumSignMessage {
+ repeated uint32 address_n = 1; // BIP-32 path to derive the key from master node
+ optional bytes message = 2; // message to be signed
+}
+
+/**
+ * Response: Signed message
+ * @end
+ */
+message EthereumMessageSignature {
+ optional bytes addressBin = 1; // address used to sign the message (20 bytes, legacy firmware)
+ optional bytes signature = 2; // signature of the message
+ optional string addressHex = 3; // address used to sign the message (hex string, newer firmware)
+}
+
+/**
+ * Request: Ask device to verify message
+ * @start
+ * @next Success
+ * @next Failure
+ */
+message EthereumVerifyMessage {
+ optional bytes addressBin = 1; // address to verify (20 bytes, legacy firmware)
+ optional bytes signature = 2; // signature to verify
+ optional bytes message = 3; // message to verify
+ optional string addressHex = 4; // address to verify (hex string, newer firmware)
+}
diff --git a/accounts/usbwallet/trezor/messages-management.pb.go b/accounts/usbwallet/trezor/messages-management.pb.go
new file mode 100644
index 0000000..f5c872f
--- /dev/null
+++ b/accounts/usbwallet/trezor/messages-management.pb.go
@@ -0,0 +1,1621 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: messages-management.proto
+
+package trezor
+
+import (
+ fmt "fmt"
+ math "math"
+
+ proto "github.com/golang/protobuf/proto"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+//*
+// Structure representing passphrase source
+type ApplySettings_PassphraseSourceType int32
+
+const (
+ ApplySettings_ASK ApplySettings_PassphraseSourceType = 0
+ ApplySettings_DEVICE ApplySettings_PassphraseSourceType = 1
+ ApplySettings_HOST ApplySettings_PassphraseSourceType = 2
+)
+
+var ApplySettings_PassphraseSourceType_name = map[int32]string{
+ 0: "ASK",
+ 1: "DEVICE",
+ 2: "HOST",
+}
+
+var ApplySettings_PassphraseSourceType_value = map[string]int32{
+ "ASK": 0,
+ "DEVICE": 1,
+ "HOST": 2,
+}
+
+func (x ApplySettings_PassphraseSourceType) Enum() *ApplySettings_PassphraseSourceType {
+ p := new(ApplySettings_PassphraseSourceType)
+ *p = x
+ return p
+}
+
+func (x ApplySettings_PassphraseSourceType) String() string {
+ return proto.EnumName(ApplySettings_PassphraseSourceType_name, int32(x))
+}
+
+func (x *ApplySettings_PassphraseSourceType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(ApplySettings_PassphraseSourceType_value, data, "ApplySettings_PassphraseSourceType")
+ if err != nil {
+ return err
+ }
+ *x = ApplySettings_PassphraseSourceType(value)
+ return nil
+}
+
+func (ApplySettings_PassphraseSourceType) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{4, 0}
+}
+
+//*
+// Type of recovery procedure. These should be used as bitmask, e.g.,
+// `RecoveryDeviceType_ScrambledWords | RecoveryDeviceType_Matrix`
+// listing every method supported by the host computer.
+//
+// Note that ScrambledWords must be supported by every implementation
+// for backward compatibility; there is no way to not support it.
+type RecoveryDevice_RecoveryDeviceType int32
+
+const (
+ // use powers of two when extending this field
+ RecoveryDevice_RecoveryDeviceType_ScrambledWords RecoveryDevice_RecoveryDeviceType = 0
+ RecoveryDevice_RecoveryDeviceType_Matrix RecoveryDevice_RecoveryDeviceType = 1
+)
+
+var RecoveryDevice_RecoveryDeviceType_name = map[int32]string{
+ 0: "RecoveryDeviceType_ScrambledWords",
+ 1: "RecoveryDeviceType_Matrix",
+}
+
+var RecoveryDevice_RecoveryDeviceType_value = map[string]int32{
+ "RecoveryDeviceType_ScrambledWords": 0,
+ "RecoveryDeviceType_Matrix": 1,
+}
+
+func (x RecoveryDevice_RecoveryDeviceType) Enum() *RecoveryDevice_RecoveryDeviceType {
+ p := new(RecoveryDevice_RecoveryDeviceType)
+ *p = x
+ return p
+}
+
+func (x RecoveryDevice_RecoveryDeviceType) String() string {
+ return proto.EnumName(RecoveryDevice_RecoveryDeviceType_name, int32(x))
+}
+
+func (x *RecoveryDevice_RecoveryDeviceType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(RecoveryDevice_RecoveryDeviceType_value, data, "RecoveryDevice_RecoveryDeviceType")
+ if err != nil {
+ return err
+ }
+ *x = RecoveryDevice_RecoveryDeviceType(value)
+ return nil
+}
+
+func (RecoveryDevice_RecoveryDeviceType) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{17, 0}
+}
+
+//*
+// Type of Recovery Word request
+type WordRequest_WordRequestType int32
+
+const (
+ WordRequest_WordRequestType_Plain WordRequest_WordRequestType = 0
+ WordRequest_WordRequestType_Matrix9 WordRequest_WordRequestType = 1
+ WordRequest_WordRequestType_Matrix6 WordRequest_WordRequestType = 2
+)
+
+var WordRequest_WordRequestType_name = map[int32]string{
+ 0: "WordRequestType_Plain",
+ 1: "WordRequestType_Matrix9",
+ 2: "WordRequestType_Matrix6",
+}
+
+var WordRequest_WordRequestType_value = map[string]int32{
+ "WordRequestType_Plain": 0,
+ "WordRequestType_Matrix9": 1,
+ "WordRequestType_Matrix6": 2,
+}
+
+func (x WordRequest_WordRequestType) Enum() *WordRequest_WordRequestType {
+ p := new(WordRequest_WordRequestType)
+ *p = x
+ return p
+}
+
+func (x WordRequest_WordRequestType) String() string {
+ return proto.EnumName(WordRequest_WordRequestType_name, int32(x))
+}
+
+func (x *WordRequest_WordRequestType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(WordRequest_WordRequestType_value, data, "WordRequest_WordRequestType")
+ if err != nil {
+ return err
+ }
+ *x = WordRequest_WordRequestType(value)
+ return nil
+}
+
+func (WordRequest_WordRequestType) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{18, 0}
+}
+
+//*
+// Request: Reset device to default state and ask for device details
+// @start
+// @next Features
+type Initialize struct {
+ State []byte `protobuf:"bytes,1,opt,name=state" json:"state,omitempty"`
+ SkipPassphrase *bool `protobuf:"varint,2,opt,name=skip_passphrase,json=skipPassphrase" json:"skip_passphrase,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Initialize) Reset() { *m = Initialize{} }
+func (m *Initialize) String() string { return proto.CompactTextString(m) }
+func (*Initialize) ProtoMessage() {}
+func (*Initialize) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{0}
+}
+
+func (m *Initialize) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Initialize.Unmarshal(m, b)
+}
+func (m *Initialize) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Initialize.Marshal(b, m, deterministic)
+}
+func (m *Initialize) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Initialize.Merge(m, src)
+}
+func (m *Initialize) XXX_Size() int {
+ return xxx_messageInfo_Initialize.Size(m)
+}
+func (m *Initialize) XXX_DiscardUnknown() {
+ xxx_messageInfo_Initialize.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Initialize proto.InternalMessageInfo
+
+func (m *Initialize) GetState() []byte {
+ if m != nil {
+ return m.State
+ }
+ return nil
+}
+
+func (m *Initialize) GetSkipPassphrase() bool {
+ if m != nil && m.SkipPassphrase != nil {
+ return *m.SkipPassphrase
+ }
+ return false
+}
+
+//*
+// Request: Ask for device details (no device reset)
+// @start
+// @next Features
+type GetFeatures struct {
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *GetFeatures) Reset() { *m = GetFeatures{} }
+func (m *GetFeatures) String() string { return proto.CompactTextString(m) }
+func (*GetFeatures) ProtoMessage() {}
+func (*GetFeatures) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{1}
+}
+
+func (m *GetFeatures) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_GetFeatures.Unmarshal(m, b)
+}
+func (m *GetFeatures) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_GetFeatures.Marshal(b, m, deterministic)
+}
+func (m *GetFeatures) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_GetFeatures.Merge(m, src)
+}
+func (m *GetFeatures) XXX_Size() int {
+ return xxx_messageInfo_GetFeatures.Size(m)
+}
+func (m *GetFeatures) XXX_DiscardUnknown() {
+ xxx_messageInfo_GetFeatures.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GetFeatures proto.InternalMessageInfo
+
+//*
+// Response: Reports various information about the device
+// @end
+type Features struct {
+ Vendor *string `protobuf:"bytes,1,opt,name=vendor" json:"vendor,omitempty"`
+ MajorVersion *uint32 `protobuf:"varint,2,opt,name=major_version,json=majorVersion" json:"major_version,omitempty"`
+ MinorVersion *uint32 `protobuf:"varint,3,opt,name=minor_version,json=minorVersion" json:"minor_version,omitempty"`
+ PatchVersion *uint32 `protobuf:"varint,4,opt,name=patch_version,json=patchVersion" json:"patch_version,omitempty"`
+ BootloaderMode *bool `protobuf:"varint,5,opt,name=bootloader_mode,json=bootloaderMode" json:"bootloader_mode,omitempty"`
+ DeviceId *string `protobuf:"bytes,6,opt,name=device_id,json=deviceId" json:"device_id,omitempty"`
+ PinProtection *bool `protobuf:"varint,7,opt,name=pin_protection,json=pinProtection" json:"pin_protection,omitempty"`
+ PassphraseProtection *bool `protobuf:"varint,8,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"`
+ Language *string `protobuf:"bytes,9,opt,name=language" json:"language,omitempty"`
+ Label *string `protobuf:"bytes,10,opt,name=label" json:"label,omitempty"`
+ Initialized *bool `protobuf:"varint,12,opt,name=initialized" json:"initialized,omitempty"`
+ Revision []byte `protobuf:"bytes,13,opt,name=revision" json:"revision,omitempty"`
+ BootloaderHash []byte `protobuf:"bytes,14,opt,name=bootloader_hash,json=bootloaderHash" json:"bootloader_hash,omitempty"`
+ Imported *bool `protobuf:"varint,15,opt,name=imported" json:"imported,omitempty"`
+ PinCached *bool `protobuf:"varint,16,opt,name=pin_cached,json=pinCached" json:"pin_cached,omitempty"`
+ PassphraseCached *bool `protobuf:"varint,17,opt,name=passphrase_cached,json=passphraseCached" json:"passphrase_cached,omitempty"`
+ FirmwarePresent *bool `protobuf:"varint,18,opt,name=firmware_present,json=firmwarePresent" json:"firmware_present,omitempty"`
+ NeedsBackup *bool `protobuf:"varint,19,opt,name=needs_backup,json=needsBackup" json:"needs_backup,omitempty"`
+ Flags *uint32 `protobuf:"varint,20,opt,name=flags" json:"flags,omitempty"`
+ Model *string `protobuf:"bytes,21,opt,name=model" json:"model,omitempty"`
+ FwMajor *uint32 `protobuf:"varint,22,opt,name=fw_major,json=fwMajor" json:"fw_major,omitempty"`
+ FwMinor *uint32 `protobuf:"varint,23,opt,name=fw_minor,json=fwMinor" json:"fw_minor,omitempty"`
+ FwPatch *uint32 `protobuf:"varint,24,opt,name=fw_patch,json=fwPatch" json:"fw_patch,omitempty"`
+ FwVendor *string `protobuf:"bytes,25,opt,name=fw_vendor,json=fwVendor" json:"fw_vendor,omitempty"`
+ FwVendorKeys []byte `protobuf:"bytes,26,opt,name=fw_vendor_keys,json=fwVendorKeys" json:"fw_vendor_keys,omitempty"`
+ UnfinishedBackup *bool `protobuf:"varint,27,opt,name=unfinished_backup,json=unfinishedBackup" json:"unfinished_backup,omitempty"`
+ NoBackup *bool `protobuf:"varint,28,opt,name=no_backup,json=noBackup" json:"no_backup,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Features) Reset() { *m = Features{} }
+func (m *Features) String() string { return proto.CompactTextString(m) }
+func (*Features) ProtoMessage() {}
+func (*Features) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{2}
+}
+
+func (m *Features) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Features.Unmarshal(m, b)
+}
+func (m *Features) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Features.Marshal(b, m, deterministic)
+}
+func (m *Features) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Features.Merge(m, src)
+}
+func (m *Features) XXX_Size() int {
+ return xxx_messageInfo_Features.Size(m)
+}
+func (m *Features) XXX_DiscardUnknown() {
+ xxx_messageInfo_Features.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Features proto.InternalMessageInfo
+
+func (m *Features) GetVendor() string {
+ if m != nil && m.Vendor != nil {
+ return *m.Vendor
+ }
+ return ""
+}
+
+func (m *Features) GetMajorVersion() uint32 {
+ if m != nil && m.MajorVersion != nil {
+ return *m.MajorVersion
+ }
+ return 0
+}
+
+func (m *Features) GetMinorVersion() uint32 {
+ if m != nil && m.MinorVersion != nil {
+ return *m.MinorVersion
+ }
+ return 0
+}
+
+func (m *Features) GetPatchVersion() uint32 {
+ if m != nil && m.PatchVersion != nil {
+ return *m.PatchVersion
+ }
+ return 0
+}
+
+func (m *Features) GetBootloaderMode() bool {
+ if m != nil && m.BootloaderMode != nil {
+ return *m.BootloaderMode
+ }
+ return false
+}
+
+func (m *Features) GetDeviceId() string {
+ if m != nil && m.DeviceId != nil {
+ return *m.DeviceId
+ }
+ return ""
+}
+
+func (m *Features) GetPinProtection() bool {
+ if m != nil && m.PinProtection != nil {
+ return *m.PinProtection
+ }
+ return false
+}
+
+func (m *Features) GetPassphraseProtection() bool {
+ if m != nil && m.PassphraseProtection != nil {
+ return *m.PassphraseProtection
+ }
+ return false
+}
+
+func (m *Features) GetLanguage() string {
+ if m != nil && m.Language != nil {
+ return *m.Language
+ }
+ return ""
+}
+
+func (m *Features) GetLabel() string {
+ if m != nil && m.Label != nil {
+ return *m.Label
+ }
+ return ""
+}
+
+func (m *Features) GetInitialized() bool {
+ if m != nil && m.Initialized != nil {
+ return *m.Initialized
+ }
+ return false
+}
+
+func (m *Features) GetRevision() []byte {
+ if m != nil {
+ return m.Revision
+ }
+ return nil
+}
+
+func (m *Features) GetBootloaderHash() []byte {
+ if m != nil {
+ return m.BootloaderHash
+ }
+ return nil
+}
+
+func (m *Features) GetImported() bool {
+ if m != nil && m.Imported != nil {
+ return *m.Imported
+ }
+ return false
+}
+
+func (m *Features) GetPinCached() bool {
+ if m != nil && m.PinCached != nil {
+ return *m.PinCached
+ }
+ return false
+}
+
+func (m *Features) GetPassphraseCached() bool {
+ if m != nil && m.PassphraseCached != nil {
+ return *m.PassphraseCached
+ }
+ return false
+}
+
+func (m *Features) GetFirmwarePresent() bool {
+ if m != nil && m.FirmwarePresent != nil {
+ return *m.FirmwarePresent
+ }
+ return false
+}
+
+func (m *Features) GetNeedsBackup() bool {
+ if m != nil && m.NeedsBackup != nil {
+ return *m.NeedsBackup
+ }
+ return false
+}
+
+func (m *Features) GetFlags() uint32 {
+ if m != nil && m.Flags != nil {
+ return *m.Flags
+ }
+ return 0
+}
+
+func (m *Features) GetModel() string {
+ if m != nil && m.Model != nil {
+ return *m.Model
+ }
+ return ""
+}
+
+func (m *Features) GetFwMajor() uint32 {
+ if m != nil && m.FwMajor != nil {
+ return *m.FwMajor
+ }
+ return 0
+}
+
+func (m *Features) GetFwMinor() uint32 {
+ if m != nil && m.FwMinor != nil {
+ return *m.FwMinor
+ }
+ return 0
+}
+
+func (m *Features) GetFwPatch() uint32 {
+ if m != nil && m.FwPatch != nil {
+ return *m.FwPatch
+ }
+ return 0
+}
+
+func (m *Features) GetFwVendor() string {
+ if m != nil && m.FwVendor != nil {
+ return *m.FwVendor
+ }
+ return ""
+}
+
+func (m *Features) GetFwVendorKeys() []byte {
+ if m != nil {
+ return m.FwVendorKeys
+ }
+ return nil
+}
+
+func (m *Features) GetUnfinishedBackup() bool {
+ if m != nil && m.UnfinishedBackup != nil {
+ return *m.UnfinishedBackup
+ }
+ return false
+}
+
+func (m *Features) GetNoBackup() bool {
+ if m != nil && m.NoBackup != nil {
+ return *m.NoBackup
+ }
+ return false
+}
+
+//*
+// Request: clear session (removes cached PIN, passphrase, etc).
+// @start
+// @next Success
+type ClearSession struct {
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ClearSession) Reset() { *m = ClearSession{} }
+func (m *ClearSession) String() string { return proto.CompactTextString(m) }
+func (*ClearSession) ProtoMessage() {}
+func (*ClearSession) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{3}
+}
+
+func (m *ClearSession) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ClearSession.Unmarshal(m, b)
+}
+func (m *ClearSession) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ClearSession.Marshal(b, m, deterministic)
+}
+func (m *ClearSession) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ClearSession.Merge(m, src)
+}
+func (m *ClearSession) XXX_Size() int {
+ return xxx_messageInfo_ClearSession.Size(m)
+}
+func (m *ClearSession) XXX_DiscardUnknown() {
+ xxx_messageInfo_ClearSession.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ClearSession proto.InternalMessageInfo
+
+//*
+// Request: change language and/or label of the device
+// @start
+// @next Success
+// @next Failure
+type ApplySettings struct {
+ Language *string `protobuf:"bytes,1,opt,name=language" json:"language,omitempty"`
+ Label *string `protobuf:"bytes,2,opt,name=label" json:"label,omitempty"`
+ UsePassphrase *bool `protobuf:"varint,3,opt,name=use_passphrase,json=usePassphrase" json:"use_passphrase,omitempty"`
+ Homescreen []byte `protobuf:"bytes,4,opt,name=homescreen" json:"homescreen,omitempty"`
+ PassphraseSource *ApplySettings_PassphraseSourceType `protobuf:"varint,5,opt,name=passphrase_source,json=passphraseSource,enum=hw.trezor.messages.management.ApplySettings_PassphraseSourceType" json:"passphrase_source,omitempty"`
+ AutoLockDelayMs *uint32 `protobuf:"varint,6,opt,name=auto_lock_delay_ms,json=autoLockDelayMs" json:"auto_lock_delay_ms,omitempty"`
+ DisplayRotation *uint32 `protobuf:"varint,7,opt,name=display_rotation,json=displayRotation" json:"display_rotation,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ApplySettings) Reset() { *m = ApplySettings{} }
+func (m *ApplySettings) String() string { return proto.CompactTextString(m) }
+func (*ApplySettings) ProtoMessage() {}
+func (*ApplySettings) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{4}
+}
+
+func (m *ApplySettings) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ApplySettings.Unmarshal(m, b)
+}
+func (m *ApplySettings) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ApplySettings.Marshal(b, m, deterministic)
+}
+func (m *ApplySettings) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ApplySettings.Merge(m, src)
+}
+func (m *ApplySettings) XXX_Size() int {
+ return xxx_messageInfo_ApplySettings.Size(m)
+}
+func (m *ApplySettings) XXX_DiscardUnknown() {
+ xxx_messageInfo_ApplySettings.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApplySettings proto.InternalMessageInfo
+
+func (m *ApplySettings) GetLanguage() string {
+ if m != nil && m.Language != nil {
+ return *m.Language
+ }
+ return ""
+}
+
+func (m *ApplySettings) GetLabel() string {
+ if m != nil && m.Label != nil {
+ return *m.Label
+ }
+ return ""
+}
+
+func (m *ApplySettings) GetUsePassphrase() bool {
+ if m != nil && m.UsePassphrase != nil {
+ return *m.UsePassphrase
+ }
+ return false
+}
+
+func (m *ApplySettings) GetHomescreen() []byte {
+ if m != nil {
+ return m.Homescreen
+ }
+ return nil
+}
+
+func (m *ApplySettings) GetPassphraseSource() ApplySettings_PassphraseSourceType {
+ if m != nil && m.PassphraseSource != nil {
+ return *m.PassphraseSource
+ }
+ return ApplySettings_ASK
+}
+
+func (m *ApplySettings) GetAutoLockDelayMs() uint32 {
+ if m != nil && m.AutoLockDelayMs != nil {
+ return *m.AutoLockDelayMs
+ }
+ return 0
+}
+
+func (m *ApplySettings) GetDisplayRotation() uint32 {
+ if m != nil && m.DisplayRotation != nil {
+ return *m.DisplayRotation
+ }
+ return 0
+}
+
+//*
+// Request: set flags of the device
+// @start
+// @next Success
+// @next Failure
+type ApplyFlags struct {
+ Flags *uint32 `protobuf:"varint,1,opt,name=flags" json:"flags,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ApplyFlags) Reset() { *m = ApplyFlags{} }
+func (m *ApplyFlags) String() string { return proto.CompactTextString(m) }
+func (*ApplyFlags) ProtoMessage() {}
+func (*ApplyFlags) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{5}
+}
+
+func (m *ApplyFlags) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ApplyFlags.Unmarshal(m, b)
+}
+func (m *ApplyFlags) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ApplyFlags.Marshal(b, m, deterministic)
+}
+func (m *ApplyFlags) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ApplyFlags.Merge(m, src)
+}
+func (m *ApplyFlags) XXX_Size() int {
+ return xxx_messageInfo_ApplyFlags.Size(m)
+}
+func (m *ApplyFlags) XXX_DiscardUnknown() {
+ xxx_messageInfo_ApplyFlags.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApplyFlags proto.InternalMessageInfo
+
+func (m *ApplyFlags) GetFlags() uint32 {
+ if m != nil && m.Flags != nil {
+ return *m.Flags
+ }
+ return 0
+}
+
+//*
+// Request: Starts workflow for setting/changing/removing the PIN
+// @start
+// @next Success
+// @next Failure
+type ChangePin struct {
+ Remove *bool `protobuf:"varint,1,opt,name=remove" json:"remove,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ChangePin) Reset() { *m = ChangePin{} }
+func (m *ChangePin) String() string { return proto.CompactTextString(m) }
+func (*ChangePin) ProtoMessage() {}
+func (*ChangePin) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{6}
+}
+
+func (m *ChangePin) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ChangePin.Unmarshal(m, b)
+}
+func (m *ChangePin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ChangePin.Marshal(b, m, deterministic)
+}
+func (m *ChangePin) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ChangePin.Merge(m, src)
+}
+func (m *ChangePin) XXX_Size() int {
+ return xxx_messageInfo_ChangePin.Size(m)
+}
+func (m *ChangePin) XXX_DiscardUnknown() {
+ xxx_messageInfo_ChangePin.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ChangePin proto.InternalMessageInfo
+
+func (m *ChangePin) GetRemove() bool {
+ if m != nil && m.Remove != nil {
+ return *m.Remove
+ }
+ return false
+}
+
+//*
+// Request: Test if the device is alive, device sends back the message in Success response
+// @start
+// @next Success
+type Ping struct {
+ Message *string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"`
+ ButtonProtection *bool `protobuf:"varint,2,opt,name=button_protection,json=buttonProtection" json:"button_protection,omitempty"`
+ PinProtection *bool `protobuf:"varint,3,opt,name=pin_protection,json=pinProtection" json:"pin_protection,omitempty"`
+ PassphraseProtection *bool `protobuf:"varint,4,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Ping) Reset() { *m = Ping{} }
+func (m *Ping) String() string { return proto.CompactTextString(m) }
+func (*Ping) ProtoMessage() {}
+func (*Ping) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{7}
+}
+
+func (m *Ping) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Ping.Unmarshal(m, b)
+}
+func (m *Ping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Ping.Marshal(b, m, deterministic)
+}
+func (m *Ping) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Ping.Merge(m, src)
+}
+func (m *Ping) XXX_Size() int {
+ return xxx_messageInfo_Ping.Size(m)
+}
+func (m *Ping) XXX_DiscardUnknown() {
+ xxx_messageInfo_Ping.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Ping proto.InternalMessageInfo
+
+func (m *Ping) GetMessage() string {
+ if m != nil && m.Message != nil {
+ return *m.Message
+ }
+ return ""
+}
+
+func (m *Ping) GetButtonProtection() bool {
+ if m != nil && m.ButtonProtection != nil {
+ return *m.ButtonProtection
+ }
+ return false
+}
+
+func (m *Ping) GetPinProtection() bool {
+ if m != nil && m.PinProtection != nil {
+ return *m.PinProtection
+ }
+ return false
+}
+
+func (m *Ping) GetPassphraseProtection() bool {
+ if m != nil && m.PassphraseProtection != nil {
+ return *m.PassphraseProtection
+ }
+ return false
+}
+
+//*
+// Request: Abort last operation that required user interaction
+// @start
+// @next Failure
+type Cancel struct {
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Cancel) Reset() { *m = Cancel{} }
+func (m *Cancel) String() string { return proto.CompactTextString(m) }
+func (*Cancel) ProtoMessage() {}
+func (*Cancel) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{8}
+}
+
+func (m *Cancel) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Cancel.Unmarshal(m, b)
+}
+func (m *Cancel) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Cancel.Marshal(b, m, deterministic)
+}
+func (m *Cancel) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Cancel.Merge(m, src)
+}
+func (m *Cancel) XXX_Size() int {
+ return xxx_messageInfo_Cancel.Size(m)
+}
+func (m *Cancel) XXX_DiscardUnknown() {
+ xxx_messageInfo_Cancel.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Cancel proto.InternalMessageInfo
+
+//*
+// Request: Request a sample of random data generated by hardware RNG. May be used for testing.
+// @start
+// @next Entropy
+// @next Failure
+type GetEntropy struct {
+ Size *uint32 `protobuf:"varint,1,req,name=size" json:"size,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *GetEntropy) Reset() { *m = GetEntropy{} }
+func (m *GetEntropy) String() string { return proto.CompactTextString(m) }
+func (*GetEntropy) ProtoMessage() {}
+func (*GetEntropy) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{9}
+}
+
+func (m *GetEntropy) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_GetEntropy.Unmarshal(m, b)
+}
+func (m *GetEntropy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_GetEntropy.Marshal(b, m, deterministic)
+}
+func (m *GetEntropy) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_GetEntropy.Merge(m, src)
+}
+func (m *GetEntropy) XXX_Size() int {
+ return xxx_messageInfo_GetEntropy.Size(m)
+}
+func (m *GetEntropy) XXX_DiscardUnknown() {
+ xxx_messageInfo_GetEntropy.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GetEntropy proto.InternalMessageInfo
+
+func (m *GetEntropy) GetSize() uint32 {
+ if m != nil && m.Size != nil {
+ return *m.Size
+ }
+ return 0
+}
+
+//*
+// Response: Reply with random data generated by internal RNG
+// @end
+type Entropy struct {
+ Entropy []byte `protobuf:"bytes,1,req,name=entropy" json:"entropy,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Entropy) Reset() { *m = Entropy{} }
+func (m *Entropy) String() string { return proto.CompactTextString(m) }
+func (*Entropy) ProtoMessage() {}
+func (*Entropy) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{10}
+}
+
+func (m *Entropy) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Entropy.Unmarshal(m, b)
+}
+func (m *Entropy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Entropy.Marshal(b, m, deterministic)
+}
+func (m *Entropy) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Entropy.Merge(m, src)
+}
+func (m *Entropy) XXX_Size() int {
+ return xxx_messageInfo_Entropy.Size(m)
+}
+func (m *Entropy) XXX_DiscardUnknown() {
+ xxx_messageInfo_Entropy.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Entropy proto.InternalMessageInfo
+
+func (m *Entropy) GetEntropy() []byte {
+ if m != nil {
+ return m.Entropy
+ }
+ return nil
+}
+
+//*
+// Request: Request device to wipe all sensitive data and settings
+// @start
+// @next Success
+// @next Failure
+type WipeDevice struct {
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *WipeDevice) Reset() { *m = WipeDevice{} }
+func (m *WipeDevice) String() string { return proto.CompactTextString(m) }
+func (*WipeDevice) ProtoMessage() {}
+func (*WipeDevice) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{11}
+}
+
+func (m *WipeDevice) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_WipeDevice.Unmarshal(m, b)
+}
+func (m *WipeDevice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_WipeDevice.Marshal(b, m, deterministic)
+}
+func (m *WipeDevice) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_WipeDevice.Merge(m, src)
+}
+func (m *WipeDevice) XXX_Size() int {
+ return xxx_messageInfo_WipeDevice.Size(m)
+}
+func (m *WipeDevice) XXX_DiscardUnknown() {
+ xxx_messageInfo_WipeDevice.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_WipeDevice proto.InternalMessageInfo
+
+//*
+// Request: Load seed and related internal settings from the computer
+// @start
+// @next Success
+// @next Failure
+type LoadDevice struct {
+ Mnemonic *string `protobuf:"bytes,1,opt,name=mnemonic" json:"mnemonic,omitempty"`
+ Node *HDNodeType `protobuf:"bytes,2,opt,name=node" json:"node,omitempty"`
+ Pin *string `protobuf:"bytes,3,opt,name=pin" json:"pin,omitempty"`
+ PassphraseProtection *bool `protobuf:"varint,4,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"`
+ Language *string `protobuf:"bytes,5,opt,name=language,def=english" json:"language,omitempty"`
+ Label *string `protobuf:"bytes,6,opt,name=label" json:"label,omitempty"`
+ SkipChecksum *bool `protobuf:"varint,7,opt,name=skip_checksum,json=skipChecksum" json:"skip_checksum,omitempty"`
+ U2FCounter *uint32 `protobuf:"varint,8,opt,name=u2f_counter,json=u2fCounter" json:"u2f_counter,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *LoadDevice) Reset() { *m = LoadDevice{} }
+func (m *LoadDevice) String() string { return proto.CompactTextString(m) }
+func (*LoadDevice) ProtoMessage() {}
+func (*LoadDevice) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{12}
+}
+
+func (m *LoadDevice) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_LoadDevice.Unmarshal(m, b)
+}
+func (m *LoadDevice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_LoadDevice.Marshal(b, m, deterministic)
+}
+func (m *LoadDevice) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_LoadDevice.Merge(m, src)
+}
+func (m *LoadDevice) XXX_Size() int {
+ return xxx_messageInfo_LoadDevice.Size(m)
+}
+func (m *LoadDevice) XXX_DiscardUnknown() {
+ xxx_messageInfo_LoadDevice.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_LoadDevice proto.InternalMessageInfo
+
+const Default_LoadDevice_Language string = "english"
+
+func (m *LoadDevice) GetMnemonic() string {
+ if m != nil && m.Mnemonic != nil {
+ return *m.Mnemonic
+ }
+ return ""
+}
+
+func (m *LoadDevice) GetNode() *HDNodeType {
+ if m != nil {
+ return m.Node
+ }
+ return nil
+}
+
+func (m *LoadDevice) GetPin() string {
+ if m != nil && m.Pin != nil {
+ return *m.Pin
+ }
+ return ""
+}
+
+func (m *LoadDevice) GetPassphraseProtection() bool {
+ if m != nil && m.PassphraseProtection != nil {
+ return *m.PassphraseProtection
+ }
+ return false
+}
+
+func (m *LoadDevice) GetLanguage() string {
+ if m != nil && m.Language != nil {
+ return *m.Language
+ }
+ return Default_LoadDevice_Language
+}
+
+func (m *LoadDevice) GetLabel() string {
+ if m != nil && m.Label != nil {
+ return *m.Label
+ }
+ return ""
+}
+
+func (m *LoadDevice) GetSkipChecksum() bool {
+ if m != nil && m.SkipChecksum != nil {
+ return *m.SkipChecksum
+ }
+ return false
+}
+
+func (m *LoadDevice) GetU2FCounter() uint32 {
+ if m != nil && m.U2FCounter != nil {
+ return *m.U2FCounter
+ }
+ return 0
+}
+
+//*
+// Request: Ask device to do initialization involving user interaction
+// @start
+// @next EntropyRequest
+// @next Failure
+type ResetDevice struct {
+ DisplayRandom *bool `protobuf:"varint,1,opt,name=display_random,json=displayRandom" json:"display_random,omitempty"`
+ Strength *uint32 `protobuf:"varint,2,opt,name=strength,def=256" json:"strength,omitempty"`
+ PassphraseProtection *bool `protobuf:"varint,3,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"`
+ PinProtection *bool `protobuf:"varint,4,opt,name=pin_protection,json=pinProtection" json:"pin_protection,omitempty"`
+ Language *string `protobuf:"bytes,5,opt,name=language,def=english" json:"language,omitempty"`
+ Label *string `protobuf:"bytes,6,opt,name=label" json:"label,omitempty"`
+ U2FCounter *uint32 `protobuf:"varint,7,opt,name=u2f_counter,json=u2fCounter" json:"u2f_counter,omitempty"`
+ SkipBackup *bool `protobuf:"varint,8,opt,name=skip_backup,json=skipBackup" json:"skip_backup,omitempty"`
+ NoBackup *bool `protobuf:"varint,9,opt,name=no_backup,json=noBackup" json:"no_backup,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ResetDevice) Reset() { *m = ResetDevice{} }
+func (m *ResetDevice) String() string { return proto.CompactTextString(m) }
+func (*ResetDevice) ProtoMessage() {}
+func (*ResetDevice) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{13}
+}
+
+func (m *ResetDevice) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ResetDevice.Unmarshal(m, b)
+}
+func (m *ResetDevice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ResetDevice.Marshal(b, m, deterministic)
+}
+func (m *ResetDevice) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ResetDevice.Merge(m, src)
+}
+func (m *ResetDevice) XXX_Size() int {
+ return xxx_messageInfo_ResetDevice.Size(m)
+}
+func (m *ResetDevice) XXX_DiscardUnknown() {
+ xxx_messageInfo_ResetDevice.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ResetDevice proto.InternalMessageInfo
+
+const Default_ResetDevice_Strength uint32 = 256
+const Default_ResetDevice_Language string = "english"
+
+func (m *ResetDevice) GetDisplayRandom() bool {
+ if m != nil && m.DisplayRandom != nil {
+ return *m.DisplayRandom
+ }
+ return false
+}
+
+func (m *ResetDevice) GetStrength() uint32 {
+ if m != nil && m.Strength != nil {
+ return *m.Strength
+ }
+ return Default_ResetDevice_Strength
+}
+
+func (m *ResetDevice) GetPassphraseProtection() bool {
+ if m != nil && m.PassphraseProtection != nil {
+ return *m.PassphraseProtection
+ }
+ return false
+}
+
+func (m *ResetDevice) GetPinProtection() bool {
+ if m != nil && m.PinProtection != nil {
+ return *m.PinProtection
+ }
+ return false
+}
+
+func (m *ResetDevice) GetLanguage() string {
+ if m != nil && m.Language != nil {
+ return *m.Language
+ }
+ return Default_ResetDevice_Language
+}
+
+func (m *ResetDevice) GetLabel() string {
+ if m != nil && m.Label != nil {
+ return *m.Label
+ }
+ return ""
+}
+
+func (m *ResetDevice) GetU2FCounter() uint32 {
+ if m != nil && m.U2FCounter != nil {
+ return *m.U2FCounter
+ }
+ return 0
+}
+
+func (m *ResetDevice) GetSkipBackup() bool {
+ if m != nil && m.SkipBackup != nil {
+ return *m.SkipBackup
+ }
+ return false
+}
+
+func (m *ResetDevice) GetNoBackup() bool {
+ if m != nil && m.NoBackup != nil {
+ return *m.NoBackup
+ }
+ return false
+}
+
+//*
+// Request: Perform backup of the device seed if not backed up using ResetDevice
+// @start
+// @next Success
+type BackupDevice struct {
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *BackupDevice) Reset() { *m = BackupDevice{} }
+func (m *BackupDevice) String() string { return proto.CompactTextString(m) }
+func (*BackupDevice) ProtoMessage() {}
+func (*BackupDevice) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{14}
+}
+
+func (m *BackupDevice) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_BackupDevice.Unmarshal(m, b)
+}
+func (m *BackupDevice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_BackupDevice.Marshal(b, m, deterministic)
+}
+func (m *BackupDevice) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_BackupDevice.Merge(m, src)
+}
+func (m *BackupDevice) XXX_Size() int {
+ return xxx_messageInfo_BackupDevice.Size(m)
+}
+func (m *BackupDevice) XXX_DiscardUnknown() {
+ xxx_messageInfo_BackupDevice.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_BackupDevice proto.InternalMessageInfo
+
+//*
+// Response: Ask for additional entropy from host computer
+// @next EntropyAck
+type EntropyRequest struct {
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *EntropyRequest) Reset() { *m = EntropyRequest{} }
+func (m *EntropyRequest) String() string { return proto.CompactTextString(m) }
+func (*EntropyRequest) ProtoMessage() {}
+func (*EntropyRequest) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{15}
+}
+
+func (m *EntropyRequest) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_EntropyRequest.Unmarshal(m, b)
+}
+func (m *EntropyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_EntropyRequest.Marshal(b, m, deterministic)
+}
+func (m *EntropyRequest) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_EntropyRequest.Merge(m, src)
+}
+func (m *EntropyRequest) XXX_Size() int {
+ return xxx_messageInfo_EntropyRequest.Size(m)
+}
+func (m *EntropyRequest) XXX_DiscardUnknown() {
+ xxx_messageInfo_EntropyRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EntropyRequest proto.InternalMessageInfo
+
+//*
+// Request: Provide additional entropy for seed generation function
+// @next Success
+type EntropyAck struct {
+ Entropy []byte `protobuf:"bytes,1,opt,name=entropy" json:"entropy,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *EntropyAck) Reset() { *m = EntropyAck{} }
+func (m *EntropyAck) String() string { return proto.CompactTextString(m) }
+func (*EntropyAck) ProtoMessage() {}
+func (*EntropyAck) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{16}
+}
+
+func (m *EntropyAck) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_EntropyAck.Unmarshal(m, b)
+}
+func (m *EntropyAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_EntropyAck.Marshal(b, m, deterministic)
+}
+func (m *EntropyAck) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_EntropyAck.Merge(m, src)
+}
+func (m *EntropyAck) XXX_Size() int {
+ return xxx_messageInfo_EntropyAck.Size(m)
+}
+func (m *EntropyAck) XXX_DiscardUnknown() {
+ xxx_messageInfo_EntropyAck.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_EntropyAck proto.InternalMessageInfo
+
+func (m *EntropyAck) GetEntropy() []byte {
+ if m != nil {
+ return m.Entropy
+ }
+ return nil
+}
+
+//*
+// Request: Start recovery workflow asking user for specific words of mnemonic
+// Used to recovery device safely even on untrusted computer.
+// @start
+// @next WordRequest
+type RecoveryDevice struct {
+ WordCount *uint32 `protobuf:"varint,1,opt,name=word_count,json=wordCount" json:"word_count,omitempty"`
+ PassphraseProtection *bool `protobuf:"varint,2,opt,name=passphrase_protection,json=passphraseProtection" json:"passphrase_protection,omitempty"`
+ PinProtection *bool `protobuf:"varint,3,opt,name=pin_protection,json=pinProtection" json:"pin_protection,omitempty"`
+ Language *string `protobuf:"bytes,4,opt,name=language,def=english" json:"language,omitempty"`
+ Label *string `protobuf:"bytes,5,opt,name=label" json:"label,omitempty"`
+ EnforceWordlist *bool `protobuf:"varint,6,opt,name=enforce_wordlist,json=enforceWordlist" json:"enforce_wordlist,omitempty"`
+ // 7 reserved for unused recovery method
+ Type *RecoveryDevice_RecoveryDeviceType `protobuf:"varint,8,opt,name=type,enum=hw.trezor.messages.management.RecoveryDevice_RecoveryDeviceType" json:"type,omitempty"`
+ U2FCounter *uint32 `protobuf:"varint,9,opt,name=u2f_counter,json=u2fCounter" json:"u2f_counter,omitempty"`
+ DryRun *bool `protobuf:"varint,10,opt,name=dry_run,json=dryRun" json:"dry_run,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *RecoveryDevice) Reset() { *m = RecoveryDevice{} }
+func (m *RecoveryDevice) String() string { return proto.CompactTextString(m) }
+func (*RecoveryDevice) ProtoMessage() {}
+func (*RecoveryDevice) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{17}
+}
+
+func (m *RecoveryDevice) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_RecoveryDevice.Unmarshal(m, b)
+}
+func (m *RecoveryDevice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_RecoveryDevice.Marshal(b, m, deterministic)
+}
+func (m *RecoveryDevice) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_RecoveryDevice.Merge(m, src)
+}
+func (m *RecoveryDevice) XXX_Size() int {
+ return xxx_messageInfo_RecoveryDevice.Size(m)
+}
+func (m *RecoveryDevice) XXX_DiscardUnknown() {
+ xxx_messageInfo_RecoveryDevice.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_RecoveryDevice proto.InternalMessageInfo
+
+const Default_RecoveryDevice_Language string = "english"
+
+func (m *RecoveryDevice) GetWordCount() uint32 {
+ if m != nil && m.WordCount != nil {
+ return *m.WordCount
+ }
+ return 0
+}
+
+func (m *RecoveryDevice) GetPassphraseProtection() bool {
+ if m != nil && m.PassphraseProtection != nil {
+ return *m.PassphraseProtection
+ }
+ return false
+}
+
+func (m *RecoveryDevice) GetPinProtection() bool {
+ if m != nil && m.PinProtection != nil {
+ return *m.PinProtection
+ }
+ return false
+}
+
+func (m *RecoveryDevice) GetLanguage() string {
+ if m != nil && m.Language != nil {
+ return *m.Language
+ }
+ return Default_RecoveryDevice_Language
+}
+
+func (m *RecoveryDevice) GetLabel() string {
+ if m != nil && m.Label != nil {
+ return *m.Label
+ }
+ return ""
+}
+
+func (m *RecoveryDevice) GetEnforceWordlist() bool {
+ if m != nil && m.EnforceWordlist != nil {
+ return *m.EnforceWordlist
+ }
+ return false
+}
+
+func (m *RecoveryDevice) GetType() RecoveryDevice_RecoveryDeviceType {
+ if m != nil && m.Type != nil {
+ return *m.Type
+ }
+ return RecoveryDevice_RecoveryDeviceType_ScrambledWords
+}
+
+func (m *RecoveryDevice) GetU2FCounter() uint32 {
+ if m != nil && m.U2FCounter != nil {
+ return *m.U2FCounter
+ }
+ return 0
+}
+
+func (m *RecoveryDevice) GetDryRun() bool {
+ if m != nil && m.DryRun != nil {
+ return *m.DryRun
+ }
+ return false
+}
+
+//*
+// Response: Device is waiting for user to enter word of the mnemonic
+// Its position is shown only on device's internal display.
+// @next WordAck
+type WordRequest struct {
+ Type *WordRequest_WordRequestType `protobuf:"varint,1,opt,name=type,enum=hw.trezor.messages.management.WordRequest_WordRequestType" json:"type,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *WordRequest) Reset() { *m = WordRequest{} }
+func (m *WordRequest) String() string { return proto.CompactTextString(m) }
+func (*WordRequest) ProtoMessage() {}
+func (*WordRequest) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{18}
+}
+
+func (m *WordRequest) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_WordRequest.Unmarshal(m, b)
+}
+func (m *WordRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_WordRequest.Marshal(b, m, deterministic)
+}
+func (m *WordRequest) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_WordRequest.Merge(m, src)
+}
+func (m *WordRequest) XXX_Size() int {
+ return xxx_messageInfo_WordRequest.Size(m)
+}
+func (m *WordRequest) XXX_DiscardUnknown() {
+ xxx_messageInfo_WordRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_WordRequest proto.InternalMessageInfo
+
+func (m *WordRequest) GetType() WordRequest_WordRequestType {
+ if m != nil && m.Type != nil {
+ return *m.Type
+ }
+ return WordRequest_WordRequestType_Plain
+}
+
+//*
+// Request: Computer replies with word from the mnemonic
+// @next WordRequest
+// @next Success
+// @next Failure
+type WordAck struct {
+ Word *string `protobuf:"bytes,1,req,name=word" json:"word,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *WordAck) Reset() { *m = WordAck{} }
+func (m *WordAck) String() string { return proto.CompactTextString(m) }
+func (*WordAck) ProtoMessage() {}
+func (*WordAck) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{19}
+}
+
+func (m *WordAck) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_WordAck.Unmarshal(m, b)
+}
+func (m *WordAck) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_WordAck.Marshal(b, m, deterministic)
+}
+func (m *WordAck) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_WordAck.Merge(m, src)
+}
+func (m *WordAck) XXX_Size() int {
+ return xxx_messageInfo_WordAck.Size(m)
+}
+func (m *WordAck) XXX_DiscardUnknown() {
+ xxx_messageInfo_WordAck.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_WordAck proto.InternalMessageInfo
+
+func (m *WordAck) GetWord() string {
+ if m != nil && m.Word != nil {
+ return *m.Word
+ }
+ return ""
+}
+
+//*
+// Request: Set U2F counter
+// @start
+// @next Success
+type SetU2FCounter struct {
+ U2FCounter *uint32 `protobuf:"varint,1,opt,name=u2f_counter,json=u2fCounter" json:"u2f_counter,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *SetU2FCounter) Reset() { *m = SetU2FCounter{} }
+func (m *SetU2FCounter) String() string { return proto.CompactTextString(m) }
+func (*SetU2FCounter) ProtoMessage() {}
+func (*SetU2FCounter) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0c720c20d27aa029, []int{20}
+}
+
+func (m *SetU2FCounter) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_SetU2FCounter.Unmarshal(m, b)
+}
+func (m *SetU2FCounter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_SetU2FCounter.Marshal(b, m, deterministic)
+}
+func (m *SetU2FCounter) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_SetU2FCounter.Merge(m, src)
+}
+func (m *SetU2FCounter) XXX_Size() int {
+ return xxx_messageInfo_SetU2FCounter.Size(m)
+}
+func (m *SetU2FCounter) XXX_DiscardUnknown() {
+ xxx_messageInfo_SetU2FCounter.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SetU2FCounter proto.InternalMessageInfo
+
+func (m *SetU2FCounter) GetU2FCounter() uint32 {
+ if m != nil && m.U2FCounter != nil {
+ return *m.U2FCounter
+ }
+ return 0
+}
+
+func init() {
+ proto.RegisterEnum("hw.trezor.messages.management.ApplySettings_PassphraseSourceType", ApplySettings_PassphraseSourceType_name, ApplySettings_PassphraseSourceType_value)
+ proto.RegisterEnum("hw.trezor.messages.management.RecoveryDevice_RecoveryDeviceType", RecoveryDevice_RecoveryDeviceType_name, RecoveryDevice_RecoveryDeviceType_value)
+ proto.RegisterEnum("hw.trezor.messages.management.WordRequest_WordRequestType", WordRequest_WordRequestType_name, WordRequest_WordRequestType_value)
+ proto.RegisterType((*Initialize)(nil), "hw.trezor.messages.management.Initialize")
+ proto.RegisterType((*GetFeatures)(nil), "hw.trezor.messages.management.GetFeatures")
+ proto.RegisterType((*Features)(nil), "hw.trezor.messages.management.Features")
+ proto.RegisterType((*ClearSession)(nil), "hw.trezor.messages.management.ClearSession")
+ proto.RegisterType((*ApplySettings)(nil), "hw.trezor.messages.management.ApplySettings")
+ proto.RegisterType((*ApplyFlags)(nil), "hw.trezor.messages.management.ApplyFlags")
+ proto.RegisterType((*ChangePin)(nil), "hw.trezor.messages.management.ChangePin")
+ proto.RegisterType((*Ping)(nil), "hw.trezor.messages.management.Ping")
+ proto.RegisterType((*Cancel)(nil), "hw.trezor.messages.management.Cancel")
+ proto.RegisterType((*GetEntropy)(nil), "hw.trezor.messages.management.GetEntropy")
+ proto.RegisterType((*Entropy)(nil), "hw.trezor.messages.management.Entropy")
+ proto.RegisterType((*WipeDevice)(nil), "hw.trezor.messages.management.WipeDevice")
+ proto.RegisterType((*LoadDevice)(nil), "hw.trezor.messages.management.LoadDevice")
+ proto.RegisterType((*ResetDevice)(nil), "hw.trezor.messages.management.ResetDevice")
+ proto.RegisterType((*BackupDevice)(nil), "hw.trezor.messages.management.BackupDevice")
+ proto.RegisterType((*EntropyRequest)(nil), "hw.trezor.messages.management.EntropyRequest")
+ proto.RegisterType((*EntropyAck)(nil), "hw.trezor.messages.management.EntropyAck")
+ proto.RegisterType((*RecoveryDevice)(nil), "hw.trezor.messages.management.RecoveryDevice")
+ proto.RegisterType((*WordRequest)(nil), "hw.trezor.messages.management.WordRequest")
+ proto.RegisterType((*WordAck)(nil), "hw.trezor.messages.management.WordAck")
+ proto.RegisterType((*SetU2FCounter)(nil), "hw.trezor.messages.management.SetU2FCounter")
+}
+
+func init() { proto.RegisterFile("messages-management.proto", fileDescriptor_0c720c20d27aa029) }
+
+var fileDescriptor_0c720c20d27aa029 = []byte{
+ // 1393 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x57, 0xdd, 0x6e, 0xdb, 0xc8,
+ 0x15, 0x8e, 0x7e, 0x62, 0x49, 0xc7, 0xfa, 0xcb, 0xd4, 0x8e, 0xe9, 0xb8, 0x6e, 0x1c, 0xba, 0x6e,
+ 0x12, 0x04, 0x15, 0x0a, 0x17, 0x09, 0x90, 0x5c, 0x14, 0x75, 0xec, 0xfc, 0x21, 0x71, 0x6a, 0xd0,
+ 0x6e, 0x02, 0xf4, 0x86, 0x18, 0x91, 0x47, 0xd2, 0xd4, 0xe4, 0x0c, 0xcb, 0x19, 0xda, 0x55, 0x5e,
+ 0x60, 0x6f, 0xf6, 0x45, 0x16, 0xfb, 0x1c, 0x7b, 0xb5, 0xcf, 0xb0, 0xef, 0xb2, 0x98, 0x19, 0x52,
+ 0xa2, 0x65, 0x3b, 0x46, 0x76, 0xef, 0xe6, 0x7c, 0xe7, 0xe3, 0x68, 0xce, 0x77, 0xbe, 0x39, 0x63,
+ 0xc3, 0x7a, 0x8c, 0x52, 0xd2, 0x31, 0xca, 0xbf, 0xc6, 0x94, 0xd3, 0x31, 0xc6, 0xc8, 0xd5, 0x20,
+ 0x49, 0x85, 0x12, 0x64, 0x73, 0x72, 0x3e, 0x50, 0x29, 0x7e, 0x11, 0xe9, 0xa0, 0x20, 0x0d, 0xe6,
+ 0xa4, 0x7b, 0xab, 0xb3, 0x2f, 0x03, 0x11, 0xc7, 0x82, 0xdb, 0xaf, 0xdc, 0xf7, 0x00, 0xef, 0x38,
+ 0x53, 0x8c, 0x46, 0xec, 0x0b, 0x92, 0x15, 0xb8, 0x2d, 0x15, 0x55, 0xe8, 0x54, 0xb6, 0x2a, 0x8f,
+ 0xda, 0x9e, 0x0d, 0xc8, 0x43, 0xe8, 0xc9, 0x53, 0x96, 0xf8, 0x09, 0x95, 0x32, 0x99, 0xa4, 0x54,
+ 0xa2, 0x53, 0xdd, 0xaa, 0x3c, 0x6a, 0x7a, 0x5d, 0x0d, 0x1f, 0xcd, 0x50, 0xb7, 0x03, 0xcb, 0x6f,
+ 0x50, 0xbd, 0x46, 0xaa, 0xb2, 0x14, 0xa5, 0xfb, 0x7d, 0x03, 0x9a, 0x45, 0x40, 0xee, 0xc2, 0xd2,
+ 0x19, 0xf2, 0x50, 0xa4, 0x66, 0xef, 0x96, 0x97, 0x47, 0x64, 0x1b, 0x3a, 0x31, 0xfd, 0xaf, 0x48,
+ 0xfd, 0x33, 0x4c, 0x25, 0x13, 0xdc, 0x6c, 0xdd, 0xf1, 0xda, 0x06, 0xfc, 0x64, 0x31, 0x43, 0x62,
+ 0xbc, 0x44, 0xaa, 0xe5, 0x24, 0x0d, 0x96, 0x48, 0x09, 0x55, 0xc1, 0x64, 0x46, 0xaa, 0x5b, 0x92,
+ 0x01, 0x0b, 0xd2, 0x43, 0xe8, 0x0d, 0x85, 0x50, 0x91, 0xa0, 0x21, 0xa6, 0x7e, 0x2c, 0x42, 0x74,
+ 0x6e, 0xdb, 0x5a, 0xe6, 0xf0, 0xa1, 0x08, 0x91, 0x6c, 0x40, 0x2b, 0xc4, 0x33, 0x16, 0xa0, 0xcf,
+ 0x42, 0x67, 0xc9, 0x1c, 0xb9, 0x69, 0x81, 0x77, 0x21, 0xd9, 0x81, 0x6e, 0xc2, 0xb8, 0xaf, 0x25,
+ 0xc4, 0x40, 0xe9, 0xdf, 0x6a, 0x98, 0x4d, 0x3a, 0x09, 0xe3, 0x47, 0x33, 0x90, 0xfc, 0x1d, 0x56,
+ 0xe7, 0x9a, 0x95, 0xd9, 0x4d, 0xc3, 0x5e, 0x99, 0x27, 0x4b, 0x1f, 0xdd, 0x83, 0x66, 0x44, 0xf9,
+ 0x38, 0xa3, 0x63, 0x74, 0x5a, 0xf6, 0x77, 0x8b, 0x58, 0xf7, 0x27, 0xa2, 0x43, 0x8c, 0x1c, 0x30,
+ 0x09, 0x1b, 0x90, 0x2d, 0x58, 0x66, 0xb3, 0x1e, 0x86, 0x4e, 0xdb, 0x6c, 0x5e, 0x86, 0xf4, 0x9e,
+ 0x29, 0x9e, 0x31, 0xa3, 0x4a, 0xc7, 0xb4, 0x76, 0x16, 0x2f, 0x28, 0x32, 0xa1, 0x72, 0xe2, 0x74,
+ 0x0d, 0xa5, 0xa4, 0xc8, 0x5b, 0x2a, 0x27, 0x7a, 0x13, 0x16, 0x27, 0x22, 0x55, 0x18, 0x3a, 0x3d,
+ 0xf3, 0x1b, 0xb3, 0x98, 0x6c, 0x02, 0x68, 0x41, 0x02, 0x1a, 0x4c, 0x30, 0x74, 0xfa, 0x26, 0xdb,
+ 0x4a, 0x18, 0xdf, 0x37, 0x00, 0x79, 0x02, 0x77, 0x4a, 0x42, 0xe4, 0xac, 0x3b, 0x86, 0xd5, 0x9f,
+ 0x27, 0x72, 0xf2, 0x63, 0xe8, 0x8f, 0x58, 0x1a, 0x9f, 0xd3, 0x54, 0x6b, 0x86, 0x12, 0xb9, 0x72,
+ 0x88, 0xe1, 0xf6, 0x0a, 0xfc, 0xc8, 0xc2, 0xe4, 0x01, 0xb4, 0x39, 0x62, 0x28, 0xfd, 0x21, 0x0d,
+ 0x4e, 0xb3, 0xc4, 0xf9, 0x83, 0x2d, 0xdd, 0x60, 0x2f, 0x0d, 0xa4, 0x25, 0x1b, 0x45, 0x74, 0x2c,
+ 0x9d, 0x15, 0xe3, 0x06, 0x1b, 0x68, 0x54, 0xf7, 0x3e, 0x72, 0x56, 0xad, 0x90, 0x26, 0x20, 0xeb,
+ 0xd0, 0x1c, 0x9d, 0xfb, 0xc6, 0x79, 0xce, 0x5d, 0x43, 0x6f, 0x8c, 0xce, 0x0f, 0x75, 0x58, 0xa4,
+ 0xb4, 0xdf, 0x9c, 0xb5, 0x59, 0x4a, 0x87, 0x79, 0xca, 0xb8, 0xcc, 0x71, 0x8a, 0xd4, 0x91, 0x0e,
+ 0xb5, 0x89, 0x46, 0xe7, 0x7e, 0xee, 0xfb, 0x75, 0xdb, 0xcc, 0xd1, 0xf9, 0x27, 0xeb, 0xfc, 0x3f,
+ 0x43, 0x77, 0x96, 0xf4, 0x4f, 0x71, 0x2a, 0x9d, 0x7b, 0x46, 0xf7, 0x76, 0xc1, 0x78, 0x8f, 0x53,
+ 0xa9, 0xa5, 0xcb, 0xf8, 0x88, 0x71, 0x26, 0x27, 0x18, 0x16, 0x75, 0x6e, 0x58, 0xe9, 0xe6, 0x89,
+ 0xbc, 0xd8, 0x0d, 0x68, 0x71, 0x51, 0x90, 0xfe, 0x68, 0x7b, 0xc4, 0x85, 0x4d, 0xba, 0x5d, 0x68,
+ 0xef, 0x47, 0x48, 0xd3, 0x63, 0x94, 0xba, 0xf1, 0xee, 0x77, 0x35, 0xe8, 0xec, 0x25, 0x49, 0x34,
+ 0x3d, 0x46, 0xa5, 0x18, 0x1f, 0xcb, 0x0b, 0xd6, 0xab, 0x5c, 0x67, 0xbd, 0x6a, 0xd9, 0x7a, 0x3b,
+ 0xd0, 0xcd, 0xb4, 0xb5, 0xe7, 0x93, 0xa1, 0x66, 0x2f, 0x42, 0x26, 0x71, 0x3e, 0x18, 0xc8, 0x9f,
+ 0x00, 0x26, 0x22, 0x46, 0x19, 0xa4, 0x88, 0xf6, 0x5e, 0xb6, 0xbd, 0x12, 0x42, 0xf8, 0x05, 0x7f,
+ 0x48, 0x91, 0xa5, 0x81, 0xbd, 0x97, 0xdd, 0xdd, 0xbd, 0xc1, 0x57, 0xe7, 0xda, 0xe0, 0x42, 0x05,
+ 0x83, 0xf9, 0x6f, 0x1e, 0x9b, 0x4d, 0x4e, 0xa6, 0x09, 0x96, 0x2d, 0x66, 0x51, 0xf2, 0x04, 0x08,
+ 0xcd, 0x94, 0xf0, 0x23, 0x11, 0x9c, 0xfa, 0x21, 0x46, 0x74, 0xea, 0xc7, 0xd2, 0xdc, 0xf2, 0x8e,
+ 0xd7, 0xd3, 0x99, 0x0f, 0x22, 0x38, 0x3d, 0xd0, 0xf8, 0xa1, 0xd4, 0x7e, 0x0c, 0x99, 0x4c, 0x34,
+ 0x29, 0x15, 0x8a, 0xce, 0xae, 0x7b, 0xc7, 0xeb, 0xe5, 0xb8, 0x97, 0xc3, 0xee, 0x53, 0x58, 0xb9,
+ 0xea, 0x04, 0xa4, 0x01, 0xb5, 0xbd, 0xe3, 0xf7, 0xfd, 0x5b, 0x04, 0x60, 0xe9, 0xe0, 0xd5, 0xa7,
+ 0x77, 0xfb, 0xaf, 0xfa, 0x15, 0xd2, 0x84, 0xfa, 0xdb, 0x7f, 0x1d, 0x9f, 0xf4, 0xab, 0xae, 0x0b,
+ 0x60, 0xca, 0x78, 0x5d, 0x78, 0xd3, 0x3a, 0xb6, 0x52, 0x72, 0xac, 0xbb, 0x0d, 0xad, 0xfd, 0x09,
+ 0xe5, 0x63, 0x3c, 0x62, 0x5c, 0x0f, 0xd3, 0x14, 0x63, 0x71, 0x66, 0xdb, 0xd4, 0xf4, 0xf2, 0xc8,
+ 0xfd, 0xa1, 0x02, 0xf5, 0x23, 0xc6, 0xc7, 0xc4, 0x81, 0x46, 0x2e, 0x56, 0xde, 0xc8, 0x22, 0xd4,
+ 0x7e, 0x1a, 0x66, 0x4a, 0x89, 0x0b, 0xd3, 0xcb, 0x8e, 0xf3, 0xbe, 0x4d, 0x94, 0x66, 0xd1, 0xe5,
+ 0x39, 0x57, 0xfb, 0xa6, 0x39, 0x57, 0xbf, 0x7e, 0xce, 0xb9, 0x4d, 0x58, 0xda, 0xa7, 0x3c, 0xc0,
+ 0xc8, 0xdd, 0x02, 0x78, 0x83, 0xea, 0x15, 0x57, 0xa9, 0x48, 0xa6, 0x84, 0x40, 0x5d, 0xb2, 0x2f,
+ 0xfa, 0xdc, 0xd5, 0x47, 0x1d, 0xcf, 0xac, 0xdd, 0x6d, 0x68, 0x14, 0x69, 0x07, 0x1a, 0x68, 0x97,
+ 0x86, 0xd1, 0xf6, 0x8a, 0xd0, 0x6d, 0x03, 0x7c, 0x66, 0x09, 0x1e, 0x98, 0x21, 0xed, 0xfe, 0x58,
+ 0x05, 0xf8, 0x20, 0x68, 0x68, 0x43, 0x6d, 0xed, 0x98, 0x63, 0x2c, 0x38, 0x0b, 0x0a, 0x6b, 0x17,
+ 0x31, 0x79, 0x0e, 0x75, 0xae, 0x1f, 0x02, 0xad, 0xc2, 0xf2, 0xee, 0xce, 0x55, 0x86, 0xcb, 0xdf,
+ 0xcc, 0xb7, 0x07, 0x1f, 0x45, 0x68, 0x4d, 0x65, 0x3e, 0x21, 0x7d, 0xa8, 0x25, 0xcc, 0xaa, 0xd2,
+ 0xf2, 0xf4, 0xf2, 0x37, 0x69, 0x41, 0xb6, 0x4b, 0x17, 0x4f, 0xdb, 0xbe, 0xf5, 0xa2, 0x81, 0x7c,
+ 0x1c, 0x31, 0x39, 0xb9, 0xea, 0x06, 0x2e, 0x95, 0x6f, 0xe0, 0x36, 0x74, 0xcc, 0xe3, 0x1c, 0x4c,
+ 0x30, 0x38, 0x95, 0x59, 0x9c, 0xbf, 0x44, 0x6d, 0x0d, 0xee, 0xe7, 0x18, 0xb9, 0x0f, 0xcb, 0xd9,
+ 0xee, 0xc8, 0x0f, 0x44, 0xc6, 0x15, 0xa6, 0xe6, 0xf9, 0xe9, 0x78, 0x90, 0xed, 0x8e, 0xf6, 0x2d,
+ 0xe2, 0xfe, 0x5c, 0x85, 0x65, 0x0f, 0x25, 0xaa, 0x5c, 0xae, 0x1d, 0xe8, 0xce, 0x3c, 0x4f, 0x79,
+ 0x28, 0xe2, 0xdc, 0x68, 0x9d, 0xc2, 0xf1, 0x06, 0x24, 0xf7, 0xa1, 0x29, 0x55, 0x8a, 0x7c, 0xac,
+ 0x26, 0xf6, 0xdd, 0x7e, 0x51, 0xdb, 0x7d, 0xfa, 0xcc, 0x9b, 0x81, 0xd7, 0xab, 0x51, 0xfb, 0x8a,
+ 0x1a, 0x97, 0x5d, 0x57, 0xbf, 0xca, 0x75, 0xbf, 0x43, 0xb4, 0x05, 0x3d, 0x1a, 0x8b, 0x7a, 0x68,
+ 0x82, 0x51, 0x35, 0x1f, 0xa5, 0xf6, 0xbd, 0x06, 0x0d, 0x5d, 0x35, 0x69, 0x5b, 0x97, 0x27, 0xad,
+ 0x5d, 0xe5, 0x5e, 0xec, 0x43, 0x37, 0xb7, 0xaf, 0x87, 0xff, 0xcb, 0x50, 0x2a, 0xf7, 0x2f, 0x00,
+ 0x39, 0xb2, 0x17, 0x9c, 0x5e, 0xf4, 0x74, 0xa5, 0xec, 0xe9, 0x5f, 0x6a, 0xd0, 0xf5, 0x30, 0x10,
+ 0x67, 0x98, 0x4e, 0xf3, 0xd6, 0x6c, 0x02, 0x9c, 0x8b, 0x34, 0xb4, 0x87, 0xcf, 0x67, 0x44, 0x4b,
+ 0x23, 0xe6, 0xec, 0xd7, 0x2b, 0x5e, 0xfd, 0x26, 0xc5, 0x6b, 0x37, 0x29, 0x5e, 0xbf, 0x51, 0xf1,
+ 0xdb, 0x65, 0xc5, 0x1f, 0x43, 0x1f, 0xf9, 0x48, 0xa4, 0x01, 0xfa, 0xfa, 0xac, 0x11, 0x93, 0xca,
+ 0xb4, 0xa4, 0xe9, 0xf5, 0x72, 0xfc, 0x73, 0x0e, 0x93, 0x13, 0xa8, 0xab, 0x69, 0x82, 0x46, 0xf4,
+ 0xee, 0xee, 0x3f, 0x6f, 0x98, 0xff, 0x17, 0xd5, 0x59, 0x08, 0xed, 0x4d, 0xd5, 0xbb, 0x2d, 0xb6,
+ 0xbc, 0x75, 0xa9, 0xe5, 0x6b, 0xd0, 0x08, 0xd3, 0xa9, 0x9f, 0x66, 0xdc, 0xfc, 0x75, 0xd5, 0xf4,
+ 0x96, 0xc2, 0x74, 0xea, 0x65, 0xdc, 0xfd, 0x0f, 0x90, 0xcb, 0xbb, 0x92, 0x1d, 0x78, 0x70, 0x19,
+ 0xf5, 0x8f, 0x83, 0x94, 0xc6, 0xc3, 0x08, 0x43, 0x5d, 0x8d, 0xec, 0xdf, 0x22, 0x9b, 0xb0, 0x7e,
+ 0x05, 0xed, 0x90, 0xaa, 0x94, 0xfd, 0xbf, 0x5f, 0x71, 0x7f, 0xaa, 0xc0, 0xb2, 0xa6, 0xe6, 0xbe,
+ 0x20, 0x1f, 0xf3, 0xda, 0x2b, 0xa6, 0xf6, 0x17, 0x37, 0xd4, 0x5e, 0xfa, 0xb2, 0xbc, 0x9e, 0x57,
+ 0xed, 0x8e, 0xa0, 0xb7, 0x90, 0x20, 0xeb, 0xb0, 0xba, 0x00, 0xf9, 0x47, 0x11, 0x65, 0xbc, 0x7f,
+ 0x8b, 0x6c, 0xc0, 0xda, 0x62, 0xca, 0x9e, 0xf4, 0x79, 0xbf, 0x72, 0x7d, 0xf2, 0x59, 0xbf, 0xea,
+ 0x6e, 0x42, 0x43, 0x27, 0xb5, 0x99, 0x09, 0xd4, 0x75, 0x87, 0xcd, 0x74, 0x6e, 0x79, 0x66, 0xed,
+ 0xfe, 0x0d, 0x3a, 0xc7, 0xa8, 0xfe, 0xbd, 0xfb, 0xba, 0x74, 0xbf, 0xca, 0xdd, 0xa8, 0x2c, 0x76,
+ 0xe3, 0xe5, 0x3f, 0x60, 0x3b, 0x10, 0xf1, 0x40, 0x52, 0x25, 0xe4, 0x84, 0x45, 0x74, 0x28, 0x0b,
+ 0x21, 0x22, 0x36, 0xb4, 0xff, 0xbb, 0x0c, 0xb3, 0xd1, 0xcb, 0xb5, 0x13, 0x03, 0x1e, 0x5a, 0x71,
+ 0x0e, 0x67, 0xd2, 0xfc, 0x1a, 0x00, 0x00, 0xff, 0xff, 0xd7, 0x6e, 0xfc, 0x59, 0x29, 0x0d, 0x00,
+ 0x00,
+}
diff --git a/accounts/usbwallet/trezor/messages-management.proto b/accounts/usbwallet/trezor/messages-management.proto
new file mode 100644
index 0000000..0ab825a
--- /dev/null
+++ b/accounts/usbwallet/trezor/messages-management.proto
@@ -0,0 +1,289 @@
+// This file originates from the SatoshiLabs Trezor `common` repository at:
+// https://github.com/trezor/trezor-common/blob/master/protob/messages-management.proto
+// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9.
+
+syntax = "proto2";
+package hw.trezor.messages.management;
+
+// Sugar for easier handling in Java
+option java_package = "com.satoshilabs.trezor.lib.protobuf";
+option java_outer_classname = "TrezorMessageManagement";
+
+import "messages-common.proto";
+
+/**
+ * Request: Reset device to default state and ask for device details
+ * @start
+ * @next Features
+ */
+message Initialize {
+ optional bytes state = 1; // assumed device state, clear session if set and different
+ optional bool skip_passphrase = 2; // this session should always assume empty passphrase
+}
+
+/**
+ * Request: Ask for device details (no device reset)
+ * @start
+ * @next Features
+ */
+message GetFeatures {
+}
+
+/**
+ * Response: Reports various information about the device
+ * @end
+ */
+message Features {
+ optional string vendor = 1; // name of the manufacturer, e.g. "trezor.io"
+ optional uint32 major_version = 2; // major version of the firmware/bootloader, e.g. 1
+ optional uint32 minor_version = 3; // minor version of the firmware/bootloader, e.g. 0
+ optional uint32 patch_version = 4; // patch version of the firmware/bootloader, e.g. 0
+ optional bool bootloader_mode = 5; // is device in bootloader mode?
+ optional string device_id = 6; // device's unique identifier
+ optional bool pin_protection = 7; // is device protected by PIN?
+ optional bool passphrase_protection = 8; // is node/mnemonic encrypted using passphrase?
+ optional string language = 9; // device language
+ optional string label = 10; // device description label
+ optional bool initialized = 12; // does device contain seed?
+ optional bytes revision = 13; // SCM revision of firmware
+ optional bytes bootloader_hash = 14; // hash of the bootloader
+ optional bool imported = 15; // was storage imported from an external source?
+ optional bool pin_cached = 16; // is PIN already cached in session?
+ optional bool passphrase_cached = 17; // is passphrase already cached in session?
+ optional bool firmware_present = 18; // is valid firmware loaded?
+ optional bool needs_backup = 19; // does storage need backup? (equals to Storage.needs_backup)
+ optional uint32 flags = 20; // device flags (equals to Storage.flags)
+ optional string model = 21; // device hardware model
+ optional uint32 fw_major = 22; // reported firmware version if in bootloader mode
+ optional uint32 fw_minor = 23; // reported firmware version if in bootloader mode
+ optional uint32 fw_patch = 24; // reported firmware version if in bootloader mode
+ optional string fw_vendor = 25; // reported firmware vendor if in bootloader mode
+ optional bytes fw_vendor_keys = 26; // reported firmware vendor keys (their hash)
+ optional bool unfinished_backup = 27; // report unfinished backup (equals to Storage.unfinished_backup)
+ optional bool no_backup = 28; // report no backup (equals to Storage.no_backup)
+}
+
+/**
+ * Request: clear session (removes cached PIN, passphrase, etc).
+ * @start
+ * @next Success
+ */
+message ClearSession {
+}
+
+/**
+ * Request: change language and/or label of the device
+ * @start
+ * @next Success
+ * @next Failure
+ */
+message ApplySettings {
+ optional string language = 1;
+ optional string label = 2;
+ optional bool use_passphrase = 3;
+ optional bytes homescreen = 4;
+ optional PassphraseSourceType passphrase_source = 5;
+ optional uint32 auto_lock_delay_ms = 6;
+ optional uint32 display_rotation = 7; // in degrees from North
+ /**
+ * Structure representing passphrase source
+ */
+ enum PassphraseSourceType {
+ ASK = 0;
+ DEVICE = 1;
+ HOST = 2;
+ }
+}
+
+/**
+ * Request: set flags of the device
+ * @start
+ * @next Success
+ * @next Failure
+ */
+message ApplyFlags {
+ optional uint32 flags = 1; // bitmask, can only set bits, not unset
+}
+
+/**
+ * Request: Starts workflow for setting/changing/removing the PIN
+ * @start
+ * @next Success
+ * @next Failure
+ */
+message ChangePin {
+ optional bool remove = 1; // is PIN removal requested?
+}
+
+/**
+ * Request: Test if the device is alive, device sends back the message in Success response
+ * @start
+ * @next Success
+ */
+message Ping {
+ optional string message = 1; // message to send back in Success message
+ optional bool button_protection = 2; // ask for button press
+ optional bool pin_protection = 3; // ask for PIN if set in device
+ optional bool passphrase_protection = 4; // ask for passphrase if set in device
+}
+
+/**
+ * Request: Abort last operation that required user interaction
+ * @start
+ * @next Failure
+ */
+message Cancel {
+}
+
+/**
+ * Request: Request a sample of random data generated by hardware RNG. May be used for testing.
+ * @start
+ * @next Entropy
+ * @next Failure
+ */
+message GetEntropy {
+ required uint32 size = 1; // size of requested entropy
+}
+
+/**
+ * Response: Reply with random data generated by internal RNG
+ * @end
+ */
+message Entropy {
+ required bytes entropy = 1; // chunk of random generated bytes
+}
+
+/**
+ * Request: Request device to wipe all sensitive data and settings
+ * @start
+ * @next Success
+ * @next Failure
+ */
+message WipeDevice {
+}
+
+/**
+ * Request: Load seed and related internal settings from the computer
+ * @start
+ * @next Success
+ * @next Failure
+ */
+message LoadDevice {
+ optional string mnemonic = 1; // seed encoded as BIP-39 mnemonic (12, 18 or 24 words)
+ optional hw.trezor.messages.common.HDNodeType node = 2; // BIP-32 node
+ optional string pin = 3; // set PIN protection
+ optional bool passphrase_protection = 4; // enable master node encryption using passphrase
+ optional string language = 5 [default='english']; // device language
+ optional string label = 6; // device label
+ optional bool skip_checksum = 7; // do not test mnemonic for valid BIP-39 checksum
+ optional uint32 u2f_counter = 8; // U2F counter
+}
+
+/**
+ * Request: Ask device to do initialization involving user interaction
+ * @start
+ * @next EntropyRequest
+ * @next Failure
+ */
+message ResetDevice {
+ optional bool display_random = 1; // display entropy generated by the device before asking for additional entropy
+ optional uint32 strength = 2 [default=256]; // strength of seed in bits
+ optional bool passphrase_protection = 3; // enable master node encryption using passphrase
+ optional bool pin_protection = 4; // enable PIN protection
+ optional string language = 5 [default='english']; // device language
+ optional string label = 6; // device label
+ optional uint32 u2f_counter = 7; // U2F counter
+ optional bool skip_backup = 8; // postpone seed backup to BackupDevice workflow
+ optional bool no_backup = 9; // indicate that no backup is going to be made
+}
+
+/**
+ * Request: Perform backup of the device seed if not backed up using ResetDevice
+ * @start
+ * @next Success
+ */
+message BackupDevice {
+}
+
+/**
+ * Response: Ask for additional entropy from host computer
+ * @next EntropyAck
+ */
+message EntropyRequest {
+}
+
+/**
+ * Request: Provide additional entropy for seed generation function
+ * @next Success
+ */
+message EntropyAck {
+ optional bytes entropy = 1; // 256 bits (32 bytes) of random data
+}
+
+/**
+ * Request: Start recovery workflow asking user for specific words of mnemonic
+ * Used to recovery device safely even on untrusted computer.
+ * @start
+ * @next WordRequest
+ */
+message RecoveryDevice {
+ optional uint32 word_count = 1; // number of words in BIP-39 mnemonic
+ optional bool passphrase_protection = 2; // enable master node encryption using passphrase
+ optional bool pin_protection = 3; // enable PIN protection
+ optional string language = 4 [default='english']; // device language
+ optional string label = 5; // device label
+ optional bool enforce_wordlist = 6; // enforce BIP-39 wordlist during the process
+ // 7 reserved for unused recovery method
+ optional RecoveryDeviceType type = 8; // supported recovery type
+ optional uint32 u2f_counter = 9; // U2F counter
+ optional bool dry_run = 10; // perform dry-run recovery workflow (for safe mnemonic validation)
+ /**
+ * Type of recovery procedure. These should be used as bitmask, e.g.,
+ * `RecoveryDeviceType_ScrambledWords | RecoveryDeviceType_Matrix`
+ * listing every method supported by the host computer.
+ *
+ * Note that ScrambledWords must be supported by every implementation
+ * for backward compatibility; there is no way to not support it.
+ */
+ enum RecoveryDeviceType {
+ // use powers of two when extending this field
+ RecoveryDeviceType_ScrambledWords = 0; // words in scrambled order
+ RecoveryDeviceType_Matrix = 1; // matrix recovery type
+ }
+}
+
+/**
+ * Response: Device is waiting for user to enter word of the mnemonic
+ * Its position is shown only on device's internal display.
+ * @next WordAck
+ */
+message WordRequest {
+ optional WordRequestType type = 1;
+ /**
+ * Type of Recovery Word request
+ */
+ enum WordRequestType {
+ WordRequestType_Plain = 0;
+ WordRequestType_Matrix9 = 1;
+ WordRequestType_Matrix6 = 2;
+ }
+}
+
+/**
+ * Request: Computer replies with word from the mnemonic
+ * @next WordRequest
+ * @next Success
+ * @next Failure
+ */
+message WordAck {
+ required string word = 1; // one word of mnemonic on asked position
+}
+
+/**
+ * Request: Set U2F counter
+ * @start
+ * @next Success
+ */
+message SetU2FCounter {
+ optional uint32 u2f_counter = 1; // counter
+}
diff --git a/accounts/usbwallet/trezor/messages.pb.go b/accounts/usbwallet/trezor/messages.pb.go
new file mode 100644
index 0000000..6278bd8
--- /dev/null
+++ b/accounts/usbwallet/trezor/messages.pb.go
@@ -0,0 +1,889 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: messages.proto
+
+package trezor
+
+import (
+ fmt "fmt"
+ math "math"
+
+ proto "github.com/golang/protobuf/proto"
+ descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+//*
+// Mapping between TREZOR wire identifier (uint) and a protobuf message
+type MessageType int32
+
+const (
+ // Management
+ MessageType_MessageType_Initialize MessageType = 0
+ MessageType_MessageType_Ping MessageType = 1
+ MessageType_MessageType_Success MessageType = 2
+ MessageType_MessageType_Failure MessageType = 3
+ MessageType_MessageType_ChangePin MessageType = 4
+ MessageType_MessageType_WipeDevice MessageType = 5
+ MessageType_MessageType_GetEntropy MessageType = 9
+ MessageType_MessageType_Entropy MessageType = 10
+ MessageType_MessageType_LoadDevice MessageType = 13
+ MessageType_MessageType_ResetDevice MessageType = 14
+ MessageType_MessageType_Features MessageType = 17
+ MessageType_MessageType_PinMatrixRequest MessageType = 18
+ MessageType_MessageType_PinMatrixAck MessageType = 19
+ MessageType_MessageType_Cancel MessageType = 20
+ MessageType_MessageType_ClearSession MessageType = 24
+ MessageType_MessageType_ApplySettings MessageType = 25
+ MessageType_MessageType_ButtonRequest MessageType = 26
+ MessageType_MessageType_ButtonAck MessageType = 27
+ MessageType_MessageType_ApplyFlags MessageType = 28
+ MessageType_MessageType_BackupDevice MessageType = 34
+ MessageType_MessageType_EntropyRequest MessageType = 35
+ MessageType_MessageType_EntropyAck MessageType = 36
+ MessageType_MessageType_PassphraseRequest MessageType = 41
+ MessageType_MessageType_PassphraseAck MessageType = 42
+ MessageType_MessageType_PassphraseStateRequest MessageType = 77
+ MessageType_MessageType_PassphraseStateAck MessageType = 78
+ MessageType_MessageType_RecoveryDevice MessageType = 45
+ MessageType_MessageType_WordRequest MessageType = 46
+ MessageType_MessageType_WordAck MessageType = 47
+ MessageType_MessageType_GetFeatures MessageType = 55
+ MessageType_MessageType_SetU2FCounter MessageType = 63
+ // Bootloader
+ MessageType_MessageType_FirmwareErase MessageType = 6
+ MessageType_MessageType_FirmwareUpload MessageType = 7
+ MessageType_MessageType_FirmwareRequest MessageType = 8
+ MessageType_MessageType_SelfTest MessageType = 32
+ // Bitcoin
+ MessageType_MessageType_GetPublicKey MessageType = 11
+ MessageType_MessageType_PublicKey MessageType = 12
+ MessageType_MessageType_SignTx MessageType = 15
+ MessageType_MessageType_TxRequest MessageType = 21
+ MessageType_MessageType_TxAck MessageType = 22
+ MessageType_MessageType_GetAddress MessageType = 29
+ MessageType_MessageType_Address MessageType = 30
+ MessageType_MessageType_SignMessage MessageType = 38
+ MessageType_MessageType_VerifyMessage MessageType = 39
+ MessageType_MessageType_MessageSignature MessageType = 40
+ // Crypto
+ MessageType_MessageType_CipherKeyValue MessageType = 23
+ MessageType_MessageType_CipheredKeyValue MessageType = 48
+ MessageType_MessageType_SignIdentity MessageType = 53
+ MessageType_MessageType_SignedIdentity MessageType = 54
+ MessageType_MessageType_GetECDHSessionKey MessageType = 61
+ MessageType_MessageType_ECDHSessionKey MessageType = 62
+ MessageType_MessageType_CosiCommit MessageType = 71
+ MessageType_MessageType_CosiCommitment MessageType = 72
+ MessageType_MessageType_CosiSign MessageType = 73
+ MessageType_MessageType_CosiSignature MessageType = 74
+ // Debug
+ MessageType_MessageType_DebugLinkDecision MessageType = 100
+ MessageType_MessageType_DebugLinkGetState MessageType = 101
+ MessageType_MessageType_DebugLinkState MessageType = 102
+ MessageType_MessageType_DebugLinkStop MessageType = 103
+ MessageType_MessageType_DebugLinkLog MessageType = 104
+ MessageType_MessageType_DebugLinkMemoryRead MessageType = 110
+ MessageType_MessageType_DebugLinkMemory MessageType = 111
+ MessageType_MessageType_DebugLinkMemoryWrite MessageType = 112
+ MessageType_MessageType_DebugLinkFlashErase MessageType = 113
+ // Ethereum
+ MessageType_MessageType_EthereumGetPublicKey MessageType = 450
+ MessageType_MessageType_EthereumPublicKey MessageType = 451
+ MessageType_MessageType_EthereumGetAddress MessageType = 56
+ MessageType_MessageType_EthereumAddress MessageType = 57
+ MessageType_MessageType_EthereumSignTx MessageType = 58
+ MessageType_MessageType_EthereumTxRequest MessageType = 59
+ MessageType_MessageType_EthereumTxAck MessageType = 60
+ MessageType_MessageType_EthereumSignMessage MessageType = 64
+ MessageType_MessageType_EthereumVerifyMessage MessageType = 65
+ MessageType_MessageType_EthereumMessageSignature MessageType = 66
+ // NEM
+ MessageType_MessageType_NEMGetAddress MessageType = 67
+ MessageType_MessageType_NEMAddress MessageType = 68
+ MessageType_MessageType_NEMSignTx MessageType = 69
+ MessageType_MessageType_NEMSignedTx MessageType = 70
+ MessageType_MessageType_NEMDecryptMessage MessageType = 75
+ MessageType_MessageType_NEMDecryptedMessage MessageType = 76
+ // Lisk
+ MessageType_MessageType_LiskGetAddress MessageType = 114
+ MessageType_MessageType_LiskAddress MessageType = 115
+ MessageType_MessageType_LiskSignTx MessageType = 116
+ MessageType_MessageType_LiskSignedTx MessageType = 117
+ MessageType_MessageType_LiskSignMessage MessageType = 118
+ MessageType_MessageType_LiskMessageSignature MessageType = 119
+ MessageType_MessageType_LiskVerifyMessage MessageType = 120
+ MessageType_MessageType_LiskGetPublicKey MessageType = 121
+ MessageType_MessageType_LiskPublicKey MessageType = 122
+ // Tezos
+ MessageType_MessageType_TezosGetAddress MessageType = 150
+ MessageType_MessageType_TezosAddress MessageType = 151
+ MessageType_MessageType_TezosSignTx MessageType = 152
+ MessageType_MessageType_TezosSignedTx MessageType = 153
+ MessageType_MessageType_TezosGetPublicKey MessageType = 154
+ MessageType_MessageType_TezosPublicKey MessageType = 155
+ // Stellar
+ MessageType_MessageType_StellarSignTx MessageType = 202
+ MessageType_MessageType_StellarTxOpRequest MessageType = 203
+ MessageType_MessageType_StellarGetAddress MessageType = 207
+ MessageType_MessageType_StellarAddress MessageType = 208
+ MessageType_MessageType_StellarCreateAccountOp MessageType = 210
+ MessageType_MessageType_StellarPaymentOp MessageType = 211
+ MessageType_MessageType_StellarPathPaymentOp MessageType = 212
+ MessageType_MessageType_StellarManageOfferOp MessageType = 213
+ MessageType_MessageType_StellarCreatePassiveOfferOp MessageType = 214
+ MessageType_MessageType_StellarSetOptionsOp MessageType = 215
+ MessageType_MessageType_StellarChangeTrustOp MessageType = 216
+ MessageType_MessageType_StellarAllowTrustOp MessageType = 217
+ MessageType_MessageType_StellarAccountMergeOp MessageType = 218
+ // omitted: StellarInflationOp is not a supported operation, would be 219
+ MessageType_MessageType_StellarManageDataOp MessageType = 220
+ MessageType_MessageType_StellarBumpSequenceOp MessageType = 221
+ MessageType_MessageType_StellarSignedTx MessageType = 230
+ // TRON
+ MessageType_MessageType_TronGetAddress MessageType = 250
+ MessageType_MessageType_TronAddress MessageType = 251
+ MessageType_MessageType_TronSignTx MessageType = 252
+ MessageType_MessageType_TronSignedTx MessageType = 253
+ // Cardano
+ // dropped Sign/VerifyMessage ids 300-302
+ MessageType_MessageType_CardanoSignTx MessageType = 303
+ MessageType_MessageType_CardanoTxRequest MessageType = 304
+ MessageType_MessageType_CardanoGetPublicKey MessageType = 305
+ MessageType_MessageType_CardanoPublicKey MessageType = 306
+ MessageType_MessageType_CardanoGetAddress MessageType = 307
+ MessageType_MessageType_CardanoAddress MessageType = 308
+ MessageType_MessageType_CardanoTxAck MessageType = 309
+ MessageType_MessageType_CardanoSignedTx MessageType = 310
+ // Ontology
+ MessageType_MessageType_OntologyGetAddress MessageType = 350
+ MessageType_MessageType_OntologyAddress MessageType = 351
+ MessageType_MessageType_OntologyGetPublicKey MessageType = 352
+ MessageType_MessageType_OntologyPublicKey MessageType = 353
+ MessageType_MessageType_OntologySignTransfer MessageType = 354
+ MessageType_MessageType_OntologySignedTransfer MessageType = 355
+ MessageType_MessageType_OntologySignWithdrawOng MessageType = 356
+ MessageType_MessageType_OntologySignedWithdrawOng MessageType = 357
+ MessageType_MessageType_OntologySignOntIdRegister MessageType = 358
+ MessageType_MessageType_OntologySignedOntIdRegister MessageType = 359
+ MessageType_MessageType_OntologySignOntIdAddAttributes MessageType = 360
+ MessageType_MessageType_OntologySignedOntIdAddAttributes MessageType = 361
+ // Ripple
+ MessageType_MessageType_RippleGetAddress MessageType = 400
+ MessageType_MessageType_RippleAddress MessageType = 401
+ MessageType_MessageType_RippleSignTx MessageType = 402
+ MessageType_MessageType_RippleSignedTx MessageType = 403
+ // Monero
+ MessageType_MessageType_MoneroTransactionInitRequest MessageType = 501
+ MessageType_MessageType_MoneroTransactionInitAck MessageType = 502
+ MessageType_MessageType_MoneroTransactionSetInputRequest MessageType = 503
+ MessageType_MessageType_MoneroTransactionSetInputAck MessageType = 504
+ MessageType_MessageType_MoneroTransactionInputsPermutationRequest MessageType = 505
+ MessageType_MessageType_MoneroTransactionInputsPermutationAck MessageType = 506
+ MessageType_MessageType_MoneroTransactionInputViniRequest MessageType = 507
+ MessageType_MessageType_MoneroTransactionInputViniAck MessageType = 508
+ MessageType_MessageType_MoneroTransactionAllInputsSetRequest MessageType = 509
+ MessageType_MessageType_MoneroTransactionAllInputsSetAck MessageType = 510
+ MessageType_MessageType_MoneroTransactionSetOutputRequest MessageType = 511
+ MessageType_MessageType_MoneroTransactionSetOutputAck MessageType = 512
+ MessageType_MessageType_MoneroTransactionAllOutSetRequest MessageType = 513
+ MessageType_MessageType_MoneroTransactionAllOutSetAck MessageType = 514
+ MessageType_MessageType_MoneroTransactionSignInputRequest MessageType = 515
+ MessageType_MessageType_MoneroTransactionSignInputAck MessageType = 516
+ MessageType_MessageType_MoneroTransactionFinalRequest MessageType = 517
+ MessageType_MessageType_MoneroTransactionFinalAck MessageType = 518
+ MessageType_MessageType_MoneroKeyImageExportInitRequest MessageType = 530
+ MessageType_MessageType_MoneroKeyImageExportInitAck MessageType = 531
+ MessageType_MessageType_MoneroKeyImageSyncStepRequest MessageType = 532
+ MessageType_MessageType_MoneroKeyImageSyncStepAck MessageType = 533
+ MessageType_MessageType_MoneroKeyImageSyncFinalRequest MessageType = 534
+ MessageType_MessageType_MoneroKeyImageSyncFinalAck MessageType = 535
+ MessageType_MessageType_MoneroGetAddress MessageType = 540
+ MessageType_MessageType_MoneroAddress MessageType = 541
+ MessageType_MessageType_MoneroGetWatchKey MessageType = 542
+ MessageType_MessageType_MoneroWatchKey MessageType = 543
+ MessageType_MessageType_DebugMoneroDiagRequest MessageType = 546
+ MessageType_MessageType_DebugMoneroDiagAck MessageType = 547
+ MessageType_MessageType_MoneroGetTxKeyRequest MessageType = 550
+ MessageType_MessageType_MoneroGetTxKeyAck MessageType = 551
+ MessageType_MessageType_MoneroLiveRefreshStartRequest MessageType = 552
+ MessageType_MessageType_MoneroLiveRefreshStartAck MessageType = 553
+ MessageType_MessageType_MoneroLiveRefreshStepRequest MessageType = 554
+ MessageType_MessageType_MoneroLiveRefreshStepAck MessageType = 555
+ MessageType_MessageType_MoneroLiveRefreshFinalRequest MessageType = 556
+ MessageType_MessageType_MoneroLiveRefreshFinalAck MessageType = 557
+ // EOS
+ MessageType_MessageType_EosGetPublicKey MessageType = 600
+ MessageType_MessageType_EosPublicKey MessageType = 601
+ MessageType_MessageType_EosSignTx MessageType = 602
+ MessageType_MessageType_EosTxActionRequest MessageType = 603
+ MessageType_MessageType_EosTxActionAck MessageType = 604
+ MessageType_MessageType_EosSignedTx MessageType = 605
+ // Binance
+ MessageType_MessageType_BinanceGetAddress MessageType = 700
+ MessageType_MessageType_BinanceAddress MessageType = 701
+ MessageType_MessageType_BinanceGetPublicKey MessageType = 702
+ MessageType_MessageType_BinancePublicKey MessageType = 703
+ MessageType_MessageType_BinanceSignTx MessageType = 704
+ MessageType_MessageType_BinanceTxRequest MessageType = 705
+ MessageType_MessageType_BinanceTransferMsg MessageType = 706
+ MessageType_MessageType_BinanceOrderMsg MessageType = 707
+ MessageType_MessageType_BinanceCancelMsg MessageType = 708
+ MessageType_MessageType_BinanceSignedTx MessageType = 709
+)
+
+var MessageType_name = map[int32]string{
+ 0: "MessageType_Initialize",
+ 1: "MessageType_Ping",
+ 2: "MessageType_Success",
+ 3: "MessageType_Failure",
+ 4: "MessageType_ChangePin",
+ 5: "MessageType_WipeDevice",
+ 9: "MessageType_GetEntropy",
+ 10: "MessageType_Entropy",
+ 13: "MessageType_LoadDevice",
+ 14: "MessageType_ResetDevice",
+ 17: "MessageType_Features",
+ 18: "MessageType_PinMatrixRequest",
+ 19: "MessageType_PinMatrixAck",
+ 20: "MessageType_Cancel",
+ 24: "MessageType_ClearSession",
+ 25: "MessageType_ApplySettings",
+ 26: "MessageType_ButtonRequest",
+ 27: "MessageType_ButtonAck",
+ 28: "MessageType_ApplyFlags",
+ 34: "MessageType_BackupDevice",
+ 35: "MessageType_EntropyRequest",
+ 36: "MessageType_EntropyAck",
+ 41: "MessageType_PassphraseRequest",
+ 42: "MessageType_PassphraseAck",
+ 77: "MessageType_PassphraseStateRequest",
+ 78: "MessageType_PassphraseStateAck",
+ 45: "MessageType_RecoveryDevice",
+ 46: "MessageType_WordRequest",
+ 47: "MessageType_WordAck",
+ 55: "MessageType_GetFeatures",
+ 63: "MessageType_SetU2FCounter",
+ 6: "MessageType_FirmwareErase",
+ 7: "MessageType_FirmwareUpload",
+ 8: "MessageType_FirmwareRequest",
+ 32: "MessageType_SelfTest",
+ 11: "MessageType_GetPublicKey",
+ 12: "MessageType_PublicKey",
+ 15: "MessageType_SignTx",
+ 21: "MessageType_TxRequest",
+ 22: "MessageType_TxAck",
+ 29: "MessageType_GetAddress",
+ 30: "MessageType_Address",
+ 38: "MessageType_SignMessage",
+ 39: "MessageType_VerifyMessage",
+ 40: "MessageType_MessageSignature",
+ 23: "MessageType_CipherKeyValue",
+ 48: "MessageType_CipheredKeyValue",
+ 53: "MessageType_SignIdentity",
+ 54: "MessageType_SignedIdentity",
+ 61: "MessageType_GetECDHSessionKey",
+ 62: "MessageType_ECDHSessionKey",
+ 71: "MessageType_CosiCommit",
+ 72: "MessageType_CosiCommitment",
+ 73: "MessageType_CosiSign",
+ 74: "MessageType_CosiSignature",
+ 100: "MessageType_DebugLinkDecision",
+ 101: "MessageType_DebugLinkGetState",
+ 102: "MessageType_DebugLinkState",
+ 103: "MessageType_DebugLinkStop",
+ 104: "MessageType_DebugLinkLog",
+ 110: "MessageType_DebugLinkMemoryRead",
+ 111: "MessageType_DebugLinkMemory",
+ 112: "MessageType_DebugLinkMemoryWrite",
+ 113: "MessageType_DebugLinkFlashErase",
+ 450: "MessageType_EthereumGetPublicKey",
+ 451: "MessageType_EthereumPublicKey",
+ 56: "MessageType_EthereumGetAddress",
+ 57: "MessageType_EthereumAddress",
+ 58: "MessageType_EthereumSignTx",
+ 59: "MessageType_EthereumTxRequest",
+ 60: "MessageType_EthereumTxAck",
+ 64: "MessageType_EthereumSignMessage",
+ 65: "MessageType_EthereumVerifyMessage",
+ 66: "MessageType_EthereumMessageSignature",
+ 67: "MessageType_NEMGetAddress",
+ 68: "MessageType_NEMAddress",
+ 69: "MessageType_NEMSignTx",
+ 70: "MessageType_NEMSignedTx",
+ 75: "MessageType_NEMDecryptMessage",
+ 76: "MessageType_NEMDecryptedMessage",
+ 114: "MessageType_LiskGetAddress",
+ 115: "MessageType_LiskAddress",
+ 116: "MessageType_LiskSignTx",
+ 117: "MessageType_LiskSignedTx",
+ 118: "MessageType_LiskSignMessage",
+ 119: "MessageType_LiskMessageSignature",
+ 120: "MessageType_LiskVerifyMessage",
+ 121: "MessageType_LiskGetPublicKey",
+ 122: "MessageType_LiskPublicKey",
+ 150: "MessageType_TezosGetAddress",
+ 151: "MessageType_TezosAddress",
+ 152: "MessageType_TezosSignTx",
+ 153: "MessageType_TezosSignedTx",
+ 154: "MessageType_TezosGetPublicKey",
+ 155: "MessageType_TezosPublicKey",
+ 202: "MessageType_StellarSignTx",
+ 203: "MessageType_StellarTxOpRequest",
+ 207: "MessageType_StellarGetAddress",
+ 208: "MessageType_StellarAddress",
+ 210: "MessageType_StellarCreateAccountOp",
+ 211: "MessageType_StellarPaymentOp",
+ 212: "MessageType_StellarPathPaymentOp",
+ 213: "MessageType_StellarManageOfferOp",
+ 214: "MessageType_StellarCreatePassiveOfferOp",
+ 215: "MessageType_StellarSetOptionsOp",
+ 216: "MessageType_StellarChangeTrustOp",
+ 217: "MessageType_StellarAllowTrustOp",
+ 218: "MessageType_StellarAccountMergeOp",
+ 220: "MessageType_StellarManageDataOp",
+ 221: "MessageType_StellarBumpSequenceOp",
+ 230: "MessageType_StellarSignedTx",
+ 250: "MessageType_TronGetAddress",
+ 251: "MessageType_TronAddress",
+ 252: "MessageType_TronSignTx",
+ 253: "MessageType_TronSignedTx",
+ 303: "MessageType_CardanoSignTx",
+ 304: "MessageType_CardanoTxRequest",
+ 305: "MessageType_CardanoGetPublicKey",
+ 306: "MessageType_CardanoPublicKey",
+ 307: "MessageType_CardanoGetAddress",
+ 308: "MessageType_CardanoAddress",
+ 309: "MessageType_CardanoTxAck",
+ 310: "MessageType_CardanoSignedTx",
+ 350: "MessageType_OntologyGetAddress",
+ 351: "MessageType_OntologyAddress",
+ 352: "MessageType_OntologyGetPublicKey",
+ 353: "MessageType_OntologyPublicKey",
+ 354: "MessageType_OntologySignTransfer",
+ 355: "MessageType_OntologySignedTransfer",
+ 356: "MessageType_OntologySignWithdrawOng",
+ 357: "MessageType_OntologySignedWithdrawOng",
+ 358: "MessageType_OntologySignOntIdRegister",
+ 359: "MessageType_OntologySignedOntIdRegister",
+ 360: "MessageType_OntologySignOntIdAddAttributes",
+ 361: "MessageType_OntologySignedOntIdAddAttributes",
+ 400: "MessageType_RippleGetAddress",
+ 401: "MessageType_RippleAddress",
+ 402: "MessageType_RippleSignTx",
+ 403: "MessageType_RippleSignedTx",
+ 501: "MessageType_MoneroTransactionInitRequest",
+ 502: "MessageType_MoneroTransactionInitAck",
+ 503: "MessageType_MoneroTransactionSetInputRequest",
+ 504: "MessageType_MoneroTransactionSetInputAck",
+ 505: "MessageType_MoneroTransactionInputsPermutationRequest",
+ 506: "MessageType_MoneroTransactionInputsPermutationAck",
+ 507: "MessageType_MoneroTransactionInputViniRequest",
+ 508: "MessageType_MoneroTransactionInputViniAck",
+ 509: "MessageType_MoneroTransactionAllInputsSetRequest",
+ 510: "MessageType_MoneroTransactionAllInputsSetAck",
+ 511: "MessageType_MoneroTransactionSetOutputRequest",
+ 512: "MessageType_MoneroTransactionSetOutputAck",
+ 513: "MessageType_MoneroTransactionAllOutSetRequest",
+ 514: "MessageType_MoneroTransactionAllOutSetAck",
+ 515: "MessageType_MoneroTransactionSignInputRequest",
+ 516: "MessageType_MoneroTransactionSignInputAck",
+ 517: "MessageType_MoneroTransactionFinalRequest",
+ 518: "MessageType_MoneroTransactionFinalAck",
+ 530: "MessageType_MoneroKeyImageExportInitRequest",
+ 531: "MessageType_MoneroKeyImageExportInitAck",
+ 532: "MessageType_MoneroKeyImageSyncStepRequest",
+ 533: "MessageType_MoneroKeyImageSyncStepAck",
+ 534: "MessageType_MoneroKeyImageSyncFinalRequest",
+ 535: "MessageType_MoneroKeyImageSyncFinalAck",
+ 540: "MessageType_MoneroGetAddress",
+ 541: "MessageType_MoneroAddress",
+ 542: "MessageType_MoneroGetWatchKey",
+ 543: "MessageType_MoneroWatchKey",
+ 546: "MessageType_DebugMoneroDiagRequest",
+ 547: "MessageType_DebugMoneroDiagAck",
+ 550: "MessageType_MoneroGetTxKeyRequest",
+ 551: "MessageType_MoneroGetTxKeyAck",
+ 552: "MessageType_MoneroLiveRefreshStartRequest",
+ 553: "MessageType_MoneroLiveRefreshStartAck",
+ 554: "MessageType_MoneroLiveRefreshStepRequest",
+ 555: "MessageType_MoneroLiveRefreshStepAck",
+ 556: "MessageType_MoneroLiveRefreshFinalRequest",
+ 557: "MessageType_MoneroLiveRefreshFinalAck",
+ 600: "MessageType_EosGetPublicKey",
+ 601: "MessageType_EosPublicKey",
+ 602: "MessageType_EosSignTx",
+ 603: "MessageType_EosTxActionRequest",
+ 604: "MessageType_EosTxActionAck",
+ 605: "MessageType_EosSignedTx",
+ 700: "MessageType_BinanceGetAddress",
+ 701: "MessageType_BinanceAddress",
+ 702: "MessageType_BinanceGetPublicKey",
+ 703: "MessageType_BinancePublicKey",
+ 704: "MessageType_BinanceSignTx",
+ 705: "MessageType_BinanceTxRequest",
+ 706: "MessageType_BinanceTransferMsg",
+ 707: "MessageType_BinanceOrderMsg",
+ 708: "MessageType_BinanceCancelMsg",
+ 709: "MessageType_BinanceSignedTx",
+}
+
+var MessageType_value = map[string]int32{
+ "MessageType_Initialize": 0,
+ "MessageType_Ping": 1,
+ "MessageType_Success": 2,
+ "MessageType_Failure": 3,
+ "MessageType_ChangePin": 4,
+ "MessageType_WipeDevice": 5,
+ "MessageType_GetEntropy": 9,
+ "MessageType_Entropy": 10,
+ "MessageType_LoadDevice": 13,
+ "MessageType_ResetDevice": 14,
+ "MessageType_Features": 17,
+ "MessageType_PinMatrixRequest": 18,
+ "MessageType_PinMatrixAck": 19,
+ "MessageType_Cancel": 20,
+ "MessageType_ClearSession": 24,
+ "MessageType_ApplySettings": 25,
+ "MessageType_ButtonRequest": 26,
+ "MessageType_ButtonAck": 27,
+ "MessageType_ApplyFlags": 28,
+ "MessageType_BackupDevice": 34,
+ "MessageType_EntropyRequest": 35,
+ "MessageType_EntropyAck": 36,
+ "MessageType_PassphraseRequest": 41,
+ "MessageType_PassphraseAck": 42,
+ "MessageType_PassphraseStateRequest": 77,
+ "MessageType_PassphraseStateAck": 78,
+ "MessageType_RecoveryDevice": 45,
+ "MessageType_WordRequest": 46,
+ "MessageType_WordAck": 47,
+ "MessageType_GetFeatures": 55,
+ "MessageType_SetU2FCounter": 63,
+ "MessageType_FirmwareErase": 6,
+ "MessageType_FirmwareUpload": 7,
+ "MessageType_FirmwareRequest": 8,
+ "MessageType_SelfTest": 32,
+ "MessageType_GetPublicKey": 11,
+ "MessageType_PublicKey": 12,
+ "MessageType_SignTx": 15,
+ "MessageType_TxRequest": 21,
+ "MessageType_TxAck": 22,
+ "MessageType_GetAddress": 29,
+ "MessageType_Address": 30,
+ "MessageType_SignMessage": 38,
+ "MessageType_VerifyMessage": 39,
+ "MessageType_MessageSignature": 40,
+ "MessageType_CipherKeyValue": 23,
+ "MessageType_CipheredKeyValue": 48,
+ "MessageType_SignIdentity": 53,
+ "MessageType_SignedIdentity": 54,
+ "MessageType_GetECDHSessionKey": 61,
+ "MessageType_ECDHSessionKey": 62,
+ "MessageType_CosiCommit": 71,
+ "MessageType_CosiCommitment": 72,
+ "MessageType_CosiSign": 73,
+ "MessageType_CosiSignature": 74,
+ "MessageType_DebugLinkDecision": 100,
+ "MessageType_DebugLinkGetState": 101,
+ "MessageType_DebugLinkState": 102,
+ "MessageType_DebugLinkStop": 103,
+ "MessageType_DebugLinkLog": 104,
+ "MessageType_DebugLinkMemoryRead": 110,
+ "MessageType_DebugLinkMemory": 111,
+ "MessageType_DebugLinkMemoryWrite": 112,
+ "MessageType_DebugLinkFlashErase": 113,
+ "MessageType_EthereumGetPublicKey": 450,
+ "MessageType_EthereumPublicKey": 451,
+ "MessageType_EthereumGetAddress": 56,
+ "MessageType_EthereumAddress": 57,
+ "MessageType_EthereumSignTx": 58,
+ "MessageType_EthereumTxRequest": 59,
+ "MessageType_EthereumTxAck": 60,
+ "MessageType_EthereumSignMessage": 64,
+ "MessageType_EthereumVerifyMessage": 65,
+ "MessageType_EthereumMessageSignature": 66,
+ "MessageType_NEMGetAddress": 67,
+ "MessageType_NEMAddress": 68,
+ "MessageType_NEMSignTx": 69,
+ "MessageType_NEMSignedTx": 70,
+ "MessageType_NEMDecryptMessage": 75,
+ "MessageType_NEMDecryptedMessage": 76,
+ "MessageType_LiskGetAddress": 114,
+ "MessageType_LiskAddress": 115,
+ "MessageType_LiskSignTx": 116,
+ "MessageType_LiskSignedTx": 117,
+ "MessageType_LiskSignMessage": 118,
+ "MessageType_LiskMessageSignature": 119,
+ "MessageType_LiskVerifyMessage": 120,
+ "MessageType_LiskGetPublicKey": 121,
+ "MessageType_LiskPublicKey": 122,
+ "MessageType_TezosGetAddress": 150,
+ "MessageType_TezosAddress": 151,
+ "MessageType_TezosSignTx": 152,
+ "MessageType_TezosSignedTx": 153,
+ "MessageType_TezosGetPublicKey": 154,
+ "MessageType_TezosPublicKey": 155,
+ "MessageType_StellarSignTx": 202,
+ "MessageType_StellarTxOpRequest": 203,
+ "MessageType_StellarGetAddress": 207,
+ "MessageType_StellarAddress": 208,
+ "MessageType_StellarCreateAccountOp": 210,
+ "MessageType_StellarPaymentOp": 211,
+ "MessageType_StellarPathPaymentOp": 212,
+ "MessageType_StellarManageOfferOp": 213,
+ "MessageType_StellarCreatePassiveOfferOp": 214,
+ "MessageType_StellarSetOptionsOp": 215,
+ "MessageType_StellarChangeTrustOp": 216,
+ "MessageType_StellarAllowTrustOp": 217,
+ "MessageType_StellarAccountMergeOp": 218,
+ "MessageType_StellarManageDataOp": 220,
+ "MessageType_StellarBumpSequenceOp": 221,
+ "MessageType_StellarSignedTx": 230,
+ "MessageType_TronGetAddress": 250,
+ "MessageType_TronAddress": 251,
+ "MessageType_TronSignTx": 252,
+ "MessageType_TronSignedTx": 253,
+ "MessageType_CardanoSignTx": 303,
+ "MessageType_CardanoTxRequest": 304,
+ "MessageType_CardanoGetPublicKey": 305,
+ "MessageType_CardanoPublicKey": 306,
+ "MessageType_CardanoGetAddress": 307,
+ "MessageType_CardanoAddress": 308,
+ "MessageType_CardanoTxAck": 309,
+ "MessageType_CardanoSignedTx": 310,
+ "MessageType_OntologyGetAddress": 350,
+ "MessageType_OntologyAddress": 351,
+ "MessageType_OntologyGetPublicKey": 352,
+ "MessageType_OntologyPublicKey": 353,
+ "MessageType_OntologySignTransfer": 354,
+ "MessageType_OntologySignedTransfer": 355,
+ "MessageType_OntologySignWithdrawOng": 356,
+ "MessageType_OntologySignedWithdrawOng": 357,
+ "MessageType_OntologySignOntIdRegister": 358,
+ "MessageType_OntologySignedOntIdRegister": 359,
+ "MessageType_OntologySignOntIdAddAttributes": 360,
+ "MessageType_OntologySignedOntIdAddAttributes": 361,
+ "MessageType_RippleGetAddress": 400,
+ "MessageType_RippleAddress": 401,
+ "MessageType_RippleSignTx": 402,
+ "MessageType_RippleSignedTx": 403,
+ "MessageType_MoneroTransactionInitRequest": 501,
+ "MessageType_MoneroTransactionInitAck": 502,
+ "MessageType_MoneroTransactionSetInputRequest": 503,
+ "MessageType_MoneroTransactionSetInputAck": 504,
+ "MessageType_MoneroTransactionInputsPermutationRequest": 505,
+ "MessageType_MoneroTransactionInputsPermutationAck": 506,
+ "MessageType_MoneroTransactionInputViniRequest": 507,
+ "MessageType_MoneroTransactionInputViniAck": 508,
+ "MessageType_MoneroTransactionAllInputsSetRequest": 509,
+ "MessageType_MoneroTransactionAllInputsSetAck": 510,
+ "MessageType_MoneroTransactionSetOutputRequest": 511,
+ "MessageType_MoneroTransactionSetOutputAck": 512,
+ "MessageType_MoneroTransactionAllOutSetRequest": 513,
+ "MessageType_MoneroTransactionAllOutSetAck": 514,
+ "MessageType_MoneroTransactionSignInputRequest": 515,
+ "MessageType_MoneroTransactionSignInputAck": 516,
+ "MessageType_MoneroTransactionFinalRequest": 517,
+ "MessageType_MoneroTransactionFinalAck": 518,
+ "MessageType_MoneroKeyImageExportInitRequest": 530,
+ "MessageType_MoneroKeyImageExportInitAck": 531,
+ "MessageType_MoneroKeyImageSyncStepRequest": 532,
+ "MessageType_MoneroKeyImageSyncStepAck": 533,
+ "MessageType_MoneroKeyImageSyncFinalRequest": 534,
+ "MessageType_MoneroKeyImageSyncFinalAck": 535,
+ "MessageType_MoneroGetAddress": 540,
+ "MessageType_MoneroAddress": 541,
+ "MessageType_MoneroGetWatchKey": 542,
+ "MessageType_MoneroWatchKey": 543,
+ "MessageType_DebugMoneroDiagRequest": 546,
+ "MessageType_DebugMoneroDiagAck": 547,
+ "MessageType_MoneroGetTxKeyRequest": 550,
+ "MessageType_MoneroGetTxKeyAck": 551,
+ "MessageType_MoneroLiveRefreshStartRequest": 552,
+ "MessageType_MoneroLiveRefreshStartAck": 553,
+ "MessageType_MoneroLiveRefreshStepRequest": 554,
+ "MessageType_MoneroLiveRefreshStepAck": 555,
+ "MessageType_MoneroLiveRefreshFinalRequest": 556,
+ "MessageType_MoneroLiveRefreshFinalAck": 557,
+ "MessageType_EosGetPublicKey": 600,
+ "MessageType_EosPublicKey": 601,
+ "MessageType_EosSignTx": 602,
+ "MessageType_EosTxActionRequest": 603,
+ "MessageType_EosTxActionAck": 604,
+ "MessageType_EosSignedTx": 605,
+ "MessageType_BinanceGetAddress": 700,
+ "MessageType_BinanceAddress": 701,
+ "MessageType_BinanceGetPublicKey": 702,
+ "MessageType_BinancePublicKey": 703,
+ "MessageType_BinanceSignTx": 704,
+ "MessageType_BinanceTxRequest": 705,
+ "MessageType_BinanceTransferMsg": 706,
+ "MessageType_BinanceOrderMsg": 707,
+ "MessageType_BinanceCancelMsg": 708,
+ "MessageType_BinanceSignedTx": 709,
+}
+
+func (x MessageType) Enum() *MessageType {
+ p := new(MessageType)
+ *p = x
+ return p
+}
+
+func (x MessageType) String() string {
+ return proto.EnumName(MessageType_name, int32(x))
+}
+
+func (x *MessageType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(MessageType_value, data, "MessageType")
+ if err != nil {
+ return err
+ }
+ *x = MessageType(value)
+ return nil
+}
+
+func (MessageType) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_4dc296cbfe5ffcd5, []int{0}
+}
+
+var E_WireIn = &proto.ExtensionDesc{
+ ExtendedType: (*descriptor.EnumValueOptions)(nil),
+ ExtensionType: (*bool)(nil),
+ Field: 50002,
+ Name: "hw.trezor.messages.wire_in",
+ Tag: "varint,50002,opt,name=wire_in",
+ Filename: "messages.proto",
+}
+
+var E_WireOut = &proto.ExtensionDesc{
+ ExtendedType: (*descriptor.EnumValueOptions)(nil),
+ ExtensionType: (*bool)(nil),
+ Field: 50003,
+ Name: "hw.trezor.messages.wire_out",
+ Tag: "varint,50003,opt,name=wire_out",
+ Filename: "messages.proto",
+}
+
+var E_WireDebugIn = &proto.ExtensionDesc{
+ ExtendedType: (*descriptor.EnumValueOptions)(nil),
+ ExtensionType: (*bool)(nil),
+ Field: 50004,
+ Name: "hw.trezor.messages.wire_debug_in",
+ Tag: "varint,50004,opt,name=wire_debug_in",
+ Filename: "messages.proto",
+}
+
+var E_WireDebugOut = &proto.ExtensionDesc{
+ ExtendedType: (*descriptor.EnumValueOptions)(nil),
+ ExtensionType: (*bool)(nil),
+ Field: 50005,
+ Name: "hw.trezor.messages.wire_debug_out",
+ Tag: "varint,50005,opt,name=wire_debug_out",
+ Filename: "messages.proto",
+}
+
+var E_WireTiny = &proto.ExtensionDesc{
+ ExtendedType: (*descriptor.EnumValueOptions)(nil),
+ ExtensionType: (*bool)(nil),
+ Field: 50006,
+ Name: "hw.trezor.messages.wire_tiny",
+ Tag: "varint,50006,opt,name=wire_tiny",
+ Filename: "messages.proto",
+}
+
+var E_WireBootloader = &proto.ExtensionDesc{
+ ExtendedType: (*descriptor.EnumValueOptions)(nil),
+ ExtensionType: (*bool)(nil),
+ Field: 50007,
+ Name: "hw.trezor.messages.wire_bootloader",
+ Tag: "varint,50007,opt,name=wire_bootloader",
+ Filename: "messages.proto",
+}
+
+var E_WireNoFsm = &proto.ExtensionDesc{
+ ExtendedType: (*descriptor.EnumValueOptions)(nil),
+ ExtensionType: (*bool)(nil),
+ Field: 50008,
+ Name: "hw.trezor.messages.wire_no_fsm",
+ Tag: "varint,50008,opt,name=wire_no_fsm",
+ Filename: "messages.proto",
+}
+
+func init() {
+ proto.RegisterEnum("hw.trezor.messages.MessageType", MessageType_name, MessageType_value)
+ proto.RegisterExtension(E_WireIn)
+ proto.RegisterExtension(E_WireOut)
+ proto.RegisterExtension(E_WireDebugIn)
+ proto.RegisterExtension(E_WireDebugOut)
+ proto.RegisterExtension(E_WireTiny)
+ proto.RegisterExtension(E_WireBootloader)
+ proto.RegisterExtension(E_WireNoFsm)
+}
+
+func init() { proto.RegisterFile("messages.proto", fileDescriptor_4dc296cbfe5ffcd5) }
+
+var fileDescriptor_4dc296cbfe5ffcd5 = []byte{
+ // 2430 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x9a, 0xd9, 0x73, 0x1c, 0xc5,
+ 0x1d, 0xc7, 0xb3, 0xab, 0x11, 0x88, 0xf6, 0x41, 0x23, 0xb0, 0x2d, 0xaf, 0x2f, 0xf9, 0xc0, 0x96,
+ 0x2f, 0xd9, 0x10, 0x0c, 0x44, 0x38, 0x60, 0x69, 0xb5, 0x12, 0x8a, 0xb5, 0x5a, 0x97, 0x76, 0xb1,
+ 0x1f, 0x5d, 0xa3, 0x9d, 0xd6, 0x6e, 0x97, 0x67, 0x67, 0x86, 0x9e, 0x1e, 0x49, 0xeb, 0xa7, 0x9c,
+ 0x3c, 0x13, 0x48, 0xc0, 0xb9, 0xa9, 0xa4, 0x2a, 0x21, 0x57, 0x85, 0x1c, 0x4e, 0x25, 0x55, 0x39,
+ 0x08, 0x24, 0x2f, 0xc9, 0x43, 0x52, 0x9c, 0x86, 0x40, 0xee, 0x90, 0xe4, 0x0f, 0xc8, 0xc5, 0x91,
+ 0xa4, 0x7a, 0xa6, 0xbb, 0xe7, 0xd8, 0xdf, 0xae, 0x36, 0x6f, 0x58, 0xf3, 0xf9, 0x7d, 0x7f, 0x47,
+ 0xff, 0xfa, 0x37, 0xdd, 0xb3, 0xa0, 0xcd, 0x2d, 0xe2, 0xfb, 0x66, 0x83, 0xf8, 0xe3, 0x1e, 0x73,
+ 0xb9, 0x3b, 0x3c, 0xdc, 0x5c, 0x1d, 0xe7, 0x8c, 0x5c, 0x76, 0xd9, 0xb8, 0x7a, 0x52, 0x18, 0x6d,
+ 0xb8, 0x6e, 0xc3, 0x26, 0x27, 0x42, 0x62, 0x29, 0x58, 0x3e, 0x61, 0x11, 0xbf, 0xce, 0xa8, 0xc7,
+ 0x5d, 0x16, 0x59, 0x1d, 0xf9, 0xfe, 0x7d, 0x68, 0x43, 0x39, 0xc2, 0x6b, 0x6d, 0x8f, 0x0c, 0x1f,
+ 0x40, 0x5b, 0x13, 0xff, 0xbc, 0x38, 0xe7, 0x50, 0x4e, 0x4d, 0x9b, 0x5e, 0x26, 0xf8, 0x5d, 0x85,
+ 0xa1, 0x87, 0xaf, 0x8e, 0xe4, 0x9e, 0xba, 0x3a, 0x92, 0x1b, 0x2e, 0x20, 0x9c, 0xa4, 0xce, 0x51,
+ 0xa7, 0x81, 0x73, 0x05, 0x43, 0x3c, 0x1f, 0xde, 0x85, 0x6e, 0x4e, 0x3e, 0xab, 0x06, 0xf5, 0x3a,
+ 0xf1, 0x7d, 0x9c, 0x2f, 0x18, 0x57, 0x80, 0xc7, 0x33, 0x26, 0xb5, 0x03, 0x46, 0xf0, 0x80, 0x7c,
+ 0xbc, 0x07, 0x6d, 0x49, 0x3e, 0x2e, 0x36, 0x4d, 0xa7, 0x41, 0xce, 0x51, 0x07, 0x1b, 0x52, 0x7e,
+ 0x34, 0x1d, 0xe0, 0x05, 0xea, 0x91, 0x69, 0xb2, 0x42, 0xeb, 0x04, 0x0f, 0xc2, 0xc4, 0x2c, 0xe1,
+ 0x25, 0x87, 0x33, 0xd7, 0x6b, 0xe3, 0x1b, 0xe0, 0x10, 0xd5, 0x63, 0x24, 0x63, 0xc8, 0x08, 0xcc,
+ 0xbb, 0xa6, 0x25, 0x5d, 0x6c, 0x92, 0x02, 0x7b, 0xd1, 0xb6, 0x24, 0xb1, 0x48, 0x7c, 0xc2, 0x25,
+ 0xb2, 0x59, 0x22, 0xbb, 0xd1, 0x2d, 0xa9, 0x3c, 0x89, 0xc9, 0x03, 0x46, 0x7c, 0x7c, 0x93, 0x74,
+ 0x72, 0x10, 0xed, 0xcc, 0x94, 0xb0, 0x6c, 0x72, 0x46, 0xd7, 0x16, 0xc9, 0x83, 0x01, 0xf1, 0x39,
+ 0x1e, 0x96, 0xdc, 0x11, 0x34, 0x02, 0x72, 0x93, 0xf5, 0x4b, 0xf8, 0xe6, 0xc2, 0x46, 0xb5, 0x24,
+ 0x4f, 0x47, 0x81, 0x0f, 0xa7, 0x8a, 0x67, 0x3a, 0x75, 0x62, 0xe3, 0x5b, 0x12, 0x0b, 0xb7, 0x2f,
+ 0xad, 0x56, 0xb4, 0x89, 0xc9, 0xaa, 0xc4, 0xf7, 0xa9, 0xeb, 0xe0, 0x11, 0x19, 0xf9, 0x7e, 0xb4,
+ 0x3d, 0xc9, 0x4c, 0x7a, 0x9e, 0xdd, 0xae, 0x12, 0xce, 0xa9, 0xd3, 0xf0, 0xf1, 0x76, 0x18, 0x9a,
+ 0x0a, 0x38, 0x77, 0x1d, 0x15, 0x7b, 0x41, 0xc6, 0x7e, 0x28, 0xbd, 0x98, 0x11, 0x24, 0x02, 0xdf,
+ 0xd1, 0x11, 0xf8, 0xd6, 0x0e, 0x97, 0x33, 0xb6, 0xd9, 0xf0, 0xf1, 0x4e, 0xe9, 0x2f, 0x13, 0xf8,
+ 0x94, 0x59, 0xbf, 0x14, 0x78, 0xb2, 0xe4, 0xfb, 0x24, 0x73, 0x00, 0x15, 0x80, 0x65, 0x55, 0x41,
+ 0xed, 0x87, 0x57, 0x57, 0x52, 0x22, 0xaa, 0x03, 0x52, 0xe7, 0x10, 0xda, 0x95, 0x2a, 0xb9, 0xe9,
+ 0xfb, 0x5e, 0x93, 0x99, 0x3e, 0x51, 0x52, 0x87, 0xa5, 0xd4, 0xd1, 0x74, 0x11, 0x62, 0x50, 0xa8,
+ 0x1d, 0xc9, 0xe4, 0x78, 0x0c, 0xed, 0x83, 0xe1, 0x2a, 0x37, 0xb9, 0x96, 0x2e, 0x4b, 0xe9, 0x93,
+ 0x68, 0x77, 0x0f, 0x5a, 0xe8, 0x2f, 0x64, 0xf4, 0x33, 0xd9, 0x2f, 0x92, 0xba, 0xbb, 0x42, 0x58,
+ 0x5b, 0xd6, 0xe8, 0x38, 0xdc, 0xb9, 0x17, 0x5c, 0x66, 0x29, 0xd7, 0xe3, 0xf0, 0x0e, 0x15, 0x88,
+ 0xf0, 0x77, 0x02, 0x56, 0x98, 0x25, 0x5c, 0xf7, 0xf6, 0x5d, 0x70, 0x73, 0x54, 0x09, 0x7f, 0xe0,
+ 0xf6, 0x99, 0xa2, 0x1b, 0x38, 0x9c, 0x30, 0x7c, 0x9f, 0xae, 0x72, 0x0a, 0x9a, 0xa1, 0xac, 0xb5,
+ 0x6a, 0x32, 0x52, 0x12, 0x49, 0xe2, 0xeb, 0xa2, 0x9e, 0xfd, 0x9e, 0x00, 0xc7, 0xd2, 0x89, 0x29,
+ 0xf0, 0x01, 0xcf, 0x76, 0x4d, 0x0b, 0x5f, 0x9f, 0x20, 0x0f, 0xa3, 0x1d, 0x10, 0xa9, 0x12, 0x1c,
+ 0x2a, 0x0c, 0x5d, 0x51, 0xe8, 0xbe, 0xf4, 0xf6, 0xac, 0x12, 0x7b, 0xb9, 0x26, 0x98, 0xd1, 0x84,
+ 0x5c, 0xa6, 0xe7, 0x66, 0x09, 0x3f, 0x17, 0x2c, 0xd9, 0xb4, 0x7e, 0x96, 0xb4, 0xf1, 0x06, 0x99,
+ 0x45, 0x66, 0x5e, 0xc5, 0xc0, 0x46, 0x59, 0xcd, 0x9d, 0xe9, 0x3d, 0x59, 0xa5, 0x0d, 0xa7, 0xb6,
+ 0x86, 0x6f, 0x84, 0xcd, 0x6b, 0x7a, 0xfb, 0x6f, 0x91, 0xe6, 0x3b, 0xd0, 0x4d, 0x69, 0x40, 0x2c,
+ 0xc5, 0xd6, 0xae, 0x93, 0x6e, 0xd2, 0xb2, 0x98, 0x98, 0xb6, 0xbb, 0xe0, 0x49, 0xa7, 0x1e, 0xef,
+ 0x96, 0xea, 0x99, 0xb5, 0x14, 0xc1, 0xc9, 0x7f, 0xe3, 0x83, 0xf0, 0x5a, 0x9e, 0x27, 0x8c, 0x2e,
+ 0xb7, 0x15, 0x74, 0x48, 0x42, 0x99, 0x61, 0x26, 0xff, 0x5b, 0xc8, 0x85, 0x9d, 0x81, 0xc7, 0xa4,
+ 0xbf, 0x4c, 0x8f, 0x16, 0xa9, 0xd7, 0x24, 0xec, 0x2c, 0x69, 0x9f, 0x37, 0xed, 0x80, 0xe0, 0x6d,
+ 0xb0, 0x5a, 0x44, 0x11, 0x4b, 0x73, 0x27, 0xa5, 0x5a, 0x66, 0x7d, 0x84, 0xbb, 0x39, 0x8b, 0x38,
+ 0x9c, 0xf2, 0x36, 0x3e, 0x05, 0xcf, 0x04, 0xc1, 0x10, 0x4b, 0x53, 0x77, 0xea, 0x41, 0xb5, 0x2b,
+ 0xfb, 0xca, 0x28, 0x4e, 0xdf, 0x2f, 0x07, 0xa3, 0x58, 0xcd, 0xf7, 0x76, 0x19, 0x31, 0x69, 0xea,
+ 0x5e, 0x78, 0xc4, 0x14, 0x5d, 0x9f, 0x16, 0xdd, 0x56, 0x8b, 0x72, 0x3c, 0x0b, 0xeb, 0xc4, 0x44,
+ 0x8b, 0x38, 0x1c, 0xdf, 0x2f, 0x75, 0x32, 0xef, 0x10, 0x41, 0x89, 0x04, 0xf0, 0x1c, 0xbc, 0x36,
+ 0xea, 0x79, 0x54, 0xf3, 0xf7, 0x49, 0x91, 0x13, 0xe9, 0xdc, 0xa6, 0xc9, 0x52, 0xd0, 0x98, 0xa7,
+ 0xce, 0xa5, 0x69, 0x52, 0xa7, 0xe1, 0xdc, 0xb7, 0x0a, 0x1b, 0x9f, 0x48, 0x0e, 0x92, 0xa3, 0x5d,
+ 0x0c, 0x66, 0x09, 0x0f, 0x87, 0x0f, 0x26, 0x85, 0x21, 0x65, 0x90, 0x4d, 0x44, 0xc3, 0x11, 0xb9,
+ 0x5c, 0x30, 0x9e, 0x04, 0x02, 0x4d, 0x50, 0xae, 0x87, 0x1b, 0x05, 0xe3, 0x09, 0x60, 0x39, 0x35,
+ 0x34, 0xef, 0x36, 0x70, 0x53, 0x0a, 0x1d, 0x46, 0x7b, 0x40, 0xa6, 0x4c, 0x5a, 0x2e, 0x6b, 0x2f,
+ 0x12, 0xd3, 0xc2, 0x8e, 0x94, 0xbb, 0x35, 0x3d, 0x0c, 0x32, 0x28, 0x76, 0xa5, 0xe2, 0x11, 0x34,
+ 0xda, 0x03, 0xbb, 0xc0, 0x28, 0x27, 0xd8, 0x93, 0x92, 0xdd, 0xbc, 0xcf, 0xd8, 0xa6, 0xdf, 0x8c,
+ 0x06, 0xd7, 0x83, 0x12, 0x3d, 0x9a, 0x96, 0x2d, 0x71, 0xd1, 0xc2, 0x41, 0x2b, 0x35, 0x43, 0x9e,
+ 0x19, 0x90, 0xeb, 0x38, 0x96, 0xae, 0xb8, 0x82, 0x63, 0xf2, 0x59, 0x75, 0x3c, 0x1a, 0x4b, 0xbf,
+ 0x16, 0x12, 0xb2, 0x6a, 0x6b, 0xdf, 0x2d, 0x35, 0x33, 0xe9, 0x2b, 0x52, 0x61, 0xef, 0x81, 0x77,
+ 0xa4, 0xc2, 0xe4, 0x98, 0x9a, 0x80, 0xdf, 0x88, 0x8a, 0x8a, 0xc7, 0xd5, 0x3d, 0x52, 0x2e, 0xb3,
+ 0xd0, 0x31, 0x28, 0xc6, 0xd6, 0x69, 0xa9, 0x96, 0x29, 0x63, 0xd2, 0xa7, 0x1a, 0x2c, 0x67, 0x24,
+ 0x7a, 0x14, 0xed, 0x85, 0xd0, 0xf4, 0x14, 0x9a, 0x94, 0xf0, 0x38, 0x3a, 0x00, 0xc1, 0x1d, 0xd3,
+ 0x68, 0x0a, 0x0e, 0x76, 0xa1, 0x54, 0x4e, 0xd4, 0xb1, 0x08, 0xcf, 0xd8, 0x85, 0x52, 0x59, 0x11,
+ 0xd3, 0xf0, 0x91, 0x75, 0xa1, 0x54, 0x96, 0xd5, 0x2b, 0xc1, 0x6f, 0x4c, 0x09, 0x10, 0xab, 0xb6,
+ 0x86, 0x67, 0xe0, 0x01, 0xb4, 0x50, 0x2a, 0x4f, 0x93, 0x3a, 0x6b, 0x7b, 0x5c, 0xe5, 0x78, 0x16,
+ 0xae, 0x5d, 0x0c, 0x12, 0x4b, 0xa1, 0xf3, 0xf0, 0xd2, 0xce, 0x53, 0xff, 0x52, 0x22, 0x3f, 0x06,
+ 0x07, 0x27, 0x28, 0x85, 0xf8, 0x5d, 0xce, 0xc3, 0xd4, 0xbf, 0x24, 0x33, 0xe4, 0xf0, 0xe9, 0x4c,
+ 0x11, 0x61, 0x8a, 0x81, 0x54, 0xc9, 0x34, 0xa4, 0x62, 0x54, 0xd4, 0x2b, 0x52, 0x2a, 0xb3, 0x1f,
+ 0x05, 0xd6, 0xb1, 0x80, 0xab, 0x70, 0xd5, 0x04, 0x9b, 0xee, 0x8c, 0x35, 0xf8, 0x8d, 0x22, 0x4b,
+ 0x11, 0xef, 0xaf, 0x36, 0x3c, 0x50, 0x05, 0x17, 0x43, 0x97, 0xf5, 0xc9, 0x3d, 0x95, 0x48, 0x8d,
+ 0x5c, 0x76, 0xfd, 0x44, 0x61, 0x1f, 0xcb, 0x69, 0xb1, 0x91, 0x0e, 0x4e, 0x41, 0x8f, 0xe7, 0xf4,
+ 0x3b, 0x6c, 0x5b, 0x07, 0x24, 0x8b, 0x7b, 0x25, 0xa7, 0x5f, 0x16, 0xdb, 0x41, 0x26, 0x2c, 0xef,
+ 0x27, 0x72, 0x7a, 0x34, 0xec, 0x82, 0xc2, 0x8a, 0xe3, 0xff, 0x64, 0x4e, 0x8f, 0x86, 0x42, 0x07,
+ 0x19, 0x63, 0x9f, 0xca, 0xe9, 0xfe, 0x49, 0x9f, 0xe2, 0x38, 0xb1, 0x6d, 0x93, 0xc9, 0xe0, 0x7e,
+ 0x9e, 0xd3, 0x0d, 0xb9, 0x1b, 0xa0, 0x6a, 0x6b, 0x15, 0x4f, 0xcd, 0x86, 0x5f, 0x74, 0x89, 0x50,
+ 0xa2, 0x89, 0xd2, 0xfd, 0xb2, 0x4b, 0x84, 0x92, 0x54, 0xd8, 0xaf, 0x94, 0xe0, 0xf1, 0xf4, 0x91,
+ 0x5a, 0x62, 0x45, 0x46, 0xc2, 0x23, 0x72, 0x5d, 0x1c, 0x38, 0x2b, 0x1e, 0x7e, 0x2e, 0xa7, 0xa7,
+ 0xd8, 0x4e, 0x00, 0x3f, 0x67, 0xb6, 0xc5, 0x4b, 0xb7, 0xe2, 0xe1, 0xe7, 0x73, 0x7a, 0xea, 0x8c,
+ 0x82, 0x20, 0x6f, 0xc6, 0xf0, 0x0b, 0xbd, 0xe1, 0xb2, 0xe9, 0x98, 0x0d, 0x52, 0x59, 0x5e, 0x26,
+ 0xac, 0xe2, 0xe1, 0x17, 0x15, 0x7c, 0x3b, 0x3a, 0xd4, 0x35, 0x62, 0x71, 0xc6, 0xa7, 0x2b, 0xda,
+ 0xe6, 0xa5, 0x9c, 0xde, 0x11, 0x7b, 0xa0, 0x75, 0x20, 0xbc, 0xe2, 0x71, 0xea, 0x3a, 0x7e, 0xc5,
+ 0xc3, 0x2f, 0xf7, 0x0e, 0x26, 0xba, 0x45, 0xd7, 0x58, 0xe0, 0x8b, 0xc8, 0xaf, 0xf5, 0x16, 0x9e,
+ 0xb4, 0x6d, 0x77, 0x55, 0xb1, 0xaf, 0x28, 0xf6, 0x58, 0x7a, 0x10, 0x2b, 0x36, 0x2a, 0x72, 0x99,
+ 0xb0, 0x06, 0xa9, 0x78, 0xf8, 0xd5, 0xde, 0xca, 0x51, 0x4d, 0xa6, 0x4d, 0x6e, 0x56, 0x3c, 0xfc,
+ 0x5a, 0x6f, 0xe5, 0xa9, 0xa0, 0xe5, 0x55, 0x45, 0x03, 0x39, 0x75, 0xa1, 0xfc, 0x7a, 0x4e, 0xef,
+ 0xe4, 0x1d, 0x5d, 0x9a, 0x32, 0xdc, 0x0d, 0x6f, 0xe4, 0xf4, 0xb4, 0x49, 0xf7, 0x38, 0x73, 0x9d,
+ 0x44, 0xa3, 0xbd, 0x99, 0xd3, 0x83, 0x6b, 0x5b, 0x16, 0x53, 0xcc, 0x5b, 0x39, 0x7d, 0x48, 0xde,
+ 0x9a, 0x65, 0xe4, 0x26, 0x78, 0xbb, 0xdb, 0x56, 0x97, 0x48, 0x18, 0xd2, 0x3b, 0x5d, 0xf6, 0x53,
+ 0xd1, 0x64, 0x96, 0xe9, 0xb8, 0x52, 0xea, 0x1b, 0x79, 0xb8, 0x49, 0x25, 0x15, 0xbf, 0x69, 0x9f,
+ 0xca, 0xeb, 0x0f, 0x03, 0x7b, 0x00, 0x30, 0xb5, 0xe3, 0xbf, 0xd9, 0x5b, 0x34, 0x06, 0xbf, 0x95,
+ 0x87, 0xb7, 0x68, 0x2c, 0xaa, 0xaa, 0xf2, 0xed, 0x3c, 0xbc, 0x45, 0x25, 0xa9, 0xb0, 0xef, 0xe4,
+ 0xf5, 0x3b, 0x76, 0x04, 0x4c, 0x47, 0x9c, 0x07, 0xae, 0xe6, 0xe1, 0x45, 0x4d, 0x54, 0x26, 0xac,
+ 0xe0, 0x77, 0x95, 0x58, 0x66, 0xd6, 0x54, 0x1c, 0xee, 0xda, 0x6e, 0xa3, 0x9d, 0x08, 0xef, 0x37,
+ 0x5d, 0x24, 0x15, 0xaa, 0xb8, 0xdf, 0xe6, 0xf5, 0x15, 0x7e, 0xb4, 0x8b, 0x64, 0x5c, 0x9d, 0xdf,
+ 0xe5, 0xe1, 0x73, 0x9a, 0x82, 0x63, 0xf2, 0xf7, 0xeb, 0xc8, 0x86, 0x8b, 0xcd, 0x4c, 0xc7, 0x5f,
+ 0x26, 0x0c, 0xff, 0x41, 0xc9, 0x66, 0xc6, 0x58, 0x12, 0x26, 0x96, 0xc6, 0xff, 0xa8, 0xb4, 0xc7,
+ 0xd1, 0xfe, 0x6e, 0xf8, 0x05, 0xca, 0x9b, 0x16, 0x33, 0x57, 0x2b, 0x4e, 0x03, 0xff, 0x49, 0xc9,
+ 0x9f, 0x44, 0xb7, 0x76, 0x97, 0x4f, 0x5a, 0xfc, 0x39, 0xaf, 0x3f, 0x3e, 0x74, 0xb5, 0xa8, 0x38,
+ 0x7c, 0xce, 0x5a, 0x24, 0x0d, 0xea, 0x8b, 0xbb, 0xfc, 0x1b, 0x79, 0x78, 0xae, 0xa5, 0x7d, 0xa4,
+ 0x6d, 0xfe, 0xa2, 0xbc, 0x9c, 0x42, 0x47, 0x7a, 0x7a, 0x99, 0xb4, 0xac, 0x49, 0xce, 0x19, 0x5d,
+ 0x0a, 0x38, 0xf1, 0xf1, 0x5f, 0x95, 0xab, 0xbb, 0xd0, 0xb1, 0x75, 0x5c, 0xa5, 0x0d, 0xff, 0x96,
+ 0xd7, 0xa7, 0x85, 0xd4, 0x26, 0x58, 0xa4, 0x9e, 0x67, 0x93, 0x44, 0xef, 0x3c, 0x3c, 0x00, 0xbf,
+ 0x6f, 0x23, 0x50, 0x51, 0x1f, 0x1d, 0x80, 0x3b, 0x3b, 0xa2, 0xe4, 0x6e, 0x7e, 0x64, 0x00, 0xde,
+ 0x25, 0x31, 0x14, 0x36, 0xf6, 0xa3, 0x0a, 0x7b, 0x37, 0x1a, 0x4b, 0xdd, 0x9f, 0x5d, 0x87, 0x30,
+ 0x37, 0x5c, 0x79, 0xb3, 0x2e, 0x66, 0xfc, 0x9c, 0x43, 0xb9, 0x1a, 0x00, 0x7f, 0x1f, 0xd0, 0x17,
+ 0xbb, 0x03, 0xeb, 0x1a, 0x89, 0x6d, 0xf6, 0x0f, 0x65, 0x90, 0xa9, 0x5c, 0x87, 0x41, 0x95, 0xf0,
+ 0x39, 0xc7, 0x0b, 0xb4, 0xa7, 0x7f, 0x2a, 0xc3, 0xf5, 0xc2, 0x53, 0x86, 0xc2, 0xdb, 0xbf, 0x94,
+ 0xd1, 0x19, 0x74, 0x6a, 0x9d, 0xf0, 0xbc, 0x80, 0xfb, 0xe7, 0x08, 0x6b, 0x05, 0xdc, 0x14, 0x7f,
+ 0x50, 0x6e, 0xff, 0xad, 0x14, 0x4e, 0xa3, 0xdb, 0xfe, 0x3f, 0x05, 0xe1, 0xff, 0x4d, 0x65, 0x7d,
+ 0x37, 0x3a, 0xbe, 0xbe, 0xf5, 0x79, 0xea, 0x50, 0xe5, 0xf7, 0x2d, 0x65, 0x79, 0x07, 0x3a, 0xdc,
+ 0x9f, 0xa5, 0xf0, 0xf7, 0xb6, 0xb2, 0xba, 0x07, 0x9d, 0xec, 0x69, 0x35, 0x69, 0xdb, 0x51, 0xc0,
+ 0x55, 0xa2, 0x2b, 0xfc, 0x4e, 0xbf, 0x4b, 0x93, 0x34, 0x16, 0x5e, 0xff, 0xd3, 0x6f, 0x96, 0xe2,
+ 0x98, 0x10, 0xf0, 0xc4, 0xa2, 0xfe, 0xb7, 0xdf, 0x2c, 0xb5, 0xa5, 0xf0, 0xf7, 0x7e, 0xa3, 0x4f,
+ 0x7f, 0x93, 0xb6, 0x5d, 0x09, 0x78, 0x22, 0xc5, 0x0f, 0x18, 0x7d, 0xfa, 0xd3, 0x96, 0xc2, 0xdf,
+ 0x07, 0xfb, 0xf5, 0x17, 0x7e, 0xf4, 0x49, 0x36, 0xed, 0x87, 0xfa, 0xf5, 0xa7, 0x2d, 0x85, 0xbf,
+ 0x0f, 0xf7, 0x6b, 0x35, 0x43, 0x1d, 0xd3, 0x56, 0xbe, 0x3e, 0x62, 0xc0, 0x03, 0x13, 0xb6, 0x12,
+ 0x7e, 0x1e, 0x52, 0x16, 0x77, 0xa2, 0xa3, 0x9d, 0x16, 0x67, 0x49, 0x7b, 0xae, 0x65, 0x36, 0x48,
+ 0x69, 0xcd, 0x73, 0x19, 0x4f, 0x6e, 0xfa, 0x47, 0x94, 0x5d, 0x66, 0xd0, 0x76, 0xb3, 0x13, 0xbe,
+ 0x1e, 0xed, 0x99, 0x93, 0xb2, 0xa9, 0xb6, 0x9d, 0x7a, 0x95, 0x13, 0x7d, 0x5a, 0xff, 0x58, 0xcf,
+ 0x9c, 0xb2, 0x56, 0xc2, 0xcf, 0xc7, 0x0d, 0x78, 0xa0, 0x77, 0x5a, 0xa4, 0x8a, 0xf7, 0x98, 0x32,
+ 0xbb, 0x0d, 0x1d, 0xec, 0xc3, 0x4c, 0x78, 0x7a, 0xdc, 0x80, 0x47, 0x79, 0x64, 0x92, 0x18, 0xe5,
+ 0x9f, 0x36, 0xe0, 0x51, 0x1e, 0x81, 0x8a, 0xfa, 0x8c, 0x01, 0x9f, 0x7a, 0xb4, 0xdc, 0x05, 0x93,
+ 0xd7, 0x9b, 0xe2, 0xbd, 0xfe, 0x59, 0x03, 0x9e, 0xe7, 0x11, 0xa9, 0xb1, 0xcf, 0x19, 0xf0, 0xc5,
+ 0x24, 0xfc, 0x50, 0x14, 0xb1, 0xd3, 0xd4, 0x6c, 0xa8, 0x0a, 0x7c, 0xde, 0x80, 0xef, 0x50, 0x19,
+ 0x5c, 0x64, 0xfe, 0x05, 0xa5, 0x9c, 0x39, 0x2d, 0xeb, 0x50, 0x6b, 0x6b, 0x67, 0x89, 0xfe, 0xa9,
+ 0xe3, 0x8b, 0x06, 0x7c, 0x60, 0x49, 0xd3, 0x42, 0xf7, 0x4b, 0x3d, 0x7b, 0x64, 0x9e, 0xae, 0x90,
+ 0x45, 0xb2, 0xcc, 0x88, 0xdf, 0xac, 0x72, 0x93, 0xe9, 0x6e, 0x7c, 0xd2, 0x80, 0x8f, 0x16, 0xb0,
+ 0x95, 0xf0, 0xf3, 0x65, 0xa3, 0xd7, 0xab, 0x24, 0x65, 0x11, 0xb7, 0xe2, 0x57, 0x94, 0x1b, 0xf0,
+ 0x4d, 0x97, 0x31, 0x12, 0x5e, 0xbe, 0xda, 0x6f, 0x36, 0xa9, 0x46, 0xfc, 0x5a, 0xbf, 0xd9, 0xe8,
+ 0x3e, 0xfc, 0xba, 0x01, 0x7f, 0x0a, 0x28, 0x65, 0x6e, 0xdc, 0xd7, 0x0c, 0xf8, 0x7e, 0x50, 0x4a,
+ 0xde, 0xb7, 0x5f, 0x31, 0xf4, 0x67, 0x96, 0x2d, 0x19, 0x48, 0x9e, 0x26, 0x5e, 0xed, 0xd2, 0x27,
+ 0x25, 0xd7, 0x17, 0x07, 0xe9, 0xe4, 0xbb, 0xf3, 0xd7, 0x06, 0x7c, 0xff, 0x49, 0xa0, 0x22, 0x81,
+ 0xd7, 0x0c, 0xf8, 0xfe, 0x53, 0x4a, 0x7c, 0x58, 0x78, 0xbd, 0xcb, 0xee, 0x98, 0xa2, 0x8e, 0xe9,
+ 0xd4, 0x93, 0x07, 0xa7, 0x1f, 0x0c, 0xc2, 0xbb, 0x43, 0x92, 0x0a, 0xfb, 0xe1, 0x20, 0x7c, 0x73,
+ 0x89, 0x05, 0xe3, 0xa2, 0xfc, 0x68, 0x10, 0xbe, 0xb9, 0x48, 0x36, 0x06, 0x7f, 0x3c, 0x08, 0xdf,
+ 0xae, 0x24, 0x28, 0x2b, 0xf8, 0x74, 0x6f, 0xb9, 0xf8, 0x76, 0xf5, 0x93, 0x41, 0xf8, 0xaa, 0xa1,
+ 0x40, 0x79, 0x18, 0x2f, 0xfb, 0x0d, 0xfc, 0xcc, 0x20, 0x7c, 0xd5, 0x90, 0x68, 0x85, 0x59, 0x11,
+ 0xf7, 0x6c, 0x6f, 0xdf, 0xd1, 0x8f, 0xb4, 0x02, 0xfc, 0x69, 0x6f, 0x41, 0xbd, 0x30, 0x3f, 0x93,
+ 0x31, 0x4e, 0x9c, 0x46, 0xd7, 0xaf, 0x52, 0x46, 0x2e, 0x52, 0x67, 0x78, 0xef, 0x78, 0xf4, 0x4b,
+ 0xff, 0xb8, 0xfa, 0xa5, 0x7f, 0xbc, 0xe4, 0x04, 0xad, 0xf0, 0xe7, 0x12, 0xf9, 0x95, 0x60, 0xe4,
+ 0xb9, 0x87, 0x06, 0x46, 0x73, 0x63, 0x43, 0x8b, 0xd7, 0x09, 0x9b, 0x39, 0x67, 0xe2, 0x5e, 0x34,
+ 0x14, 0x5a, 0xbb, 0x01, 0xef, 0xc7, 0xfc, 0x79, 0x69, 0x1e, 0xba, 0xac, 0x04, 0x7c, 0x62, 0x16,
+ 0x6d, 0x0a, 0xed, 0x2d, 0x31, 0xad, 0xfa, 0x8c, 0xe1, 0x05, 0x29, 0xb2, 0x41, 0x58, 0x86, 0x63,
+ 0x6e, 0xce, 0x99, 0x98, 0x43, 0x9b, 0x13, 0x42, 0x7d, 0x86, 0xf3, 0xa2, 0x54, 0xda, 0xa8, 0x95,
+ 0x44, 0x4c, 0x67, 0xd0, 0x0d, 0xa1, 0x14, 0xa7, 0x4e, 0xbb, 0x1f, 0x95, 0x97, 0xa4, 0x4a, 0x58,
+ 0x89, 0x1a, 0x75, 0xda, 0x13, 0xf3, 0xe8, 0xc6, 0x50, 0x61, 0xc9, 0x75, 0xb9, 0xed, 0x9a, 0x16,
+ 0x61, 0xfd, 0xe8, 0xbc, 0x2c, 0x75, 0xc2, 0x44, 0xa6, 0xb4, 0xe9, 0x44, 0x11, 0x85, 0x99, 0x5e,
+ 0x74, 0xdc, 0x8b, 0xcb, 0x7e, 0xab, 0x1f, 0xa5, 0x6b, 0x52, 0x29, 0xcc, 0x63, 0xc1, 0x9d, 0xf1,
+ 0x5b, 0x53, 0x77, 0xa0, 0xfd, 0x75, 0xb7, 0x35, 0xee, 0x9b, 0xdc, 0xf5, 0x9b, 0xd4, 0x36, 0x97,
+ 0x7c, 0xf5, 0xff, 0x79, 0xd8, 0x74, 0x49, 0x4b, 0x4d, 0x6d, 0xaa, 0x85, 0x7f, 0x94, 0x9d, 0xf3,
+ 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa3, 0x69, 0x67, 0x5d, 0x1f, 0x22, 0x00, 0x00,
+}
diff --git a/accounts/usbwallet/trezor/messages.proto b/accounts/usbwallet/trezor/messages.proto
new file mode 100644
index 0000000..3e0482e
--- /dev/null
+++ b/accounts/usbwallet/trezor/messages.proto
@@ -0,0 +1,264 @@
+// This file originates from the SatoshiLabs Trezor `common` repository at:
+// https://github.com/trezor/trezor-common/blob/master/protob/messages.proto
+// dated 28.05.2019, commit 893fd219d4a01bcffa0cd9cfa631856371ec5aa9.
+
+syntax = "proto2";
+package hw.trezor.messages;
+
+/**
+ * Messages for TREZOR communication
+ */
+
+// Sugar for easier handling in Java
+option java_package = "com.satoshilabs.trezor.lib.protobuf";
+option java_outer_classname = "TrezorMessage";
+
+import "google/protobuf/descriptor.proto";
+
+/**
+ * Options for specifying message direction and type of wire (normal/debug)
+ */
+extend google.protobuf.EnumValueOptions {
+ optional bool wire_in = 50002; // message can be transmitted via wire from PC to TREZOR
+ optional bool wire_out = 50003; // message can be transmitted via wire from TREZOR to PC
+ optional bool wire_debug_in = 50004; // message can be transmitted via debug wire from PC to TREZOR
+ optional bool wire_debug_out = 50005; // message can be transmitted via debug wire from TREZOR to PC
+ optional bool wire_tiny = 50006; // message is handled by TREZOR when the USB stack is in tiny mode
+ optional bool wire_bootloader = 50007; // message is only handled by TREZOR Bootloader
+ optional bool wire_no_fsm = 50008; // message is not handled by TREZOR unless the USB stack is in tiny mode
+}
+
+/**
+ * Mapping between TREZOR wire identifier (uint) and a protobuf message
+ */
+enum MessageType {
+
+ // Management
+ MessageType_Initialize = 0 [(wire_in) = true, (wire_tiny) = true];
+ MessageType_Ping = 1 [(wire_in) = true];
+ MessageType_Success = 2 [(wire_out) = true];
+ MessageType_Failure = 3 [(wire_out) = true];
+ MessageType_ChangePin = 4 [(wire_in) = true];
+ MessageType_WipeDevice = 5 [(wire_in) = true];
+ MessageType_GetEntropy = 9 [(wire_in) = true];
+ MessageType_Entropy = 10 [(wire_out) = true];
+ MessageType_LoadDevice = 13 [(wire_in) = true];
+ MessageType_ResetDevice = 14 [(wire_in) = true];
+ MessageType_Features = 17 [(wire_out) = true];
+ MessageType_PinMatrixRequest = 18 [(wire_out) = true];
+ MessageType_PinMatrixAck = 19 [(wire_in) = true, (wire_tiny) = true, (wire_no_fsm) = true];
+ MessageType_Cancel = 20 [(wire_in) = true, (wire_tiny) = true];
+ MessageType_ClearSession = 24 [(wire_in) = true];
+ MessageType_ApplySettings = 25 [(wire_in) = true];
+ MessageType_ButtonRequest = 26 [(wire_out) = true];
+ MessageType_ButtonAck = 27 [(wire_in) = true, (wire_tiny) = true, (wire_no_fsm) = true];
+ MessageType_ApplyFlags = 28 [(wire_in) = true];
+ MessageType_BackupDevice = 34 [(wire_in) = true];
+ MessageType_EntropyRequest = 35 [(wire_out) = true];
+ MessageType_EntropyAck = 36 [(wire_in) = true];
+ MessageType_PassphraseRequest = 41 [(wire_out) = true];
+ MessageType_PassphraseAck = 42 [(wire_in) = true, (wire_tiny) = true, (wire_no_fsm) = true];
+ MessageType_PassphraseStateRequest = 77 [(wire_out) = true];
+ MessageType_PassphraseStateAck = 78 [(wire_in) = true, (wire_tiny) = true, (wire_no_fsm) = true];
+ MessageType_RecoveryDevice = 45 [(wire_in) = true];
+ MessageType_WordRequest = 46 [(wire_out) = true];
+ MessageType_WordAck = 47 [(wire_in) = true];
+ MessageType_GetFeatures = 55 [(wire_in) = true];
+ MessageType_SetU2FCounter = 63 [(wire_in) = true];
+
+ // Bootloader
+ MessageType_FirmwareErase = 6 [(wire_in) = true, (wire_bootloader) = true];
+ MessageType_FirmwareUpload = 7 [(wire_in) = true, (wire_bootloader) = true];
+ MessageType_FirmwareRequest = 8 [(wire_out) = true, (wire_bootloader) = true];
+ MessageType_SelfTest = 32 [(wire_in) = true, (wire_bootloader) = true];
+
+ // Bitcoin
+ MessageType_GetPublicKey = 11 [(wire_in) = true];
+ MessageType_PublicKey = 12 [(wire_out) = true];
+ MessageType_SignTx = 15 [(wire_in) = true];
+ MessageType_TxRequest = 21 [(wire_out) = true];
+ MessageType_TxAck = 22 [(wire_in) = true];
+ MessageType_GetAddress = 29 [(wire_in) = true];
+ MessageType_Address = 30 [(wire_out) = true];
+ MessageType_SignMessage = 38 [(wire_in) = true];
+ MessageType_VerifyMessage = 39 [(wire_in) = true];
+ MessageType_MessageSignature = 40 [(wire_out) = true];
+
+ // Crypto
+ MessageType_CipherKeyValue = 23 [(wire_in) = true];
+ MessageType_CipheredKeyValue = 48 [(wire_out) = true];
+ MessageType_SignIdentity = 53 [(wire_in) = true];
+ MessageType_SignedIdentity = 54 [(wire_out) = true];
+ MessageType_GetECDHSessionKey = 61 [(wire_in) = true];
+ MessageType_ECDHSessionKey = 62 [(wire_out) = true];
+ MessageType_CosiCommit = 71 [(wire_in) = true];
+ MessageType_CosiCommitment = 72 [(wire_out) = true];
+ MessageType_CosiSign = 73 [(wire_in) = true];
+ MessageType_CosiSignature = 74 [(wire_out) = true];
+
+ // Debug
+ MessageType_DebugLinkDecision = 100 [(wire_debug_in) = true, (wire_tiny) = true, (wire_no_fsm) = true];
+ MessageType_DebugLinkGetState = 101 [(wire_debug_in) = true, (wire_tiny) = true];
+ MessageType_DebugLinkState = 102 [(wire_debug_out) = true];
+ MessageType_DebugLinkStop = 103 [(wire_debug_in) = true];
+ MessageType_DebugLinkLog = 104 [(wire_debug_out) = true];
+ MessageType_DebugLinkMemoryRead = 110 [(wire_debug_in) = true];
+ MessageType_DebugLinkMemory = 111 [(wire_debug_out) = true];
+ MessageType_DebugLinkMemoryWrite = 112 [(wire_debug_in) = true];
+ MessageType_DebugLinkFlashErase = 113 [(wire_debug_in) = true];
+
+ // Ethereum
+ MessageType_EthereumGetPublicKey = 450 [(wire_in) = true];
+ MessageType_EthereumPublicKey = 451 [(wire_out) = true];
+ MessageType_EthereumGetAddress = 56 [(wire_in) = true];
+ MessageType_EthereumAddress = 57 [(wire_out) = true];
+ MessageType_EthereumSignTx = 58 [(wire_in) = true];
+ MessageType_EthereumTxRequest = 59 [(wire_out) = true];
+ MessageType_EthereumTxAck = 60 [(wire_in) = true];
+ MessageType_EthereumSignMessage = 64 [(wire_in) = true];
+ MessageType_EthereumVerifyMessage = 65 [(wire_in) = true];
+ MessageType_EthereumMessageSignature = 66 [(wire_out) = true];
+
+ // NEM
+ MessageType_NEMGetAddress = 67 [(wire_in) = true];
+ MessageType_NEMAddress = 68 [(wire_out) = true];
+ MessageType_NEMSignTx = 69 [(wire_in) = true];
+ MessageType_NEMSignedTx = 70 [(wire_out) = true];
+ MessageType_NEMDecryptMessage = 75 [(wire_in) = true];
+ MessageType_NEMDecryptedMessage = 76 [(wire_out) = true];
+
+ // Lisk
+ MessageType_LiskGetAddress = 114 [(wire_in) = true];
+ MessageType_LiskAddress = 115 [(wire_out) = true];
+ MessageType_LiskSignTx = 116 [(wire_in) = true];
+ MessageType_LiskSignedTx = 117 [(wire_out) = true];
+ MessageType_LiskSignMessage = 118 [(wire_in) = true];
+ MessageType_LiskMessageSignature = 119 [(wire_out) = true];
+ MessageType_LiskVerifyMessage = 120 [(wire_in) = true];
+ MessageType_LiskGetPublicKey = 121 [(wire_in) = true];
+ MessageType_LiskPublicKey = 122 [(wire_out) = true];
+
+ // Tezos
+ MessageType_TezosGetAddress = 150 [(wire_in) = true];
+ MessageType_TezosAddress = 151 [(wire_out) = true];
+ MessageType_TezosSignTx = 152 [(wire_in) = true];
+ MessageType_TezosSignedTx = 153 [(wire_out) = true];
+ MessageType_TezosGetPublicKey = 154 [(wire_in) = true];
+ MessageType_TezosPublicKey = 155 [(wire_out) = true];
+
+ // Stellar
+ MessageType_StellarSignTx = 202 [(wire_in) = true];
+ MessageType_StellarTxOpRequest = 203 [(wire_out) = true];
+ MessageType_StellarGetAddress = 207 [(wire_in) = true];
+ MessageType_StellarAddress = 208 [(wire_out) = true];
+ MessageType_StellarCreateAccountOp = 210 [(wire_in) = true];
+ MessageType_StellarPaymentOp = 211 [(wire_in) = true];
+ MessageType_StellarPathPaymentOp = 212 [(wire_in) = true];
+ MessageType_StellarManageOfferOp = 213 [(wire_in) = true];
+ MessageType_StellarCreatePassiveOfferOp = 214 [(wire_in) = true];
+ MessageType_StellarSetOptionsOp = 215 [(wire_in) = true];
+ MessageType_StellarChangeTrustOp = 216 [(wire_in) = true];
+ MessageType_StellarAllowTrustOp = 217 [(wire_in) = true];
+ MessageType_StellarAccountMergeOp = 218 [(wire_in) = true];
+ // omitted: StellarInflationOp is not a supported operation, would be 219
+ MessageType_StellarManageDataOp = 220 [(wire_in) = true];
+ MessageType_StellarBumpSequenceOp = 221 [(wire_in) = true];
+ MessageType_StellarSignedTx = 230 [(wire_out) = true];
+
+ // TRON
+ MessageType_TronGetAddress = 250 [(wire_in) = true];
+ MessageType_TronAddress = 251 [(wire_out) = true];
+ MessageType_TronSignTx = 252 [(wire_in) = true];
+ MessageType_TronSignedTx = 253 [(wire_out) = true];
+
+ // Cardano
+ // dropped Sign/VerifyMessage ids 300-302
+ MessageType_CardanoSignTx = 303 [(wire_in) = true];
+ MessageType_CardanoTxRequest = 304 [(wire_out) = true];
+ MessageType_CardanoGetPublicKey = 305 [(wire_in) = true];
+ MessageType_CardanoPublicKey = 306 [(wire_out) = true];
+ MessageType_CardanoGetAddress = 307 [(wire_in) = true];
+ MessageType_CardanoAddress = 308 [(wire_out) = true];
+ MessageType_CardanoTxAck = 309 [(wire_in) = true];
+ MessageType_CardanoSignedTx = 310 [(wire_out) = true];
+
+ // Ontology
+ MessageType_OntologyGetAddress = 350 [(wire_in) = true];
+ MessageType_OntologyAddress = 351 [(wire_out) = true];
+ MessageType_OntologyGetPublicKey = 352 [(wire_in) = true];
+ MessageType_OntologyPublicKey = 353 [(wire_out) = true];
+ MessageType_OntologySignTransfer = 354 [(wire_in) = true];
+ MessageType_OntologySignedTransfer = 355 [(wire_out) = true];
+ MessageType_OntologySignWithdrawOng = 356 [(wire_in) = true];
+ MessageType_OntologySignedWithdrawOng = 357 [(wire_out) = true];
+ MessageType_OntologySignOntIdRegister = 358 [(wire_in) = true];
+ MessageType_OntologySignedOntIdRegister = 359 [(wire_out) = true];
+ MessageType_OntologySignOntIdAddAttributes = 360 [(wire_in) = true];
+ MessageType_OntologySignedOntIdAddAttributes = 361 [(wire_out) = true];
+
+ // Ripple
+ MessageType_RippleGetAddress = 400 [(wire_in) = true];
+ MessageType_RippleAddress = 401 [(wire_out) = true];
+ MessageType_RippleSignTx = 402 [(wire_in) = true];
+ MessageType_RippleSignedTx = 403 [(wire_in) = true];
+
+ // Monero
+ MessageType_MoneroTransactionInitRequest = 501 [(wire_out) = true];
+ MessageType_MoneroTransactionInitAck = 502 [(wire_out) = true];
+ MessageType_MoneroTransactionSetInputRequest = 503 [(wire_out) = true];
+ MessageType_MoneroTransactionSetInputAck = 504 [(wire_out) = true];
+ MessageType_MoneroTransactionInputsPermutationRequest = 505 [(wire_out) = true];
+ MessageType_MoneroTransactionInputsPermutationAck = 506 [(wire_out) = true];
+ MessageType_MoneroTransactionInputViniRequest = 507 [(wire_out) = true];
+ MessageType_MoneroTransactionInputViniAck = 508 [(wire_out) = true];
+ MessageType_MoneroTransactionAllInputsSetRequest = 509 [(wire_out) = true];
+ MessageType_MoneroTransactionAllInputsSetAck = 510 [(wire_out) = true];
+ MessageType_MoneroTransactionSetOutputRequest = 511 [(wire_out) = true];
+ MessageType_MoneroTransactionSetOutputAck = 512 [(wire_out) = true];
+ MessageType_MoneroTransactionAllOutSetRequest = 513 [(wire_out) = true];
+ MessageType_MoneroTransactionAllOutSetAck = 514 [(wire_out) = true];
+ MessageType_MoneroTransactionSignInputRequest = 515 [(wire_out) = true];
+ MessageType_MoneroTransactionSignInputAck = 516 [(wire_out) = true];
+ MessageType_MoneroTransactionFinalRequest = 517 [(wire_out) = true];
+ MessageType_MoneroTransactionFinalAck = 518 [(wire_out) = true];
+ MessageType_MoneroKeyImageExportInitRequest = 530 [(wire_out) = true];
+ MessageType_MoneroKeyImageExportInitAck = 531 [(wire_out) = true];
+ MessageType_MoneroKeyImageSyncStepRequest = 532 [(wire_out) = true];
+ MessageType_MoneroKeyImageSyncStepAck = 533 [(wire_out) = true];
+ MessageType_MoneroKeyImageSyncFinalRequest = 534 [(wire_out) = true];
+ MessageType_MoneroKeyImageSyncFinalAck = 535 [(wire_out) = true];
+ MessageType_MoneroGetAddress = 540 [(wire_in) = true];
+ MessageType_MoneroAddress = 541 [(wire_out) = true];
+ MessageType_MoneroGetWatchKey = 542 [(wire_in) = true];
+ MessageType_MoneroWatchKey = 543 [(wire_out) = true];
+ MessageType_DebugMoneroDiagRequest = 546 [(wire_in) = true];
+ MessageType_DebugMoneroDiagAck = 547 [(wire_out) = true];
+ MessageType_MoneroGetTxKeyRequest = 550 [(wire_in) = true];
+ MessageType_MoneroGetTxKeyAck = 551 [(wire_out) = true];
+ MessageType_MoneroLiveRefreshStartRequest = 552 [(wire_in) = true];
+ MessageType_MoneroLiveRefreshStartAck = 553 [(wire_out) = true];
+ MessageType_MoneroLiveRefreshStepRequest = 554 [(wire_in) = true];
+ MessageType_MoneroLiveRefreshStepAck = 555 [(wire_out) = true];
+ MessageType_MoneroLiveRefreshFinalRequest = 556 [(wire_in) = true];
+ MessageType_MoneroLiveRefreshFinalAck = 557 [(wire_out) = true];
+
+ // EOS
+ MessageType_EosGetPublicKey = 600 [(wire_in) = true];
+ MessageType_EosPublicKey = 601 [(wire_out) = true];
+ MessageType_EosSignTx = 602 [(wire_in) = true];
+ MessageType_EosTxActionRequest = 603 [(wire_out) = true];
+ MessageType_EosTxActionAck = 604 [(wire_in) = true];
+ MessageType_EosSignedTx = 605 [(wire_out) = true];
+
+ // Binance
+ MessageType_BinanceGetAddress = 700 [(wire_in) = true];
+ MessageType_BinanceAddress = 701 [(wire_out) = true];
+ MessageType_BinanceGetPublicKey = 702 [(wire_in) = true];
+ MessageType_BinancePublicKey = 703 [(wire_out) = true];
+ MessageType_BinanceSignTx = 704 [(wire_in) = true];
+ MessageType_BinanceTxRequest = 705 [(wire_out) = true];
+ MessageType_BinanceTransferMsg = 706 [(wire_in) = true];
+ MessageType_BinanceOrderMsg = 707 [(wire_in) = true];
+ MessageType_BinanceCancelMsg = 708 [(wire_in) = true];
+ MessageType_BinanceSignedTx = 709 [(wire_out) = true];
+}
diff --git a/accounts/usbwallet/trezor/trezor.go b/accounts/usbwallet/trezor/trezor.go
new file mode 100644
index 0000000..7e756e6
--- /dev/null
+++ b/accounts/usbwallet/trezor/trezor.go
@@ -0,0 +1,70 @@
+// Copyright 2017 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 file contains the implementation for interacting with the Trezor hardware
+// wallets. The wire protocol spec can be found on the SatoshiLabs website:
+// https://wiki.trezor.io/Developers_guide-Message_Workflows
+
+// !!! STAHP !!!
+//
+// Before you touch the protocol files, you need to be aware of a breaking change
+// that occurred between firmware versions 1.7.3->1.8.0 (Model One) and 2.0.10->
+// 2.1.0 (Model T). The Ethereum address representation was changed from the 20
+// byte binary blob to a 42 byte hex string. The upstream protocol buffer files
+// only support the new format, so blindly pulling in a new spec will break old
+// devices!
+//
+// The Trezor devs had the foresight to add the string version as a new message
+// code instead of replacing the binary one. This means that the proto file can
+// actually define both the old and the new versions as optional. Please ensure
+// that you add back the old addresses everywhere (to avoid name clash. use the
+// addressBin and addressHex names).
+//
+// If in doubt, reach out to @karalabe.
+
+// To regenerate the protocol files in this package:
+// - Download the latest protoc https://github.com/protocolbuffers/protobuf/releases
+// - Build with the usual `./configure && make` and ensure it's on your $PATH
+// - Delete all the .proto and .pb.go files, pull in fresh ones from Trezor
+// - Grab the latest Go plugin `go get -u github.com/golang/protobuf/protoc-gen-go`
+// - Vendor in the latest Go plugin `govendor fetch github.com/golang/protobuf/...`
+
+//go:generate protoc -I/usr/local/include:. --go_out=import_path=trezor:. messages.proto messages-common.proto messages-management.proto messages-ethereum.proto
+
+// Package trezor contains the wire protocol.
+package trezor
+
+import (
+ "reflect"
+
+ "github.com/golang/protobuf/proto"
+)
+
+// Type returns the protocol buffer type number of a specific message. If the
+// message is nil, this method panics!
+func Type(msg proto.Message) uint16 {
+ return uint16(MessageType_value["MessageType_"+reflect.TypeOf(msg).Elem().Name()])
+}
+
+// Name returns the friendly message type name of a specific protocol buffer
+// type number.
+func Name(kind uint16) string {
+ name := MessageType_name[int32(kind)]
+ if len(name) < 12 {
+ return name
+ }
+ return name[12:]
+}
diff --git a/accounts/usbwallet/wallet.go b/accounts/usbwallet/wallet.go
new file mode 100644
index 0000000..3622c92
--- /dev/null
+++ b/accounts/usbwallet/wallet.go
@@ -0,0 +1,595 @@
+// Copyright 2017 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 usbwallet implements support for USB hardware wallets.
+package usbwallet
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "math/big"
+ "sync"
+ "time"
+
+ "github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/coreth/core/types"
+ ethereum "github.com/ava-labs/go-ethereum"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/crypto"
+ "github.com/ava-labs/go-ethereum/log"
+ "github.com/karalabe/usb"
+)
+
+// Maximum time between wallet health checks to detect USB unplugs.
+const heartbeatCycle = time.Second
+
+// Minimum time to wait between self derivation attempts, even it the user is
+// requesting accounts like crazy.
+const selfDeriveThrottling = time.Second
+
+// driver defines the vendor specific functionality hardware wallets instances
+// must implement to allow using them with the wallet lifecycle management.
+type driver interface {
+ // Status returns a textual status to aid the user in the current state of the
+ // wallet. It also returns an error indicating any failure the wallet might have
+ // encountered.
+ Status() (string, error)
+
+ // Open initializes access to a wallet instance. The passphrase parameter may
+ // or may not be used by the implementation of a particular wallet instance.
+ Open(device io.ReadWriter, passphrase string) error
+
+ // Close releases any resources held by an open wallet instance.
+ Close() error
+
+ // Heartbeat performs a sanity check against the hardware wallet to see if it
+ // is still online and healthy.
+ Heartbeat() error
+
+ // Derive sends a derivation request to the USB device and returns the Ethereum
+ // address located on that path.
+ Derive(path accounts.DerivationPath) (common.Address, error)
+
+ // SignTx sends the transaction to the USB device and waits for the user to confirm
+ // or deny the transaction.
+ SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error)
+}
+
+// wallet represents the common functionality shared by all USB hardware
+// wallets to prevent reimplementing the same complex maintenance mechanisms
+// for different vendors.
+type wallet struct {
+ hub *Hub // USB hub scanning
+ driver driver // Hardware implementation of the low level device operations
+ url *accounts.URL // Textual URL uniquely identifying this wallet
+
+ info usb.DeviceInfo // Known USB device infos about the wallet
+ device usb.Device // USB device advertising itself as a hardware wallet
+
+ accounts []accounts.Account // List of derive accounts pinned on the hardware wallet
+ paths map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations
+
+ deriveNextPaths []accounts.DerivationPath // Next derivation paths for account auto-discovery (multiple bases supported)
+ deriveNextAddrs []common.Address // Next derived account addresses for auto-discovery (multiple bases supported)
+ deriveChain ethereum.ChainStateReader // Blockchain state reader to discover used account with
+ deriveReq chan chan struct{} // Channel to request a self-derivation on
+ deriveQuit chan chan error // Channel to terminate the self-deriver with
+
+ healthQuit chan chan error
+
+ // Locking a hardware wallet is a bit special. Since hardware devices are lower
+ // performing, any communication with them might take a non negligible amount of
+ // time. Worse still, waiting for user confirmation can take arbitrarily long,
+ // but exclusive communication must be upheld during. Locking the entire wallet
+ // in the mean time however would stall any parts of the system that don't want
+ // to communicate, just read some state (e.g. list the accounts).
+ //
+ // As such, a hardware wallet needs two locks to function correctly. A state
+ // lock can be used to protect the wallet's software-side internal state, which
+ // must not be held exclusively during hardware communication. A communication
+ // lock can be used to achieve exclusive access to the device itself, this one
+ // however should allow "skipping" waiting for operations that might want to
+ // use the device, but can live without too (e.g. account self-derivation).
+ //
+ // Since we have two locks, it's important to know how to properly use them:
+ // - Communication requires the `device` to not change, so obtaining the
+ // commsLock should be done after having a stateLock.
+ // - Communication must not disable read access to the wallet state, so it
+ // must only ever hold a *read* lock to stateLock.
+ commsLock chan struct{} // Mutex (buf=1) for the USB comms without keeping the state locked
+ stateLock sync.RWMutex // Protects read and write access to the wallet struct fields
+
+ log log.Logger // Contextual logger to tag the base with its id
+}
+
+// URL implements accounts.Wallet, returning the URL of the USB hardware device.
+func (w *wallet) URL() accounts.URL {
+ return *w.url // Immutable, no need for a lock
+}
+
+// Status implements accounts.Wallet, returning a custom status message from the
+// underlying vendor-specific hardware wallet implementation.
+func (w *wallet) Status() (string, error) {
+ w.stateLock.RLock() // No device communication, state lock is enough
+ defer w.stateLock.RUnlock()
+
+ status, failure := w.driver.Status()
+ if w.device == nil {
+ return "Closed", failure
+ }
+ return status, failure
+}
+
+// Open implements accounts.Wallet, attempting to open a USB connection to the
+// hardware wallet.
+func (w *wallet) Open(passphrase string) error {
+ w.stateLock.Lock() // State lock is enough since there's no connection yet at this point
+ defer w.stateLock.Unlock()
+
+ // If the device was already opened once, refuse to try again
+ if w.paths != nil {
+ return accounts.ErrWalletAlreadyOpen
+ }
+ // Make sure the actual device connection is done only once
+ if w.device == nil {
+ device, err := w.info.Open()
+ if err != nil {
+ return err
+ }
+ w.device = device
+ w.commsLock = make(chan struct{}, 1)
+ w.commsLock <- struct{}{} // Enable lock
+ }
+ // Delegate device initialization to the underlying driver
+ if err := w.driver.Open(w.device, passphrase); err != nil {
+ return err
+ }
+ // Connection successful, start life-cycle management
+ w.paths = make(map[common.Address]accounts.DerivationPath)
+
+ w.deriveReq = make(chan chan struct{})
+ w.deriveQuit = make(chan chan error)
+ w.healthQuit = make(chan chan error)
+
+ go w.heartbeat()
+ go w.selfDerive()
+
+ // Notify anyone listening for wallet events that a new device is accessible
+ go w.hub.updateFeed.Send(accounts.WalletEvent{Wallet: w, Kind: accounts.WalletOpened})
+
+ return nil
+}
+
+// heartbeat is a health check loop for the USB wallets to periodically verify
+// whether they are still present or if they malfunctioned.
+func (w *wallet) heartbeat() {
+ w.log.Debug("USB wallet health-check started")
+ defer w.log.Debug("USB wallet health-check stopped")
+
+ // Execute heartbeat checks until termination or error
+ var (
+ errc chan error
+ err error
+ )
+ for errc == nil && err == nil {
+ // Wait until termination is requested or the heartbeat cycle arrives
+ select {
+ case errc = <-w.healthQuit:
+ // Termination requested
+ continue
+ case <-time.After(heartbeatCycle):
+ // Heartbeat time
+ }
+ // Execute a tiny data exchange to see responsiveness
+ w.stateLock.RLock()
+ if w.device == nil {
+ // Terminated while waiting for the lock
+ w.stateLock.RUnlock()
+ continue
+ }
+ <-w.commsLock // Don't lock state while resolving version
+ err = w.driver.Heartbeat()
+ w.commsLock <- struct{}{}
+ w.stateLock.RUnlock()
+
+ if err != nil {
+ w.stateLock.Lock() // Lock state to tear the wallet down
+ w.close()
+ w.stateLock.Unlock()
+ }
+ // Ignore non hardware related errors
+ err = nil
+ }
+ // In case of error, wait for termination
+ if err != nil {
+ w.log.Debug("USB wallet health-check failed", "err", err)
+ errc = <-w.healthQuit
+ }
+ errc <- err
+}
+
+// Close implements accounts.Wallet, closing the USB connection to the device.
+func (w *wallet) Close() error {
+ // Ensure the wallet was opened
+ w.stateLock.RLock()
+ hQuit, dQuit := w.healthQuit, w.deriveQuit
+ w.stateLock.RUnlock()
+
+ // Terminate the health checks
+ var herr error
+ if hQuit != nil {
+ errc := make(chan error)
+ hQuit <- errc
+ herr = <-errc // Save for later, we *must* close the USB
+ }
+ // Terminate the self-derivations
+ var derr error
+ if dQuit != nil {
+ errc := make(chan error)
+ dQuit <- errc
+ derr = <-errc // Save for later, we *must* close the USB
+ }
+ // Terminate the device connection
+ w.stateLock.Lock()
+ defer w.stateLock.Unlock()
+
+ w.healthQuit = nil
+ w.deriveQuit = nil
+ w.deriveReq = nil
+
+ if err := w.close(); err != nil {
+ return err
+ }
+ if herr != nil {
+ return herr
+ }
+ return derr
+}
+
+// close is the internal wallet closer that terminates the USB connection and
+// resets all the fields to their defaults.
+//
+// Note, close assumes the state lock is held!
+func (w *wallet) close() error {
+ // Allow duplicate closes, especially for health-check failures
+ if w.device == nil {
+ return nil
+ }
+ // Close the device, clear everything, then return
+ w.device.Close()
+ w.device = nil
+
+ w.accounts, w.paths = nil, nil
+ return w.driver.Close()
+}
+
+// Accounts implements accounts.Wallet, returning the list of accounts pinned to
+// the USB hardware wallet. If self-derivation was enabled, the account list is
+// periodically expanded based on current chain state.
+func (w *wallet) Accounts() []accounts.Account {
+ // Attempt self-derivation if it's running
+ reqc := make(chan struct{}, 1)
+ select {
+ case w.deriveReq <- reqc:
+ // Self-derivation request accepted, wait for it
+ <-reqc
+ default:
+ // Self-derivation offline, throttled or busy, skip
+ }
+ // Return whatever account list we ended up with
+ w.stateLock.RLock()
+ defer w.stateLock.RUnlock()
+
+ cpy := make([]accounts.Account, len(w.accounts))
+ copy(cpy, w.accounts)
+ return cpy
+}
+
+// selfDerive is an account derivation loop that upon request attempts to find
+// new non-zero accounts.
+func (w *wallet) selfDerive() {
+ w.log.Debug("USB wallet self-derivation started")
+ defer w.log.Debug("USB wallet self-derivation stopped")
+
+ // Execute self-derivations until termination or error
+ var (
+ reqc chan struct{}
+ errc chan error
+ err error
+ )
+ for errc == nil && err == nil {
+ // Wait until either derivation or termination is requested
+ select {
+ case errc = <-w.deriveQuit:
+ // Termination requested
+ continue
+ case reqc = <-w.deriveReq:
+ // Account discovery requested
+ }
+ // Derivation needs a chain and device access, skip if either unavailable
+ w.stateLock.RLock()
+ if w.device == nil || w.deriveChain == nil {
+ w.stateLock.RUnlock()
+ reqc <- struct{}{}
+ continue
+ }
+ select {
+ case <-w.commsLock:
+ default:
+ w.stateLock.RUnlock()
+ reqc <- struct{}{}
+ continue
+ }
+ // Device lock obtained, derive the next batch of accounts
+ var (
+ accs []accounts.Account
+ paths []accounts.DerivationPath
+
+ nextPaths = append([]accounts.DerivationPath{}, w.deriveNextPaths...)
+ nextAddrs = append([]common.Address{}, w.deriveNextAddrs...)
+
+ context = context.Background()
+ )
+ for i := 0; i < len(nextAddrs); i++ {
+ for empty := false; !empty; {
+ // Retrieve the next derived Ethereum account
+ if nextAddrs[i] == (common.Address{}) {
+ if nextAddrs[i], err = w.driver.Derive(nextPaths[i]); err != nil {
+ w.log.Warn("USB wallet account derivation failed", "err", err)
+ break
+ }
+ }
+ // Check the account's status against the current chain state
+ var (
+ balance *big.Int
+ nonce uint64
+ )
+ balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil)
+ if err != nil {
+ w.log.Warn("USB wallet balance retrieval failed", "err", err)
+ break
+ }
+ nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil)
+ if err != nil {
+ w.log.Warn("USB wallet nonce retrieval failed", "err", err)
+ break
+ }
+ // If the next account is empty, stop self-derivation, but add for the last base path
+ if balance.Sign() == 0 && nonce == 0 {
+ empty = true
+ if i < len(nextAddrs)-1 {
+ break
+ }
+ }
+ // We've just self-derived a new account, start tracking it locally
+ path := make(accounts.DerivationPath, len(nextPaths[i]))
+ copy(path[:], nextPaths[i][:])
+ paths = append(paths, path)
+
+ account := accounts.Account{
+ Address: nextAddrs[i],
+ URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
+ }
+ accs = append(accs, account)
+
+ // Display a log message to the user for new (or previously empty accounts)
+ if _, known := w.paths[nextAddrs[i]]; !known || (!empty && nextAddrs[i] == w.deriveNextAddrs[i]) {
+ w.log.Info("USB wallet discovered new account", "address", nextAddrs[i], "path", path, "balance", balance, "nonce", nonce)
+ }
+ // Fetch the next potential account
+ if !empty {
+ nextAddrs[i] = common.Address{}
+ nextPaths[i][len(nextPaths[i])-1]++
+ }
+ }
+ }
+ // Self derivation complete, release device lock
+ w.commsLock <- struct{}{}
+ w.stateLock.RUnlock()
+
+ // Insert any accounts successfully derived
+ w.stateLock.Lock()
+ for i := 0; i < len(accs); i++ {
+ if _, ok := w.paths[accs[i].Address]; !ok {
+ w.accounts = append(w.accounts, accs[i])
+ w.paths[accs[i].Address] = paths[i]
+ }
+ }
+ // Shift the self-derivation forward
+ // TODO(karalabe): don't overwrite changes from wallet.SelfDerive
+ w.deriveNextAddrs = nextAddrs
+ w.deriveNextPaths = nextPaths
+ w.stateLock.Unlock()
+
+ // Notify the user of termination and loop after a bit of time (to avoid trashing)
+ reqc <- struct{}{}
+ if err == nil {
+ select {
+ case errc = <-w.deriveQuit:
+ // Termination requested, abort
+ case <-time.After(selfDeriveThrottling):
+ // Waited enough, willing to self-derive again
+ }
+ }
+ }
+ // In case of error, wait for termination
+ if err != nil {
+ w.log.Debug("USB wallet self-derivation failed", "err", err)
+ errc = <-w.deriveQuit
+ }
+ errc <- err
+}
+
+// Contains implements accounts.Wallet, returning whether a particular account is
+// or is not pinned into this wallet instance. Although we could attempt to resolve
+// unpinned accounts, that would be an non-negligible hardware operation.
+func (w *wallet) Contains(account accounts.Account) bool {
+ w.stateLock.RLock()
+ defer w.stateLock.RUnlock()
+
+ _, exists := w.paths[account.Address]
+ return exists
+}
+
+// Derive implements accounts.Wallet, deriving a new account at the specific
+// derivation path. If pin is set to true, the account will be added to the list
+// of tracked accounts.
+func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
+ // Try to derive the actual account and update its URL if successful
+ w.stateLock.RLock() // Avoid device disappearing during derivation
+
+ if w.device == nil {
+ w.stateLock.RUnlock()
+ return accounts.Account{}, accounts.ErrWalletClosed
+ }
+ <-w.commsLock // Avoid concurrent hardware access
+ address, err := w.driver.Derive(path)
+ w.commsLock <- struct{}{}
+
+ w.stateLock.RUnlock()
+
+ // If an error occurred or no pinning was requested, return
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ account := accounts.Account{
+ Address: address,
+ URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
+ }
+ if !pin {
+ return account, nil
+ }
+ // Pinning needs to modify the state
+ w.stateLock.Lock()
+ defer w.stateLock.Unlock()
+
+ if _, ok := w.paths[address]; !ok {
+ w.accounts = append(w.accounts, account)
+ w.paths[address] = make(accounts.DerivationPath, len(path))
+ copy(w.paths[address], path)
+ }
+ return account, nil
+}
+
+// SelfDerive sets a base account derivation path from which the wallet attempts
+// to discover non zero accounts and automatically add them to list of tracked
+// accounts.
+//
+// Note, self derivaton will increment the last component of the specified path
+// opposed to decending into a child path to allow discovering accounts starting
+// from non zero components.
+//
+// Some hardware wallets switched derivation paths through their evolution, so
+// this method supports providing multiple bases to discover old user accounts
+// too. Only the last base will be used to derive the next empty account.
+//
+// You can disable automatic account discovery by calling SelfDerive with a nil
+// chain state reader.
+func (w *wallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) {
+ w.stateLock.Lock()
+ defer w.stateLock.Unlock()
+
+ w.deriveNextPaths = make([]accounts.DerivationPath, len(bases))
+ for i, base := range bases {
+ w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base))
+ copy(w.deriveNextPaths[i][:], base[:])
+ }
+ w.deriveNextAddrs = make([]common.Address, len(bases))
+ w.deriveChain = chain
+}
+
+// signHash implements accounts.Wallet, however signing arbitrary data is not
+// supported for hardware wallets, so this method will always return an error.
+func (w *wallet) signHash(account accounts.Account, hash []byte) ([]byte, error) {
+ return nil, accounts.ErrNotSupported
+}
+
+// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
+func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
+ return w.signHash(account, crypto.Keccak256(data))
+}
+
+// SignDataWithPassphrase implements accounts.Wallet, attempting to sign the given
+// data with the given account using passphrase as extra authentication.
+// Since USB wallets don't rely on passphrases, these are silently ignored.
+func (w *wallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
+ return w.SignData(account, mimeType, data)
+}
+
+func (w *wallet) SignText(account accounts.Account, text []byte) ([]byte, error) {
+ return w.signHash(account, accounts.TextHash(text))
+}
+
+// SignTx implements accounts.Wallet. It sends the transaction over to the Ledger
+// wallet to request a confirmation from the user. It returns either the signed
+// transaction or a failure if the user denied the transaction.
+//
+// Note, if the version of the Ethereum application running on the Ledger wallet is
+// too old to sign EIP-155 transactions, but such is requested nonetheless, an error
+// will be returned opposed to silently signing in Homestead mode.
+func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ w.stateLock.RLock() // Comms have own mutex, this is for the state fields
+ defer w.stateLock.RUnlock()
+
+ // If the wallet is closed, abort
+ if w.device == nil {
+ return nil, accounts.ErrWalletClosed
+ }
+ // Make sure the requested account is contained within
+ path, ok := w.paths[account.Address]
+ if !ok {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // All infos gathered and metadata checks out, request signing
+ <-w.commsLock
+ defer func() { w.commsLock <- struct{}{} }()
+
+ // Ensure the device isn't screwed with while user confirmation is pending
+ // TODO(karalabe): remove if hotplug lands on Windows
+ w.hub.commsLock.Lock()
+ w.hub.commsPend++
+ w.hub.commsLock.Unlock()
+
+ defer func() {
+ w.hub.commsLock.Lock()
+ w.hub.commsPend--
+ w.hub.commsLock.Unlock()
+ }()
+ // Sign the transaction and verify the sender to avoid hardware fault surprises
+ sender, signed, err := w.driver.SignTx(path, tx, chainID)
+ if err != nil {
+ return nil, err
+ }
+ if sender != account.Address {
+ return nil, fmt.Errorf("signer mismatch: expected %s, got %s", account.Address.Hex(), sender.Hex())
+ }
+ return signed, nil
+}
+
+// SignHashWithPassphrase implements accounts.Wallet, however signing arbitrary
+// data is not supported for Ledger wallets, so this method will always return
+// an error.
+func (w *wallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
+ return w.SignText(account, accounts.TextHash(text))
+}
+
+// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
+// transaction with the given account using passphrase as extra authentication.
+// Since USB wallets don't rely on passphrases, these are silently ignored.
+func (w *wallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ return w.SignTx(account, tx, chainID)
+}