dev: fix: most of wsl lint issues

This commit is contained in:
marcello33 2023-06-15 15:48:26 +02:00
parent 82f33b22f5
commit f3ffacf2d7
1025 changed files with 22753 additions and 176 deletions

View file

@ -49,7 +49,7 @@ linters:
- tparallel - tparallel
- unconvert - unconvert
- unparam - unparam
# - wsl - wsl
- asasalint - asasalint
#- errorlint causes stack overflow. TODO: recheck after each golangci update #- errorlint causes stack overflow. TODO: recheck after each golangci update

View file

@ -51,6 +51,7 @@ func JSON(reader io.Reader) (ABI, error) {
if err := dec.Decode(&abi); err != nil { if err := dec.Decode(&abi); err != nil {
return ABI{}, err return ABI{}, err
} }
return abi, nil return abi, nil
} }
@ -67,12 +68,15 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return arguments, nil return arguments, nil
} }
method, exist := abi.Methods[name] method, exist := abi.Methods[name]
if !exist { if !exist {
return nil, fmt.Errorf("method '%s' not found", name) return nil, fmt.Errorf("method '%s' not found", name)
} }
arguments, err := method.Inputs.Pack(args...) arguments, err := method.Inputs.Pack(args...)
if err != nil { if err != nil {
return nil, err return nil, err
@ -85,18 +89,23 @@ func (abi ABI) getArguments(name string, data []byte) (Arguments, error) {
// since there can't be naming collisions with contracts and events, // since there can't be naming collisions with contracts and events,
// we need to decide whether we're calling a method or an event // we need to decide whether we're calling a method or an event
var args Arguments var args Arguments
if method, ok := abi.Methods[name]; ok { if method, ok := abi.Methods[name]; ok {
if len(data)%32 != 0 { if len(data)%32 != 0 {
return nil, fmt.Errorf("abi: improperly formatted output: %q - Bytes: %+v", data, data) return nil, fmt.Errorf("abi: improperly formatted output: %q - Bytes: %+v", data, data)
} }
args = method.Outputs args = method.Outputs
} }
if event, ok := abi.Events[name]; ok { if event, ok := abi.Events[name]; ok {
args = event.Inputs args = event.Inputs
} }
if args == nil { if args == nil {
return nil, fmt.Errorf("abi: could not locate named method or event: %s", name) return nil, fmt.Errorf("abi: could not locate named method or event: %s", name)
} }
return args, nil return args, nil
} }
@ -106,6 +115,7 @@ func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return args.Unpack(data) return args.Unpack(data)
} }
@ -117,10 +127,12 @@ func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) erro
if err != nil { if err != nil {
return err return err
} }
unpacked, err := args.Unpack(data) unpacked, err := args.Unpack(data)
if err != nil { if err != nil {
return err return err
} }
return args.Copy(v, unpacked) return args.Copy(v, unpacked)
} }
@ -130,6 +142,7 @@ func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte)
if err != nil { if err != nil {
return err return err
} }
return args.UnpackIntoMap(v, data) return args.UnpackIntoMap(v, data)
} }
@ -153,12 +166,15 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
// declared as anonymous. // declared as anonymous.
Anonymous bool Anonymous bool
} }
if err := json.Unmarshal(data, &fields); err != nil { if err := json.Unmarshal(data, &fields); err != nil {
return err return err
} }
abi.Methods = make(map[string]Method) abi.Methods = make(map[string]Method)
abi.Events = make(map[string]Event) abi.Events = make(map[string]Event)
abi.Errors = make(map[string]Error) abi.Errors = make(map[string]Error)
for _, field := range fields { for _, field := range fields {
switch field.Type { switch field.Type {
case "constructor": case "constructor":
@ -172,6 +188,7 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
if abi.HasFallback() { if abi.HasFallback() {
return errors.New("only single fallback is allowed") return errors.New("only single fallback is allowed")
} }
abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil) abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
case "receive": case "receive":
// New introduced function type in v0.6.0, check more detail // New introduced function type in v0.6.0, check more detail
@ -179,9 +196,11 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
if abi.HasReceive() { if abi.HasReceive() {
return errors.New("only single receive is allowed") return errors.New("only single receive is allowed")
} }
if field.StateMutability != "payable" { if field.StateMutability != "payable" {
return errors.New("the statemutability of receive can only be payable") return errors.New("the statemutability of receive can only be payable")
} }
abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil) abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
case "event": case "event":
name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok }) name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
@ -194,6 +213,7 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name) return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
} }
} }
return nil return nil
} }
@ -203,11 +223,13 @@ func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
if len(sigdata) < 4 { if len(sigdata) < 4 {
return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata)) return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
} }
for _, method := range abi.Methods { for _, method := range abi.Methods {
if bytes.Equal(method.ID, sigdata[:4]) { if bytes.Equal(method.ID, sigdata[:4]) {
return &method, nil return &method, nil
} }
} }
return nil, fmt.Errorf("no method with id: %#x", sigdata[:4]) return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
} }
@ -219,6 +241,7 @@ func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
return &event, nil return &event, nil
} }
} }
return nil, fmt.Errorf("no event with id: %#x", topic.Hex()) return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
} }
@ -243,6 +266,7 @@ func UnpackRevert(data []byte) (string, error) {
if len(data) < 4 { if len(data) < 4 {
return "", errors.New("invalid data for unpacking") return "", errors.New("invalid data for unpacking")
} }
if !bytes.Equal(data[:4], revertSelector) { if !bytes.Equal(data[:4], revertSelector) {
return "", errors.New("invalid data for unpacking") return "", errors.New("invalid data for unpacking")
} }
@ -252,9 +276,11 @@ func UnpackRevert(data []byte) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:]) unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
if err != nil { if err != nil {
return "", err return "", err
} }
return unpacked[0].(string), nil return unpacked[0].(string), nil
} }

View file

@ -134,6 +134,7 @@ func TestReader(t *testing.T) {
if !exist { if !exist {
t.Errorf("Missing expected method %v", name) t.Errorf("Missing expected method %v", name)
} }
if !reflect.DeepEqual(gotM, expM) { if !reflect.DeepEqual(gotM, expM) {
t.Errorf("\nGot abi method: \n%v\ndoes not match expected method\n%v", gotM, expM) t.Errorf("\nGot abi method: \n%v\ndoes not match expected method\n%v", gotM, expM)
} }
@ -144,6 +145,7 @@ func TestReader(t *testing.T) {
if !exist { if !exist {
t.Errorf("Found extra method %v", name) t.Errorf("Found extra method %v", name)
} }
if !reflect.DeepEqual(gotM, expM) { if !reflect.DeepEqual(gotM, expM) {
t.Errorf("\nGot abi method: \n%v\ndoes not match expected method\n%v", gotM, expM) t.Errorf("\nGot abi method: \n%v\ndoes not match expected method\n%v", gotM, expM)
} }
@ -152,11 +154,14 @@ func TestReader(t *testing.T) {
func TestInvalidABI(t *testing.T) { func TestInvalidABI(t *testing.T) {
json := `[{ "type" : "function", "name" : "", "constant" : fals }]` json := `[{ "type" : "function", "name" : "", "constant" : fals }]`
_, err := JSON(strings.NewReader(json)) _, err := JSON(strings.NewReader(json))
if err == nil { if err == nil {
t.Fatal("invalid json should produce error") t.Fatal("invalid json should produce error")
} }
json2 := `[{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "typ" : "uint256" } ] }]` json2 := `[{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "typ" : "uint256" } ] }]`
_, err = JSON(strings.NewReader(json2)) _, err = JSON(strings.NewReader(json2))
if err == nil { if err == nil {
t.Fatal("invalid json should produce error") t.Fatal("invalid json should produce error")
@ -177,6 +182,7 @@ func TestConstructor(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !reflect.DeepEqual(abi.Constructor, method) { if !reflect.DeepEqual(abi.Constructor, method) {
t.Error("Missing expected constructor") t.Error("Missing expected constructor")
} }
@ -185,6 +191,7 @@ func TestConstructor(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
unpacked, err := abi.Constructor.Inputs.Unpack(packed) unpacked, err := abi.Constructor.Inputs.Unpack(packed)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -193,6 +200,7 @@ func TestConstructor(t *testing.T) {
if !reflect.DeepEqual(unpacked[0], big.NewInt(1)) { if !reflect.DeepEqual(unpacked[0], big.NewInt(1)) {
t.Error("Unable to pack/unpack from constructor") t.Error("Unable to pack/unpack from constructor")
} }
if !reflect.DeepEqual(unpacked[1], big.NewInt(2)) { if !reflect.DeepEqual(unpacked[1], big.NewInt(2)) {
t.Error("Unable to pack/unpack from constructor") t.Error("Unable to pack/unpack from constructor")
} }
@ -226,6 +234,7 @@ func TestTestNumbers(t *testing.T) {
i := new(int) i := new(int)
*i = 1000 *i = 1000
if _, err := abi.Pack("send", i); err == nil { if _, err := abi.Pack("send", i); err == nil {
t.Errorf("expected send( ptr ) to throw, requires *big.Int instead of *int") t.Errorf("expected send( ptr ) to throw, requires *big.Int instead of *int")
} }
@ -238,6 +247,7 @@ func TestTestNumbers(t *testing.T) {
func TestMethodSignature(t *testing.T) { func TestMethodSignature(t *testing.T) {
m := NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil) m := NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil)
exp := "foo(string,string)" exp := "foo(string,string)"
if m.Sig != exp { if m.Sig != exp {
t.Error("signature mismatch", exp, "!=", m.Sig) t.Error("signature mismatch", exp, "!=", m.Sig)
} }
@ -249,6 +259,7 @@ func TestMethodSignature(t *testing.T) {
m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", Uint256, false}}, nil) m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", Uint256, false}}, nil)
exp = "foo(uint256)" exp = "foo(uint256)"
if m.Sig != exp { if m.Sig != exp {
t.Error("signature mismatch", exp, "!=", m.Sig) t.Error("signature mismatch", exp, "!=", m.Sig)
} }
@ -268,6 +279,7 @@ func TestMethodSignature(t *testing.T) {
}) })
m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"s", s, false}, {"bar", String, false}}, nil) m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"s", s, false}, {"bar", String, false}}, nil)
exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)" exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
if m.Sig != exp { if m.Sig != exp {
t.Error("signature mismatch", exp, "!=", m.Sig) t.Error("signature mismatch", exp, "!=", m.Sig)
} }
@ -275,10 +287,12 @@ func TestMethodSignature(t *testing.T) {
func TestOverloadedMethodSignature(t *testing.T) { func TestOverloadedMethodSignature(t *testing.T) {
json := `[{"constant":true,"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"i","type":"uint256"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"i","type":"uint256"}],"name":"bar","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"i","type":"uint256"},{"indexed":false,"name":"j","type":"uint256"}],"name":"bar","type":"event"}]` json := `[{"constant":true,"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"i","type":"uint256"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"i","type":"uint256"}],"name":"bar","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"i","type":"uint256"},{"indexed":false,"name":"j","type":"uint256"}],"name":"bar","type":"event"}]`
abi, err := JSON(strings.NewReader(json)) abi, err := JSON(strings.NewReader(json))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
check := func(name string, expect string, method bool) { check := func(name string, expect string, method bool) {
if method { if method {
if abi.Methods[name].Sig != expect { if abi.Methods[name].Sig != expect {
@ -298,10 +312,12 @@ func TestOverloadedMethodSignature(t *testing.T) {
func TestCustomErrors(t *testing.T) { func TestCustomErrors(t *testing.T) {
json := `[{ "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ],"name": "MyError", "type": "error"} ]` json := `[{ "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ],"name": "MyError", "type": "error"} ]`
abi, err := JSON(strings.NewReader(json)) abi, err := JSON(strings.NewReader(json))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
check := func(name string, expect string) { check := func(name string, expect string) {
if abi.Errors[name].Sig != expect { if abi.Errors[name].Sig != expect {
t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig) t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig)
@ -325,6 +341,7 @@ func TestMultiPack(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !bytes.Equal(packed, sig) { if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed) t.Errorf("expected %x got %x", sig, packed)
} }
@ -337,6 +354,7 @@ func ExampleJSON() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
out, err := abi.Pack("isBar", common.HexToAddress("01")) out, err := abi.Pack("isBar", common.HexToAddress("01"))
if err != nil { if err != nil {
panic(err) panic(err)
@ -361,6 +379,7 @@ func TestInputVariableInputLength(t *testing.T) {
// test one string // test one string
strin := "hello world" strin := "hello world"
strpack, err := abi.Pack("strOne", strin) strpack, err := abi.Pack("strOne", strin)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -393,6 +412,7 @@ func TestInputVariableInputLength(t *testing.T) {
// test two strings // test two strings
str1 := "hello" str1 := "hello"
str2 := "world" str2 := "world"
str2pack, err := abi.Pack("strTwo", str1, str2) str2pack, err := abi.Pack("strTwo", str1, str2)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -422,6 +442,7 @@ func TestInputVariableInputLength(t *testing.T) {
// test two strings, first > 32, second < 32 // test two strings, first > 32, second < 32
str1 = strings.Repeat("a", 33) str1 = strings.Repeat("a", 33)
str2pack, err = abi.Pack("strTwo", str1, str2) str2pack, err = abi.Pack("strTwo", str1, str2)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -447,6 +468,7 @@ func TestInputVariableInputLength(t *testing.T) {
// test two strings, first > 32, second >32 // test two strings, first > 32, second >32
str1 = strings.Repeat("a", 33) str1 = strings.Repeat("a", 33)
str2 = strings.Repeat("a", 33) str2 = strings.Repeat("a", 33)
str2pack, err = abi.Pack("strTwo", str1, str2) str2pack, err = abi.Pack("strTwo", str1, str2)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -484,6 +506,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
// test string, fixed array uint256[2] // test string, fixed array uint256[2]
strin := "hello world" strin := "hello world"
arrin := [2]*big.Int{big.NewInt(1), big.NewInt(2)} arrin := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
fixedArrStrPack, err := abi.Pack("fixedArrStr", strin, arrin) fixedArrStrPack, err := abi.Pack("fixedArrStr", strin, arrin)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -497,6 +520,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
strvalue := common.RightPadBytes([]byte(strin), 32) strvalue := common.RightPadBytes([]byte(strin), 32)
arrinvalue1 := common.LeftPadBytes(arrin[0].Bytes(), 32) arrinvalue1 := common.LeftPadBytes(arrin[0].Bytes(), 32)
arrinvalue2 := common.LeftPadBytes(arrin[1].Bytes(), 32) arrinvalue2 := common.LeftPadBytes(arrin[1].Bytes(), 32)
exp := append(offset, arrinvalue1...) exp := append(offset, arrinvalue1...)
exp = append(exp, arrinvalue2...) exp = append(exp, arrinvalue2...)
exp = append(exp, append(length, strvalue...)...) exp = append(exp, append(length, strvalue...)...)
@ -510,6 +534,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
// test byte array, fixed array uint256[2] // test byte array, fixed array uint256[2]
bytesin := []byte(strin) bytesin := []byte(strin)
arrin = [2]*big.Int{big.NewInt(1), big.NewInt(2)} arrin = [2]*big.Int{big.NewInt(1), big.NewInt(2)}
fixedArrBytesPack, err := abi.Pack("fixedArrBytes", bytesin, arrin) fixedArrBytesPack, err := abi.Pack("fixedArrBytes", bytesin, arrin)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -523,6 +548,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
strvalue = common.RightPadBytes([]byte(strin), 32) strvalue = common.RightPadBytes([]byte(strin), 32)
arrinvalue1 = common.LeftPadBytes(arrin[0].Bytes(), 32) arrinvalue1 = common.LeftPadBytes(arrin[0].Bytes(), 32)
arrinvalue2 = common.LeftPadBytes(arrin[1].Bytes(), 32) arrinvalue2 = common.LeftPadBytes(arrin[1].Bytes(), 32)
exp = append(offset, arrinvalue1...) exp = append(offset, arrinvalue1...)
exp = append(exp, arrinvalue2...) exp = append(exp, arrinvalue2...)
exp = append(exp, append(length, strvalue...)...) exp = append(exp, append(length, strvalue...)...)
@ -537,6 +563,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
strin = "hello world" strin = "hello world"
fixedarrin := [2]*big.Int{big.NewInt(1), big.NewInt(2)} fixedarrin := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
dynarrin := []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)} dynarrin := []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
mixedArrStrPack, err := abi.Pack("mixedArrStr", strin, fixedarrin, dynarrin) mixedArrStrPack, err := abi.Pack("mixedArrStr", strin, fixedarrin, dynarrin)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -557,6 +584,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
dynarrinvalue1 := common.LeftPadBytes(dynarrin[0].Bytes(), 32) dynarrinvalue1 := common.LeftPadBytes(dynarrin[0].Bytes(), 32)
dynarrinvalue2 := common.LeftPadBytes(dynarrin[1].Bytes(), 32) dynarrinvalue2 := common.LeftPadBytes(dynarrin[1].Bytes(), 32)
dynarrinvalue3 := common.LeftPadBytes(dynarrin[2].Bytes(), 32) dynarrinvalue3 := common.LeftPadBytes(dynarrin[2].Bytes(), 32)
exp = append(stroffset, fixedarrinvalue1...) exp = append(stroffset, fixedarrinvalue1...)
exp = append(exp, fixedarrinvalue2...) exp = append(exp, fixedarrinvalue2...)
exp = append(exp, dynarroffset...) exp = append(exp, dynarroffset...)
@ -576,6 +604,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
strin = "hello world" strin = "hello world"
fixedarrin1 := [2]*big.Int{big.NewInt(1), big.NewInt(2)} fixedarrin1 := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
fixedarrin2 := [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)} fixedarrin2 := [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
doubleFixedArrStrPack, err := abi.Pack("doubleFixedArrStr", strin, fixedarrin1, fixedarrin2) doubleFixedArrStrPack, err := abi.Pack("doubleFixedArrStr", strin, fixedarrin1, fixedarrin2)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -592,6 +621,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
fixedarrin2value1 := common.LeftPadBytes(fixedarrin2[0].Bytes(), 32) fixedarrin2value1 := common.LeftPadBytes(fixedarrin2[0].Bytes(), 32)
fixedarrin2value2 := common.LeftPadBytes(fixedarrin2[1].Bytes(), 32) fixedarrin2value2 := common.LeftPadBytes(fixedarrin2[1].Bytes(), 32)
fixedarrin2value3 := common.LeftPadBytes(fixedarrin2[2].Bytes(), 32) fixedarrin2value3 := common.LeftPadBytes(fixedarrin2[2].Bytes(), 32)
exp = append(stroffset, fixedarrin1value1...) exp = append(stroffset, fixedarrin1value1...)
exp = append(exp, fixedarrin1value2...) exp = append(exp, fixedarrin1value2...)
exp = append(exp, fixedarrin2value1...) exp = append(exp, fixedarrin2value1...)
@ -610,6 +640,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
fixedarrin1 = [2]*big.Int{big.NewInt(1), big.NewInt(2)} fixedarrin1 = [2]*big.Int{big.NewInt(1), big.NewInt(2)}
dynarrin = []*big.Int{big.NewInt(1), big.NewInt(2)} dynarrin = []*big.Int{big.NewInt(1), big.NewInt(2)}
fixedarrin2 = [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)} fixedarrin2 = [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
multipleMixedArrStrPack, err := abi.Pack("multipleMixedArrStr", strin, fixedarrin1, dynarrin, fixedarrin2) multipleMixedArrStrPack, err := abi.Pack("multipleMixedArrStr", strin, fixedarrin1, dynarrin, fixedarrin2)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -631,6 +662,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
fixedarrin2value1 = common.LeftPadBytes(fixedarrin2[0].Bytes(), 32) fixedarrin2value1 = common.LeftPadBytes(fixedarrin2[0].Bytes(), 32)
fixedarrin2value2 = common.LeftPadBytes(fixedarrin2[1].Bytes(), 32) fixedarrin2value2 = common.LeftPadBytes(fixedarrin2[1].Bytes(), 32)
fixedarrin2value3 = common.LeftPadBytes(fixedarrin2[2].Bytes(), 32) fixedarrin2value3 = common.LeftPadBytes(fixedarrin2[2].Bytes(), 32)
exp = append(stroffset, fixedarrin1value1...) exp = append(stroffset, fixedarrin1value1...)
exp = append(exp, fixedarrin1value2...) exp = append(exp, fixedarrin1value2...)
exp = append(exp, dynarroffset...) exp = append(exp, dynarroffset...)
@ -703,20 +735,25 @@ func TestBareEvents(t *testing.T) {
t.Errorf("could not found event %s", name) t.Errorf("could not found event %s", name)
continue continue
} }
if got.Anonymous != exp.Anonymous { if got.Anonymous != exp.Anonymous {
t.Errorf("invalid anonymous indication for event %s, want %v, got %v", name, exp.Anonymous, got.Anonymous) t.Errorf("invalid anonymous indication for event %s, want %v, got %v", name, exp.Anonymous, got.Anonymous)
} }
if len(got.Inputs) != len(exp.Args) { if len(got.Inputs) != len(exp.Args) {
t.Errorf("invalid number of args, want %d, got %d", len(exp.Args), len(got.Inputs)) t.Errorf("invalid number of args, want %d, got %d", len(exp.Args), len(got.Inputs))
continue continue
} }
for i, arg := range exp.Args { for i, arg := range exp.Args {
if arg.Name != got.Inputs[i].Name { if arg.Name != got.Inputs[i].Name {
t.Errorf("events[%s].Input[%d] has an invalid name, want %s, got %s", name, i, arg.Name, got.Inputs[i].Name) t.Errorf("events[%s].Input[%d] has an invalid name, want %s, got %s", name, i, arg.Name, got.Inputs[i].Name)
} }
if arg.Indexed != got.Inputs[i].Indexed { if arg.Indexed != got.Inputs[i].Indexed {
t.Errorf("events[%s].Input[%d] has an invalid indexed indication, want %v, got %v", name, i, arg.Indexed, got.Inputs[i].Indexed) t.Errorf("events[%s].Input[%d] has an invalid indexed indication, want %v, got %v", name, i, arg.Indexed, got.Inputs[i].Indexed)
} }
if arg.Type.T != got.Inputs[i].Type.T { if arg.Type.T != got.Inputs[i].Type.T {
t.Errorf("events[%s].Input[%d] has an invalid type, want %x, got %x", name, i, arg.Type.T, got.Inputs[i].Type.T) t.Errorf("events[%s].Input[%d] has an invalid type, want %x, got %x", name, i, arg.Type.T, got.Inputs[i].Type.T)
} }
@ -740,16 +777,19 @@ func TestBareEvents(t *testing.T) {
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]} // receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
func TestUnpackEvent(t *testing.T) { func TestUnpackEvent(t *testing.T) {
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]` const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
abi, err := JSON(strings.NewReader(abiJSON)) abi, err := JSON(strings.NewReader(abiJSON))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
const hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158` const hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
data, err := hex.DecodeString(hexdata) data, err := hex.DecodeString(hexdata)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(data)%32 == 0 { if len(data)%32 == 0 {
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data)) t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
} }
@ -759,6 +799,7 @@ func TestUnpackEvent(t *testing.T) {
Amount *big.Int Amount *big.Int
Memo []byte Memo []byte
} }
var ev ReceivedEvent var ev ReceivedEvent
err = abi.UnpackIntoInterface(&ev, "received", data) err = abi.UnpackIntoInterface(&ev, "received", data)
@ -769,7 +810,9 @@ func TestUnpackEvent(t *testing.T) {
type ReceivedAddrEvent struct { type ReceivedAddrEvent struct {
Sender common.Address Sender common.Address
} }
var receivedAddrEv ReceivedAddrEvent var receivedAddrEv ReceivedAddrEvent
err = abi.UnpackIntoInterface(&receivedAddrEv, "receivedAddr", data) err = abi.UnpackIntoInterface(&receivedAddrEv, "receivedAddr", data)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -778,16 +821,19 @@ func TestUnpackEvent(t *testing.T) {
func TestUnpackEventIntoMap(t *testing.T) { func TestUnpackEventIntoMap(t *testing.T) {
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]` const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
abi, err := JSON(strings.NewReader(abiJSON)) abi, err := JSON(strings.NewReader(abiJSON))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
const hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158` const hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
data, err := hex.DecodeString(hexdata) data, err := hex.DecodeString(hexdata)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(data)%32 == 0 { if len(data)%32 == 0 {
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data)) t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
} }
@ -798,18 +844,23 @@ func TestUnpackEventIntoMap(t *testing.T) {
"amount": big.NewInt(1), "amount": big.NewInt(1),
"memo": []byte{88}, "memo": []byte{88},
} }
if err := abi.UnpackIntoMap(receivedMap, "received", data); err != nil { if err := abi.UnpackIntoMap(receivedMap, "received", data); err != nil {
t.Error(err) t.Error(err)
} }
if len(receivedMap) != 3 { if len(receivedMap) != 3 {
t.Error("unpacked `received` map expected to have length 3") t.Error("unpacked `received` map expected to have length 3")
} }
if receivedMap["sender"] != expectedReceivedMap["sender"] { if receivedMap["sender"] != expectedReceivedMap["sender"] {
t.Error("unpacked `received` map does not match expected map") t.Error("unpacked `received` map does not match expected map")
} }
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 { if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
t.Error("unpacked `received` map does not match expected map") t.Error("unpacked `received` map does not match expected map")
} }
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) { if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
t.Error("unpacked `received` map does not match expected map") t.Error("unpacked `received` map does not match expected map")
} }
@ -818,9 +869,11 @@ func TestUnpackEventIntoMap(t *testing.T) {
if err = abi.UnpackIntoMap(receivedAddrMap, "receivedAddr", data); err != nil { if err = abi.UnpackIntoMap(receivedAddrMap, "receivedAddr", data); err != nil {
t.Error(err) t.Error(err)
} }
if len(receivedAddrMap) != 1 { if len(receivedAddrMap) != 1 {
t.Error("unpacked `receivedAddr` map expected to have length 1") t.Error("unpacked `receivedAddr` map expected to have length 1")
} }
if receivedAddrMap["sender"] != expectedReceivedMap["sender"] { if receivedAddrMap["sender"] != expectedReceivedMap["sender"] {
t.Error("unpacked `receivedAddr` map does not match expected map") t.Error("unpacked `receivedAddr` map does not match expected map")
} }
@ -828,15 +881,19 @@ func TestUnpackEventIntoMap(t *testing.T) {
func TestUnpackMethodIntoMap(t *testing.T) { func TestUnpackMethodIntoMap(t *testing.T) {
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"send","outputs":[{"name":"amount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"get","outputs":[{"name":"hash","type":"bytes"}],"payable":true,"stateMutability":"payable","type":"function"}]` const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"send","outputs":[{"name":"amount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"get","outputs":[{"name":"hash","type":"bytes"}],"payable":true,"stateMutability":"payable","type":"function"}]`
abi, err := JSON(strings.NewReader(abiJSON)) abi, err := JSON(strings.NewReader(abiJSON))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
const hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000015800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000158000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001580000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000015800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000158` const hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000015800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000158000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001580000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000015800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000158`
data, err := hex.DecodeString(hexdata) data, err := hex.DecodeString(hexdata)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(data)%32 != 0 { if len(data)%32 != 0 {
t.Errorf("len(data) is %d, want a multiple of 32", len(data)) t.Errorf("len(data) is %d, want a multiple of 32", len(data))
} }
@ -846,6 +903,7 @@ func TestUnpackMethodIntoMap(t *testing.T) {
if err = abi.UnpackIntoMap(receiveMap, "receive", data); err != nil { if err = abi.UnpackIntoMap(receiveMap, "receive", data); err != nil {
t.Error(err) t.Error(err)
} }
if len(receiveMap) > 0 { if len(receiveMap) > 0 {
t.Error("unpacked `receive` map expected to have length 0") t.Error("unpacked `receive` map expected to have length 0")
} }
@ -855,9 +913,11 @@ func TestUnpackMethodIntoMap(t *testing.T) {
if err = abi.UnpackIntoMap(sendMap, "send", data); err != nil { if err = abi.UnpackIntoMap(sendMap, "send", data); err != nil {
t.Error(err) t.Error(err)
} }
if len(sendMap) != 1 { if len(sendMap) != 1 {
t.Error("unpacked `send` map expected to have length 1") t.Error("unpacked `send` map expected to have length 1")
} }
if sendMap["amount"].(*big.Int).Cmp(big.NewInt(1)) != 0 { if sendMap["amount"].(*big.Int).Cmp(big.NewInt(1)) != 0 {
t.Error("unpacked `send` map expected `amount` value of 1") t.Error("unpacked `send` map expected `amount` value of 1")
} }
@ -867,9 +927,11 @@ func TestUnpackMethodIntoMap(t *testing.T) {
if err = abi.UnpackIntoMap(getMap, "get", data); err != nil { if err = abi.UnpackIntoMap(getMap, "get", data); err != nil {
t.Error(err) t.Error(err)
} }
if len(getMap) != 1 { if len(getMap) != 1 {
t.Error("unpacked `get` map expected to have length 1") t.Error("unpacked `get` map expected to have length 1")
} }
expectedBytes := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 88, 0} expectedBytes := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 88, 0}
if !bytes.Equal(getMap["hash"].([]byte), expectedBytes) { if !bytes.Equal(getMap["hash"].([]byte), expectedBytes) {
t.Errorf("unpacked `get` map expected `hash` value of %v", expectedBytes) t.Errorf("unpacked `get` map expected `hash` value of %v", expectedBytes)
@ -879,18 +941,23 @@ func TestUnpackMethodIntoMap(t *testing.T) {
func TestUnpackIntoMapNamingConflict(t *testing.T) { func TestUnpackIntoMapNamingConflict(t *testing.T) {
// Two methods have the same name // Two methods have the same name
var abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"get","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"send","outputs":[{"name":"amount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"get","outputs":[{"name":"hash","type":"bytes"}],"payable":true,"stateMutability":"payable","type":"function"}]` var abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"get","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"send","outputs":[{"name":"amount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"get","outputs":[{"name":"hash","type":"bytes"}],"payable":true,"stateMutability":"payable","type":"function"}]`
abi, err := JSON(strings.NewReader(abiJSON)) abi, err := JSON(strings.NewReader(abiJSON))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
var hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158` var hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
data, err := hex.DecodeString(hexdata) data, err := hex.DecodeString(hexdata)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(data)%32 == 0 { if len(data)%32 == 0 {
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data)) t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
} }
getMap := map[string]interface{}{} getMap := map[string]interface{}{}
if err = abi.UnpackIntoMap(getMap, "get", data); err == nil { if err = abi.UnpackIntoMap(getMap, "get", data); err == nil {
t.Error("naming conflict between two methods; error expected") t.Error("naming conflict between two methods; error expected")
@ -898,18 +965,23 @@ func TestUnpackIntoMapNamingConflict(t *testing.T) {
// Two events have the same name // Two events have the same name
abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"received","type":"event"}]` abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"received","type":"event"}]`
abi, err = JSON(strings.NewReader(abiJSON)) abi, err = JSON(strings.NewReader(abiJSON))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158` hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
data, err = hex.DecodeString(hexdata) data, err = hex.DecodeString(hexdata)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(data)%32 == 0 { if len(data)%32 == 0 {
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data)) t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
} }
receivedMap := map[string]interface{}{} receivedMap := map[string]interface{}{}
if err = abi.UnpackIntoMap(receivedMap, "received", data); err != nil { if err = abi.UnpackIntoMap(receivedMap, "received", data); err != nil {
t.Error("naming conflict between two events; no error expected") t.Error("naming conflict between two events; no error expected")
@ -917,43 +989,54 @@ func TestUnpackIntoMapNamingConflict(t *testing.T) {
// Method and event have the same name // Method and event have the same name
abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"received","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]` abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"received","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
abi, err = JSON(strings.NewReader(abiJSON)) abi, err = JSON(strings.NewReader(abiJSON))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(data)%32 == 0 { if len(data)%32 == 0 {
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data)) t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
} }
if err = abi.UnpackIntoMap(receivedMap, "received", data); err == nil { if err = abi.UnpackIntoMap(receivedMap, "received", data); err == nil {
t.Error("naming conflict between an event and a method; error expected") t.Error("naming conflict between an event and a method; error expected")
} }
// Conflict is case sensitive // Conflict is case sensitive
abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"received","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"Received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]` abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"received","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"Received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
abi, err = JSON(strings.NewReader(abiJSON)) abi, err = JSON(strings.NewReader(abiJSON))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(data)%32 == 0 { if len(data)%32 == 0 {
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data)) t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
} }
expectedReceivedMap := map[string]interface{}{ expectedReceivedMap := map[string]interface{}{
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"), "sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
"amount": big.NewInt(1), "amount": big.NewInt(1),
"memo": []byte{88}, "memo": []byte{88},
} }
if err = abi.UnpackIntoMap(receivedMap, "Received", data); err != nil { if err = abi.UnpackIntoMap(receivedMap, "Received", data); err != nil {
t.Error(err) t.Error(err)
} }
if len(receivedMap) != 3 { if len(receivedMap) != 3 {
t.Error("unpacked `received` map expected to have length 3") t.Error("unpacked `received` map expected to have length 3")
} }
if receivedMap["sender"] != expectedReceivedMap["sender"] { if receivedMap["sender"] != expectedReceivedMap["sender"] {
t.Error("unpacked `received` map does not match expected map") t.Error("unpacked `received` map does not match expected map")
} }
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 { if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
t.Error("unpacked `received` map does not match expected map") t.Error("unpacked `received` map does not match expected map")
} }
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) { if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
t.Error("unpacked `received` map does not match expected map") t.Error("unpacked `received` map does not match expected map")
} }
@ -964,12 +1047,15 @@ func TestABI_MethodById(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
for name, m := range abi.Methods { for name, m := range abi.Methods {
a := fmt.Sprintf("%v", m) a := fmt.Sprintf("%v", m)
m2, err := abi.MethodById(m.ID) m2, err := abi.MethodById(m.ID)
if err != nil { if err != nil {
t.Fatalf("Failed to look up ABI method: %v", err) t.Fatalf("Failed to look up ABI method: %v", err)
} }
b := fmt.Sprintf("%v", m2) b := fmt.Sprintf("%v", m2)
if a != b { if a != b {
t.Errorf("Method %v (id %x) not 'findable' by id in ABI", name, m.ID) t.Errorf("Method %v (id %x) not 'findable' by id in ABI", name, m.ID)
@ -983,9 +1069,11 @@ func TestABI_MethodById(t *testing.T) {
if _, err := abi.MethodById([]byte{0x00}); err == nil { if _, err := abi.MethodById([]byte{0x00}); err == nil {
t.Errorf("Expected error, too short to decode data") t.Errorf("Expected error, too short to decode data")
} }
if _, err := abi.MethodById([]byte{}); err == nil { if _, err := abi.MethodById([]byte{}); err == nil {
t.Errorf("Expected error, too short to decode data") t.Errorf("Expected error, too short to decode data")
} }
if _, err := abi.MethodById(nil); err == nil { if _, err := abi.MethodById(nil); err == nil {
t.Errorf("Expected error, nil is short to decode data") t.Errorf("Expected error, nil is short to decode data")
} }
@ -1040,6 +1128,7 @@ func TestABI_EventById(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Failed to look up ABI method: %v, test #%d", err, testnum) t.Fatalf("Failed to look up ABI method: %v, test #%d", err, testnum)
} }
if event == nil { if event == nil {
t.Errorf("We should find a event for topic %s, test #%d", topicID.Hex(), testnum) t.Errorf("We should find a event for topic %s, test #%d", topicID.Hex(), testnum)
} else if event.ID != topicID { } else if event.ID != topicID {
@ -1047,10 +1136,12 @@ func TestABI_EventById(t *testing.T) {
} }
unknowntopicID := crypto.Keccak256Hash([]byte("unknownEvent")) unknowntopicID := crypto.Keccak256Hash([]byte("unknownEvent"))
unknownEvent, err := abi.EventByID(unknowntopicID) unknownEvent, err := abi.EventByID(unknowntopicID)
if err == nil { if err == nil {
t.Errorf("EventByID should return an error if a topic is not found, test #%d", testnum) t.Errorf("EventByID should return an error if a topic is not found, test #%d", testnum)
} }
if unknownEvent != nil { if unknownEvent != nil {
t.Errorf("We should not find any event for topic %s, test #%d", unknowntopicID.Hex(), testnum) t.Errorf("We should not find any event for topic %s, test #%d", unknowntopicID.Hex(), testnum)
} }
@ -1061,19 +1152,24 @@ func TestABI_EventById(t *testing.T) {
// conflict and that the second transfer method will be renamed transfer1. // conflict and that the second transfer method will be renamed transfer1.
func TestDoubleDuplicateMethodNames(t *testing.T) { func TestDoubleDuplicateMethodNames(t *testing.T) {
abiJSON := `[{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"}],"name":"transfer0","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"},{"name":"customFallback","type":"string"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]` abiJSON := `[{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"}],"name":"transfer0","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"},{"name":"customFallback","type":"string"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]`
contractAbi, err := JSON(strings.NewReader(abiJSON)) contractAbi, err := JSON(strings.NewReader(abiJSON))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if _, ok := contractAbi.Methods["transfer"]; !ok { if _, ok := contractAbi.Methods["transfer"]; !ok {
t.Fatalf("Could not find original method") t.Fatalf("Could not find original method")
} }
if _, ok := contractAbi.Methods["transfer0"]; !ok { if _, ok := contractAbi.Methods["transfer0"]; !ok {
t.Fatalf("Could not find duplicate method") t.Fatalf("Could not find duplicate method")
} }
if _, ok := contractAbi.Methods["transfer1"]; !ok { if _, ok := contractAbi.Methods["transfer1"]; !ok {
t.Fatalf("Could not find duplicate method") t.Fatalf("Could not find duplicate method")
} }
if _, ok := contractAbi.Methods["transfer2"]; ok { if _, ok := contractAbi.Methods["transfer2"]; ok {
t.Fatalf("Should not have found extra method") t.Fatalf("Should not have found extra method")
} }
@ -1090,19 +1186,24 @@ func TestDoubleDuplicateMethodNames(t *testing.T) {
// } // }
func TestDoubleDuplicateEventNames(t *testing.T) { func TestDoubleDuplicateEventNames(t *testing.T) {
abiJSON := `[{"anonymous": false,"inputs": [{"indexed": false,"internalType": "uint256","name": "a","type": "uint256"}],"name": "send","type": "event"},{"anonymous": false,"inputs": [],"name": "send0","type": "event"},{ "anonymous": false, "inputs": [],"name": "send","type": "event"}]` abiJSON := `[{"anonymous": false,"inputs": [{"indexed": false,"internalType": "uint256","name": "a","type": "uint256"}],"name": "send","type": "event"},{"anonymous": false,"inputs": [],"name": "send0","type": "event"},{ "anonymous": false, "inputs": [],"name": "send","type": "event"}]`
contractAbi, err := JSON(strings.NewReader(abiJSON)) contractAbi, err := JSON(strings.NewReader(abiJSON))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if _, ok := contractAbi.Events["send"]; !ok { if _, ok := contractAbi.Events["send"]; !ok {
t.Fatalf("Could not find original event") t.Fatalf("Could not find original event")
} }
if _, ok := contractAbi.Events["send0"]; !ok { if _, ok := contractAbi.Events["send0"]; !ok {
t.Fatalf("Could not find duplicate event") t.Fatalf("Could not find duplicate event")
} }
if _, ok := contractAbi.Events["send1"]; !ok { if _, ok := contractAbi.Events["send1"]; !ok {
t.Fatalf("Could not find duplicate event") t.Fatalf("Could not find duplicate event")
} }
if _, ok := contractAbi.Events["send2"]; ok { if _, ok := contractAbi.Events["send2"]; ok {
t.Fatalf("Should not have found extra event") t.Fatalf("Should not have found extra event")
} }
@ -1117,6 +1218,7 @@ func TestDoubleDuplicateEventNames(t *testing.T) {
// } // }
func TestUnnamedEventParam(t *testing.T) { func TestUnnamedEventParam(t *testing.T) {
abiJSON := `[{ "anonymous": false, "inputs": [{ "indexed": false,"internalType": "uint256", "name": "","type": "uint256"},{"indexed": false,"internalType": "uint256","name": "","type": "uint256"}],"name": "send","type": "event"}]` abiJSON := `[{ "anonymous": false, "inputs": [{ "indexed": false,"internalType": "uint256", "name": "","type": "uint256"},{"indexed": false,"internalType": "uint256","name": "","type": "uint256"}],"name": "send","type": "event"}]`
contractAbi, err := JSON(strings.NewReader(abiJSON)) contractAbi, err := JSON(strings.NewReader(abiJSON))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -1126,9 +1228,11 @@ func TestUnnamedEventParam(t *testing.T) {
if !ok { if !ok {
t.Fatalf("Could not find event") t.Fatalf("Could not find event")
} }
if event.Inputs[0].Name != "arg0" { if event.Inputs[0].Name != "arg0" {
t.Fatalf("Could not find input") t.Fatalf("Could not find input")
} }
if event.Inputs[1].Name != "arg1" { if event.Inputs[1].Name != "arg1" {
t.Fatalf("Could not find input") t.Fatalf("Could not find input")
} }
@ -1146,6 +1250,7 @@ func TestUnpackRevert(t *testing.T) {
{"08c379a1", "", errors.New("invalid data for unpacking")}, {"08c379a1", "", errors.New("invalid data for unpacking")},
{"08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000", "revert reason", nil}, {"08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000", "revert reason", nil},
} }
for index, c := range cases { for index, c := range cases {
t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) { t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) {
got, err := UnpackRevert(common.Hex2Bytes(c.input)) got, err := UnpackRevert(common.Hex2Bytes(c.input))
@ -1153,11 +1258,14 @@ func TestUnpackRevert(t *testing.T) {
if err == nil { if err == nil {
t.Fatalf("Expected non-nil error") t.Fatalf("Expected non-nil error")
} }
if err.Error() != c.expectErr.Error() { if err.Error() != c.expectErr.Error() {
t.Fatalf("Expected error mismatch, want %v, got %v", c.expectErr, err) t.Fatalf("Expected error mismatch, want %v, got %v", c.expectErr, err)
} }
return return
} }
if c.expect != got { if c.expect != got {
t.Fatalf("Output mismatch, want %v, got %v", c.expect, got) t.Fatalf("Output mismatch, want %v, got %v", c.expect, got)
} }

View file

@ -45,6 +45,7 @@ type ArgumentMarshaling struct {
// UnmarshalJSON implements json.Unmarshaler interface. // UnmarshalJSON implements json.Unmarshaler interface.
func (argument *Argument) UnmarshalJSON(data []byte) error { func (argument *Argument) UnmarshalJSON(data []byte) error {
var arg ArgumentMarshaling var arg ArgumentMarshaling
err := json.Unmarshal(data, &arg) err := json.Unmarshal(data, &arg)
if err != nil { if err != nil {
return fmt.Errorf("argument json err: %v", err) return fmt.Errorf("argument json err: %v", err)
@ -54,6 +55,7 @@ func (argument *Argument) UnmarshalJSON(data []byte) error {
if err != nil { if err != nil {
return err return err
} }
argument.Name = arg.Name argument.Name = arg.Name
argument.Indexed = arg.Indexed argument.Indexed = arg.Indexed
@ -63,11 +65,13 @@ func (argument *Argument) UnmarshalJSON(data []byte) error {
// NonIndexed returns the arguments with indexed arguments filtered out. // NonIndexed returns the arguments with indexed arguments filtered out.
func (arguments Arguments) NonIndexed() Arguments { func (arguments Arguments) NonIndexed() Arguments {
var ret []Argument var ret []Argument
for _, arg := range arguments { for _, arg := range arguments {
if !arg.Indexed { if !arg.Indexed {
ret = append(ret, arg) ret = append(ret, arg)
} }
} }
return ret return ret
} }
@ -82,8 +86,10 @@ func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
if len(arguments.NonIndexed()) != 0 { if len(arguments.NonIndexed()) != 0 {
return nil, errors.New("abi: attempting to unmarshall an empty string while arguments are expected") return nil, errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
} }
return make([]interface{}, 0), nil return make([]interface{}, 0), nil
} }
return arguments.UnpackValues(data) return arguments.UnpackValues(data)
} }
@ -93,19 +99,24 @@ func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte)
if v == nil { if v == nil {
return errors.New("abi: cannot unpack into a nil map") return errors.New("abi: cannot unpack into a nil map")
} }
if len(data) == 0 { if len(data) == 0 {
if len(arguments.NonIndexed()) != 0 { if len(arguments.NonIndexed()) != 0 {
return errors.New("abi: attempting to unmarshall an empty string while arguments are expected") return errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
} }
return nil // Nothing to unmarshal, return return nil // Nothing to unmarshal, return
} }
marshalledValues, err := arguments.UnpackValues(data) marshalledValues, err := arguments.UnpackValues(data)
if err != nil { if err != nil {
return err return err
} }
for i, arg := range arguments.NonIndexed() { for i, arg := range arguments.NonIndexed() {
v[arg.Name] = marshalledValues[i] v[arg.Name] = marshalledValues[i]
} }
return nil return nil
} }
@ -115,15 +126,19 @@ func (arguments Arguments) Copy(v interface{}, values []interface{}) error {
if reflect.Ptr != reflect.ValueOf(v).Kind() { if reflect.Ptr != reflect.ValueOf(v).Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v) return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
} }
if len(values) == 0 { if len(values) == 0 {
if len(arguments.NonIndexed()) != 0 { if len(arguments.NonIndexed()) != 0 {
return errors.New("abi: attempting to copy no values while arguments are expected") return errors.New("abi: attempting to copy no values while arguments are expected")
} }
return nil // Nothing to copy, return return nil // Nothing to copy, return
} }
if arguments.isTuple() { if arguments.isTuple() {
return arguments.copyTuple(v, values) return arguments.copyTuple(v, values)
} }
return arguments.copyAtomic(v, values[0]) return arguments.copyAtomic(v, values[0])
} }
@ -135,6 +150,7 @@ func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{
if dst.Kind() == reflect.Struct { if dst.Kind() == reflect.Struct {
return set(dst.Field(0), src) return set(dst.Field(0), src)
} }
return set(dst, src) return set(dst, src)
} }
@ -149,16 +165,20 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
for i, arg := range nonIndexedArgs { for i, arg := range nonIndexedArgs {
argNames[i] = arg.Name argNames[i] = arg.Name
} }
var err error var err error
abi2struct, err := mapArgNamesToStructFields(argNames, value) abi2struct, err := mapArgNamesToStructFields(argNames, value)
if err != nil { if err != nil {
return err return err
} }
for i, arg := range nonIndexedArgs { for i, arg := range nonIndexedArgs {
field := value.FieldByName(abi2struct[arg.Name]) field := value.FieldByName(abi2struct[arg.Name])
if !field.IsValid() { if !field.IsValid() {
return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name) return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
} }
if err := set(field, reflect.ValueOf(marshalledValues[i])); err != nil { if err := set(field, reflect.ValueOf(marshalledValues[i])); err != nil {
return err return err
} }
@ -167,6 +187,7 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
if value.Len() < len(marshalledValues) { if value.Len() < len(marshalledValues) {
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len()) return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
} }
for i := range nonIndexedArgs { for i := range nonIndexedArgs {
if err := set(value.Index(i), reflect.ValueOf(marshalledValues[i])); err != nil { if err := set(value.Index(i), reflect.ValueOf(marshalledValues[i])); err != nil {
return err return err
@ -175,6 +196,7 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
default: default:
return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", value.Type()) return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", value.Type())
} }
return nil return nil
} }
@ -184,12 +206,14 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) { func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
nonIndexedArgs := arguments.NonIndexed() nonIndexedArgs := arguments.NonIndexed()
retval := make([]interface{}, 0, len(nonIndexedArgs)) retval := make([]interface{}, 0, len(nonIndexedArgs))
virtualArgs := 0 virtualArgs := 0
for index, arg := range nonIndexedArgs { for index, arg := range nonIndexedArgs {
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data) marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) { if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
// If we have a static array, like [3]uint256, these are coded as // If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256. // just like uint256,uint256,uint256.
@ -207,8 +231,10 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
// coded as just like uint256,bool,uint256 // coded as just like uint256,bool,uint256
virtualArgs += getTypeSize(arg.Type)/32 - 1 virtualArgs += getTypeSize(arg.Type)/32 - 1
} }
retval = append(retval, marshalledValue) retval = append(retval, marshalledValue)
} }
return retval, nil return retval, nil
} }
@ -234,7 +260,9 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
for _, abiArg := range abiArgs { for _, abiArg := range abiArgs {
inputOffset += getTypeSize(abiArg.Type) inputOffset += getTypeSize(abiArg.Type)
} }
var ret []byte var ret []byte
for i, a := range args { for i, a := range args {
input := abiArgs[i] input := abiArgs[i]
// pack the input // pack the input
@ -269,5 +297,6 @@ func ToCamelCase(input string) string {
parts[i] = strings.ToUpper(s[:1]) + s[1:] parts[i] = strings.ToUpper(s[:1]) + s[1:]
} }
} }
return strings.Join(parts, "") return strings.Join(parts, "")
} }

View file

@ -44,14 +44,17 @@ var ErrNotAuthorized = errors.New("not authorized to sign this account")
// Deprecated: Use NewTransactorWithChainID instead. // Deprecated: Use NewTransactorWithChainID instead.
func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) { func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID") log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID")
json, err := io.ReadAll(keyin) json, err := io.ReadAll(keyin)
if err != nil { if err != nil {
return nil, err return nil, err
} }
key, err := keystore.DecryptKey(json, passphrase) key, err := keystore.DecryptKey(json, passphrase)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return NewKeyedTransactor(key.PrivateKey), nil return NewKeyedTransactor(key.PrivateKey), nil
} }
@ -61,7 +64,9 @@ func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
// Deprecated: Use NewKeyStoreTransactorWithChainID instead. // Deprecated: Use NewKeyStoreTransactorWithChainID instead.
func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) { func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) {
log.Warn("WARNING: NewKeyStoreTransactor has been deprecated in favour of NewTransactorWithChainID") log.Warn("WARNING: NewKeyStoreTransactor has been deprecated in favour of NewTransactorWithChainID")
signer := types.HomesteadSigner{} signer := types.HomesteadSigner{}
return &TransactOpts{ return &TransactOpts{
From: account.Address, From: account.Address,
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
@ -84,8 +89,10 @@ func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account
// Deprecated: Use NewKeyedTransactorWithChainID instead. // Deprecated: Use NewKeyedTransactorWithChainID instead.
func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts { func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
log.Warn("WARNING: NewKeyedTransactor has been deprecated in favour of NewKeyedTransactorWithChainID") log.Warn("WARNING: NewKeyedTransactor has been deprecated in favour of NewKeyedTransactorWithChainID")
keyAddr := crypto.PubkeyToAddress(key.PublicKey) keyAddr := crypto.PubkeyToAddress(key.PublicKey)
signer := types.HomesteadSigner{} signer := types.HomesteadSigner{}
return &TransactOpts{ return &TransactOpts{
From: keyAddr, From: keyAddr,
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
@ -109,10 +116,12 @@ func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.I
if err != nil { if err != nil {
return nil, err return nil, err
} }
key, err := keystore.DecryptKey(json, passphrase) key, err := keystore.DecryptKey(json, passphrase)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return NewKeyedTransactorWithChainID(key.PrivateKey, chainID) return NewKeyedTransactorWithChainID(key.PrivateKey, chainID)
} }
@ -122,7 +131,9 @@ func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accou
if chainID == nil { if chainID == nil {
return nil, ErrNoChainID return nil, ErrNoChainID
} }
signer := types.LatestSignerForChainID(chainID) signer := types.LatestSignerForChainID(chainID)
return &TransactOpts{ return &TransactOpts{
From: account.Address, From: account.Address,
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
@ -143,10 +154,13 @@ func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accou
// from a single private key. // from a single private key.
func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) { func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) {
keyAddr := crypto.PubkeyToAddress(key.PublicKey) keyAddr := crypto.PubkeyToAddress(key.PublicKey)
if chainID == nil { if chainID == nil {
return nil, ErrNoChainID return nil, ErrNoChainID
} }
signer := types.LatestSignerForChainID(chainID) signer := types.LatestSignerForChainID(chainID)
return &TransactOpts{ return &TransactOpts{
From: keyAddr, From: keyAddr,
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {

View file

@ -15,6 +15,7 @@ func (fb *filterBackend) GetBorBlockReceipt(ctx context.Context, hash common.Has
if number == nil { if number == nil {
return nil, nil return nil, nil
} }
receipt := rawdb.ReadRawBorReceipt(fb.db, hash, *number) receipt := rawdb.ReadRawBorReceipt(fb.db, hash, *number)
if receipt == nil { if receipt == nil {
return nil, nil return nil, nil

View file

@ -99,6 +99,7 @@ func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.Genesis
block := backend.blockchain.GetBlock(header.Hash(), header.Number.Uint64()) block := backend.blockchain.GetBlock(header.Hash(), header.Number.Uint64())
backend.rollback(block) backend.rollback(block)
return backend return backend
} }
@ -171,11 +172,14 @@ func (b *SimulatedBackend) Fork(ctx context.Context, parent common.Hash) error {
if len(b.pendingBlock.Transactions()) != 0 { if len(b.pendingBlock.Transactions()) != 0 {
return errors.New("pending block dirty") return errors.New("pending block dirty")
} }
block, err := b.blockByHash(ctx, parent) block, err := b.blockByHash(ctx, parent)
if err != nil { if err != nil {
return err return err
} }
b.rollback(block) b.rollback(block)
return nil return nil
} }
@ -184,10 +188,12 @@ func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *
if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number) == 0 { if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number) == 0 {
return b.blockchain.State() return b.blockchain.State()
} }
block, err := b.blockByNumber(ctx, blockNumber) block, err := b.blockByNumber(ctx, blockNumber)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return b.blockchain.StateAt(block.Root()) return b.blockchain.StateAt(block.Root())
} }
@ -241,6 +247,7 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
} }
val := stateDB.GetState(contract, key) val := stateDB.GetState(contract, key)
return val[:], nil return val[:], nil
} }
@ -253,6 +260,7 @@ func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common
if receipt == nil { if receipt == nil {
return nil, ethereum.NotFound return nil, ethereum.NotFound
} }
return receipt, nil return receipt, nil
} }
@ -268,10 +276,12 @@ func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.
if tx != nil { if tx != nil {
return tx, true, nil return tx, true, nil
} }
tx, _, _, _ = rawdb.ReadTransaction(b.database, txHash) tx, _, _, _ = rawdb.ReadTransaction(b.database, txHash)
if tx != nil { if tx != nil {
return tx, false, nil return tx, false, nil
} }
return nil, false, ethereum.NotFound return nil, false, ethereum.NotFound
} }
@ -406,9 +416,11 @@ func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Ad
func newRevertError(result *core.ExecutionResult) *revertError { func newRevertError(result *core.ExecutionResult) *revertError {
reason, errUnpack := abi.UnpackRevert(result.Revert()) reason, errUnpack := abi.UnpackRevert(result.Revert())
err := errors.New("execution reverted") err := errors.New("execution reverted")
if errUnpack == nil { if errUnpack == nil {
err = fmt.Errorf("execution reverted: %v", reason) err = fmt.Errorf("execution reverted: %v", reason)
} }
return &revertError{ return &revertError{
error: err, error: err,
reason: hexutil.Encode(result.Revert()), reason: hexutil.Encode(result.Revert()),
@ -441,10 +453,12 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number) != 0 { if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number) != 0 {
return nil, errBlockNumberUnsupported return nil, errBlockNumberUnsupported
} }
stateDB, err := b.blockchain.State() stateDB, err := b.blockchain.State()
if err != nil { if err != nil {
return nil, err return nil, err
} }
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), stateDB) res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), stateDB)
if err != nil { if err != nil {
return nil, err return nil, err
@ -453,6 +467,7 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
if len(res.Revert()) > 0 { if len(res.Revert()) > 0 {
return nil, newRevertError(res) return nil, newRevertError(res)
} }
return res.Return(), res.Err return res.Return(), res.Err
} }
@ -470,6 +485,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
if len(res.Revert()) > 0 { if len(res.Revert()) > 0 {
return nil, newRevertError(res) return nil, newRevertError(res)
} }
return res.Return(), res.Err return res.Return(), res.Err
} }
@ -491,6 +507,7 @@ func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error
if b.pendingBlock.Header().BaseFee != nil { if b.pendingBlock.Header().BaseFee != nil {
return b.pendingBlock.Header().BaseFee, nil return b.pendingBlock.Header().BaseFee, nil
} }
return big.NewInt(1), nil return big.NewInt(1), nil
} }
@ -512,6 +529,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
hi uint64 hi uint64
cap uint64 cap uint64
) )
if call.Gas >= params.TxGas { if call.Gas >= params.TxGas {
hi = call.Gas hi = call.Gas
} else { } else {
@ -519,6 +537,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
} }
// Normalize the max fee per gas the call is willing to spend. // Normalize the max fee per gas the call is willing to spend.
var feeCap *big.Int var feeCap *big.Int
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) { if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
} else if call.GasPrice != nil { } else if call.GasPrice != nil {
@ -531,24 +550,30 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
// Recap the highest gas allowance with account's balance. // Recap the highest gas allowance with account's balance.
if feeCap.BitLen() != 0 { if feeCap.BitLen() != 0 {
balance := b.pendingState.GetBalance(call.From) // from can't be nil balance := b.pendingState.GetBalance(call.From) // from can't be nil
available := new(big.Int).Set(balance) available := new(big.Int).Set(balance)
if call.Value != nil { if call.Value != nil {
if call.Value.Cmp(available) >= 0 { if call.Value.Cmp(available) >= 0 {
return 0, core.ErrInsufficientFundsForTransfer return 0, core.ErrInsufficientFundsForTransfer
} }
available.Sub(available, call.Value) available.Sub(available, call.Value)
} }
allowance := new(big.Int).Div(available, feeCap) allowance := new(big.Int).Div(available, feeCap)
if allowance.IsUint64() && hi > allowance.Uint64() { if allowance.IsUint64() && hi > allowance.Uint64() {
transfer := call.Value transfer := call.Value
if transfer == nil { if transfer == nil {
transfer = new(big.Int) transfer = new(big.Int)
} }
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance, log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
"sent", transfer, "feecap", feeCap, "fundable", allowance) "sent", transfer, "feecap", feeCap, "fundable", allowance)
hi = allowance.Uint64() hi = allowance.Uint64()
} }
} }
cap = hi cap = hi
// Create a helper to check if a gas allowance results in an executable transaction // Create a helper to check if a gas allowance results in an executable transaction
@ -563,8 +588,10 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
if errors.Is(err, core.ErrIntrinsicGas) { if errors.Is(err, core.ErrIntrinsicGas) {
return true, nil, nil // Special case, raise gas limit return true, nil, nil // Special case, raise gas limit
} }
return true, nil, err // Bail out return true, nil, err // Bail out
} }
return res.Failed(), res, nil return res.Failed(), res, nil
} }
// Execute the binary search and hone in on an executable gas limit // Execute the binary search and hone in on an executable gas limit
@ -578,6 +605,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
if err != nil { if err != nil {
return 0, err return 0, err
} }
if failed { if failed {
lo = mid lo = mid
} else { } else {
@ -590,17 +618,20 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
if err != nil { if err != nil {
return 0, err return 0, err
} }
if failed { if failed {
if result != nil && result.Err != vm.ErrOutOfGas { if result != nil && result.Err != vm.ErrOutOfGas {
if len(result.Revert()) > 0 { if len(result.Revert()) > 0 {
return 0, newRevertError(result) return 0, newRevertError(result)
} }
return 0, result.Err return 0, result.Err
} }
// Otherwise, the specified gas cap is too low // Otherwise, the specified gas cap is too low
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap) return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
} }
} }
return hi, nil return hi, nil
} }
@ -611,12 +642,14 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) { if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
} }
head := b.blockchain.CurrentHeader() head := b.blockchain.CurrentHeader()
if !b.blockchain.Config().IsLondon(head.Number) { if !b.blockchain.Config().IsLondon(head.Number) {
// If there's no basefee, then it must be a non-1559 execution // If there's no basefee, then it must be a non-1559 execution
if call.GasPrice == nil { if call.GasPrice == nil {
call.GasPrice = new(big.Int) call.GasPrice = new(big.Int)
} }
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
} else { } else {
// A basefee is provided, necessitating 1559-type execution // A basefee is provided, necessitating 1559-type execution
@ -628,6 +661,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
if call.GasFeeCap == nil { if call.GasFeeCap == nil {
call.GasFeeCap = new(big.Int) call.GasFeeCap = new(big.Int)
} }
if call.GasTipCap == nil { if call.GasTipCap == nil {
call.GasTipCap = new(big.Int) call.GasTipCap = new(big.Int)
} }
@ -642,6 +676,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
if call.Gas == 0 { if call.Gas == 0 {
call.Gas = 50000000 call.Gas = 50000000
} }
if call.Value == nil { if call.Value == nil {
call.Value = new(big.Int) call.Value = new(big.Int)
} }
@ -687,10 +722,12 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
} }
// Check transaction validity // Check transaction validity
signer := types.MakeSigner(b.blockchain.Config(), block.Number()) signer := types.MakeSigner(b.blockchain.Config(), block.Number())
sender, err := types.Sender(signer, tx) sender, err := types.Sender(signer, tx)
if err != nil { if err != nil {
return fmt.Errorf("invalid transaction: %v", err) return fmt.Errorf("invalid transaction: %v", err)
} }
nonce := b.pendingState.GetNonce(sender) nonce := b.pendingState.GetNonce(sender)
if tx.Nonce() != nonce { if tx.Nonce() != nonce {
return fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce) return fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)
@ -701,6 +738,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
for _, tx := range b.pendingBlock.Transactions() { for _, tx := range b.pendingBlock.Transactions() {
block.AddTxWithChain(b.blockchain, tx) block.AddTxWithChain(b.blockchain, tx)
} }
block.AddTxWithChain(b.blockchain, tx) block.AddTxWithChain(b.blockchain, tx)
}) })
stateDB, _ := b.blockchain.State() stateDB, _ := b.blockchain.State()
@ -708,6 +746,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
b.pendingBlock = blocks[0] b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil) b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
b.pendingReceipts = receipts[0] b.pendingReceipts = receipts[0]
return nil return nil
} }
@ -726,6 +765,7 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter
if query.FromBlock != nil { if query.FromBlock != nil {
from = query.FromBlock.Int64() from = query.FromBlock.Int64()
} }
to := int64(-1) to := int64(-1)
if query.ToBlock != nil { if query.ToBlock != nil {
to = query.ToBlock.Int64() to = query.ToBlock.Int64()
@ -738,10 +778,12 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter
if err != nil { if err != nil {
return nil, err return nil, err
} }
res := make([]types.Log, len(logs)) res := make([]types.Log, len(logs))
for i, nLog := range logs { for i, nLog := range logs {
res[i] = *nLog res[i] = *nLog
} }
return res, nil return res, nil
} }
@ -758,6 +800,7 @@ func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethere
// Since we're getting logs in batches, we need to flatten them into a plain stream // Since we're getting logs in batches, we need to flatten them into a plain stream
return event.NewSubscription(func(quit <-chan struct{}) error { return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe() defer sub.Unsubscribe()
for { for {
select { select {
case logs := <-sink: case logs := <-sink:
@ -787,6 +830,7 @@ func (b *SimulatedBackend) SubscribeNewHead(ctx context.Context, ch chan<- *type
return event.NewSubscription(func(quit <-chan struct{}) error { return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe() defer sub.Unsubscribe()
for { for {
select { select {
case head := <-sink: case head := <-sink:
@ -856,6 +900,7 @@ func (fb *filterBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNum
if block := fb.backend.pendingBlock; block != nil { if block := fb.backend.pendingBlock; block != nil {
return block.Header(), nil return block.Header(), nil
} }
return nil, nil return nil, nil
case rpc.LatestBlockNumber: case rpc.LatestBlockNumber:
return fb.bc.CurrentHeader(), nil return fb.bc.CurrentHeader(), nil
@ -889,6 +934,7 @@ func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (typ
if number == nil { if number == nil {
return nil, nil return nil, nil
} }
return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil
} }

View file

@ -44,6 +44,7 @@ func TestSimulatedBackend(t *testing.T) {
defer goleak.VerifyNone(t, leak.IgnoreList()...) defer goleak.VerifyNone(t, leak.IgnoreList()...)
var gasLimit uint64 = 8000029 var gasLimit uint64 = 8000029
key, _ := crypto.GenerateKey() // nolint: gosec key, _ := crypto.GenerateKey() // nolint: gosec
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337)) auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
genAlloc := make(core.GenesisAlloc) genAlloc := make(core.GenesisAlloc)
@ -59,6 +60,7 @@ func TestSimulatedBackend(t *testing.T) {
if isPending { if isPending {
t.Fatal("transaction should not be pending") t.Fatal("transaction should not be pending")
} }
if err != ethereum.NotFound { if err != ethereum.NotFound {
t.Fatalf("err should be `ethereum.NotFound` but received %v", err) t.Fatalf("err should be `ethereum.NotFound` but received %v", err)
} }
@ -68,6 +70,7 @@ func TestSimulatedBackend(t *testing.T) {
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
code := `6060604052600a8060106000396000f360606040526008565b00` code := `6060604052600a8060106000396000f360606040526008565b00`
var gas uint64 = 3000000 var gas uint64 = 3000000
tx := types.NewContractCreation(0, big.NewInt(0), gas, gasPrice, common.FromHex(code)) tx := types.NewContractCreation(0, big.NewInt(0), gas, gasPrice, common.FromHex(code))
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, key) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, key)
@ -79,18 +82,22 @@ func TestSimulatedBackend(t *testing.T) {
txHash = tx.Hash() txHash = tx.Hash()
_, isPending, err = sim.TransactionByHash(context.Background(), txHash) _, isPending, err = sim.TransactionByHash(context.Background(), txHash)
if err != nil { if err != nil {
t.Fatalf("error getting transaction with hash: %v", txHash.String()) t.Fatalf("error getting transaction with hash: %v", txHash.String())
} }
if !isPending { if !isPending {
t.Fatal("transaction should have pending status") t.Fatal("transaction should have pending status")
} }
sim.Commit() sim.Commit()
_, isPending, err = sim.TransactionByHash(context.Background(), txHash) _, isPending, err = sim.TransactionByHash(context.Background(), txHash)
if err != nil { if err != nil {
t.Fatalf("error getting transaction with hash: %v", txHash.String()) t.Fatalf("error getting transaction with hash: %v", txHash.String())
} }
if isPending { if isPending {
t.Fatal("transaction should not have pending status") t.Fatal("transaction should not have pending status")
} }
@ -128,6 +135,7 @@ func simTestBackend(testAddr common.Address) *SimulatedBackend {
func TestNewSimulatedBackend(t *testing.T) { func TestNewSimulatedBackend(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
expectedBal := big.NewInt(10000000000000000) expectedBal := big.NewInt(10000000000000000)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
@ -140,6 +148,7 @@ func TestNewSimulatedBackend(t *testing.T) {
} }
stateDB, _ := sim.blockchain.State() stateDB, _ := sim.blockchain.State()
bal := stateDB.GetBalance(testAddr) bal := stateDB.GetBalance(testAddr)
if bal.Cmp(expectedBal) != 0 { if bal.Cmp(expectedBal) != 0 {
t.Errorf("expected balance for test address not received. expected: %v actual: %v", expectedBal, bal) t.Errorf("expected balance for test address not received. expected: %v actual: %v", expectedBal, bal)
@ -153,9 +162,11 @@ func TestAdjustTime(t *testing.T) {
defer sim.Close() defer sim.Close()
prevTime := sim.pendingBlock.Time() prevTime := sim.pendingBlock.Time()
if err := sim.AdjustTime(time.Second); err != nil { if err := sim.AdjustTime(time.Second); err != nil {
t.Error(err) t.Error(err)
} }
newTime := sim.pendingBlock.Time() newTime := sim.pendingBlock.Time()
if newTime-prevTime != uint64(time.Second.Seconds()) { if newTime-prevTime != uint64(time.Second.Seconds()) {
@ -172,33 +183,41 @@ func TestNewAdjustTimeFail(t *testing.T) {
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
} }
sim.SendTransaction(context.Background(), signedTx) sim.SendTransaction(context.Background(), signedTx)
// AdjustTime should fail on non-empty block // AdjustTime should fail on non-empty block
if err := sim.AdjustTime(time.Second); err == nil { if err := sim.AdjustTime(time.Second); err == nil {
t.Error("Expected adjust time to error on non-empty block") t.Error("Expected adjust time to error on non-empty block")
} }
sim.Commit() sim.Commit()
prevTime := sim.pendingBlock.Time() prevTime := sim.pendingBlock.Time()
if err := sim.AdjustTime(time.Minute); err != nil { if err := sim.AdjustTime(time.Minute); err != nil {
t.Error(err) t.Error(err)
} }
newTime := sim.pendingBlock.Time() newTime := sim.pendingBlock.Time()
if newTime-prevTime != uint64(time.Minute.Seconds()) { if newTime-prevTime != uint64(time.Minute.Seconds()) {
t.Errorf("adjusted time not equal to a minute. prev: %v, new: %v", prevTime, newTime) t.Errorf("adjusted time not equal to a minute. prev: %v, new: %v", prevTime, newTime)
} }
// Put a transaction after adjusting time // Put a transaction after adjusting time
tx2 := types.NewTransaction(1, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) tx2 := types.NewTransaction(1, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx2, err := types.SignTx(tx2, types.HomesteadSigner{}, testKey) signedTx2, err := types.SignTx(tx2, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
} }
sim.SendTransaction(context.Background(), signedTx2) sim.SendTransaction(context.Background(), signedTx2)
sim.Commit() sim.Commit()
newTime = sim.pendingBlock.Time() newTime = sim.pendingBlock.Time()
if newTime-prevTime >= uint64(time.Minute.Seconds()) { if newTime-prevTime >= uint64(time.Minute.Seconds()) {
t.Errorf("time adjusted, but shouldn't be: prev: %v, new: %v", prevTime, newTime) t.Errorf("time adjusted, but shouldn't be: prev: %v, new: %v", prevTime, newTime)
@ -208,8 +227,10 @@ func TestNewAdjustTimeFail(t *testing.T) {
func TestBalanceAt(t *testing.T) { func TestBalanceAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
expectedBal := big.NewInt(10000000000000000) expectedBal := big.NewInt(10000000000000000)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
bal, err := sim.BalanceAt(bgCtx, testAddr, nil) bal, err := sim.BalanceAt(bgCtx, testAddr, nil)
@ -227,12 +248,14 @@ func TestBlockByHash(t *testing.T) {
core.GenesisAlloc{}, 10000000, core.GenesisAlloc{}, 10000000,
) )
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
block, err := sim.BlockByNumber(bgCtx, nil) block, err := sim.BlockByNumber(bgCtx, nil)
if err != nil { if err != nil {
t.Errorf("could not get recent block: %v", err) t.Errorf("could not get recent block: %v", err)
} }
blockByHash, err := sim.BlockByHash(bgCtx, block.Hash()) blockByHash, err := sim.BlockByHash(bgCtx, block.Hash())
if err != nil { if err != nil {
t.Errorf("could not get recent block: %v", err) t.Errorf("could not get recent block: %v", err)
@ -248,12 +271,14 @@ func TestBlockByNumber(t *testing.T) {
core.GenesisAlloc{}, 10000000, core.GenesisAlloc{}, 10000000,
) )
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
block, err := sim.BlockByNumber(bgCtx, nil) block, err := sim.BlockByNumber(bgCtx, nil)
if err != nil { if err != nil {
t.Errorf("could not get recent block: %v", err) t.Errorf("could not get recent block: %v", err)
} }
if block.NumberU64() != 0 { if block.NumberU64() != 0 {
t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64()) t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64())
} }
@ -265,6 +290,7 @@ func TestBlockByNumber(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not get recent block: %v", err) t.Errorf("could not get recent block: %v", err)
} }
if block.NumberU64() != 1 { if block.NumberU64() != 1 {
t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64()) t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64())
} }
@ -273,6 +299,7 @@ func TestBlockByNumber(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not get block by number: %v", err) t.Errorf("could not get block by number: %v", err)
} }
if blockByNumber.Hash() != block.Hash() { if blockByNumber.Hash() != block.Hash() {
t.Errorf("did not get the same block with height of 1 as before") t.Errorf("did not get the same block with height of 1 as before")
} }
@ -283,6 +310,7 @@ func TestNonceAt(t *testing.T) {
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
nonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(0)) nonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(0))
@ -299,6 +327,7 @@ func TestNonceAt(t *testing.T) {
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -309,6 +338,7 @@ func TestNonceAt(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not add tx to pending block: %v", err) t.Errorf("could not add tx to pending block: %v", err)
} }
sim.Commit() sim.Commit()
newNonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(1)) newNonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(1))
@ -326,6 +356,7 @@ func TestNonceAt(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("could not get nonce for test addr: %v", err) t.Fatalf("could not get nonce for test addr: %v", err)
} }
if newNonce != nonce+uint64(1) { if newNonce != nonce+uint64(1) {
t.Fatalf("received incorrect nonce. expected 1, got %v", nonce) t.Fatalf("received incorrect nonce. expected 1, got %v", nonce)
} }
@ -336,6 +367,7 @@ func TestSendTransaction(t *testing.T) {
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
// create a signed transaction to send // create a signed transaction to send
@ -343,6 +375,7 @@ func TestSendTransaction(t *testing.T) {
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -353,6 +386,7 @@ func TestSendTransaction(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not add tx to pending block: %v", err) t.Errorf("could not add tx to pending block: %v", err)
} }
sim.Commit() sim.Commit()
block, err := sim.BlockByNumber(bgCtx, big.NewInt(1)) block, err := sim.BlockByNumber(bgCtx, big.NewInt(1))
@ -374,6 +408,7 @@ func TestTransactionByHash(t *testing.T) {
}, 10000000, }, 10000000,
) )
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
// create a signed transaction to send // create a signed transaction to send
@ -381,6 +416,7 @@ func TestTransactionByHash(t *testing.T) {
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -397,9 +433,11 @@ func TestTransactionByHash(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err) t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
} }
if !pending { if !pending {
t.Errorf("expected transaction to be in pending state") t.Errorf("expected transaction to be in pending state")
} }
if receivedTx.Hash() != signedTx.Hash() { if receivedTx.Hash() != signedTx.Hash() {
t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash()) t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash())
} }
@ -411,9 +449,11 @@ func TestTransactionByHash(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err) t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
} }
if pending { if pending {
t.Errorf("expected transaction to not be in pending state") t.Errorf("expected transaction to not be in pending state")
} }
if receivedTx.Hash() != signedTx.Hash() { if receivedTx.Hash() != signedTx.Hash() {
t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash()) t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash())
} }
@ -431,6 +471,7 @@ func TestEstimateGas(t *testing.T) {
} }
*/ */
const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033" const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033"
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
@ -514,15 +555,18 @@ func TestEstimateGas(t *testing.T) {
Data: common.Hex2Bytes("e09fface"), Data: common.Hex2Bytes("e09fface"),
}, 21275, nil, nil}, }, 21275, nil, nil},
} }
for _, c := range cases { for _, c := range cases {
got, err := sim.EstimateGas(context.Background(), c.message) got, err := sim.EstimateGas(context.Background(), c.message)
if c.expectError != nil { if c.expectError != nil {
if err == nil { if err == nil {
t.Fatalf("Expect error, got nil") t.Fatalf("Expect error, got nil")
} }
if c.expectError.Error() != err.Error() { if c.expectError.Error() != err.Error() {
t.Fatalf("Expect error, want %v, got %v", c.expectError, err) t.Fatalf("Expect error, want %v, got %v", c.expectError, err)
} }
if c.expectData != nil { if c.expectData != nil {
if err, ok := err.(*revertError); !ok { if err, ok := err.(*revertError); !ok {
t.Fatalf("Expect revert error, got %T", err) t.Fatalf("Expect revert error, got %T", err)
@ -530,8 +574,10 @@ func TestEstimateGas(t *testing.T) {
t.Fatalf("Error data mismatch, want %v, got %v", c.expectData, err.ErrorData()) t.Fatalf("Error data mismatch, want %v, got %v", c.expectData, err.ErrorData())
} }
} }
continue continue
} }
if got != c.expect { if got != c.expect {
t.Fatalf("Gas estimation mismatch, want %d, got %d", c.expect, got) t.Fatalf("Gas estimation mismatch, want %d, got %d", c.expect, got)
} }
@ -546,6 +592,7 @@ func TestEstimateGasWithPrice(t *testing.T) {
defer sim.Close() defer sim.Close()
recipient := common.HexToAddress("deadbeef") recipient := common.HexToAddress("deadbeef")
var cases = []struct { var cases = []struct {
name string name string
message ethereum.CallMsg message ethereum.CallMsg
@ -608,20 +655,25 @@ func TestEstimateGasWithPrice(t *testing.T) {
Data: nil, Data: nil,
}, params.TxGas, errors.New("gas required exceeds allowance (20999)")}, // 20999=(2.2ether-0.1ether-1wei)/(1e14) }, params.TxGas, errors.New("gas required exceeds allowance (20999)")}, // 20999=(2.2ether-0.1ether-1wei)/(1e14)
} }
for i, c := range cases { for i, c := range cases {
got, err := sim.EstimateGas(context.Background(), c.message) got, err := sim.EstimateGas(context.Background(), c.message)
if c.expectError != nil { if c.expectError != nil {
if err == nil { if err == nil {
t.Fatalf("test %d: expect error, got nil", i) t.Fatalf("test %d: expect error, got nil", i)
} }
if c.expectError.Error() != err.Error() { if c.expectError.Error() != err.Error() {
t.Fatalf("test %d: expect error, want %v, got %v", i, c.expectError, err) t.Fatalf("test %d: expect error, want %v, got %v", i, c.expectError, err)
} }
continue continue
} }
if c.expectError == nil && err != nil { if c.expectError == nil && err != nil {
t.Fatalf("test %d: didn't expect error, got %v", i, err) t.Fatalf("test %d: didn't expect error, got %v", i, err)
} }
if got != c.expect { if got != c.expect {
t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got) t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got)
} }
@ -633,12 +685,14 @@ func TestHeaderByHash(t *testing.T) {
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
header, err := sim.HeaderByNumber(bgCtx, nil) header, err := sim.HeaderByNumber(bgCtx, nil)
if err != nil { if err != nil {
t.Errorf("could not get recent block: %v", err) t.Errorf("could not get recent block: %v", err)
} }
headerByHash, err := sim.HeaderByHash(bgCtx, header.Hash()) headerByHash, err := sim.HeaderByHash(bgCtx, header.Hash())
if err != nil { if err != nil {
t.Errorf("could not get recent block: %v", err) t.Errorf("could not get recent block: %v", err)
@ -654,12 +708,14 @@ func TestHeaderByNumber(t *testing.T) {
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
latestBlockHeader, err := sim.HeaderByNumber(bgCtx, nil) latestBlockHeader, err := sim.HeaderByNumber(bgCtx, nil)
if err != nil { if err != nil {
t.Errorf("could not get header for tip of chain: %v", err) t.Errorf("could not get header for tip of chain: %v", err)
} }
if latestBlockHeader == nil { if latestBlockHeader == nil {
t.Errorf("received a nil block header") t.Errorf("received a nil block header")
} else if latestBlockHeader.Number.Uint64() != uint64(0) { } else if latestBlockHeader.Number.Uint64() != uint64(0) {
@ -681,6 +737,7 @@ func TestHeaderByNumber(t *testing.T) {
if blockHeader.Hash() != latestBlockHeader.Hash() { if blockHeader.Hash() != latestBlockHeader.Hash() {
t.Errorf("block header and latest block header are not the same") t.Errorf("block header and latest block header are not the same")
} }
if blockHeader.Number.Int64() != int64(1) { if blockHeader.Number.Int64() != int64(1) {
t.Errorf("did not get blockheader for block 1. instead got block %v", blockHeader.Number.Int64()) t.Errorf("did not get blockheader for block 1. instead got block %v", blockHeader.Number.Int64())
} }
@ -700,7 +757,9 @@ func TestTransactionCount(t *testing.T) {
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
currentBlock, err := sim.BlockByNumber(bgCtx, nil) currentBlock, err := sim.BlockByNumber(bgCtx, nil)
if err != nil || currentBlock == nil { if err != nil || currentBlock == nil {
t.Error("could not get current block") t.Error("could not get current block")
@ -719,6 +778,7 @@ func TestTransactionCount(t *testing.T) {
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -752,12 +812,14 @@ func TestTransactionInBlock(t *testing.T) {
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
transaction, err := sim.TransactionInBlock(bgCtx, sim.pendingBlock.Hash(), uint(0)) transaction, err := sim.TransactionInBlock(bgCtx, sim.pendingBlock.Hash(), uint(0))
if err == nil && err != errTransactionDoesNotExist { if err == nil && err != errTransactionDoesNotExist {
t.Errorf("expected a transaction does not exist error to be received but received %v", err) t.Errorf("expected a transaction does not exist error to be received but received %v", err)
} }
if transaction != nil { if transaction != nil {
t.Errorf("expected transaction to be nil but received %v", transaction) t.Errorf("expected transaction to be nil but received %v", transaction)
} }
@ -776,6 +838,7 @@ func TestTransactionInBlock(t *testing.T) {
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -798,6 +861,7 @@ func TestTransactionInBlock(t *testing.T) {
if err == nil && err != errTransactionDoesNotExist { if err == nil && err != errTransactionDoesNotExist {
t.Errorf("expected a transaction does not exist error to be received but received %v", err) t.Errorf("expected a transaction does not exist error to be received but received %v", err)
} }
if transaction != nil { if transaction != nil {
t.Errorf("expected transaction to be nil but received %v", transaction) t.Errorf("expected transaction to be nil but received %v", transaction)
} }
@ -817,6 +881,7 @@ func TestPendingNonceAt(t *testing.T) {
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
// expect pending nonce to be 0 since account has not been used // expect pending nonce to be 0 since account has not been used
@ -834,6 +899,7 @@ func TestPendingNonceAt(t *testing.T) {
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -857,10 +923,12 @@ func TestPendingNonceAt(t *testing.T) {
// make a new transaction with a nonce of 1 // make a new transaction with a nonce of 1
tx = types.NewTransaction(uint64(1), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) tx = types.NewTransaction(uint64(1), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err = types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err = types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
} }
err = sim.SendTransaction(bgCtx, signedTx) err = sim.SendTransaction(bgCtx, signedTx)
if err != nil { if err != nil {
t.Errorf("could not send tx: %v", err) t.Errorf("could not send tx: %v", err)
@ -882,6 +950,7 @@ func TestTransactionReceipt(t *testing.T) {
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
// create a signed transaction to send // create a signed transaction to send
@ -889,6 +958,7 @@ func TestTransactionReceipt(t *testing.T) {
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil) tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey) signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil { if err != nil {
t.Errorf("could not sign tx: %v", err) t.Errorf("could not sign tx: %v", err)
@ -899,6 +969,7 @@ func TestTransactionReceipt(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not add tx to pending block: %v", err) t.Errorf("could not add tx to pending block: %v", err)
} }
sim.Commit() sim.Commit()
receipt, err := sim.TransactionReceipt(bgCtx, signedTx.Hash()) receipt, err := sim.TransactionReceipt(bgCtx, signedTx.Hash())
@ -917,11 +988,14 @@ func TestSuggestGasPrice(t *testing.T) {
10000000, 10000000,
) )
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
gasPrice, err := sim.SuggestGasPrice(bgCtx) gasPrice, err := sim.SuggestGasPrice(bgCtx)
if err != nil { if err != nil {
t.Errorf("could not get gas price: %v", err) t.Errorf("could not get gas price: %v", err)
} }
if gasPrice.Uint64() != sim.pendingBlock.Header().BaseFee.Uint64() { if gasPrice.Uint64() != sim.pendingBlock.Header().BaseFee.Uint64() {
t.Errorf("gas price was not expected value of %v. actual: %v", sim.pendingBlock.Header().BaseFee.Uint64(), gasPrice.Uint64()) t.Errorf("gas price was not expected value of %v. actual: %v", sim.pendingBlock.Header().BaseFee.Uint64(), gasPrice.Uint64())
} }
@ -929,13 +1003,17 @@ func TestSuggestGasPrice(t *testing.T) {
func TestPendingCodeAt(t *testing.T) { func TestPendingCodeAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
code, err := sim.CodeAt(bgCtx, testAddr, nil) code, err := sim.CodeAt(bgCtx, testAddr, nil)
if err != nil { if err != nil {
t.Errorf("could not get code at test addr: %v", err) t.Errorf("could not get code at test addr: %v", err)
} }
if len(code) != 0 { if len(code) != 0 {
t.Errorf("got code for account that does not have contract code") t.Errorf("got code for account that does not have contract code")
} }
@ -944,7 +1022,9 @@ func TestPendingCodeAt(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not get code at test addr: %v", err) t.Errorf("could not get code at test addr: %v", err)
} }
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim) contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
if err != nil { if err != nil {
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract) t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
@ -954,6 +1034,7 @@ func TestPendingCodeAt(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not get code at test addr: %v", err) t.Errorf("could not get code at test addr: %v", err)
} }
if len(code) == 0 { if len(code) == 0 {
t.Errorf("did not get code for account that has contract code") t.Errorf("did not get code for account that has contract code")
} }
@ -965,13 +1046,17 @@ func TestPendingCodeAt(t *testing.T) {
func TestCodeAt(t *testing.T) { func TestCodeAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
code, err := sim.CodeAt(bgCtx, testAddr, nil) code, err := sim.CodeAt(bgCtx, testAddr, nil)
if err != nil { if err != nil {
t.Errorf("could not get code at test addr: %v", err) t.Errorf("could not get code at test addr: %v", err)
} }
if len(code) != 0 { if len(code) != 0 {
t.Errorf("got code for account that does not have contract code") t.Errorf("got code for account that does not have contract code")
} }
@ -980,17 +1065,21 @@ func TestCodeAt(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not get code at test addr: %v", err) t.Errorf("could not get code at test addr: %v", err)
} }
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim) contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
if err != nil { if err != nil {
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract) t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
} }
sim.Commit() sim.Commit()
code, err = sim.CodeAt(bgCtx, contractAddr, nil) code, err = sim.CodeAt(bgCtx, contractAddr, nil)
if err != nil { if err != nil {
t.Errorf("could not get code at test addr: %v", err) t.Errorf("could not get code at test addr: %v", err)
} }
if len(code) == 0 { if len(code) == 0 {
t.Errorf("did not get code for account that has contract code") t.Errorf("did not get code for account that has contract code")
} }
@ -1005,15 +1094,19 @@ func TestCodeAt(t *testing.T) {
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]} // receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
func TestPendingAndCallContract(t *testing.T) { func TestPendingAndCallContract(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
parsed, err := abi.JSON(strings.NewReader(abiJSON)) parsed, err := abi.JSON(strings.NewReader(abiJSON))
if err != nil { if err != nil {
t.Errorf("could not get code at test addr: %v", err) t.Errorf("could not get code at test addr: %v", err)
} }
contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(abiBin), sim) addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(abiBin), sim)
if err != nil { if err != nil {
t.Errorf("could not deploy contract: %v", err) t.Errorf("could not deploy contract: %v", err)
@ -1033,6 +1126,7 @@ func TestPendingAndCallContract(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not call receive method on contract: %v", err) t.Errorf("could not call receive method on contract: %v", err)
} }
if len(res) == 0 { if len(res) == 0 {
t.Errorf("result of contract call was empty: %v", res) t.Errorf("result of contract call was empty: %v", res)
} }
@ -1053,6 +1147,7 @@ func TestPendingAndCallContract(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not call receive method on contract: %v", err) t.Errorf("could not call receive method on contract: %v", err)
} }
if len(res) == 0 { if len(res) == 0 {
t.Errorf("result of contract call was empty: %v", res) t.Errorf("result of contract call was empty: %v", res)
} }
@ -1089,8 +1184,10 @@ contract Reverter {
}*/ }*/
func TestCallContractRevert(t *testing.T) { func TestCallContractRevert(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
reverterABI := `[{"inputs": [],"name": "noRevert","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertASM","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertNoString","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertString","outputs": [],"stateMutability": "pure","type": "function"}]` reverterABI := `[{"inputs": [],"name": "noRevert","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertASM","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertNoString","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertString","outputs": [],"stateMutability": "pure","type": "function"}]`
@ -1100,7 +1197,9 @@ func TestCallContractRevert(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not get code at test addr: %v", err) t.Errorf("could not get code at test addr: %v", err)
} }
contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(reverterBin), sim) addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(reverterBin), sim)
if err != nil { if err != nil {
t.Errorf("could not deploy contract: %v", err) t.Errorf("could not deploy contract: %v", err)
@ -1139,14 +1238,17 @@ func TestCallContractRevert(t *testing.T) {
if err == nil { if err == nil {
t.Errorf("call to %v was not reverted", key) t.Errorf("call to %v was not reverted", key)
} }
if res != nil { if res != nil {
t.Errorf("result from %v was not nil: %v", key, res) t.Errorf("result from %v was not nil: %v", key, res)
} }
if val != nil { if val != nil {
rerr, ok := err.(*revertError) rerr, ok := err.(*revertError)
if !ok { if !ok {
t.Errorf("expect revert error") t.Errorf("expect revert error")
} }
if rerr.Error() != "execution reverted: "+val.(string) { if rerr.Error() != "execution reverted: "+val.(string) {
t.Errorf("error was malformed: got %v want %v", rerr.Error(), val) t.Errorf("error was malformed: got %v want %v", rerr.Error(), val)
} }
@ -1157,17 +1259,21 @@ func TestCallContractRevert(t *testing.T) {
} }
} }
} }
input, err := parsed.Pack("noRevert") input, err := parsed.Pack("noRevert")
if err != nil { if err != nil {
t.Errorf("could not pack noRevert function on contract: %v", err) t.Errorf("could not pack noRevert function on contract: %v", err)
} }
res, err := cl(input) res, err := cl(input)
if err != nil { if err != nil {
t.Error("call to noRevert was reverted") t.Error("call to noRevert was reverted")
} }
if res == nil { if res == nil {
t.Errorf("result from noRevert was nil") t.Errorf("result from noRevert was nil")
} }
sim.Commit() sim.Commit()
} }
} }
@ -1184,6 +1290,7 @@ func TestCallContractRevert(t *testing.T) {
// having a chain length of just n+1 means that a reorg occurred. // having a chain length of just n+1 means that a reorg occurred.
func TestFork(t *testing.T) { func TestFork(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
// 1. // 1.
@ -1237,15 +1344,18 @@ const callableBin = "6080604052348015600f57600080fd5b5060998061001e6000396000f3f
// 10. Check that the event was reborn. // 10. Check that the event was reborn.
func TestForkLogsReborn(t *testing.T) { func TestForkLogsReborn(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
// 1. // 1.
parsed, _ := abi.JSON(strings.NewReader(callableAbi)) parsed, _ := abi.JSON(strings.NewReader(callableAbi))
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337)) auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
_, _, contract, err := bind.DeployContract(auth, parsed, common.FromHex(callableBin), sim) _, _, contract, err := bind.DeployContract(auth, parsed, common.FromHex(callableBin), sim)
if err != nil { if err != nil {
t.Errorf("deploying contract: %v", err) t.Errorf("deploying contract: %v", err)
} }
sim.Commit() sim.Commit()
// 2. // 2.
logs, sub, err := contract.WatchLogs(nil, "Called") logs, sub, err := contract.WatchLogs(nil, "Called")
@ -1260,12 +1370,14 @@ func TestForkLogsReborn(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("transacting: %v", err) t.Errorf("transacting: %v", err)
} }
sim.Commit() sim.Commit()
// 5. // 5.
log := <-logs log := <-logs
if log.TxHash != tx.Hash() { if log.TxHash != tx.Hash() {
t.Error("wrong event tx hash") t.Error("wrong event tx hash")
} }
if log.Removed { if log.Removed {
t.Error("Event should be included") t.Error("Event should be included")
} }
@ -1281,6 +1393,7 @@ func TestForkLogsReborn(t *testing.T) {
if log.TxHash != tx.Hash() { if log.TxHash != tx.Hash() {
t.Error("wrong event tx hash") t.Error("wrong event tx hash")
} }
if !log.Removed { if !log.Removed {
t.Error("Event should be removed") t.Error("Event should be removed")
} }
@ -1288,12 +1401,14 @@ func TestForkLogsReborn(t *testing.T) {
if err := sim.SendTransaction(context.Background(), tx); err != nil { if err := sim.SendTransaction(context.Background(), tx); err != nil {
t.Errorf("sending transaction: %v", err) t.Errorf("sending transaction: %v", err)
} }
sim.Commit() sim.Commit()
// 10. // 10.
log = <-logs log = <-logs
if log.TxHash != tx.Hash() { if log.TxHash != tx.Hash() {
t.Error("wrong event tx hash") t.Error("wrong event tx hash")
} }
if log.Removed { if log.Removed {
t.Error("Event should be included") t.Error("Event should be included")
} }
@ -1310,6 +1425,7 @@ func TestForkLogsReborn(t *testing.T) {
// 6. Check that the TX is now included in block 2. // 6. Check that the TX is now included in block 2.
func TestForkResendTx(t *testing.T) { func TestForkResendTx(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
// 1. // 1.
@ -1333,9 +1449,11 @@ func TestForkResendTx(t *testing.T) {
} }
// 5. // 5.
sim.Commit() sim.Commit()
if err := sim.SendTransaction(context.Background(), tx); err != nil { if err := sim.SendTransaction(context.Background(), tx); err != nil {
t.Errorf("sending transaction: %v", err) t.Errorf("sending transaction: %v", err)
} }
sim.Commit() sim.Commit()
// 6. // 6.
receipt, _ = sim.TransactionReceipt(context.Background(), tx.Hash()) receipt, _ = sim.TransactionReceipt(context.Background(), tx.Hash())

View file

@ -97,14 +97,17 @@ type MetaData struct {
func (m *MetaData) GetAbi() (*abi.ABI, error) { func (m *MetaData) GetAbi() (*abi.ABI, error) {
m.mu.Lock() m.mu.Lock()
defer m.mu.Unlock() defer m.mu.Unlock()
if m.ab != nil { if m.ab != nil {
return m.ab, nil return m.ab, nil
} }
if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil { if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil {
return nil, err return nil, err
} else { } else {
m.ab = &parsed m.ab = &parsed
} }
return m.ab, nil return m.ab, nil
} }
@ -141,11 +144,14 @@ func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend Co
if err != nil { if err != nil {
return common.Address{}, nil, nil, err return common.Address{}, nil, nil, err
} }
tx, err := c.transact(opts, nil, append(bytecode, input...)) tx, err := c.transact(opts, nil, append(bytecode, input...))
if err != nil { if err != nil {
return common.Address{}, nil, nil, err return common.Address{}, nil, nil, err
} }
c.address = crypto.CreateAddress(opts.From, tx.Nonce()) c.address = crypto.CreateAddress(opts.From, tx.Nonce())
return c.address, tx, c, nil return c.address, tx, c, nil
} }
@ -158,6 +164,7 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
if opts == nil { if opts == nil {
opts = new(CallOpts) opts = new(CallOpts)
} }
if results == nil { if results == nil {
results = new([]interface{}) results = new([]interface{})
} }
@ -166,21 +173,25 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
if err != nil { if err != nil {
return err return err
} }
var ( var (
msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input} msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input}
ctx = ensureContext(opts.Context) ctx = ensureContext(opts.Context)
code []byte code []byte
output []byte output []byte
) )
if opts.Pending { if opts.Pending {
pb, ok := c.caller.(PendingContractCaller) pb, ok := c.caller.(PendingContractCaller)
if !ok { if !ok {
return ErrNoPendingState return ErrNoPendingState
} }
output, err = pb.PendingCallContract(ctx, msg) output, err = pb.PendingCallContract(ctx, msg)
if err != nil { if err != nil {
return err return err
} }
if len(output) == 0 { if len(output) == 0 {
// Make sure we have a contract to operate on, and bail out otherwise. // Make sure we have a contract to operate on, and bail out otherwise.
if code, err = pb.PendingCodeAt(ctx, c.address); err != nil { if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
@ -194,6 +205,7 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
if err != nil { if err != nil {
return err return err
} }
if len(output) == 0 { if len(output) == 0 {
// Make sure we have a contract to operate on, and bail out otherwise. // 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 { if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil {
@ -207,9 +219,12 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
if len(*results) == 0 { if len(*results) == 0 {
res, err := c.abi.Unpack(method, output) res, err := c.abi.Unpack(method, output)
*results = res *results = res
return err return err
} }
res := *results res := *results
return c.abi.UnpackIntoInterface(res[0], method, output) return c.abi.UnpackIntoInterface(res[0], method, output)
} }
@ -254,6 +269,7 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
if err != nil { if err != nil {
return nil, err return nil, err
} }
gasTipCap = tip gasTipCap = tip
} }
// Estimate FeeCap // Estimate FeeCap
@ -264,13 +280,16 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
new(big.Int).Mul(head.BaseFee, big.NewInt(basefeeWiggleMultiplier)), new(big.Int).Mul(head.BaseFee, big.NewInt(basefeeWiggleMultiplier)),
) )
} }
if gasFeeCap.Cmp(gasTipCap) < 0 { if gasFeeCap.Cmp(gasTipCap) < 0 {
return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap) return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap)
} }
// Estimate GasLimit // Estimate GasLimit
gasLimit := opts.GasLimit gasLimit := opts.GasLimit
if opts.GasLimit == 0 { if opts.GasLimit == 0 {
var err error var err error
gasLimit, err = c.estimateGasLimit(opts, contract, input, nil, gasTipCap, gasFeeCap, value) gasLimit, err = c.estimateGasLimit(opts, contract, input, nil, gasTipCap, gasFeeCap, value)
if err != nil { if err != nil {
return nil, err return nil, err
@ -281,6 +300,7 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
if err != nil { if err != nil {
return nil, err return nil, err
} }
baseTx := &types.DynamicFeeTx{ baseTx := &types.DynamicFeeTx{
To: contract, To: contract,
Nonce: nonce, Nonce: nonce,
@ -290,6 +310,7 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
Value: value, Value: value,
Data: input, Data: input,
} }
return types.NewTx(baseTx), nil return types.NewTx(baseTx), nil
} }
@ -309,12 +330,15 @@ func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Addr
if err != nil { if err != nil {
return nil, err return nil, err
} }
gasPrice = price gasPrice = price
} }
// Estimate GasLimit // Estimate GasLimit
gasLimit := opts.GasLimit gasLimit := opts.GasLimit
if opts.GasLimit == 0 { if opts.GasLimit == 0 {
var err error var err error
gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value) gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value)
if err != nil { if err != nil {
return nil, err return nil, err
@ -325,6 +349,7 @@ func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Addr
if err != nil { if err != nil {
return nil, err return nil, err
} }
baseTx := &types.LegacyTx{ baseTx := &types.LegacyTx{
To: contract, To: contract,
Nonce: nonce, Nonce: nonce,
@ -333,6 +358,7 @@ func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Addr
Value: value, Value: value,
Data: input, Data: input,
} }
return types.NewTx(baseTx), nil return types.NewTx(baseTx), nil
} }
@ -345,6 +371,7 @@ func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Ad
return 0, ErrNoCode return 0, ErrNoCode
} }
} }
msg := ethereum.CallMsg{ msg := ethereum.CallMsg{
From: opts.From, From: opts.From,
To: contract, To: contract,
@ -354,6 +381,7 @@ func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Ad
Value: value, Value: value,
Data: input, Data: input,
} }
return c.transactor.EstimateGas(ensureContext(opts.Context), msg) return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
} }
@ -376,6 +404,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
rawTx *types.Transaction rawTx *types.Transaction
err error err error
) )
if opts.GasPrice != nil { if opts.GasPrice != nil {
rawTx, err = c.createLegacyTx(opts, contract, input) rawTx, err = c.createLegacyTx(opts, contract, input)
} else if opts.GasFeeCap != nil && opts.GasTipCap != nil { } else if opts.GasFeeCap != nil && opts.GasTipCap != nil {
@ -391,6 +420,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
rawTx, err = c.createLegacyTx(opts, contract, input) rawTx, err = c.createLegacyTx(opts, contract, input)
} }
} }
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -398,16 +428,20 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
if opts.Signer == nil { if opts.Signer == nil {
return nil, errors.New("no signer to authorize the transaction with") return nil, errors.New("no signer to authorize the transaction with")
} }
signedTx, err := opts.Signer(opts.From, rawTx) signedTx, err := opts.Signer(opts.From, rawTx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if opts.NoSend { if opts.NoSend {
return signedTx, nil return signedTx, nil
} }
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil { if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
return nil, err return nil, err
} }
return signedTx, nil return signedTx, nil
} }
@ -443,6 +477,7 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
sub, err := event.NewSubscription(func(quit <-chan struct{}) error { sub, err := event.NewSubscription(func(quit <-chan struct{}) error {
for _, log := range buff { for _, log := range buff {
select { select {
@ -451,12 +486,14 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
return nil return nil
} }
} }
return nil return nil
}), nil }), nil
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
return logs, sub, nil return logs, sub, nil
} }
@ -484,10 +521,12 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter
if opts.Start != nil { if opts.Start != nil {
config.FromBlock = new(big.Int).SetUint64(*opts.Start) config.FromBlock = new(big.Int).SetUint64(*opts.Start)
} }
sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs) sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
return logs, sub, nil return logs, sub, nil
} }
@ -497,20 +536,25 @@ func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log)
if len(log.Topics) == 0 { if len(log.Topics) == 0 {
return errNoEventSignature return errNoEventSignature
} }
if log.Topics[0] != c.abi.Events[event].ID { if log.Topics[0] != c.abi.Events[event].ID {
return errEventSignatureMismatch return errEventSignatureMismatch
} }
if len(log.Data) > 0 { if len(log.Data) > 0 {
if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil { if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
return err return err
} }
} }
var indexed abi.Arguments var indexed abi.Arguments
for _, arg := range c.abi.Events[event].Inputs { for _, arg := range c.abi.Events[event].Inputs {
if arg.Indexed { if arg.Indexed {
indexed = append(indexed, arg) indexed = append(indexed, arg)
} }
} }
return abi.ParseTopics(out, indexed, log.Topics[1:]) return abi.ParseTopics(out, indexed, log.Topics[1:])
} }
@ -520,20 +564,25 @@ func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event strin
if len(log.Topics) == 0 { if len(log.Topics) == 0 {
return errNoEventSignature return errNoEventSignature
} }
if log.Topics[0] != c.abi.Events[event].ID { if log.Topics[0] != c.abi.Events[event].ID {
return errEventSignatureMismatch return errEventSignatureMismatch
} }
if len(log.Data) > 0 { if len(log.Data) > 0 {
if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil { if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
return err return err
} }
} }
var indexed abi.Arguments var indexed abi.Arguments
for _, arg := range c.abi.Events[event].Inputs { for _, arg := range c.abi.Events[event].Inputs {
if arg.Indexed { if arg.Indexed {
indexed = append(indexed, arg) indexed = append(indexed, arg)
} }
} }
return abi.ParseTopicsIntoMap(out, indexed, log.Topics[1:]) return abi.ParseTopicsIntoMap(out, indexed, log.Topics[1:])
} }
@ -543,5 +592,6 @@ func ensureContext(ctx context.Context) context.Context {
if ctx == nil { if ctx == nil {
return context.Background() return context.Background()
} }
return ctx return ctx
} }

View file

@ -214,6 +214,7 @@ func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
hash := crypto.Keccak256Hash(sliceBytes) hash := crypto.Keccak256Hash(sliceBytes)
topics := []common.Hash{ topics := []common.Hash{
crypto.Keccak256Hash([]byte("received(string[],address,uint256,bytes)")), crypto.Keccak256Hash([]byte("received(string[],address,uint256,bytes)")),
@ -239,6 +240,7 @@ func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
hash := crypto.Keccak256Hash(arrBytes) hash := crypto.Keccak256Hash(arrBytes)
topics := []common.Hash{ topics := []common.Hash{
crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")), crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")),
@ -265,7 +267,9 @@ func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)")) hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)"))
functionSelector := hash[:4] functionSelector := hash[:4]
functionTyBytes := append(addrBytes, functionSelector...) functionTyBytes := append(addrBytes, functionSelector...)
var functionTy [24]byte var functionTy [24]byte
copy(functionTy[:], functionTyBytes[0:24]) copy(functionTy[:], functionTyBytes[0:24])
topics := []common.Hash{ topics := []common.Hash{
crypto.Keccak256Hash([]byte("received(function,address,uint256,bytes)")), crypto.Keccak256Hash([]byte("received(function,address,uint256,bytes)")),
@ -361,6 +365,7 @@ func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]in
if len(received) != len(expected) { if len(received) != len(expected) {
t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected)) t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected))
} }
for name, elem := range expected { for name, elem := range expected {
if !reflect.DeepEqual(elem, received[name]) { if !reflect.DeepEqual(elem, received[name]) {
t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name]) t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name])

View file

@ -92,6 +92,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
// isLib is the map used to flag each encountered library as such // isLib is the map used to flag each encountered library as such
isLib = make(map[string]struct{}) isLib = make(map[string]struct{})
) )
for i := 0; i < len(types); i++ { for i := 0; i < len(types); i++ {
// Parse the actual ABI to generate the binding for // Parse the actual ABI to generate the binding for
evmABI, err := abi.JSON(strings.NewReader(abis[i])) evmABI, err := abi.JSON(strings.NewReader(abis[i]))
@ -103,6 +104,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
if unicode.IsSpace(r) { if unicode.IsSpace(r) {
return -1 return -1
} }
return r return r
}, abis[i]) }, abis[i])
@ -139,28 +141,35 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
if !original.IsConstant() { if !original.IsConstant() {
identifiers = transactIdentifiers identifiers = transactIdentifiers
} }
if identifiers[normalizedName] { if identifiers[normalizedName] {
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
} }
identifiers[normalizedName] = true identifiers[normalizedName] = true
normalized.Name = normalizedName normalized.Name = normalizedName
normalized.Inputs = make([]abi.Argument, len(original.Inputs)) normalized.Inputs = make([]abi.Argument, len(original.Inputs))
copy(normalized.Inputs, original.Inputs) copy(normalized.Inputs, original.Inputs)
for j, input := range normalized.Inputs { for j, input := range normalized.Inputs {
if input.Name == "" || isKeyWord(input.Name) { if input.Name == "" || isKeyWord(input.Name) {
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
} }
if hasStruct(input.Type) { if hasStruct(input.Type) {
bindStructType[lang](input.Type, structs) bindStructType[lang](input.Type, structs)
} }
} }
normalized.Outputs = make([]abi.Argument, len(original.Outputs)) normalized.Outputs = make([]abi.Argument, len(original.Outputs))
copy(normalized.Outputs, original.Outputs) copy(normalized.Outputs, original.Outputs)
for j, output := range normalized.Outputs { for j, output := range normalized.Outputs {
if output.Name != "" { if output.Name != "" {
normalized.Outputs[j].Name = capitalise(output.Name) normalized.Outputs[j].Name = capitalise(output.Name)
} }
if hasStruct(output.Type) { if hasStruct(output.Type) {
bindStructType[lang](output.Type, structs) bindStructType[lang](output.Type, structs)
} }
@ -172,6 +181,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)} transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
} }
} }
for _, original := range evmABI.Events { for _, original := range evmABI.Events {
// Skip anonymous events as they don't support explicit filtering // Skip anonymous events as they don't support explicit filtering
if original.Anonymous { if original.Anonymous {
@ -185,12 +195,14 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
if eventIdentifiers[normalizedName] { if eventIdentifiers[normalizedName] {
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
} }
eventIdentifiers[normalizedName] = true eventIdentifiers[normalizedName] = true
normalized.Name = normalizedName normalized.Name = normalizedName
used := make(map[string]bool) used := make(map[string]bool)
normalized.Inputs = make([]abi.Argument, len(original.Inputs)) normalized.Inputs = make([]abi.Argument, len(original.Inputs))
copy(normalized.Inputs, original.Inputs) copy(normalized.Inputs, original.Inputs)
for j, input := range normalized.Inputs { for j, input := range normalized.Inputs {
if input.Name == "" || isKeyWord(input.Name) { if input.Name == "" || isKeyWord(input.Name) {
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
@ -202,8 +214,10 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
used[capitalise(normalized.Inputs[j].Name)] = true used[capitalise(normalized.Inputs[j].Name)] = true
break break
} }
normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index) normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index)
} }
if hasStruct(input.Type) { if hasStruct(input.Type) {
bindStructType[lang](input.Type, structs) bindStructType[lang](input.Type, structs)
} }
@ -215,9 +229,11 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
if evmABI.HasFallback() { if evmABI.HasFallback() {
fallback = &tmplMethod{Original: evmABI.Fallback} fallback = &tmplMethod{Original: evmABI.Fallback}
} }
if evmABI.HasReceive() { if evmABI.HasReceive() {
receive = &tmplMethod{Original: evmABI.Receive} receive = &tmplMethod{Original: evmABI.Receive}
} }
contracts[types[i]] = &tmplContract{ contracts[types[i]] = &tmplContract{
Type: capitalise(types[i]), Type: capitalise(types[i]),
InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""), InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
@ -241,6 +257,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
if err != nil { if err != nil {
log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err) log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
} }
if matched { if matched {
contracts[types[i]].Libraries[pattern] = name contracts[types[i]].Libraries[pattern] = name
// keep track that this type is a library // keep track that this type is a library
@ -271,6 +288,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
"capitalise": capitalise, "capitalise": capitalise,
"decapitalise": decapitalise, "decapitalise": decapitalise,
} }
tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang])) tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
if err := tmpl.Execute(buffer, data); err != nil { if err := tmpl.Execute(buffer, data); err != nil {
return "", err return "", err
@ -281,6 +299,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
if err != nil { if err != nil {
return "", fmt.Errorf("%v\n%s", err, buffer) return "", fmt.Errorf("%v\n%s", err, buffer)
} }
return string(code), nil return string(code), nil
} }
// For all others just return as is for now // For all others just return as is for now
@ -304,6 +323,7 @@ func bindBasicTypeGo(kind abi.Type) string {
case "8", "16", "32", "64": case "8", "16", "32", "64":
return fmt.Sprintf("%sint%s", parts[1], parts[2]) return fmt.Sprintf("%sint%s", parts[1], parts[2])
} }
return "*big.Int" return "*big.Int"
case abi.FixedBytesTy: case abi.FixedBytesTy:
return fmt.Sprintf("[%d]byte", kind.Size) return fmt.Sprintf("[%d]byte", kind.Size)
@ -353,6 +373,7 @@ func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
if bound == "string" || bound == "[]byte" { if bound == "string" || bound == "[]byte" {
bound = "common.Hash" bound = "common.Hash"
} }
return bound return bound
} }
@ -378,26 +399,32 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
if s, exist := structs[id]; exist { if s, exist := structs[id]; exist {
return s.Name return s.Name
} }
var ( var (
names = make(map[string]bool) names = make(map[string]bool)
fields []*tmplField fields []*tmplField
) )
for i, elem := range kind.TupleElems { for i, elem := range kind.TupleElems {
name := capitalise(kind.TupleRawNames[i]) name := capitalise(kind.TupleRawNames[i])
name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] }) name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] })
names[name] = true names[name] = true
fields = append(fields, &tmplField{Type: bindStructTypeGo(*elem, structs), Name: name, SolKind: *elem}) fields = append(fields, &tmplField{Type: bindStructTypeGo(*elem, structs), Name: name, SolKind: *elem})
} }
name := kind.TupleRawName name := kind.TupleRawName
if name == "" { if name == "" {
name = fmt.Sprintf("Struct%d", len(structs)) name = fmt.Sprintf("Struct%d", len(structs))
} }
name = capitalise(name) name = capitalise(name)
structs[id] = &tmplStruct{ structs[id] = &tmplStruct{
Name: name, Name: name,
Fields: fields, Fields: fields,
} }
return name return name
case abi.ArrayTy: case abi.ArrayTy:
return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs) return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs)
@ -420,6 +447,7 @@ func alias(aliases map[string]string, n string) string {
if alias, exist := aliases[n]; exist { if alias, exist := aliases[n]; exist {
return alias return alias
} }
return n return n
} }
@ -439,6 +467,7 @@ func decapitalise(input string) string {
} }
goForm := abi.ToCamelCase(input) goForm := abi.ToCamelCase(input)
return strings.ToLower(goForm[:1]) + goForm[1:] return strings.ToLower(goForm[:1]) + goForm[1:]
} }
@ -448,7 +477,9 @@ func structured(args abi.Arguments) bool {
if len(args) < 2 { if len(args) < 2 {
return false return false
} }
exists := make(map[string]bool) exists := make(map[string]bool)
for _, out := range args { for _, out := range args {
// If the name is anonymous, we can't organize into a struct // If the name is anonymous, we can't organize into a struct
if out.Name == "" { if out.Name == "" {
@ -460,8 +491,10 @@ func structured(args abi.Arguments) bool {
if field == "" || exists[field] { if field == "" || exists[field] {
return false return false
} }
exists[field] = true exists[field] = true
} }
return true return true
} }

View file

@ -2071,6 +2071,7 @@ func TestGolangBindings(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("test %d: failed to generate binding: %v", i, err) t.Fatalf("test %d: failed to generate binding: %v", i, err)
} }
if err = os.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+".go"), []byte(bind), 0600); err != nil { if err = os.WriteFile(filepath.Join(pkg, strings.ToLower(tt.name)+".go"), []byte(bind), 0600); err != nil {
t.Fatalf("test %d: failed to write binding: %v", i, err) t.Fatalf("test %d: failed to write binding: %v", i, err)
} }
@ -2095,23 +2096,29 @@ func TestGolangBindings(t *testing.T) {
// Convert the package to go modules and use the current source for go-ethereum // Convert the package to go modules and use the current source for go-ethereum
moder := exec.Command(gocmd, "mod", "init", "bindtest") moder := exec.Command(gocmd, "mod", "init", "bindtest")
moder.Dir = pkg moder.Dir = pkg
if out, err := moder.CombinedOutput(); err != nil { if out, err := moder.CombinedOutput(); err != nil {
t.Fatalf("failed to convert binding test to modules: %v\n%s", err, out) t.Fatalf("failed to convert binding test to modules: %v\n%s", err, out)
} }
pwd, _ := os.Getwd() pwd, _ := os.Getwd()
replacer := exec.Command(gocmd, "mod", "edit", "-x", "-require", "github.com/ethereum/go-ethereum@v0.0.0", "-replace", "github.com/ethereum/go-ethereum="+filepath.Join(pwd, "..", "..", "..")) // Repo root replacer := exec.Command(gocmd, "mod", "edit", "-x", "-require", "github.com/ethereum/go-ethereum@v0.0.0", "-replace", "github.com/ethereum/go-ethereum="+filepath.Join(pwd, "..", "..", "..")) // Repo root
replacer.Dir = pkg replacer.Dir = pkg
if out, err := replacer.CombinedOutput(); err != nil { if out, err := replacer.CombinedOutput(); err != nil {
t.Fatalf("failed to replace binding test dependency to current source tree: %v\n%s", err, out) t.Fatalf("failed to replace binding test dependency to current source tree: %v\n%s", err, out)
} }
tidier := exec.Command(gocmd, "mod", "tidy") tidier := exec.Command(gocmd, "mod", "tidy")
tidier.Dir = pkg tidier.Dir = pkg
if out, err := tidier.CombinedOutput(); err != nil { if out, err := tidier.CombinedOutput(); err != nil {
t.Fatalf("failed to tidy Go module file: %v\n%s", err, out) t.Fatalf("failed to tidy Go module file: %v\n%s", err, out)
} }
// Test the entire package and report any failures // Test the entire package and report any failures
cmd := exec.Command(gocmd, "test", "-v", "-count", "1") cmd := exec.Command(gocmd, "test", "-v", "-count", "1")
cmd.Dir = pkg cmd.Dir = pkg
if out, err := cmd.CombinedOutput(); err != nil { if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("failed to run binding test: %v\n%s", err, out) t.Fatalf("failed to run binding test: %v\n%s", err, out)
} }

View file

@ -34,6 +34,7 @@ func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*ty
defer queryTicker.Stop() defer queryTicker.Stop()
logger := log.New("hash", tx.Hash()) logger := log.New("hash", tx.Hash())
for { for {
receipt, err := b.TransactionReceipt(ctx, tx.Hash()) receipt, err := b.TransactionReceipt(ctx, tx.Hash())
if err == nil { if err == nil {
@ -61,10 +62,12 @@ func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (
if tx.To() != nil { if tx.To() != nil {
return common.Address{}, errors.New("tx is not contract creation") return common.Address{}, errors.New("tx is not contract creation")
} }
receipt, err := WaitMined(ctx, b, tx) receipt, err := WaitMined(ctx, b, tx)
if err != nil { if err != nil {
return common.Address{}, err return common.Address{}, err
} }
if receipt.ContractAddress == (common.Address{}) { if receipt.ContractAddress == (common.Address{}) {
return common.Address{}, errors.New("zero address") return common.Address{}, errors.New("zero address")
} }
@ -75,5 +78,6 @@ func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (
if err == nil && len(code) == 0 { if err == nil && len(code) == 0 {
err = ErrNoCodeAfterDeploy err = ErrNoCodeAfterDeploy
} }
return receipt.ContractAddress, err return receipt.ContractAddress, err
} }

View file

@ -76,8 +76,10 @@ func TestWaitDeployed(t *testing.T) {
mined = make(chan struct{}) mined = make(chan struct{})
ctx = context.Background() ctx = context.Background()
) )
go func() { go func() {
address, err = bind.WaitDeployed(ctx, backend, tx) address, err = bind.WaitDeployed(ctx, backend, tx)
close(mined) close(mined)
}() }()
@ -90,6 +92,7 @@ func TestWaitDeployed(t *testing.T) {
if err != test.wantErr { if err != test.wantErr {
t.Errorf("test %q: error mismatch: want %q, got %q", name, test.wantErr, err) t.Errorf("test %q: error mismatch: want %q, got %q", name, test.wantErr, err)
} }
if address != test.wantAddress { if address != test.wantAddress {
t.Errorf("test %q: unexpected contract address %s", name, address.Hex()) t.Errorf("test %q: unexpected contract address %s", name, address.Hex())
} }
@ -115,10 +118,12 @@ func TestWaitDeployedCornerCases(t *testing.T) {
code := "6060604052600a8060106000396000f360606040526008565b00" code := "6060604052600a8060106000396000f360606040526008565b00"
tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, gasPrice, common.FromHex(code)) tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, gasPrice, common.FromHex(code))
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
backend.SendTransaction(ctx, tx) backend.SendTransaction(ctx, tx)
backend.Commit() backend.Commit()
notContentCreation := errors.New("tx is not contract creation") notContentCreation := errors.New("tx is not contract creation")
if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContentCreation.Error() { if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContentCreation.Error() {
t.Errorf("error missmatch: want %q, got %q, ", notContentCreation, err) t.Errorf("error missmatch: want %q, got %q, ", notContentCreation, err)

View file

@ -46,6 +46,7 @@ func NewError(name string, inputs Arguments) Error {
// and precompute string and sig representation. // and precompute string and sig representation.
names := make([]string, len(inputs)) names := make([]string, len(inputs))
types := make([]string, len(inputs)) types := make([]string, len(inputs))
for i, input := range inputs { for i, input := range inputs {
if input.Name == "" { if input.Name == "" {
inputs[i] = Argument{ inputs[i] = Argument{
@ -86,8 +87,10 @@ func (e *Error) Unpack(data []byte) (interface{}, error) {
if len(data) < 4 { if len(data) < 4 {
return "", errors.New("invalid data for unpacking") return "", errors.New("invalid data for unpacking")
} }
if !bytes.Equal(data[:4], e.ID[:4]) { if !bytes.Equal(data[:4], e.ID[:4]) {
return "", errors.New("invalid data for unpacking") return "", errors.New("invalid data for unpacking")
} }
return e.Inputs.Unpack(data[4:]) return e.Inputs.Unpack(data[4:])
} }

View file

@ -40,6 +40,7 @@ func formatSliceString(kind reflect.Kind, sliceSize int) string {
if sliceSize == -1 { if sliceSize == -1 {
return fmt.Sprintf("[]%v", kind) return fmt.Sprintf("[]%v", kind)
} }
return fmt.Sprintf("[%d]%v", sliceSize, kind) return fmt.Sprintf("[%d]%v", sliceSize, kind)
} }
@ -63,6 +64,7 @@ func sliceTypeCheck(t Type, val reflect.Value) error {
if val.Type().Elem().Kind() != t.Elem.GetType().Kind() { if val.Type().Elem().Kind() != t.Elem.GetType().Kind() {
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type()) return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
} }
return nil return nil
} }

View file

@ -64,6 +64,7 @@ func NewEvent(name, rawName string, anonymous bool, inputs Arguments) Event {
// and precompute string and sig representation. // and precompute string and sig representation.
names := make([]string, len(inputs)) names := make([]string, len(inputs))
types := make([]string, len(inputs)) types := make([]string, len(inputs))
for i, input := range inputs { for i, input := range inputs {
if input.Name == "" { if input.Name == "" {
inputs[i] = Argument{ inputs[i] = Argument{

View file

@ -149,11 +149,14 @@ func TestEventMultiValueWithArrayUnpack(t *testing.T) {
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]` definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
abi, err := JSON(strings.NewReader(definition)) abi, err := JSON(strings.NewReader(definition))
require.NoError(t, err) require.NoError(t, err)
var b bytes.Buffer var b bytes.Buffer
var i uint8 = 1 var i uint8 = 1
for ; i <= 3; i++ { for ; i <= 3; i++ {
b.Write(packNum(reflect.ValueOf(i))) b.Write(packNum(reflect.ValueOf(i)))
} }
unpacked, err := abi.Unpack("test", b.Bytes()) unpacked, err := abi.Unpack("test", b.Bytes())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, [2]uint8{1, 2}, unpacked[0]) require.Equal(t, [2]uint8{1, 2}, unpacked[0])
@ -209,6 +212,7 @@ func TestEventTupleUnpack(t *testing.T) {
bigintExpected2 := big.NewInt(2218516807680) bigintExpected2 := big.NewInt(2218516807680)
bigintExpected3 := big.NewInt(1000001) bigintExpected3 := big.NewInt(1000001)
addr := common.HexToAddress("0x00Ce0d46d924CC8437c806721496599FC3FFA268") addr := common.HexToAddress("0x00Ce0d46d924CC8437c806721496599FC3FFA268")
var testCases = []struct { var testCases = []struct {
data string data string
dest interface{} dest interface{}
@ -343,24 +347,33 @@ func TestEventTupleUnpack(t *testing.T) {
func unpackTestEventData(dest interface{}, hexData string, jsonEvent []byte, assert *assert.Assertions) error { func unpackTestEventData(dest interface{}, hexData string, jsonEvent []byte, assert *assert.Assertions) error {
data, err := hex.DecodeString(hexData) data, err := hex.DecodeString(hexData)
assert.NoError(err, "Hex data should be a correct hex-string") assert.NoError(err, "Hex data should be a correct hex-string")
var e Event var e Event
assert.NoError(json.Unmarshal(jsonEvent, &e), "Should be able to unmarshal event ABI") assert.NoError(json.Unmarshal(jsonEvent, &e), "Should be able to unmarshal event ABI")
a := ABI{Events: map[string]Event{"e": e}} a := ABI{Events: map[string]Event{"e": e}}
return a.UnpackIntoInterface(dest, "e", data) return a.UnpackIntoInterface(dest, "e", data)
} }
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder. // TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
func TestEventUnpackIndexed(t *testing.T) { func TestEventUnpackIndexed(t *testing.T) {
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]` definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
type testStruct struct { type testStruct struct {
Value1 uint8 // indexed Value1 uint8 // indexed
Value2 uint8 Value2 uint8
} }
abi, err := JSON(strings.NewReader(definition)) abi, err := JSON(strings.NewReader(definition))
require.NoError(t, err) require.NoError(t, err)
var b bytes.Buffer var b bytes.Buffer
b.Write(packNum(reflect.ValueOf(uint8(8)))) b.Write(packNum(reflect.ValueOf(uint8(8))))
var rst testStruct var rst testStruct
require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes())) require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes()))
require.Equal(t, uint8(0), rst.Value1) require.Equal(t, uint8(0), rst.Value1)
require.Equal(t, uint8(8), rst.Value2) require.Equal(t, uint8(8), rst.Value2)
@ -369,13 +382,17 @@ func TestEventUnpackIndexed(t *testing.T) {
// TestEventIndexedWithArrayUnpack verifies that decoder will not overflow when static array is indexed input. // TestEventIndexedWithArrayUnpack verifies that decoder will not overflow when static array is indexed input.
func TestEventIndexedWithArrayUnpack(t *testing.T) { func TestEventIndexedWithArrayUnpack(t *testing.T) {
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"string"}]}]` definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"string"}]}]`
type testStruct struct { type testStruct struct {
Value1 [2]uint8 // indexed Value1 [2]uint8 // indexed
Value2 string Value2 string
} }
abi, err := JSON(strings.NewReader(definition)) abi, err := JSON(strings.NewReader(definition))
require.NoError(t, err) require.NoError(t, err)
var b bytes.Buffer var b bytes.Buffer
stringOut := "abc" stringOut := "abc"
// number of fields that will be encoded * 32 // number of fields that will be encoded * 32
b.Write(packNum(reflect.ValueOf(32))) b.Write(packNum(reflect.ValueOf(32)))
@ -383,6 +400,7 @@ func TestEventIndexedWithArrayUnpack(t *testing.T) {
b.Write(common.RightPadBytes([]byte(stringOut), 32)) b.Write(common.RightPadBytes([]byte(stringOut), 32))
var rst testStruct var rst testStruct
require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes())) require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes()))
require.Equal(t, [2]uint8{0, 0}, rst.Value1) require.Equal(t, [2]uint8{0, 0}, rst.Value1)
require.Equal(t, stringOut, rst.Value2) require.Equal(t, stringOut, rst.Value2)

View file

@ -97,10 +97,12 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
inputNames = make([]string, len(inputs)) inputNames = make([]string, len(inputs))
outputNames = make([]string, len(outputs)) outputNames = make([]string, len(outputs))
) )
for i, input := range inputs { for i, input := range inputs {
inputNames[i] = fmt.Sprintf("%v %v", input.Type, input.Name) inputNames[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
types[i] = input.Type.String() types[i] = input.Type.String()
} }
for i, output := range outputs { for i, output := range outputs {
outputNames[i] = output.Type.String() outputNames[i] = output.Type.String()
if len(output.Name) > 0 { if len(output.Name) > 0 {
@ -113,6 +115,7 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
sig string sig string
id []byte id []byte
) )
if funType == Function { if funType == Function {
sig = fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ",")) sig = fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
id = crypto.Keccak256([]byte(sig))[:4] id = crypto.Keccak256([]byte(sig))[:4]
@ -123,9 +126,11 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
if state == "nonpayable" { if state == "nonpayable" {
state = "" state = ""
} }
if state != "" { if state != "" {
state = state + " " state = state + " "
} }
identity := fmt.Sprintf("function %v", rawName) identity := fmt.Sprintf("function %v", rawName)
if funType == Fallback { if funType == Fallback {
identity = "fallback" identity = "fallback"
@ -134,6 +139,7 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
} else if funType == Constructor { } else if funType == Constructor {
identity = "constructor" identity = "constructor"
} }
str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", ")) str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", "))
return Method{ return Method{

View file

@ -91,6 +91,7 @@ func TestMethodString(t *testing.T) {
} else { } else {
got = abi.Methods[test.method].String() got = abi.Methods[test.method].String()
} }
if got != test.expectation { if got != test.expectation {
t.Errorf("expected string to be %s, got %s", test.expectation, got) t.Errorf("expected string to be %s, got %s", test.expectation, got)
} }
@ -131,6 +132,7 @@ func TestMethodSig(t *testing.T) {
expect: "complexTuple((uint256,uint256)[5][])", expect: "complexTuple((uint256,uint256)[5][])",
}, },
} }
abi, err := JSON(strings.NewReader(methoddata)) abi, err := JSON(strings.NewReader(methoddata))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View file

@ -51,19 +51,23 @@ func packElement(t Type, reflectValue reflect.Value) ([]byte, error) {
if reflectValue.Bool() { if reflectValue.Bool() {
return math.PaddedBigBytes(common.Big1, 32), nil return math.PaddedBigBytes(common.Big1, 32), nil
} }
return math.PaddedBigBytes(common.Big0, 32), nil return math.PaddedBigBytes(common.Big0, 32), nil
case BytesTy: case BytesTy:
if reflectValue.Kind() == reflect.Array { if reflectValue.Kind() == reflect.Array {
reflectValue = mustArrayToByteSlice(reflectValue) reflectValue = mustArrayToByteSlice(reflectValue)
} }
if reflectValue.Type() != reflect.TypeOf([]byte{}) { if reflectValue.Type() != reflect.TypeOf([]byte{}) {
return []byte{}, errors.New("Bytes type is neither slice nor array") return []byte{}, errors.New("Bytes type is neither slice nor array")
} }
return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()), nil return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()), nil
case FixedBytesTy, FunctionTy: case FixedBytesTy, FunctionTy:
if reflectValue.Kind() == reflect.Array { if reflectValue.Kind() == reflect.Array {
reflectValue = mustArrayToByteSlice(reflectValue) reflectValue = mustArrayToByteSlice(reflectValue)
} }
return common.RightPadBytes(reflectValue.Bytes(), 32), nil return common.RightPadBytes(reflectValue.Bytes(), 32), nil
default: default:
return []byte{}, fmt.Errorf("Could not pack element, unknown type: %v", t.T) return []byte{}, fmt.Errorf("Could not pack element, unknown type: %v", t.T)

View file

@ -38,17 +38,21 @@ func TestPack(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("invalid hex %s: %v", test.packed, err) t.Fatalf("invalid hex %s: %v", test.packed, err)
} }
inDef := fmt.Sprintf(`[{ "name" : "method", "type": "function", "inputs": %s}]`, test.def) inDef := fmt.Sprintf(`[{ "name" : "method", "type": "function", "inputs": %s}]`, test.def)
inAbi, err := JSON(strings.NewReader(inDef)) inAbi, err := JSON(strings.NewReader(inDef))
if err != nil { if err != nil {
t.Fatalf("invalid ABI definition %s, %v", inDef, err) t.Fatalf("invalid ABI definition %s, %v", inDef, err)
} }
var packed []byte var packed []byte
packed, err = inAbi.Pack("method", test.unpacked) packed, err = inAbi.Pack("method", test.unpacked)
if err != nil { if err != nil {
t.Fatalf("test %d (%v) failed: %v", i, test.def, err) t.Fatalf("test %d (%v) failed: %v", i, test.def, err)
} }
if !reflect.DeepEqual(packed[4:], encb) { if !reflect.DeepEqual(packed[4:], encb) {
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, encb, packed[4:]) t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, encb, packed[4:])
} }
@ -76,6 +80,7 @@ func TestMethodPack(t *testing.T) {
} }
var addrA, addrB = common.Address{1}, common.Address{2} var addrA, addrB = common.Address{1}, common.Address{2}
sig = abi.Methods["sliceAddress"].ID sig = abi.Methods["sliceAddress"].ID
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
@ -86,11 +91,13 @@ func TestMethodPack(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !bytes.Equal(packed, sig) { if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed) t.Errorf("expected %x got %x", sig, packed)
} }
var addrC, addrD = common.Address{3}, common.Address{4} var addrC, addrD = common.Address{3}, common.Address{4}
sig = abi.Methods["sliceMultiAddress"].ID sig = abi.Methods["sliceMultiAddress"].ID
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
@ -105,6 +112,7 @@ func TestMethodPack(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !bytes.Equal(packed, sig) { if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed) t.Errorf("expected %x got %x", sig, packed)
} }
@ -132,10 +140,12 @@ func TestMethodPack(t *testing.T) {
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes(addrC[:], 32)...) sig = append(sig, common.LeftPadBytes(addrC[:], 32)...)
sig = append(sig, common.LeftPadBytes(addrD[:], 32)...) sig = append(sig, common.LeftPadBytes(addrD[:], 32)...)
packed, err = abi.Pack("nestedArray", a, []common.Address{addrC, addrD}) packed, err = abi.Pack("nestedArray", a, []common.Address{addrC, addrD})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !bytes.Equal(packed, sig) { if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed) t.Errorf("expected %x got %x", sig, packed)
} }
@ -148,10 +158,12 @@ func TestMethodPack(t *testing.T) {
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
packed, err = abi.Pack("nestedArray2", [2][]uint8{{1}, {1}}) packed, err = abi.Pack("nestedArray2", [2][]uint8{{1}, {1}})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !bytes.Equal(packed, sig) { if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed) t.Errorf("expected %x got %x", sig, packed)
} }
@ -167,10 +179,12 @@ func TestMethodPack(t *testing.T) {
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
packed, err = abi.Pack("nestedSlice", [][]uint8{{1, 2}, {1, 2}}) packed, err = abi.Pack("nestedSlice", [][]uint8{{1, 2}, {1, 2}})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !bytes.Equal(packed, sig) { if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed) t.Errorf("expected %x got %x", sig, packed)
} }

View file

@ -47,6 +47,7 @@ func ConvertType(in interface{}, proto interface{}) interface{} {
if err := set(reflect.ValueOf(proto), reflect.ValueOf(in)); err != nil { if err := set(reflect.ValueOf(proto), reflect.ValueOf(in)); err != nil {
panic(err) panic(err)
} }
return proto return proto
} }
@ -56,6 +57,7 @@ func indirect(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Ptr && v.Elem().Type() != reflect.TypeOf(big.Int{}) { if v.Kind() == reflect.Ptr && v.Elem().Type() != reflect.TypeOf(big.Int{}) {
return indirect(v.Elem()) return indirect(v.Elem())
} }
return v return v
} }
@ -74,6 +76,7 @@ func reflectIntType(unsigned bool, size int) reflect.Type {
return reflect.TypeOf(uint64(0)) return reflect.TypeOf(uint64(0))
} }
} }
switch size { switch size {
case 8: case 8:
return reflect.TypeOf(int8(0)) return reflect.TypeOf(int8(0))
@ -84,6 +87,7 @@ func reflectIntType(unsigned bool, size int) reflect.Type {
case 64: case 64:
return reflect.TypeOf(int64(0)) return reflect.TypeOf(int64(0))
} }
return reflect.TypeOf(&big.Int{}) return reflect.TypeOf(&big.Int{})
} }
@ -92,6 +96,7 @@ func reflectIntType(unsigned bool, size int) reflect.Type {
func mustArrayToByteSlice(value reflect.Value) reflect.Value { func mustArrayToByteSlice(value reflect.Value) reflect.Value {
slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len()) slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len())
reflect.Copy(slice, value) reflect.Copy(slice, value)
return slice return slice
} }
@ -101,6 +106,7 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value {
// strict ruleset as bare `reflect` does. // strict ruleset as bare `reflect` does.
func set(dst, src reflect.Value) error { func set(dst, src reflect.Value) error {
dstType, srcType := dst.Type(), src.Type() dstType, srcType := dst.Type(), src.Type()
switch { switch {
case dstType.Kind() == reflect.Interface && dst.Elem().IsValid() && (dst.Elem().Type().Kind() == reflect.Ptr || dst.Elem().CanSet()): case dstType.Kind() == reflect.Interface && dst.Elem().IsValid() && (dst.Elem().Type().Kind() == reflect.Ptr || dst.Elem().CanSet()):
return set(dst.Elem(), src) return set(dst.Elem(), src)
@ -117,6 +123,7 @@ func set(dst, src reflect.Value) error {
default: default:
return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type()) return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
} }
return nil return nil
} }
@ -130,10 +137,12 @@ func setSlice(dst, src reflect.Value) error {
return err return err
} }
} }
if dst.CanSet() { if dst.CanSet() {
dst.Set(slice) dst.Set(slice)
return nil return nil
} }
return errors.New("Cannot set slice, destination not settable") return errors.New("Cannot set slice, destination not settable")
} }
@ -141,20 +150,25 @@ func setArray(dst, src reflect.Value) error {
if src.Kind() == reflect.Ptr { if src.Kind() == reflect.Ptr {
return set(dst, indirect(src)) return set(dst, indirect(src))
} }
array := reflect.New(dst.Type()).Elem() array := reflect.New(dst.Type()).Elem()
min := src.Len() min := src.Len()
if src.Len() > dst.Len() { if src.Len() > dst.Len() {
min = dst.Len() min = dst.Len()
} }
for i := 0; i < min; i++ { for i := 0; i < min; i++ {
if err := set(array.Index(i), src.Index(i)); err != nil { if err := set(array.Index(i), src.Index(i)); err != nil {
return err return err
} }
} }
if dst.CanSet() { if dst.CanSet() {
dst.Set(array) dst.Set(array)
return nil return nil
} }
return errors.New("Cannot set array, destination not settable") return errors.New("Cannot set array, destination not settable")
} }
@ -162,13 +176,16 @@ func setStruct(dst, src reflect.Value) error {
for i := 0; i < src.NumField(); i++ { for i := 0; i < src.NumField(); i++ {
srcField := src.Field(i) srcField := src.Field(i)
dstField := dst.Field(i) dstField := dst.Field(i)
if !dstField.IsValid() || !srcField.IsValid() { if !dstField.IsValid() || !srcField.IsValid() {
return fmt.Errorf("Could not find src field: %v value: %v in destination", srcField.Type().Name(), srcField) return fmt.Errorf("Could not find src field: %v value: %v in destination", srcField.Type().Name(), srcField)
} }
if err := set(dstField, srcField); err != nil { if err := set(dstField, srcField); err != nil {
return err return err
} }
} }
return nil return nil
} }
@ -206,6 +223,7 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
} }
// check which argument field matches with the abi tag. // check which argument field matches with the abi tag.
found := false found := false
for _, arg := range argNames { for _, arg := range argNames {
if arg == tagName { if arg == tagName {
if abi2struct[arg] != "" { if abi2struct[arg] != "" {
@ -241,6 +259,7 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
value.FieldByName(structFieldName).IsValid() { value.FieldByName(structFieldName).IsValid() {
return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", argName) return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", argName)
} }
continue continue
} }
@ -260,5 +279,6 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
struct2abi[structFieldName] = argName struct2abi[structFieldName] = argName
} }
} }
return abi2struct, nil return abi2struct, nil
} }

View file

@ -181,6 +181,7 @@ func TestReflectNameToStruct(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Unexpected error: %v", err) t.Fatalf("Unexpected error: %v", err)
} }
for fname := range test.want { for fname := range test.want {
if m[fname] != test.want[fname] { if m[fname] != test.want[fname] {
t.Fatalf("Incorrect value for field %s: expected %v, got %v", fname, test.want[fname], m[fname]) t.Fatalf("Incorrect value for field %s: expected %v, got %v", fname, test.want[fname], m[fname])
@ -217,6 +218,7 @@ func TestConvertType(t *testing.T) {
if out.X.Cmp(big.NewInt(1)) != 0 { if out.X.Cmp(big.NewInt(1)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out.X, big.NewInt(1)) t.Errorf("ConvertType failed, got %v want %v", out.X, big.NewInt(1))
} }
if out.Y.Cmp(big.NewInt(2)) != 0 { if out.Y.Cmp(big.NewInt(2)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out.Y, big.NewInt(2)) t.Errorf("ConvertType failed, got %v want %v", out.Y, big.NewInt(2))
} }
@ -226,16 +228,20 @@ func TestConvertType(t *testing.T) {
val2.Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2))) val2.Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2)))
val2.Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3))) val2.Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3)))
val2.Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4))) val2.Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4)))
out2 := *ConvertType(val2.Interface(), new([]T)).(*[]T) out2 := *ConvertType(val2.Interface(), new([]T)).(*[]T)
if out2[0].X.Cmp(big.NewInt(1)) != 0 { if out2[0].X.Cmp(big.NewInt(1)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1)) t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1))
} }
if out2[0].Y.Cmp(big.NewInt(2)) != 0 { if out2[0].Y.Cmp(big.NewInt(2)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2)) t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2))
} }
if out2[1].X.Cmp(big.NewInt(3)) != 0 { if out2[1].X.Cmp(big.NewInt(3)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1)) t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1))
} }
if out2[1].Y.Cmp(big.NewInt(4)) != 0 { if out2[1].Y.Cmp(big.NewInt(4)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2)) t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2))
} }
@ -245,16 +251,20 @@ func TestConvertType(t *testing.T) {
val3.Elem().Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2))) val3.Elem().Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2)))
val3.Elem().Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3))) val3.Elem().Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3)))
val3.Elem().Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4))) val3.Elem().Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4)))
out3 := *ConvertType(val3.Interface(), new([2]T)).(*[2]T) out3 := *ConvertType(val3.Interface(), new([2]T)).(*[2]T)
if out3[0].X.Cmp(big.NewInt(1)) != 0 { if out3[0].X.Cmp(big.NewInt(1)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1)) t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1))
} }
if out3[0].Y.Cmp(big.NewInt(2)) != 0 { if out3[0].Y.Cmp(big.NewInt(2)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2)) t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2))
} }
if out3[1].X.Cmp(big.NewInt(3)) != 0 { if out3[1].X.Cmp(big.NewInt(3)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1)) t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1))
} }
if out3[1].Y.Cmp(big.NewInt(4)) != 0 { if out3[1].Y.Cmp(big.NewInt(4)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2)) t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2))
} }

View file

@ -42,18 +42,23 @@ func parseToken(unescapedSelector string, isIdent bool) (string, string, error)
if len(unescapedSelector) == 0 { if len(unescapedSelector) == 0 {
return "", "", fmt.Errorf("empty token") return "", "", fmt.Errorf("empty token")
} }
firstChar := unescapedSelector[0] firstChar := unescapedSelector[0]
position := 1 position := 1
if !(isAlpha(firstChar) || (isIdent && isIdentifierSymbol(firstChar))) { if !(isAlpha(firstChar) || (isIdent && isIdentifierSymbol(firstChar))) {
return "", "", fmt.Errorf("invalid token start: %c", firstChar) return "", "", fmt.Errorf("invalid token start: %c", firstChar)
} }
for position < len(unescapedSelector) { for position < len(unescapedSelector) {
char := unescapedSelector[position] char := unescapedSelector[position]
if !(isAlpha(char) || isDigit(char) || (isIdent && isIdentifierSymbol(char))) { if !(isAlpha(char) || isDigit(char) || (isIdent && isIdentifierSymbol(char))) {
break break
} }
position++ position++
} }
return unescapedSelector[:position], unescapedSelector[position:], nil return unescapedSelector[:position], unescapedSelector[position:], nil
} }
@ -70,16 +75,20 @@ func parseElementaryType(unescapedSelector string) (string, string, error) {
for len(rest) > 0 && rest[0] == '[' { for len(rest) > 0 && rest[0] == '[' {
parsedType = parsedType + string(rest[0]) parsedType = parsedType + string(rest[0])
rest = rest[1:] rest = rest[1:]
for len(rest) > 0 && isDigit(rest[0]) { for len(rest) > 0 && isDigit(rest[0]) {
parsedType = parsedType + string(rest[0]) parsedType = parsedType + string(rest[0])
rest = rest[1:] rest = rest[1:]
} }
if len(rest) == 0 || rest[0] != ']' { if len(rest) == 0 || rest[0] != ']' {
return "", "", fmt.Errorf("failed to parse array: expected ']', got %c", unescapedSelector[0]) return "", "", fmt.Errorf("failed to parse array: expected ']', got %c", unescapedSelector[0])
} }
parsedType = parsedType + string(rest[0]) parsedType = parsedType + string(rest[0])
rest = rest[1:] rest = rest[1:]
} }
return parsedType, rest, nil return parsedType, rest, nil
} }
@ -87,18 +96,23 @@ func parseCompositeType(unescapedSelector string) ([]interface{}, string, error)
if len(unescapedSelector) == 0 || unescapedSelector[0] != '(' { if len(unescapedSelector) == 0 || unescapedSelector[0] != '(' {
return nil, "", fmt.Errorf("expected '(', got %c", unescapedSelector[0]) return nil, "", fmt.Errorf("expected '(', got %c", unescapedSelector[0])
} }
parsedType, rest, err := parseType(unescapedSelector[1:]) parsedType, rest, err := parseType(unescapedSelector[1:])
if err != nil { if err != nil {
return nil, "", fmt.Errorf("failed to parse type: %v", err) return nil, "", fmt.Errorf("failed to parse type: %v", err)
} }
result := []interface{}{parsedType} result := []interface{}{parsedType}
for len(rest) > 0 && rest[0] != ')' { for len(rest) > 0 && rest[0] != ')' {
parsedType, rest, err = parseType(rest[1:]) parsedType, rest, err = parseType(rest[1:])
if err != nil { if err != nil {
return nil, "", fmt.Errorf("failed to parse type: %v", err) return nil, "", fmt.Errorf("failed to parse type: %v", err)
} }
result = append(result, parsedType) result = append(result, parsedType)
} }
if len(rest) == 0 || rest[0] != ')' { if len(rest) == 0 || rest[0] != ')' {
return nil, "", fmt.Errorf("expected ')', got '%s'", rest) return nil, "", fmt.Errorf("expected ')', got '%s'", rest)
} }
@ -106,6 +120,7 @@ func parseCompositeType(unescapedSelector string) ([]interface{}, string, error)
if len(rest) >= 3 && rest[1] == '[' && rest[2] == ']' { if len(rest) >= 3 && rest[1] == '[' && rest[2] == ']' {
return append(result, "[]"), rest[3:], nil return append(result, "[]"), rest[3:], nil
} }
return result, rest[1:], nil return result, rest[1:], nil
} }
@ -113,6 +128,7 @@ func parseType(unescapedSelector string) (interface{}, string, error) {
if len(unescapedSelector) == 0 { if len(unescapedSelector) == 0 {
return nil, "", fmt.Errorf("empty type") return nil, "", fmt.Errorf("empty type")
} }
if unescapedSelector[0] == '(' { if unescapedSelector[0] == '(' {
return parseCompositeType(unescapedSelector) return parseCompositeType(unescapedSelector)
} else { } else {
@ -122,6 +138,7 @@ func parseType(unescapedSelector string) (interface{}, string, error) {
func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) { func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) {
arguments := make([]ArgumentMarshaling, 0) arguments := make([]ArgumentMarshaling, 0)
for i, arg := range args { for i, arg := range args {
// generate dummy name to avoid unmarshal issues // generate dummy name to avoid unmarshal issues
name := fmt.Sprintf("name%d", i) name := fmt.Sprintf("name%d", i)
@ -134,15 +151,18 @@ func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) {
} }
// nolint:goconst // nolint:goconst
tupleType := "tuple" tupleType := "tuple"
if len(subArgs) != 0 && subArgs[len(subArgs)-1].Type == "[]" { if len(subArgs) != 0 && subArgs[len(subArgs)-1].Type == "[]" {
subArgs = subArgs[:len(subArgs)-1] subArgs = subArgs[:len(subArgs)-1]
tupleType = "tuple[]" tupleType = "tuple[]"
} }
arguments = append(arguments, ArgumentMarshaling{name, tupleType, tupleType, subArgs, false}) arguments = append(arguments, ArgumentMarshaling{name, tupleType, tupleType, subArgs, false})
} else { } else {
return nil, fmt.Errorf("failed to assemble args: unexpected type %T", arg) return nil, fmt.Errorf("failed to assemble args: unexpected type %T", arg)
} }
} }
return arguments, nil return arguments, nil
} }
@ -155,7 +175,9 @@ func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) {
if err != nil { if err != nil {
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err) return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
} }
args := []interface{}{} args := []interface{}{}
if len(rest) >= 2 && rest[0] == '(' && rest[1] == ')' { if len(rest) >= 2 && rest[0] == '(' && rest[1] == ')' {
rest = rest[2:] rest = rest[2:]
} else { } else {
@ -164,6 +186,7 @@ func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) {
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err) return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
} }
} }
if len(rest) > 0 { if len(rest) > 0 {
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': unexpected string '%s'", unescapedSelector, rest) return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': unexpected string '%s'", unescapedSelector, rest)
} }

View file

@ -26,6 +26,7 @@ import (
func TestParseSelector(t *testing.T) { func TestParseSelector(t *testing.T) {
mkType := func(types ...interface{}) []ArgumentMarshaling { mkType := func(types ...interface{}) []ArgumentMarshaling {
var result []ArgumentMarshaling var result []ArgumentMarshaling
for i, typeOrComponents := range types { for i, typeOrComponents := range types {
name := fmt.Sprintf("name%d", i) name := fmt.Sprintf("name%d", i)
if typeName, ok := typeOrComponents.(string); ok { if typeName, ok := typeOrComponents.(string); ok {
@ -38,8 +39,10 @@ func TestParseSelector(t *testing.T) {
log.Fatalf("unexpected type %T", typeOrComponents) log.Fatalf("unexpected type %T", typeOrComponents)
} }
} }
return result return result
} }
tests := []struct { tests := []struct {
input string input string
name string name string
@ -65,6 +68,7 @@ func TestParseSelector(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("test %d: failed to parse selector '%v': %v", i, tt.input, err) t.Errorf("test %d: failed to parse selector '%v': %v", i, tt.input, err)
} }
if selector.Name != tt.name { if selector.Name != tt.name {
t.Errorf("test %d: unexpected function name: '%s' != '%s'", i, selector.Name, tt.name) t.Errorf("test %d: unexpected function name: '%s' != '%s'", i, selector.Name, tt.name)
} }
@ -72,6 +76,7 @@ func TestParseSelector(t *testing.T) {
if selector.Type != "function" { if selector.Type != "function" {
t.Errorf("test %d: unexpected type: '%s' != '%s'", i, selector.Type, "function") t.Errorf("test %d: unexpected type: '%s' != '%s'", i, selector.Type, "function")
} }
if !reflect.DeepEqual(selector.Inputs, tt.args) { if !reflect.DeepEqual(selector.Inputs, tt.args) {
t.Errorf("test %d: unexpected args: '%v' != '%v'", i, selector.Inputs, tt.args) t.Errorf("test %d: unexpected args: '%v' != '%v'", i, selector.Inputs, tt.args)
} }

View file

@ -30,6 +30,7 @@ import (
// MakeTopics converts a filter query argument list into a filter topic set. // MakeTopics converts a filter query argument list into a filter topic set.
func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) { func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
topics := make([][]common.Hash, len(query)) topics := make([][]common.Hash, len(query))
for i, filter := range query { for i, filter := range query {
for _, rule := range filter { for _, rule := range filter {
var topic common.Hash var topic common.Hash
@ -81,9 +82,9 @@ func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
// //
// We only convert stringS and bytes to hash, still need to deal with // We only convert stringS and bytes to hash, still need to deal with
// array(both fixed-size and dynamic-size) and struct. // array(both fixed-size and dynamic-size) and struct.
// Attempt to generate the topic from funky types // Attempt to generate the topic from funky types
val := reflect.ValueOf(rule) val := reflect.ValueOf(rule)
switch { switch {
// static byte array // static byte array
case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8: case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
@ -92,9 +93,11 @@ func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
return nil, fmt.Errorf("unsupported indexed type: %T", rule) return nil, fmt.Errorf("unsupported indexed type: %T", rule)
} }
} }
topics[i] = append(topics[i], topic) topics[i] = append(topics[i], topic)
} }
} }
return topics, nil return topics, nil
} }
@ -105,9 +108,11 @@ func genIntType(rule int64, size uint) []byte {
// extended to common.HashLength bytes. // extended to common.HashLength bytes.
topic = [common.HashLength]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255} topic = [common.HashLength]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}
} }
for i := uint(0); i < size; i++ { for i := uint(0); i < size; i++ {
topic[common.HashLength-i-1] = byte(rule >> (i * 8)) topic[common.HashLength-i-1] = byte(rule >> (i * 8))
} }
return topic[:] return topic[:]
} }
@ -143,7 +148,9 @@ func parseTopicWithSetter(fields Arguments, topics []common.Hash, setter func(Ar
if !arg.Indexed { if !arg.Indexed {
return errors.New("non-indexed field in topic reconstruction") return errors.New("non-indexed field in topic reconstruction")
} }
var reconstr interface{} var reconstr interface{}
switch arg.Type.T { switch arg.Type.T {
case TupleTy: case TupleTy:
return errors.New("tuple type in topic reconstruction") return errors.New("tuple type in topic reconstruction")
@ -155,11 +162,14 @@ func parseTopicWithSetter(fields Arguments, topics []common.Hash, setter func(Ar
if garbage := binary.BigEndian.Uint64(topics[i][0:8]); garbage != 0 { if garbage := binary.BigEndian.Uint64(topics[i][0:8]); garbage != 0 {
return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[i].Bytes()) return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[i].Bytes())
} }
var tmp [24]byte var tmp [24]byte
copy(tmp[:], topics[i][8:32]) copy(tmp[:], topics[i][8:32])
reconstr = tmp reconstr = tmp
default: default:
var err error var err error
reconstr, err = toGoType(0, arg.Type, topics[i].Bytes()) reconstr, err = toGoType(0, arg.Type, topics[i].Bytes())
if err != nil { if err != nil {
return err return err

View file

@ -29,6 +29,7 @@ func TestMakeTopics(t *testing.T) {
type args struct { type args struct {
query [][]interface{} query [][]interface{}
} }
tests := []struct { tests := []struct {
name string name string
args args args args
@ -123,6 +124,7 @@ func TestMakeTopics(t *testing.T) {
t.Errorf("makeTopics() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("makeTopics() error = %v, wantErr %v", err, tt.wantErr)
return return
} }
if !reflect.DeepEqual(got, tt.want) { if !reflect.DeepEqual(got, tt.want) {
t.Errorf("makeTopics() = %v, want %v", got, tt.want) t.Errorf("makeTopics() = %v, want %v", got, tt.want)
} }
@ -355,6 +357,7 @@ func TestParseTopics(t *testing.T) {
if err := ParseTopics(createObj, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr { if err := ParseTopics(createObj, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
t.Errorf("parseTopics() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("parseTopics() error = %v, wantErr %v", err, tt.wantErr)
} }
resultObj := tt.args.resultObj() resultObj := tt.args.resultObj()
if !reflect.DeepEqual(createObj, resultObj) { if !reflect.DeepEqual(createObj, resultObj) {
t.Errorf("parseTopics() = %v, want %v", createObj, resultObj) t.Errorf("parseTopics() = %v, want %v", createObj, resultObj)
@ -372,6 +375,7 @@ func TestParseTopicsIntoMap(t *testing.T) {
if err := ParseTopicsIntoMap(outMap, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr { if err := ParseTopicsIntoMap(outMap, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
t.Errorf("parseTopicsIntoMap() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("parseTopicsIntoMap() error = %v, wantErr %v", err, tt.wantErr)
} }
resultMap := tt.args.resultMap() resultMap := tt.args.resultMap()
if !reflect.DeepEqual(outMap, resultMap) { if !reflect.DeepEqual(outMap, resultMap) {
t.Errorf("parseTopicsIntoMap() = %v, want %v", outMap, resultMap) t.Errorf("parseTopicsIntoMap() = %v, want %v", outMap, resultMap)

View file

@ -72,6 +72,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
if strings.Count(t, "[") != strings.Count(t, "]") { if strings.Count(t, "[") != strings.Count(t, "]") {
return Type{}, fmt.Errorf("invalid arg type in abi") return Type{}, fmt.Errorf("invalid arg type in abi")
} }
typ.stringKind = t typ.stringKind = t
// if there are brackets, get ready to go into slice/array mode and // if there are brackets, get ready to go into slice/array mode and
@ -84,6 +85,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
} }
// recursively embed the type // recursively embed the type
i := strings.LastIndex(t, "[") i := strings.LastIndex(t, "[")
embeddedType, err := NewType(t[:i], subInternal, components) embeddedType, err := NewType(t[:i], subInternal, components)
if err != nil { if err != nil {
return Type{}, err return Type{}, err
@ -103,14 +105,17 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
// is an array // is an array
typ.T = ArrayTy typ.T = ArrayTy
typ.Elem = &embeddedType typ.Elem = &embeddedType
typ.Size, err = strconv.Atoi(intz[0]) typ.Size, err = strconv.Atoi(intz[0])
if err != nil { if err != nil {
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
} }
typ.stringKind = embeddedType.stringKind + sliced typ.stringKind = embeddedType.stringKind + sliced
} else { } else {
return Type{}, fmt.Errorf("invalid formatting of array type") return Type{}, fmt.Errorf("invalid formatting of array type")
} }
return typ, err return typ, err
} }
// parse the type and size of the abi-type. // parse the type and size of the abi-type.
@ -118,12 +123,15 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
if len(matches) == 0 { if len(matches) == 0 {
return Type{}, fmt.Errorf("invalid type '%v'", t) return Type{}, fmt.Errorf("invalid type '%v'", t)
} }
parsedType := matches[0] parsedType := matches[0]
// varSize is the size of the variable // varSize is the size of the variable
var varSize int var varSize int
if len(parsedType[3]) > 0 { if len(parsedType[3]) > 0 {
var err error var err error
varSize, err = strconv.Atoi(parsedType[2]) varSize, err = strconv.Atoi(parsedType[2])
if err != nil { if err != nil {
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
@ -157,6 +165,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
if varSize > 32 { if varSize > 32 {
return Type{}, fmt.Errorf("unsupported arg type: %s", t) return Type{}, fmt.Errorf("unsupported arg type: %s", t)
} }
typ.T = FixedBytesTy typ.T = FixedBytesTy
typ.Size = varSize typ.Size = varSize
} }
@ -168,7 +177,9 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
expression string // canonical parameter expression expression string // canonical parameter expression
used = make(map[string]bool) used = make(map[string]bool)
) )
expression += "(" expression += "("
for idx, c := range components { for idx, c := range components {
cType, err := NewType(c.Type, c.InternalType, c.Components) cType, err := NewType(c.Type, c.InternalType, c.Components)
if err != nil { if err != nil {
@ -182,6 +193,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
} }
fieldName := ResolveNameConflict(name, func(s string) bool { return used[s] }) fieldName := ResolveNameConflict(name, func(s string) bool { return used[s] })
if err != nil { if err != nil {
return Type{}, err return Type{}, err
} }
@ -191,18 +203,22 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
if !isValidFieldName(fieldName) { if !isValidFieldName(fieldName) {
return Type{}, fmt.Errorf("field %d has invalid name", idx) return Type{}, fmt.Errorf("field %d has invalid name", idx)
} }
fields = append(fields, reflect.StructField{ fields = append(fields, reflect.StructField{
Name: fieldName, // reflect.StructOf will panic for any exported field. Name: fieldName, // reflect.StructOf will panic for any exported field.
Type: cType.GetType(), Type: cType.GetType(),
Tag: reflect.StructTag("json:\"" + c.Name + "\""), Tag: reflect.StructTag("json:\"" + c.Name + "\""),
}) })
elems = append(elems, &cType) elems = append(elems, &cType)
names = append(names, c.Name) names = append(names, c.Name)
expression += cType.stringKind expression += cType.stringKind
if idx != len(components)-1 { if idx != len(components)-1 {
expression += "," expression += ","
} }
} }
expression += ")" expression += ")"
typ.TupleType = reflect.StructOf(fields) typ.TupleType = reflect.StructOf(fields)
@ -291,23 +307,29 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
// calculate offset if any // calculate offset if any
offset := 0 offset := 0
offsetReq := isDynamicType(*t.Elem) offsetReq := isDynamicType(*t.Elem)
if offsetReq { if offsetReq {
offset = getTypeSize(*t.Elem) * v.Len() offset = getTypeSize(*t.Elem) * v.Len()
} }
var tail []byte var tail []byte
for i := 0; i < v.Len(); i++ { for i := 0; i < v.Len(); i++ {
val, err := t.Elem.pack(v.Index(i)) val, err := t.Elem.pack(v.Index(i))
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !offsetReq { if !offsetReq {
ret = append(ret, val...) ret = append(ret, val...)
continue continue
} }
ret = append(ret, packNum(reflect.ValueOf(offset))...) ret = append(ret, packNum(reflect.ValueOf(offset))...)
offset += len(val) offset += len(val)
tail = append(tail, val...) tail = append(tail, val...)
} }
return append(ret, tail...), nil return append(ret, tail...), nil
case TupleTy: case TupleTy:
// (T1,...,Tk) for k >= 0 and any types T1, …, Tk // (T1,...,Tk) for k >= 0 and any types T1, …, Tk
@ -328,16 +350,20 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
for _, elem := range t.TupleElems { for _, elem := range t.TupleElems {
offset += getTypeSize(*elem) offset += getTypeSize(*elem)
} }
var ret, tail []byte var ret, tail []byte
for i, elem := range t.TupleElems { for i, elem := range t.TupleElems {
field := v.FieldByName(fieldmap[t.TupleRawNames[i]]) field := v.FieldByName(fieldmap[t.TupleRawNames[i]])
if !field.IsValid() { if !field.IsValid() {
return nil, fmt.Errorf("field %s for tuple not found in the given struct", t.TupleRawNames[i]) return nil, fmt.Errorf("field %s for tuple not found in the given struct", t.TupleRawNames[i])
} }
val, err := elem.pack(field) val, err := elem.pack(field)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if isDynamicType(*elem) { if isDynamicType(*elem) {
ret = append(ret, packNum(reflect.ValueOf(offset))...) ret = append(ret, packNum(reflect.ValueOf(offset))...)
tail = append(tail, val...) tail = append(tail, val...)
@ -346,6 +372,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
ret = append(ret, val...) ret = append(ret, val...)
} }
} }
return append(ret, tail...), nil return append(ret, tail...), nil
default: default:
@ -373,8 +400,10 @@ func isDynamicType(t Type) bool {
return true return true
} }
} }
return false return false
} }
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem)) return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem))
} }
@ -392,14 +421,17 @@ func getTypeSize(t Type) int {
if t.Elem.T == ArrayTy || t.Elem.T == TupleTy { if t.Elem.T == ArrayTy || t.Elem.T == TupleTy {
return t.Size * getTypeSize(*t.Elem) return t.Size * getTypeSize(*t.Elem)
} }
return t.Size * 32 return t.Size * 32
} else if t.T == TupleTy && !isDynamicType(t) { } else if t.T == TupleTy && !isDynamicType(t) {
total := 0 total := 0
for _, elem := range t.TupleElems { for _, elem := range t.TupleElems {
total += getTypeSize(*elem) total += getTypeSize(*elem)
} }
return total return total
} }
return 32 return 32
} }

View file

@ -110,6 +110,7 @@ func TestTypeRegexp(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("type %q: failed to parse type string: %v", tt.blob, err) t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
} }
if !reflect.DeepEqual(typ, tt.kind) { if !reflect.DeepEqual(typ, tt.kind) {
t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s ", tt.blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(tt.kind))) t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s ", tt.blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(tt.kind)))
} }
@ -288,6 +289,7 @@ func TestTypeCheck(t *testing.T) {
if err.Error() != test.err { if err.Error() != test.err {
t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err) t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
} }
continue continue
} }
@ -296,6 +298,7 @@ func TestTypeCheck(t *testing.T) {
t.Errorf("%d failed. Expected no err but got: %v", i, err) t.Errorf("%d failed. Expected no err but got: %v", i, err)
continue continue
} }
if err == nil && len(test.err) != 0 { if err == nil && len(test.err) != 0 {
t.Errorf("%d failed. Expected err: %v but got none", i, test.err) t.Errorf("%d failed. Expected err: %v but got none", i, test.err)
continue continue
@ -323,9 +326,11 @@ func TestInternalType(t *testing.T) {
blob := "tuple" blob := "tuple"
typ, err := NewType(blob, internalType, components) typ, err := NewType(blob, internalType, components)
if err != nil { if err != nil {
t.Errorf("type %q: failed to parse type string: %v", blob, err) t.Errorf("type %q: failed to parse type string: %v", blob, err)
} }
if !reflect.DeepEqual(typ, kind) { if !reflect.DeepEqual(typ, kind) {
t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s ", blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(kind))) t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s ", blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(kind)))
} }

View file

@ -40,6 +40,7 @@ func ReadInteger(typ Type, b []byte) (interface{}, error) {
if typ.T == UintTy { if typ.T == UintTy {
u64, isu64 := ret.Uint64(), ret.IsUint64() u64, isu64 := ret.Uint64(), ret.IsUint64()
switch typ.Size { switch typ.Size {
case 8: case 8:
if !isu64 || u64 > math.MaxUint8 { if !isu64 || u64 > math.MaxUint8 {
@ -81,6 +82,7 @@ func ReadInteger(typ Type, b []byte) (interface{}, error) {
} }
i64, isi64 := ret.Int64(), ret.IsInt64() i64, isi64 := ret.Int64(), ret.IsInt64()
switch typ.Size { switch typ.Size {
case 8: case 8:
if !isi64 || i64 < math.MinInt8 || i64 > math.MaxInt8 { if !isi64 || i64 < math.MinInt8 || i64 > math.MaxInt8 {
@ -108,7 +110,6 @@ func ReadInteger(typ Type, b []byte) (interface{}, error) {
return i64, nil return i64, nil
default: default:
// the only case left for integer is int256 // the only case left for integer is int256
return ret, nil return ret, nil
} }
} }
@ -120,6 +121,7 @@ func readBool(word []byte) (bool, error) {
return false, errBadBool return false, errBadBool
} }
} }
switch word[31] { switch word[31] {
case 0: case 0:
return false, nil return false, nil
@ -137,11 +139,13 @@ func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
if t.T != FunctionTy { if t.T != FunctionTy {
return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array") 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 { if garbage := binary.BigEndian.Uint64(word[24:32]); garbage != 0 {
err = fmt.Errorf("abi: got improperly encoded function type, got %v", word) err = fmt.Errorf("abi: got improperly encoded function type, got %v", word)
} else { } else {
copy(funcTy[:], word[0:24]) copy(funcTy[:], word[0:24])
} }
return return
} }
@ -154,6 +158,7 @@ func ReadFixedBytes(t Type, word []byte) (interface{}, error) {
array := reflect.New(t.GetType()).Elem() array := reflect.New(t.GetType()).Elem()
reflect.Copy(array, reflect.ValueOf(word[0:t.Size])) reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
return array.Interface(), nil return array.Interface(), nil
} }
@ -162,6 +167,7 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
if size < 0 { if size < 0 {
return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size) return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size)
} }
if start+32*size > len(output) { if start+32*size > len(output) {
return nil, fmt.Errorf("abi: cannot marshal into go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size) return nil, fmt.Errorf("abi: cannot marshal into go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
} }
@ -199,12 +205,14 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
func forTupleUnpack(t Type, output []byte) (interface{}, error) { func forTupleUnpack(t Type, output []byte) (interface{}, error) {
retval := reflect.New(t.GetType()).Elem() retval := reflect.New(t.GetType()).Elem()
virtualArgs := 0 virtualArgs := 0
for index, elem := range t.TupleElems { for index, elem := range t.TupleElems {
marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output) marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if elem.T == ArrayTy && !isDynamicType(*elem) { if elem.T == ArrayTy && !isDynamicType(*elem) {
// If we have a static array, like [3]uint256, these are coded as // If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256. // just like uint256,uint256,uint256.
@ -222,8 +230,10 @@ func forTupleUnpack(t Type, output []byte) (interface{}, error) {
// coded as just like uint256,bool,uint256 // coded as just like uint256,bool,uint256
virtualArgs += getTypeSize(*elem)/32 - 1 virtualArgs += getTypeSize(*elem)/32 - 1
} }
retval.Field(index).Set(reflect.ValueOf(marshalledValue)) retval.Field(index).Set(reflect.ValueOf(marshalledValue))
} }
return retval.Interface(), nil return retval.Interface(), nil
} }
@ -257,8 +267,10 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return forTupleUnpack(t, output[begin:]) return forTupleUnpack(t, output[begin:])
} }
return forTupleUnpack(t, output[index:]) return forTupleUnpack(t, output[index:])
case SliceTy: case SliceTy:
return forEachUnpack(t, output[begin:], 0, length) return forEachUnpack(t, output[begin:], 0, length)
@ -268,8 +280,10 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
if offset > uint64(len(output)) { if offset > uint64(len(output)) {
return nil, fmt.Errorf("abi: toGoType offset greater than output length: offset: %d, len(output): %d", offset, len(output)) return nil, fmt.Errorf("abi: toGoType offset greater than output length: offset: %d, len(output): %d", offset, len(output))
} }
return forEachUnpack(t, output[offset:], 0, t.Size) return forEachUnpack(t, output[offset:], 0, t.Size)
} }
return forEachUnpack(t, output[index:], 0, t.Size) return forEachUnpack(t, output[index:], 0, t.Size)
case StringTy: // variable arrays are written at the end of the return bytes case StringTy: // variable arrays are written at the end of the return bytes
return string(output[begin : begin+length]), nil return string(output[begin : begin+length]), nil
@ -296,6 +310,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) { func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
bigOffsetEnd := new(big.Int).SetBytes(output[index : index+32]) bigOffsetEnd := new(big.Int).SetBytes(output[index : index+32])
bigOffsetEnd.Add(bigOffsetEnd, common.Big32) bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
outputLength := big.NewInt(int64(len(output))) outputLength := big.NewInt(int64(len(output)))
if bigOffsetEnd.Cmp(outputLength) > 0 { if bigOffsetEnd.Cmp(outputLength) > 0 {
@ -317,8 +332,10 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
if totalSize.Cmp(outputLength) > 0 { if totalSize.Cmp(outputLength) > 0 {
return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %v require %v", outputLength, totalSize) return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %v require %v", outputLength, totalSize)
} }
start = int(bigOffsetEnd.Uint64()) start = int(bigOffsetEnd.Uint64())
length = int(lengthBig.Uint64()) length = int(lengthBig.Uint64())
return return
} }
@ -330,8 +347,10 @@ func tuplePointsTo(index int, output []byte) (start int, err error) {
if offset.Cmp(outputLen) > 0 { if offset.Cmp(outputLen) > 0 {
return 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", offset, outputLen) 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 { if offset.BitLen() > 63 {
return 0, fmt.Errorf("abi offset larger than int64: %v", offset) return 0, fmt.Errorf("abi offset larger than int64: %v", offset)
} }
return int(offset.Uint64()), nil return int(offset.Uint64()), nil
} }

View file

@ -38,18 +38,22 @@ func TestUnpack(t *testing.T) {
//Unpack //Unpack
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def) def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
abi, err := JSON(strings.NewReader(def)) abi, err := JSON(strings.NewReader(def))
if err != nil { if err != nil {
t.Fatalf("invalid ABI definition %s: %v", def, err) t.Fatalf("invalid ABI definition %s: %v", def, err)
} }
encb, err := hex.DecodeString(test.packed) encb, err := hex.DecodeString(test.packed)
if err != nil { if err != nil {
t.Fatalf("invalid hex %s: %v", test.packed, err) t.Fatalf("invalid hex %s: %v", test.packed, err)
} }
out, err := abi.Unpack("method", encb) out, err := abi.Unpack("method", encb)
if err != nil { if err != nil {
t.Errorf("test %d (%v) failed: %v", i, test.def, err) t.Errorf("test %d (%v) failed: %v", i, test.def, err)
return return
} }
if !reflect.DeepEqual(test.unpacked, ConvertType(out[0], test.unpacked)) { if !reflect.DeepEqual(test.unpacked, ConvertType(out[0], test.unpacked)) {
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.unpacked, out[0]) t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.unpacked, out[0])
} }
@ -74,6 +78,7 @@ func (test unpackTest) checkError(err error) error {
} else if len(test.err) > 0 { } else if len(test.err) > 0 {
return fmt.Errorf("expected err: %v but got none", test.err) return fmt.Errorf("expected err: %v but got none", test.err)
} }
return nil return nil
} }
@ -229,19 +234,24 @@ func TestLocalUnpackTests(t *testing.T) {
//Unpack //Unpack
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def) def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
abi, err := JSON(strings.NewReader(def)) abi, err := JSON(strings.NewReader(def))
if err != nil { if err != nil {
t.Fatalf("invalid ABI definition %s: %v", def, err) t.Fatalf("invalid ABI definition %s: %v", def, err)
} }
encb, err := hex.DecodeString(test.enc) encb, err := hex.DecodeString(test.enc)
if err != nil { if err != nil {
t.Fatalf("invalid hex %s: %v", test.enc, err) t.Fatalf("invalid hex %s: %v", test.enc, err)
} }
outptr := reflect.New(reflect.TypeOf(test.want)) outptr := reflect.New(reflect.TypeOf(test.want))
err = abi.UnpackIntoInterface(outptr.Interface(), "method", encb) err = abi.UnpackIntoInterface(outptr.Interface(), "method", encb)
if err := test.checkError(err); err != nil { if err := test.checkError(err); err != nil {
t.Errorf("test %d (%v) failed: %v", i, test.def, err) t.Errorf("test %d (%v) failed: %v", i, test.def, err)
return return
} }
out := outptr.Elem().Interface() out := outptr.Elem().Interface()
if !reflect.DeepEqual(test.want, out) { if !reflect.DeepEqual(test.want, out) {
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.want, out) t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.want, out)
@ -269,13 +279,16 @@ func TestUnpackIntoInterfaceSetDynamicArrayOutput(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(out32) != 2 { if len(out32) != 2 {
t.Fatalf("expected array with 2 values, got %d", len(out32)) t.Fatalf("expected array with 2 values, got %d", len(out32))
} }
expected := common.Hex2Bytes("3078313233343536373839300000000000000000000000000000000000000000") expected := common.Hex2Bytes("3078313233343536373839300000000000000000000000000000000000000000")
if !bytes.Equal(out32[0][:], expected) { if !bytes.Equal(out32[0][:], expected) {
t.Errorf("expected %x, got %x\n", expected, out32[0]) t.Errorf("expected %x, got %x\n", expected, out32[0])
} }
expected = common.Hex2Bytes("3078303938373635343332310000000000000000000000000000000000000000") expected = common.Hex2Bytes("3078303938373635343332310000000000000000000000000000000000000000")
if !bytes.Equal(out32[1][:], expected) { if !bytes.Equal(out32[1][:], expected) {
t.Errorf("expected %x, got %x\n", expected, out32[1]) t.Errorf("expected %x, got %x\n", expected, out32[1])
@ -286,13 +299,16 @@ func TestUnpackIntoInterfaceSetDynamicArrayOutput(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(out15) != 2 { if len(out15) != 2 {
t.Fatalf("expected array with 2 values, got %d", len(out15)) t.Fatalf("expected array with 2 values, got %d", len(out15))
} }
expected = common.Hex2Bytes("307830313233343500000000000000") expected = common.Hex2Bytes("307830313233343500000000000000")
if !bytes.Equal(out15[0][:], expected) { if !bytes.Equal(out15[0][:], expected) {
t.Errorf("expected %x, got %x\n", expected, out15[0]) t.Errorf("expected %x, got %x\n", expected, out15[0])
} }
expected = common.Hex2Bytes("307839383736353400000000000000") expected = common.Hex2Bytes("307839383736353400000000000000")
if !bytes.Equal(out15[1][:], expected) { if !bytes.Equal(out15[1][:], expected) {
t.Errorf("expected %x, got %x\n", expected, out15[1]) t.Errorf("expected %x, got %x\n", expected, out15[1])
@ -307,6 +323,7 @@ type methodMultiOutput struct {
func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOutput) { func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOutput) {
const definition = `[ const definition = `[
{ "name" : "multi", "type": "function", "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]` { "name" : "multi", "type": "function", "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
var expected = methodMultiOutput{big.NewInt(1), "hello"} var expected = methodMultiOutput{big.NewInt(1), "hello"}
abi, err := JSON(strings.NewReader(definition)) abi, err := JSON(strings.NewReader(definition))
@ -317,6 +334,7 @@ func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOut
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005")) buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
buff.Write(common.RightPadBytes([]byte(expected.String), 32)) buff.Write(common.RightPadBytes([]byte(expected.String), 32))
return abi, buff.Bytes(), expected return abi, buff.Bytes(), expected
} }
@ -333,6 +351,7 @@ func TestMethodMultiReturn(t *testing.T) {
abi, data, expected := methodMultiReturn(require.New(t)) abi, data, expected := methodMultiReturn(require.New(t))
bigint := new(big.Int) bigint := new(big.Int)
var testCases = []struct { var testCases = []struct {
dest interface{} dest interface{}
expected interface{} expected interface{}
@ -384,11 +403,13 @@ func TestMethodMultiReturn(t *testing.T) {
"abi: insufficient number of arguments for unpack, want 2, got 1", "abi: insufficient number of arguments for unpack, want 2, got 1",
"Can not unpack into a slice with wrong types", "Can not unpack into a slice with wrong types",
}} }}
for _, tc := range testCases { for _, tc := range testCases {
tc := tc tc := tc
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
require := require.New(t) require := require.New(t)
err := abi.UnpackIntoInterface(tc.dest, "multi", data) err := abi.UnpackIntoInterface(tc.dest, "multi", data)
if tc.error == "" { if tc.error == "" {
require.Nil(err, "Should be able to unpack method outputs.") require.Nil(err, "Should be able to unpack method outputs.")
require.Equal(tc.expected, tc.dest) require.Equal(tc.expected, tc.dest)
@ -401,22 +422,27 @@ func TestMethodMultiReturn(t *testing.T) {
func TestMultiReturnWithArray(t *testing.T) { func TestMultiReturnWithArray(t *testing.T) {
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]` const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]`
abi, err := JSON(strings.NewReader(definition)) abi, err := JSON(strings.NewReader(definition))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
buff := new(bytes.Buffer) buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000009")) buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000009"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000008")) buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000008"))
ret1, ret1Exp := new([3]uint64), [3]uint64{9, 9, 9} ret1, ret1Exp := new([3]uint64), [3]uint64{9, 9, 9}
ret2, ret2Exp := new(uint64), uint64(8) ret2, ret2Exp := new(uint64), uint64(8)
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !reflect.DeepEqual(*ret1, ret1Exp) { if !reflect.DeepEqual(*ret1, ret1Exp) {
t.Error("array result", *ret1, "!= Expected", ret1Exp) t.Error("array result", *ret1, "!= Expected", ret1Exp)
} }
if *ret2 != ret2Exp { if *ret2 != ret2Exp {
t.Error("int result", *ret2, "!= Expected", ret2Exp) t.Error("int result", *ret2, "!= Expected", ret2Exp)
} }
@ -424,10 +450,12 @@ func TestMultiReturnWithArray(t *testing.T) {
func TestMultiReturnWithStringArray(t *testing.T) { func TestMultiReturnWithStringArray(t *testing.T) {
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "uint256[3]"},{"name": "","type": "address"},{"name": "","type": "string[2]"},{"name": "","type": "bool"}]}]` const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "uint256[3]"},{"name": "","type": "address"},{"name": "","type": "string[2]"},{"name": "","type": "bool"}]}]`
abi, err := JSON(strings.NewReader(definition)) abi, err := JSON(strings.NewReader(definition))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
buff := new(bytes.Buffer) buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000005c1b78ea0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000001a055690d9db80000000000000000000000000000ab1257528b3782fb40d7ed5f72e624b744dffb2f00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001048656c6c6f2c20457468657265756d2100000000000000000000000000000000")) buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000005c1b78ea0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000001a055690d9db80000000000000000000000000000ab1257528b3782fb40d7ed5f72e624b744dffb2f00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001048656c6c6f2c20457468657265756d2100000000000000000000000000000000"))
@ -436,18 +464,23 @@ func TestMultiReturnWithStringArray(t *testing.T) {
ret2, ret2Exp := new(common.Address), common.HexToAddress("ab1257528b3782fb40d7ed5f72e624b744dffb2f") ret2, ret2Exp := new(common.Address), common.HexToAddress("ab1257528b3782fb40d7ed5f72e624b744dffb2f")
ret3, ret3Exp := new([2]string), [2]string{"Ethereum", "Hello, Ethereum!"} ret3, ret3Exp := new([2]string), [2]string{"Ethereum", "Hello, Ethereum!"}
ret4, ret4Exp := new(bool), false ret4, ret4Exp := new(bool), false
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2, ret3, ret4}, "multi", buff.Bytes()); err != nil { if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2, ret3, ret4}, "multi", buff.Bytes()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !reflect.DeepEqual(*ret1, ret1Exp) { if !reflect.DeepEqual(*ret1, ret1Exp) {
t.Error("big.Int array result", *ret1, "!= Expected", ret1Exp) t.Error("big.Int array result", *ret1, "!= Expected", ret1Exp)
} }
if !reflect.DeepEqual(*ret2, ret2Exp) { if !reflect.DeepEqual(*ret2, ret2Exp) {
t.Error("address result", *ret2, "!= Expected", ret2Exp) t.Error("address result", *ret2, "!= Expected", ret2Exp)
} }
if !reflect.DeepEqual(*ret3, ret3Exp) { if !reflect.DeepEqual(*ret3, ret3Exp) {
t.Error("string array result", *ret3, "!= Expected", ret3Exp) t.Error("string array result", *ret3, "!= Expected", ret3Exp)
} }
if !reflect.DeepEqual(*ret4, ret4Exp) { if !reflect.DeepEqual(*ret4, ret4Exp) {
t.Error("bool result", *ret4, "!= Expected", ret4Exp) t.Error("bool result", *ret4, "!= Expected", ret4Exp)
} }
@ -455,10 +488,12 @@ func TestMultiReturnWithStringArray(t *testing.T) {
func TestMultiReturnWithStringSlice(t *testing.T) { func TestMultiReturnWithStringSlice(t *testing.T) {
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "string[]"},{"name": "","type": "uint256[]"}]}]` const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "string[]"},{"name": "","type": "uint256[]"}]}]`
abi, err := JSON(strings.NewReader(definition)) abi, err := JSON(strings.NewReader(definition))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
buff := new(bytes.Buffer) buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // output[0] offset buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // output[0] offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000120")) // output[1] offset buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000120")) // output[1] offset
@ -472,14 +507,18 @@ func TestMultiReturnWithStringSlice(t *testing.T) {
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // output[1] length buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // output[1] length
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000064")) // output[1][0] value buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000064")) // output[1][0] value
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000065")) // output[1][1] value buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000065")) // output[1][1] value
ret1, ret1Exp := new([]string), []string{"ethereum", "go-ethereum"} ret1, ret1Exp := new([]string), []string{"ethereum", "go-ethereum"}
ret2, ret2Exp := new([]*big.Int), []*big.Int{big.NewInt(100), big.NewInt(101)} ret2, ret2Exp := new([]*big.Int), []*big.Int{big.NewInt(100), big.NewInt(101)}
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !reflect.DeepEqual(*ret1, ret1Exp) { if !reflect.DeepEqual(*ret1, ret1Exp) {
t.Error("string slice result", *ret1, "!= Expected", ret1Exp) t.Error("string slice result", *ret1, "!= Expected", ret1Exp)
} }
if !reflect.DeepEqual(*ret2, ret2Exp) { if !reflect.DeepEqual(*ret2, ret2Exp) {
t.Error("uint256 slice result", *ret2, "!= Expected", ret2Exp) t.Error("uint256 slice result", *ret2, "!= Expected", ret2Exp)
} }
@ -491,10 +530,12 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
// after such nested array argument should be read with the correct offset, // after such nested array argument should be read with the correct offset,
// so that it does not read content from the previous array argument. // so that it does not read content from the previous array argument.
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]` const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]`
abi, err := JSON(strings.NewReader(definition)) abi, err := JSON(strings.NewReader(definition))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
buff := new(bytes.Buffer) buff := new(bytes.Buffer)
// construct the test array, each 3 char element is joined with 61 '0' chars, // construct the test array, each 3 char element is joined with 61 '0' chars,
// to from the ((3 + 61) * 0.5) = 32 byte elements in the array. // to from the ((3 + 61) * 0.5) = 32 byte elements in the array.
@ -514,12 +555,15 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
{{0x411, 0x412, 0x413}, {0x421, 0x422, 0x423}}, {{0x411, 0x412, 0x413}, {0x421, 0x422, 0x423}},
} }
ret2, ret2Exp := new(uint64), uint64(0x9876) ret2, ret2Exp := new(uint64), uint64(0x9876)
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !reflect.DeepEqual(*ret1, ret1Exp) { if !reflect.DeepEqual(*ret1, ret1Exp) {
t.Error("array result", *ret1, "!= Expected", ret1Exp) t.Error("array result", *ret1, "!= Expected", ret1Exp)
} }
if *ret2 != ret2Exp { if *ret2 != ret2Exp {
t.Error("int result", *ret2, "!= Expected", ret2Exp) t.Error("int result", *ret2, "!= Expected", ret2Exp)
} }
@ -541,6 +585,7 @@ func TestUnmarshal(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
buff := new(bytes.Buffer) buff := new(bytes.Buffer)
// marshall mixed bytes (mixedBytes) // marshall mixed bytes (mixedBytes)
@ -568,6 +613,7 @@ func TestUnmarshal(t *testing.T) {
// marshal int // marshal int
var Int *big.Int var Int *big.Int
err = abi.UnpackIntoInterface(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) err = abi.UnpackIntoInterface(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -579,6 +625,7 @@ func TestUnmarshal(t *testing.T) {
// marshal bool // marshal bool
var Bool bool var Bool bool
err = abi.UnpackIntoInterface(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) err = abi.UnpackIntoInterface(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -596,6 +643,7 @@ func TestUnmarshal(t *testing.T) {
buff.Write(bytesOut) buff.Write(bytesOut)
var Bytes []byte var Bytes []byte
err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes()) err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes())
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -663,6 +711,7 @@ func TestUnmarshal(t *testing.T) {
buff.Write(common.RightPadBytes([]byte("hello"), 32)) buff.Write(common.RightPadBytes([]byte("hello"), 32))
var hash common.Hash var hash common.Hash
err = abi.UnpackIntoInterface(&hash, "fixed", buff.Bytes()) err = abi.UnpackIntoInterface(&hash, "fixed", buff.Bytes())
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -676,6 +725,7 @@ func TestUnmarshal(t *testing.T) {
// marshal error // marshal error
buff.Reset() buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes()) err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes())
if err == nil { if err == nil {
t.Error("expected error") t.Error("expected error")
@ -692,10 +742,12 @@ func TestUnmarshal(t *testing.T) {
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003")) buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003"))
// marshal int array // marshal int array
var intArray [3]*big.Int var intArray [3]*big.Int
err = abi.UnpackIntoInterface(&intArray, "intArraySingle", buff.Bytes()) err = abi.UnpackIntoInterface(&intArray, "intArraySingle", buff.Bytes())
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
var testAgainstIntArray [3]*big.Int var testAgainstIntArray [3]*big.Int
testAgainstIntArray[0] = big.NewInt(1) testAgainstIntArray[0] = big.NewInt(1)
testAgainstIntArray[1] = big.NewInt(2) testAgainstIntArray[1] = big.NewInt(2)
@ -713,6 +765,7 @@ func TestUnmarshal(t *testing.T) {
buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000")) buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"))
var outAddr []common.Address var outAddr []common.Address
err = abi.UnpackIntoInterface(&outAddr, "addressSliceSingle", buff.Bytes()) err = abi.UnpackIntoInterface(&outAddr, "addressSliceSingle", buff.Bytes())
if err != nil { if err != nil {
t.Fatal("didn't expect error:", err) t.Fatal("didn't expect error:", err)
@ -740,6 +793,7 @@ func TestUnmarshal(t *testing.T) {
A []common.Address A []common.Address
B []common.Address B []common.Address
} }
err = abi.UnpackIntoInterface(&outAddrStruct, "addressSliceDouble", buff.Bytes()) err = abi.UnpackIntoInterface(&outAddrStruct, "addressSliceDouble", buff.Bytes())
if err != nil { if err != nil {
t.Fatal("didn't expect error:", err) t.Fatal("didn't expect error:", err)
@ -760,6 +814,7 @@ func TestUnmarshal(t *testing.T) {
if outAddrStruct.B[0] != (common.Address{2}) { if outAddrStruct.B[0] != (common.Address{2}) {
t.Errorf("expected %x, got %x", common.Address{2}, outAddrStruct.B[0]) t.Errorf("expected %x, got %x", common.Address{2}, outAddrStruct.B[0])
} }
if outAddrStruct.B[1] != (common.Address{3}) { if outAddrStruct.B[1] != (common.Address{3}) {
t.Errorf("expected %x, got %x", common.Address{3}, outAddrStruct.B[1]) t.Errorf("expected %x, got %x", common.Address{3}, outAddrStruct.B[1])
} }
@ -776,10 +831,12 @@ func TestUnmarshal(t *testing.T) {
func TestUnpackTuple(t *testing.T) { func TestUnpackTuple(t *testing.T) {
const simpleTuple = `[{"name":"tuple","type":"function","outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]` const simpleTuple = `[{"name":"tuple","type":"function","outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]`
abi, err := JSON(strings.NewReader(simpleTuple)) abi, err := JSON(strings.NewReader(simpleTuple))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
buff := new(bytes.Buffer) buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // ret[a] = 1 buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // ret[a] = 1
@ -790,9 +847,11 @@ func TestUnpackTuple(t *testing.T) {
A *big.Int A *big.Int
B *big.Int B *big.Int
} }
type r struct { type r struct {
Result v Result v
} }
var ret0 = new(r) var ret0 = new(r)
err = abi.UnpackIntoInterface(ret0, "tuple", buff.Bytes()) err = abi.UnpackIntoInterface(ret0, "tuple", buff.Bytes())
@ -802,6 +861,7 @@ func TestUnpackTuple(t *testing.T) {
if ret0.Result.A.Cmp(big.NewInt(1)) != 0 { if ret0.Result.A.Cmp(big.NewInt(1)) != 0 {
t.Errorf("unexpected value unpacked: want %x, got %x", 1, ret0.Result.A) t.Errorf("unexpected value unpacked: want %x, got %x", 1, ret0.Result.A)
} }
if ret0.Result.B.Cmp(big.NewInt(-1)) != 0 { if ret0.Result.B.Cmp(big.NewInt(-1)) != 0 {
t.Errorf("unexpected value unpacked: want %x, got %x", -1, ret0.Result.B) t.Errorf("unexpected value unpacked: want %x, got %x", -1, ret0.Result.B)
} }
@ -818,6 +878,7 @@ func TestUnpackTuple(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
buff.Reset() buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // s offset buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // s offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")) // t.X = 0 buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")) // t.X = 0
@ -851,7 +912,9 @@ func TestUnpackTuple(t *testing.T) {
FieldT T `abi:"t"` FieldT T `abi:"t"`
A *big.Int A *big.Int
} }
var ret Ret var ret Ret
var expected = Ret{ var expected = Ret{
FieldS: S{ FieldS: S{
A: big.NewInt(1), A: big.NewInt(1),
@ -871,6 +934,7 @@ func TestUnpackTuple(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if reflect.DeepEqual(ret, expected) { if reflect.DeepEqual(ret, expected) {
t.Error("unexpected unpack value") t.Error("unexpected unpack value")
} }
@ -932,13 +996,16 @@ func TestOOMMaliciousInput(t *testing.T) {
for i, test := range oomTests { for i, test := range oomTests {
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def) def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
abi, err := JSON(strings.NewReader(def)) abi, err := JSON(strings.NewReader(def))
if err != nil { if err != nil {
t.Fatalf("invalid ABI definition %s: %v", def, err) t.Fatalf("invalid ABI definition %s: %v", def, err)
} }
encb, err := hex.DecodeString(test.enc) encb, err := hex.DecodeString(test.enc)
if err != nil { if err != nil {
t.Fatalf("invalid hex: %s" + test.enc) t.Fatalf("invalid hex: %s" + test.enc)
} }
_, err = abi.Methods["method"].Outputs.UnpackValues(encb) _, err = abi.Methods["method"].Outputs.UnpackValues(encb)
if err == nil { if err == nil {
t.Fatalf("Expected error on malicious input, test %d", i) t.Fatalf("Expected error on malicious input, test %d", i)

View file

@ -199,6 +199,7 @@ func TextAndHash(data []byte) ([]byte, string) {
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), string(data)) msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), string(data))
hasher := sha3.NewLegacyKeccak256() hasher := sha3.NewLegacyKeccak256()
hasher.Write([]byte(msg)) hasher.Write([]byte(msg))
return hasher.Sum(nil), msg return hasher.Sum(nil), msg
} }

View file

@ -26,6 +26,7 @@ import (
func TestTextHash(t *testing.T) { func TestTextHash(t *testing.T) {
hash := TextHash([]byte("Hello Joe")) hash := TextHash([]byte("Hello Joe"))
want := hexutil.MustDecode("0xa080337ae51c4e064c189e113edd0ba391df9206e2f49db658bb32cf2911730b") want := hexutil.MustDecode("0xa080337ae51c4e064c189e113edd0ba391df9206e2f49db658bb32cf2911730b")
if !bytes.Equal(hash, want) { if !bytes.Equal(hash, want) {
t.Fatalf("wrong hash: %x", hash) t.Fatalf("wrong hash: %x", hash)
} }

View file

@ -45,6 +45,7 @@ func NewExternalBackend(endpoint string) (*ExternalBackend, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &ExternalBackend{ return &ExternalBackend{
signers: []accounts.Wallet{signer}, signers: []accounts.Wallet{signer},
}, nil }, nil
@ -73,6 +74,7 @@ func NewExternalSigner(endpoint string) (*ExternalSigner, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
extsigner := &ExternalSigner{ extsigner := &ExternalSigner{
client: client, client: client,
endpoint: endpoint, endpoint: endpoint,
@ -82,7 +84,9 @@ func NewExternalSigner(endpoint string) (*ExternalSigner, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
extsigner.status = fmt.Sprintf("ok [version=%v]", version) extsigner.status = fmt.Sprintf("ok [version=%v]", version)
return extsigner, nil return extsigner, nil
} }
@ -107,11 +111,13 @@ func (api *ExternalSigner) Close() error {
func (api *ExternalSigner) Accounts() []accounts.Account { func (api *ExternalSigner) Accounts() []accounts.Account {
var accnts []accounts.Account var accnts []accounts.Account
res, err := api.listAccounts() res, err := api.listAccounts()
if err != nil { if err != nil {
log.Error("account listing failed", "error", err) log.Error("account listing failed", "error", err)
return accnts return accnts
} }
for _, addr := range res { for _, addr := range res {
accnts = append(accnts, accounts.Account{ accnts = append(accnts, accounts.Account{
URL: accounts.URL{ URL: accounts.URL{
@ -121,26 +127,31 @@ func (api *ExternalSigner) Accounts() []accounts.Account {
Address: addr, Address: addr,
}) })
} }
api.cacheMu.Lock() api.cacheMu.Lock()
api.cache = accnts api.cache = accnts
api.cacheMu.Unlock() api.cacheMu.Unlock()
return accnts return accnts
} }
func (api *ExternalSigner) Contains(account accounts.Account) bool { func (api *ExternalSigner) Contains(account accounts.Account) bool {
api.cacheMu.RLock() api.cacheMu.RLock()
defer api.cacheMu.RUnlock() defer api.cacheMu.RUnlock()
if api.cache == nil { if api.cache == nil {
// If we haven't already fetched the accounts, it's time to do so now // If we haven't already fetched the accounts, it's time to do so now
api.cacheMu.RUnlock() api.cacheMu.RUnlock()
api.Accounts() api.Accounts()
api.cacheMu.RLock() api.cacheMu.RLock()
} }
for _, a := range api.cache { for _, a := range api.cache {
if a.Address == account.Address && (account.URL == (accounts.URL{}) || account.URL == api.URL()) { if a.Address == account.Address && (account.URL == (accounts.URL{}) || account.URL == api.URL()) {
return true return true
} }
} }
return false return false
} }
@ -155,6 +166,7 @@ func (api *ExternalSigner) SelfDerive(bases []accounts.DerivationPath, chain eth
// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed // 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) { func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
var res hexutil.Bytes var res hexutil.Bytes
var signAddress = common.NewMixedcaseAddress(account.Address) var signAddress = common.NewMixedcaseAddress(account.Address)
if err := api.client.Call(&res, "account_signData", if err := api.client.Call(&res, "account_signData",
mimeType, mimeType,
@ -166,11 +178,13 @@ func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, d
if mimeType == accounts.MimetypeClique && (res[64] == 27 || res[64] == 28) { if mimeType == accounts.MimetypeClique && (res[64] == 27 || res[64] == 28) {
res[64] -= 27 // Transform V from 27/28 to 0/1 for Clique use res[64] -= 27 // Transform V from 27/28 to 0/1 for Clique use
} }
return res, nil return res, nil
} }
func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) { func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) {
var signature hexutil.Bytes var signature hexutil.Bytes
var signAddress = common.NewMixedcaseAddress(account.Address) var signAddress = common.NewMixedcaseAddress(account.Address)
if err := api.client.Call(&signature, "account_signData", if err := api.client.Call(&signature, "account_signData",
accounts.MimetypeTextPlain, accounts.MimetypeTextPlain,
@ -178,11 +192,13 @@ func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]by
hexutil.Encode(text)); err != nil { hexutil.Encode(text)); err != nil {
return nil, err return nil, err
} }
if signature[64] == 27 || signature[64] == 28 { if signature[64] == 27 || signature[64] == 28 {
// If clef is used as a backend, it may already have transformed // If clef is used as a backend, it may already have transformed
// the signature to ethereum-type signature. // the signature to ethereum-type signature.
signature[64] -= 27 // Transform V from Ethereum-legacy to 0/1 signature[64] -= 27 // Transform V from Ethereum-legacy to 0/1
} }
return signature, nil return signature, nil
} }
@ -198,11 +214,14 @@ type signTransactionResult struct {
// transaction overrides the chainID parameter. // transaction overrides the chainID parameter.
func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
data := hexutil.Bytes(tx.Data()) data := hexutil.Bytes(tx.Data())
var to *common.MixedcaseAddress var to *common.MixedcaseAddress
if tx.To() != nil { if tx.To() != nil {
t := common.NewMixedcaseAddress(*tx.To()) t := common.NewMixedcaseAddress(*tx.To())
to = &t to = &t
} }
args := &apitypes.SendTxArgs{ args := &apitypes.SendTxArgs{
Data: &data, Data: &data,
Nonce: hexutil.Uint64(tx.Nonce()), Nonce: hexutil.Uint64(tx.Nonce()),
@ -225,19 +244,23 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
if chainID != nil && chainID.Sign() != 0 { if chainID != nil && chainID.Sign() != 0 {
args.ChainID = (*hexutil.Big)(chainID) args.ChainID = (*hexutil.Big)(chainID)
} }
if tx.Type() != types.LegacyTxType { if tx.Type() != types.LegacyTxType {
// However, if the user asked for a particular chain id, then we should // However, if the user asked for a particular chain id, then we should
// use that instead. // use that instead.
if tx.ChainId().Sign() != 0 { if tx.ChainId().Sign() != 0 {
args.ChainID = (*hexutil.Big)(tx.ChainId()) args.ChainID = (*hexutil.Big)(tx.ChainId())
} }
accessList := tx.AccessList() accessList := tx.AccessList()
args.AccessList = &accessList args.AccessList = &accessList
} }
var res signTransactionResult var res signTransactionResult
if err := api.client.Call(&res, "account_signTransaction", args); err != nil { if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
return nil, err return nil, err
} }
return res.Tx, nil return res.Tx, nil
} }
@ -257,6 +280,7 @@ func (api *ExternalSigner) listAccounts() ([]common.Address, error) {
if err := api.client.Call(&res, "account_list"); err != nil { if err := api.client.Call(&res, "account_list"); err != nil {
return nil, err return nil, err
} }
return res, nil return res, nil
} }
@ -265,5 +289,6 @@ func (api *ExternalSigner) pingVersion() (string, error) {
if err := api.client.Call(&v, "account_version"); err != nil { if err := api.client.Call(&v, "account_version"); err != nil {
return "", err return "", err
} }
return v, nil return v, nil
} }

View file

@ -70,6 +70,7 @@ func ParseDerivationPath(path string) (DerivationPath, error) {
// Handle absolute or relative paths // Handle absolute or relative paths
components := strings.Split(path, "/") components := strings.Split(path, "/")
switch { switch {
case len(components) == 0: case len(components) == 0:
return nil, errors.New("empty derivation path") return nil, errors.New("empty derivation path")
@ -87,9 +88,11 @@ func ParseDerivationPath(path string) (DerivationPath, error) {
if len(components) == 0 { if len(components) == 0 {
return nil, errors.New("empty derivation path") // Empty relative paths return nil, errors.New("empty derivation path") // Empty relative paths
} }
for _, component := range components { for _, component := range components {
// Ignore any user added whitespace // Ignore any user added whitespace
component = strings.TrimSpace(component) component = strings.TrimSpace(component)
var value uint32 var value uint32
// Handle hardened paths // Handle hardened paths
@ -102,18 +105,22 @@ func ParseDerivationPath(path string) (DerivationPath, error) {
if !ok { if !ok {
return nil, fmt.Errorf("invalid component: %s", component) return nil, fmt.Errorf("invalid component: %s", component)
} }
max := math.MaxUint32 - value max := math.MaxUint32 - value
if bigval.Sign() < 0 || bigval.Cmp(big.NewInt(int64(max))) > 0 { if bigval.Sign() < 0 || bigval.Cmp(big.NewInt(int64(max))) > 0 {
if value == 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 range [0, %d]", bigval, max)
} }
return nil, fmt.Errorf("component %v out of allowed hardened range [0, %d]", bigval, max) return nil, fmt.Errorf("component %v out of allowed hardened range [0, %d]", bigval, max)
} }
value += uint32(bigval.Uint64()) value += uint32(bigval.Uint64())
// Append and repeat // Append and repeat
result = append(result, value) result = append(result, value)
} }
return result, nil return result, nil
} }
@ -121,17 +128,21 @@ func ParseDerivationPath(path string) (DerivationPath, error) {
// to its canonical representation. // to its canonical representation.
func (path DerivationPath) String() string { func (path DerivationPath) String() string {
result := "m" result := "m"
for _, component := range path { for _, component := range path {
var hardened bool var hardened bool
if component >= 0x80000000 { if component >= 0x80000000 {
component -= 0x80000000 component -= 0x80000000
hardened = true hardened = true
} }
result = fmt.Sprintf("%s/%d", result, component) result = fmt.Sprintf("%s/%d", result, component)
if hardened { if hardened {
result += "'" result += "'"
} }
} }
return result return result
} }
@ -143,11 +154,14 @@ func (path DerivationPath) MarshalJSON() ([]byte, error) {
// UnmarshalJSON a json-serialized string back into a derivation path // UnmarshalJSON a json-serialized string back into a derivation path
func (path *DerivationPath) UnmarshalJSON(b []byte) error { func (path *DerivationPath) UnmarshalJSON(b []byte) error {
var dp string var dp string
var err error var err error
if err = json.Unmarshal(b, &dp); err != nil { if err = json.Unmarshal(b, &dp); err != nil {
return err return err
} }
*path, err = ParseDerivationPath(dp) *path, err = ParseDerivationPath(dp)
return err return err
} }
@ -158,6 +172,7 @@ func DefaultIterator(base DerivationPath) func() DerivationPath {
copy(path[:], base[:]) copy(path[:], base[:])
// Set it back by one, so the first call gives the first result // Set it back by one, so the first call gives the first result
path[len(path)-1]-- path[len(path)-1]--
return func() DerivationPath { return func() DerivationPath {
path[len(path)-1]++ path[len(path)-1]++
return path return path
@ -172,6 +187,7 @@ func LedgerLiveIterator(base DerivationPath) func() DerivationPath {
copy(path[:], base[:]) copy(path[:], base[:])
// Set it back by one, so the first call gives the first result // Set it back by one, so the first call gives the first result
path[2]-- path[2]--
return func() DerivationPath { return func() DerivationPath {
// ledgerLivePathIterator iterates on the third component // ledgerLivePathIterator iterates on the third component
path[2]++ path[2]++

View file

@ -81,6 +81,7 @@ func TestHDPathParsing(t *testing.T) {
func testDerive(t *testing.T, next func() DerivationPath, expected []string) { func testDerive(t *testing.T, next func() DerivationPath, expected []string) {
t.Helper() t.Helper()
for i, want := range expected { for i, want := range expected {
if have := next(); fmt.Sprintf("%v", have) != want { if have := next(); fmt.Sprintf("%v", have) != want {
t.Errorf("step %d, have %v, want %v", i, have, want) t.Errorf("step %d, have %v, want %v", i, have, want)

View file

@ -60,6 +60,7 @@ func (err *AmbiguousAddrError) Error() string {
files += ", " files += ", "
} }
} }
return fmt.Sprintf("multiple keys match address (%s)", files) return fmt.Sprintf("multiple keys match address (%s)", files)
} }
@ -83,6 +84,7 @@ func newAccountCache(keydir string) (*accountCache, chan struct{}) {
fileC: fileCache{all: mapset.NewThreadUnsafeSet[string]()}, fileC: fileCache{all: mapset.NewThreadUnsafeSet[string]()},
} }
ac.watcher = newWatcher(ac) ac.watcher = newWatcher(ac)
return ac, ac.notify return ac, ac.notify
} }
@ -92,6 +94,7 @@ func (ac *accountCache) accounts() []accounts.Account {
defer ac.mu.Unlock() defer ac.mu.Unlock()
cpy := make([]accounts.Account, len(ac.all)) cpy := make([]accounts.Account, len(ac.all))
copy(cpy, ac.all) copy(cpy, ac.all)
return cpy return cpy
} }
@ -99,6 +102,7 @@ func (ac *accountCache) hasAddress(addr common.Address) bool {
ac.maybeReload() ac.maybeReload()
ac.mu.Lock() ac.mu.Lock()
defer ac.mu.Unlock() defer ac.mu.Unlock()
return len(ac.byAddr[addr]) > 0 return len(ac.byAddr[addr]) > 0
} }
@ -139,6 +143,7 @@ func (ac *accountCache) deleteByFile(path string) {
if i < len(ac.all) && ac.all[i].URL.Path == path { if i < len(ac.all) && ac.all[i].URL.Path == path {
removed := ac.all[i] removed := ac.all[i]
ac.all = append(ac.all[:i], ac.all[i+1:]...) ac.all = append(ac.all[:i], ac.all[i+1:]...)
if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 { if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 {
delete(ac.byAddr, removed.Address) delete(ac.byAddr, removed.Address)
} else { } else {
@ -152,6 +157,7 @@ func (ac *accountCache) deleteByFile(path string) {
func (ac *accountCache) watcherStarted() bool { func (ac *accountCache) watcherStarted() bool {
ac.mu.Lock() ac.mu.Lock()
defer ac.mu.Unlock() defer ac.mu.Unlock()
return ac.watcher.running || ac.watcher.runEnded return ac.watcher.running || ac.watcher.runEnded
} }
@ -161,6 +167,7 @@ func removeAccount(slice []accounts.Account, elem accounts.Account) []accounts.A
return append(slice[:i], slice[i+1:]...) return append(slice[:i], slice[i+1:]...)
} }
} }
return slice return slice
} }
@ -173,20 +180,24 @@ func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) {
if (a.Address != common.Address{}) { if (a.Address != common.Address{}) {
matches = ac.byAddr[a.Address] matches = ac.byAddr[a.Address]
} }
if a.URL.Path != "" { if a.URL.Path != "" {
// If only the basename is specified, complete the path. // If only the basename is specified, complete the path.
if !strings.ContainsRune(a.URL.Path, filepath.Separator) { if !strings.ContainsRune(a.URL.Path, filepath.Separator) {
a.URL.Path = filepath.Join(ac.keydir, a.URL.Path) a.URL.Path = filepath.Join(ac.keydir, a.URL.Path)
} }
for i := range matches { for i := range matches {
if matches[i].URL == a.URL { if matches[i].URL == a.URL {
return matches[i], nil return matches[i], nil
} }
} }
if (a.Address == common.Address{}) { if (a.Address == common.Address{}) {
return accounts.Account{}, ErrNoMatch return accounts.Account{}, ErrNoMatch
} }
} }
switch len(matches) { switch len(matches) {
case 1: case 1:
return matches[0], nil return matches[0], nil
@ -196,6 +207,7 @@ func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) {
err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))} err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))}
copy(err.Matches, matches) copy(err.Matches, matches)
sort.Sort(accountsByURL(err.Matches)) sort.Sort(accountsByURL(err.Matches))
return accounts.Account{}, err return accounts.Account{}, err
} }
} }
@ -207,6 +219,7 @@ func (ac *accountCache) maybeReload() {
ac.mu.Unlock() ac.mu.Unlock()
return // A watcher is running and will keep the cache up-to-date. return // A watcher is running and will keep the cache up-to-date.
} }
if ac.throttle == nil { if ac.throttle == nil {
ac.throttle = time.NewTimer(0) ac.throttle = time.NewTimer(0)
} else { } else {
@ -227,9 +240,11 @@ func (ac *accountCache) maybeReload() {
func (ac *accountCache) close() { func (ac *accountCache) close() {
ac.mu.Lock() ac.mu.Lock()
ac.watcher.close() ac.watcher.close()
if ac.throttle != nil { if ac.throttle != nil {
ac.throttle.Stop() ac.throttle.Stop()
} }
if ac.notify != nil { if ac.notify != nil {
close(ac.notify) close(ac.notify)
ac.notify = nil ac.notify = nil
@ -246,6 +261,7 @@ func (ac *accountCache) scanAccounts() error {
log.Debug("Failed to reload keystore contents", "err", err) log.Debug("Failed to reload keystore contents", "err", err)
return err return err
} }
if creates.Cardinality() == 0 && deletes.Cardinality() == 0 && updates.Cardinality() == 0 { if creates.Cardinality() == 0 && deletes.Cardinality() == 0 && updates.Cardinality() == 0 {
return nil return nil
} }
@ -256,18 +272,21 @@ func (ac *accountCache) scanAccounts() error {
Address string `json:"address"` Address string `json:"address"`
} }
) )
readAccount := func(path string) *accounts.Account { readAccount := func(path string) *accounts.Account {
fd, err := os.Open(path) fd, err := os.Open(path)
if err != nil { if err != nil {
log.Trace("Failed to open keystore file", "path", path, "err", err) log.Trace("Failed to open keystore file", "path", path, "err", err)
return nil return nil
} }
defer fd.Close() defer fd.Close()
buf.Reset(fd) buf.Reset(fd)
// Parse the address. // Parse the address.
key.Address = "" key.Address = ""
err = json.NewDecoder(buf).Decode(&key) err = json.NewDecoder(buf).Decode(&key)
addr := common.HexToAddress(key.Address) addr := common.HexToAddress(key.Address)
switch { switch {
case err != nil: case err != nil:
log.Debug("Failed to decode keystore key", "path", path, "err", err) log.Debug("Failed to decode keystore key", "path", path, "err", err)
@ -279,6 +298,7 @@ func (ac *accountCache) scanAccounts() error {
URL: accounts.URL{Scheme: KeyStoreScheme, Path: path}, URL: accounts.URL{Scheme: KeyStoreScheme, Path: path},
} }
} }
return nil return nil
} }
// Process all the file diffs // Process all the file diffs
@ -289,15 +309,19 @@ func (ac *accountCache) scanAccounts() error {
ac.add(*a) ac.add(*a)
} }
} }
for _, path := range deletes.ToSlice() { for _, path := range deletes.ToSlice() {
ac.deleteByFile(path) ac.deleteByFile(path)
} }
for _, path := range updates.ToSlice() { for _, path := range updates.ToSlice() {
ac.deleteByFile(path) ac.deleteByFile(path)
if a := readAccount(path); a != nil { if a := readAccount(path); a != nil {
ac.add(*a) ac.add(*a)
} }
} }
end := time.Now() end := time.Now()
select { select {
@ -305,5 +329,6 @@ func (ac *accountCache) scanAccounts() error {
default: default:
} }
log.Trace("Handled keystore changes", "time", end.Sub(start)) log.Trace("Handled keystore changes", "time", end.Sub(start))
return nil return nil
} }

View file

@ -62,6 +62,7 @@ func waitWatcherStart(ks *KeyStore) bool {
return true return true
} }
} }
return false return false
} }
@ -76,9 +77,11 @@ func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
default: default:
return fmt.Errorf("wasn't notified of new accounts") return fmt.Errorf("wasn't notified of new accounts")
} }
return nil return nil
} }
} }
return fmt.Errorf("\ngot %v\nwant %v", list, wantAccounts) return fmt.Errorf("\ngot %v\nwant %v", list, wantAccounts)
} }
@ -89,6 +92,7 @@ func TestWatchNewFile(t *testing.T) {
// Ensure the watcher is started before adding any files. // Ensure the watcher is started before adding any files.
ks.Accounts() ks.Accounts()
if !waitWatcherStart(ks) { if !waitWatcherStart(ks) {
t.Fatal("keystore watcher didn't start in time") t.Fatal("keystore watcher didn't start in time")
} }
@ -115,6 +119,7 @@ func TestWatchNoDir(t *testing.T) {
// Create ks but not the directory that it watches. // Create ks but not the directory that it watches.
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int())) dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int()))
ks := NewKeyStore(dir, LightScryptN, LightScryptP) ks := NewKeyStore(dir, LightScryptN, LightScryptP)
list := ks.Accounts() list := ks.Accounts()
if len(list) > 0 { if len(list) > 0 {
t.Error("initial account list not empty:", list) t.Error("initial account list not empty:", list)
@ -126,6 +131,7 @@ func TestWatchNoDir(t *testing.T) {
// Create the directory and copy a key file into it. // Create the directory and copy a key file into it.
os.MkdirAll(dir, 0700) os.MkdirAll(dir, 0700)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
file := filepath.Join(dir, "aaa") file := filepath.Join(dir, "aaa")
if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil { if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
t.Fatal(err) t.Fatal(err)
@ -134,6 +140,7 @@ func TestWatchNoDir(t *testing.T) {
// ks should see the account. // ks should see the account.
wantAccounts := []accounts.Account{cachetestAccounts[0]} wantAccounts := []accounts.Account{cachetestAccounts[0]}
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file} wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 { for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
list = ks.Accounts() list = ks.Accounts()
if reflect.DeepEqual(list, wantAccounts) { if reflect.DeepEqual(list, wantAccounts) {
@ -143,8 +150,10 @@ func TestWatchNoDir(t *testing.T) {
default: default:
t.Fatalf("wasn't notified of new accounts") t.Fatalf("wasn't notified of new accounts")
} }
return return
} }
time.Sleep(d) time.Sleep(d)
} }
t.Errorf("\ngot %v\nwant %v", list, wantAccounts) t.Errorf("\ngot %v\nwant %v", list, wantAccounts)
@ -152,6 +161,7 @@ func TestWatchNoDir(t *testing.T) {
func TestCacheInitialReload(t *testing.T) { func TestCacheInitialReload(t *testing.T) {
cache, _ := newAccountCache(cachetestDir) cache, _ := newAccountCache(cachetestDir)
accounts := cache.accounts() accounts := cache.accounts()
if !reflect.DeepEqual(accounts, cachetestAccounts) { if !reflect.DeepEqual(accounts, cachetestAccounts) {
t.Fatalf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts)) t.Fatalf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts))
@ -203,15 +213,18 @@ func TestCacheAddDeleteOrder(t *testing.T) {
wantAccounts := make([]accounts.Account, len(accs)) wantAccounts := make([]accounts.Account, len(accs))
copy(wantAccounts, accs) copy(wantAccounts, accs)
sort.Sort(accountsByURL(wantAccounts)) sort.Sort(accountsByURL(wantAccounts))
list := cache.accounts() list := cache.accounts()
if !reflect.DeepEqual(list, wantAccounts) { if !reflect.DeepEqual(list, wantAccounts) {
t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts)) t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts))
} }
for _, a := range accs { for _, a := range accs {
if !cache.hasAddress(a.Address) { if !cache.hasAddress(a.Address) {
t.Errorf("expected hasAccount(%x) to return true", a.Address) t.Errorf("expected hasAccount(%x) to return true", a.Address)
} }
} }
if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) { if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) {
t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"))
} }
@ -229,14 +242,17 @@ func TestCacheAddDeleteOrder(t *testing.T) {
wantAccounts[5], wantAccounts[5],
} }
list = cache.accounts() list = cache.accounts()
if !reflect.DeepEqual(list, wantAccountsAfterDelete) { if !reflect.DeepEqual(list, wantAccountsAfterDelete) {
t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete)) t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete))
} }
for _, a := range wantAccountsAfterDelete { for _, a := range wantAccountsAfterDelete {
if !cache.hasAddress(a.Address) { if !cache.hasAddress(a.Address) {
t.Errorf("expected hasAccount(%x) to return true", a.Address) t.Errorf("expected hasAccount(%x) to return true", a.Address)
} }
} }
if cache.hasAddress(wantAccounts[0].Address) { if cache.hasAddress(wantAccounts[0].Address) {
t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address) t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address)
} }
@ -273,6 +289,7 @@ func TestCacheFind(t *testing.T) {
Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "something")}, URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "something")},
} }
tests := []struct { tests := []struct {
Query accounts.Account Query accounts.Account
WantResult accounts.Account WantResult accounts.Account
@ -308,6 +325,7 @@ func TestCacheFind(t *testing.T) {
t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError) t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError)
continue continue
} }
if a != test.WantResult { if a != test.WantResult {
t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult) t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult)
continue continue
@ -328,6 +346,7 @@ func TestUpdatedKeyfileContents(t *testing.T) {
if len(list) > 0 { if len(list) > 0 {
t.Error("initial account list not empty:", list) t.Error("initial account list not empty:", list)
} }
if !waitWatcherStart(ks) { if !waitWatcherStart(ks) {
t.Fatal("keystore watcher didn't start in time") t.Fatal("keystore watcher didn't start in time")
} }
@ -344,6 +363,7 @@ func TestUpdatedKeyfileContents(t *testing.T) {
// ks should see the account. // ks should see the account.
wantAccounts := []accounts.Account{cachetestAccounts[0]} wantAccounts := []accounts.Account{cachetestAccounts[0]}
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file} wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
if err := waitForAccounts(wantAccounts, ks); err != nil { if err := waitForAccounts(wantAccounts, ks); err != nil {
t.Error(err) t.Error(err)
return return
@ -356,11 +376,14 @@ func TestUpdatedKeyfileContents(t *testing.T) {
t.Fatal(err) t.Fatal(err)
return return
} }
wantAccounts = []accounts.Account{cachetestAccounts[1]} wantAccounts = []accounts.Account{cachetestAccounts[1]}
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file} wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
if err := waitForAccounts(wantAccounts, ks); err != nil { if err := waitForAccounts(wantAccounts, ks); err != nil {
t.Errorf("First replacement failed") t.Errorf("First replacement failed")
t.Error(err) t.Error(err)
return return
} }
@ -372,11 +395,14 @@ func TestUpdatedKeyfileContents(t *testing.T) {
t.Fatal(err) t.Fatal(err)
return return
} }
wantAccounts = []accounts.Account{cachetestAccounts[2]} wantAccounts = []accounts.Account{cachetestAccounts[2]}
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file} wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
if err := waitForAccounts(wantAccounts, ks); err != nil { if err := waitForAccounts(wantAccounts, ks); err != nil {
t.Errorf("Second replacement failed") t.Errorf("Second replacement failed")
t.Error(err) t.Error(err)
return return
} }
@ -388,9 +414,11 @@ func TestUpdatedKeyfileContents(t *testing.T) {
t.Fatal(err) t.Fatal(err)
return return
} }
if err := waitForAccounts([]accounts.Account{}, ks); err != nil { if err := waitForAccounts([]accounts.Account{}, ks); err != nil {
t.Errorf("Emptying account file failed") t.Errorf("Emptying account file failed")
t.Error(err) t.Error(err)
return return
} }
} }
@ -401,5 +429,6 @@ func forceCopyFile(dst, src string) error {
if err != nil { if err != nil {
return err return err
} }
return os.WriteFile(dst, data, 0644) return os.WriteFile(dst, data, 0644)
} }

View file

@ -45,6 +45,7 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string]
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
t1 := time.Now() t1 := time.Now()
fc.mu.Lock() fc.mu.Lock()
@ -55,6 +56,7 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string]
mods := mapset.NewThreadUnsafeSet[string]() mods := mapset.NewThreadUnsafeSet[string]()
var newLastMod time.Time var newLastMod time.Time
for _, fi := range files { for _, fi := range files {
path := filepath.Join(keyDir, fi.Name()) path := filepath.Join(keyDir, fi.Name())
// Skip any non-key files from the folder // Skip any non-key files from the folder
@ -69,14 +71,17 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string]
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
modified := info.ModTime() modified := info.ModTime()
if modified.After(fc.lastMod) { if modified.After(fc.lastMod) {
mods.Add(path) mods.Add(path)
} }
if modified.After(newLastMod) { if modified.After(newLastMod) {
newLastMod = modified newLastMod = modified
} }
} }
t2 := time.Now() t2 := time.Now()
// Update the tracked files and return the three sets // Update the tracked files and return the three sets
@ -89,6 +94,7 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string]
// Report on the scanning stats and return // Report on the scanning stats and return
log.Debug("FS scan times", "list", t1.Sub(t0), "set", t2.Sub(t1), "diff", t3.Sub(t2)) log.Debug("FS scan times", "list", t1.Sub(t0), "set", t2.Sub(t1), "diff", t3.Sub(t2))
return creates, deletes, updates, nil return creates, deletes, updates, nil
} }
@ -102,5 +108,6 @@ func nonKeyFile(fi os.DirEntry) bool {
if fi.IsDir() || !fi.Type().IsRegular() { if fi.IsDir() || !fi.Type().IsRegular() {
return true return true
} }
return false return false
} }

View file

@ -98,26 +98,32 @@ func (k *Key) MarshalJSON() (j []byte, err error) {
version, version,
} }
j, err = json.Marshal(jStruct) j, err = json.Marshal(jStruct)
return j, err return j, err
} }
func (k *Key) UnmarshalJSON(j []byte) (err error) { func (k *Key) UnmarshalJSON(j []byte) (err error) {
keyJSON := new(plainKeyJSON) keyJSON := new(plainKeyJSON)
err = json.Unmarshal(j, &keyJSON) err = json.Unmarshal(j, &keyJSON)
if err != nil { if err != nil {
return err return err
} }
u := new(uuid.UUID) u := new(uuid.UUID)
*u, err = uuid.Parse(keyJSON.Id) *u, err = uuid.Parse(keyJSON.Id)
if err != nil { if err != nil {
return err return err
} }
k.Id = *u k.Id = *u
addr, err := hex.DecodeString(keyJSON.Address) addr, err := hex.DecodeString(keyJSON.Address)
if err != nil { if err != nil {
return err return err
} }
privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey) privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey)
if err != nil { if err != nil {
return err return err
@ -134,11 +140,13 @@ func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
if err != nil { if err != nil {
panic(fmt.Sprintf("Could not create random uuid: %v", err)) panic(fmt.Sprintf("Could not create random uuid: %v", err))
} }
key := &Key{ key := &Key{
Id: id, Id: id,
Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey), Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
PrivateKey: privateKeyECDSA, PrivateKey: privateKeyECDSA,
} }
return key return key
} }
@ -147,19 +155,24 @@ func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
// retry until the first byte is 0. // retry until the first byte is 0.
func NewKeyForDirectICAP(rand io.Reader) *Key { func NewKeyForDirectICAP(rand io.Reader) *Key {
randBytes := make([]byte, 64) randBytes := make([]byte, 64)
_, err := rand.Read(randBytes) _, err := rand.Read(randBytes)
if err != nil { if err != nil {
panic("key generation: could not read from random source: " + err.Error()) panic("key generation: could not read from random source: " + err.Error())
} }
reader := bytes.NewReader(randBytes) reader := bytes.NewReader(randBytes)
privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader) privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader)
if err != nil { if err != nil {
panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
} }
key := newKeyFromECDSA(privateKeyECDSA) key := newKeyFromECDSA(privateKeyECDSA)
if !strings.HasPrefix(key.Address.Hex(), "0x00") { if !strings.HasPrefix(key.Address.Hex(), "0x00") {
return NewKeyForDirectICAP(rand) return NewKeyForDirectICAP(rand)
} }
return key return key
} }
@ -168,6 +181,7 @@ func newKey(rand io.Reader) (*Key, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return newKeyFromECDSA(privateKeyECDSA), nil return newKeyFromECDSA(privateKeyECDSA), nil
} }
@ -176,6 +190,7 @@ func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Accou
if err != nil { if err != nil {
return nil, accounts.Account{}, err return nil, accounts.Account{}, err
} }
a := accounts.Account{ a := accounts.Account{
Address: key.Address, Address: key.Address,
URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))}, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))},
@ -184,6 +199,7 @@ func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Accou
zeroKey(key.PrivateKey) zeroKey(key.PrivateKey)
return nil, a, err return nil, a, err
} }
return key, a, err return key, a, err
} }
@ -200,12 +216,16 @@ func writeTemporaryKeyFile(file string, content []byte) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
if _, err := f.Write(content); err != nil { if _, err := f.Write(content); err != nil {
f.Close() f.Close()
os.Remove(f.Name()) os.Remove(f.Name())
return "", err return "", err
} }
f.Close() f.Close()
return f.Name(), nil return f.Name(), nil
} }
@ -214,6 +234,7 @@ func writeKeyFile(file string, content []byte) error {
if err != nil { if err != nil {
return err return err
} }
return os.Rename(name, file) return os.Rename(name, file)
} }
@ -226,12 +247,14 @@ func keyFileName(keyAddr common.Address) string {
func toISO8601(t time.Time) string { func toISO8601(t time.Time) string {
var tz string var tz string
name, offset := t.Zone() name, offset := t.Zone()
if name == "UTC" { if name == "UTC" {
tz = "Z" tz = "Z"
} else { } else {
tz = fmt.Sprintf("%03d00", offset/3600) tz = fmt.Sprintf("%03d00", offset/3600)
} }
return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s", 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) t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
} }

View file

@ -84,6 +84,7 @@ func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore {
keydir, _ = filepath.Abs(keydir) keydir, _ = filepath.Abs(keydir)
ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP, false}} ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP, false}}
ks.init(keydir) ks.init(keydir)
return ks return ks
} }
@ -93,6 +94,7 @@ func NewPlaintextKeyStore(keydir string) *KeyStore {
keydir, _ = filepath.Abs(keydir) keydir, _ = filepath.Abs(keydir)
ks := &KeyStore{storage: &keyStorePlain{keydir}} ks := &KeyStore{storage: &keyStorePlain{keydir}}
ks.init(keydir) ks.init(keydir)
return ks return ks
} }
@ -114,6 +116,7 @@ func (ks *KeyStore) init(keydir string) {
// Create the initial list of wallets from the cache // Create the initial list of wallets from the cache
accs := ks.cache.accounts() accs := ks.cache.accounts()
ks.wallets = make([]accounts.Wallet, len(accs)) ks.wallets = make([]accounts.Wallet, len(accs))
for i := 0; i < len(accs); i++ { for i := 0; i < len(accs); i++ {
ks.wallets[i] = &keystoreWallet{account: accs[i], keystore: ks} ks.wallets[i] = &keystoreWallet{account: accs[i], keystore: ks}
} }
@ -130,6 +133,7 @@ func (ks *KeyStore) Wallets() []accounts.Wallet {
cpy := make([]accounts.Wallet, len(ks.wallets)) cpy := make([]accounts.Wallet, len(ks.wallets))
copy(cpy, ks.wallets) copy(cpy, ks.wallets)
return cpy return cpy
} }
@ -158,12 +162,14 @@ func (ks *KeyStore) refreshWallets() {
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived}) events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
wallets = append(wallets, wallet) wallets = append(wallets, wallet)
continue continue
} }
// If the account is the same as the first wallet, keep it // If the account is the same as the first wallet, keep it
if ks.wallets[0].Accounts()[0] == account { if ks.wallets[0].Accounts()[0] == account {
wallets = append(wallets, ks.wallets[0]) wallets = append(wallets, ks.wallets[0])
ks.wallets = ks.wallets[1:] ks.wallets = ks.wallets[1:]
continue continue
} }
} }
@ -171,6 +177,7 @@ func (ks *KeyStore) refreshWallets() {
for _, wallet := range ks.wallets { for _, wallet := range ks.wallets {
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped}) events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
} }
ks.wallets = wallets ks.wallets = wallets
ks.mu.Unlock() ks.mu.Unlock()
@ -195,6 +202,7 @@ func (ks *KeyStore) Subscribe(sink chan<- accounts.WalletEvent) event.Subscripti
ks.updating = true ks.updating = true
go ks.updater() go ks.updater()
} }
return sub return sub
} }
@ -218,6 +226,7 @@ func (ks *KeyStore) updater() {
if ks.updateScope.Count() == 0 { if ks.updateScope.Count() == 0 {
ks.updating = false ks.updating = false
ks.mu.Unlock() ks.mu.Unlock()
return return
} }
ks.mu.Unlock() ks.mu.Unlock()
@ -244,6 +253,7 @@ func (ks *KeyStore) Delete(a accounts.Account, passphrase string) error {
if key != nil { if key != nil {
zeroKey(key.PrivateKey) zeroKey(key.PrivateKey)
} }
if err != nil { if err != nil {
return err return err
} }
@ -255,6 +265,7 @@ func (ks *KeyStore) Delete(a accounts.Account, passphrase string) error {
ks.cache.delete(a) ks.cache.delete(a)
ks.refreshWallets() ks.refreshWallets()
} }
return err return err
} }
@ -285,6 +296,7 @@ func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *b
} }
// Depending on the presence of the chain ID, sign with 2718 or homestead // Depending on the presence of the chain ID, sign with 2718 or homestead
signer := types.LatestSignerForChainID(chainID) signer := types.LatestSignerForChainID(chainID)
return types.SignTx(tx, signer, unlockedKey.PrivateKey) return types.SignTx(tx, signer, unlockedKey.PrivateKey)
} }
@ -296,7 +308,9 @@ func (ks *KeyStore) SignHashWithPassphrase(a accounts.Account, passphrase string
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer zeroKey(key.PrivateKey) defer zeroKey(key.PrivateKey)
return crypto.Sign(hash, key.PrivateKey) return crypto.Sign(hash, key.PrivateKey)
} }
@ -310,6 +324,7 @@ func (ks *KeyStore) SignTxWithPassphrase(a accounts.Account, passphrase string,
defer zeroKey(key.PrivateKey) defer zeroKey(key.PrivateKey)
// Depending on the presence of the chain ID, sign with or without replay protection. // Depending on the presence of the chain ID, sign with or without replay protection.
signer := types.LatestSignerForChainID(chainID) signer := types.LatestSignerForChainID(chainID)
return types.SignTx(tx, signer, key.PrivateKey) return types.SignTx(tx, signer, key.PrivateKey)
} }
@ -327,6 +342,7 @@ func (ks *KeyStore) Lock(addr common.Address) error {
} else { } else {
ks.mu.Unlock() ks.mu.Unlock()
} }
return nil return nil
} }
@ -345,6 +361,7 @@ func (ks *KeyStore) TimedUnlock(a accounts.Account, passphrase string, timeout t
ks.mu.Lock() ks.mu.Lock()
defer ks.mu.Unlock() defer ks.mu.Unlock()
u, found := ks.unlocked[a.Address] u, found := ks.unlocked[a.Address]
if found { if found {
if u.abort == nil { if u.abort == nil {
@ -356,13 +373,16 @@ func (ks *KeyStore) TimedUnlock(a accounts.Account, passphrase string, timeout t
// Terminate the expire goroutine and replace it below. // Terminate the expire goroutine and replace it below.
close(u.abort) close(u.abort)
} }
if timeout > 0 { if timeout > 0 {
u = &unlocked{Key: key, abort: make(chan struct{})} u = &unlocked{Key: key, abort: make(chan struct{})}
go ks.expire(a.Address, u, timeout) go ks.expire(a.Address, u, timeout)
} else { } else {
u = &unlocked{Key: key} u = &unlocked{Key: key}
} }
ks.unlocked[a.Address] = u ks.unlocked[a.Address] = u
return nil return nil
} }
@ -372,6 +392,7 @@ func (ks *KeyStore) Find(a accounts.Account) (accounts.Account, error) {
ks.cache.mu.Lock() ks.cache.mu.Lock()
a, err := ks.cache.find(a) a, err := ks.cache.find(a)
ks.cache.mu.Unlock() ks.cache.mu.Unlock()
return a, err return a, err
} }
@ -380,7 +401,9 @@ func (ks *KeyStore) getDecryptedKey(a accounts.Account, auth string) (accounts.A
if err != nil { if err != nil {
return a, nil, err return a, nil, err
} }
key, err := ks.storage.GetKey(a.Address, a.URL.Path, auth) key, err := ks.storage.GetKey(a.Address, a.URL.Path, auth)
return a, key, err return a, key, err
} }
@ -415,6 +438,7 @@ func (ks *KeyStore) NewAccount(passphrase string) (accounts.Account, error) {
// than waiting for file system notifications to pick it up. // than waiting for file system notifications to pick it up.
ks.cache.add(account) ks.cache.add(account)
ks.refreshWallets() ks.refreshWallets()
return account, nil return account, nil
} }
@ -424,12 +448,14 @@ func (ks *KeyStore) Export(a accounts.Account, passphrase, newPassphrase string)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var N, P int var N, P int
if store, ok := ks.storage.(*keyStorePassphrase); ok { if store, ok := ks.storage.(*keyStorePassphrase); ok {
N, P = store.scryptN, store.scryptP N, P = store.scryptN, store.scryptP
} else { } else {
N, P = StandardScryptN, StandardScryptP N, P = StandardScryptN, StandardScryptP
} }
return EncryptKey(key, newPassphrase, N, P) return EncryptKey(key, newPassphrase, N, P)
} }
@ -439,9 +465,11 @@ func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (ac
if key != nil && key.PrivateKey != nil { if key != nil && key.PrivateKey != nil {
defer zeroKey(key.PrivateKey) defer zeroKey(key.PrivateKey)
} }
if err != nil { if err != nil {
return accounts.Account{}, err return accounts.Account{}, err
} }
ks.importMu.Lock() ks.importMu.Lock()
defer ks.importMu.Unlock() defer ks.importMu.Unlock()
@ -450,6 +478,7 @@ func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (ac
Address: key.Address, Address: key.Address,
}, ErrAccountAlreadyExists }, ErrAccountAlreadyExists
} }
return ks.importKey(key, newPassphrase) return ks.importKey(key, newPassphrase)
} }
@ -464,6 +493,7 @@ func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (acco
Address: key.Address, Address: key.Address,
}, ErrAccountAlreadyExists }, ErrAccountAlreadyExists
} }
return ks.importKey(key, passphrase) return ks.importKey(key, passphrase)
} }
@ -472,8 +502,10 @@ func (ks *KeyStore) importKey(key *Key, passphrase string) (accounts.Account, er
if err := ks.storage.StoreKey(a.URL.Path, key, passphrase); err != nil { if err := ks.storage.StoreKey(a.URL.Path, key, passphrase); err != nil {
return accounts.Account{}, err return accounts.Account{}, err
} }
ks.cache.add(a) ks.cache.add(a)
ks.refreshWallets() ks.refreshWallets()
return a, nil return a, nil
} }
@ -483,6 +515,7 @@ func (ks *KeyStore) Update(a accounts.Account, passphrase, newPassphrase string)
if err != nil { if err != nil {
return err return err
} }
return ks.storage.StoreKey(a.URL.Path, key, newPassphrase) return ks.storage.StoreKey(a.URL.Path, key, newPassphrase)
} }
@ -493,8 +526,10 @@ func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (account
if err != nil { if err != nil {
return a, err return a, err
} }
ks.cache.add(a) ks.cache.add(a)
ks.refreshWallets() ks.refreshWallets()
return a, nil return a, nil
} }
@ -503,6 +538,7 @@ func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (account
func (ks *KeyStore) isUpdating() bool { func (ks *KeyStore) isUpdating() bool {
ks.mu.RLock() ks.mu.RLock()
defer ks.mu.RUnlock() defer ks.mu.RUnlock()
return ks.updating return ks.updating
} }

View file

@ -42,28 +42,36 @@ func TestKeyStore(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !strings.HasPrefix(a.URL.Path, dir) { if !strings.HasPrefix(a.URL.Path, dir) {
t.Errorf("account file %s doesn't have dir prefix", a.URL) t.Errorf("account file %s doesn't have dir prefix", a.URL)
} }
stat, err := os.Stat(a.URL.Path) stat, err := os.Stat(a.URL.Path)
if err != nil { if err != nil {
t.Fatalf("account file %s doesn't exist (%v)", a.URL, err) t.Fatalf("account file %s doesn't exist (%v)", a.URL, err)
} }
if runtime.GOOS != "windows" && stat.Mode() != 0600 { if runtime.GOOS != "windows" && stat.Mode() != 0600 {
t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600) t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600)
} }
if !ks.HasAddress(a.Address) { if !ks.HasAddress(a.Address) {
t.Errorf("HasAccount(%x) should've returned true", a.Address) t.Errorf("HasAccount(%x) should've returned true", a.Address)
} }
if err := ks.Update(a, "foo", "bar"); err != nil { if err := ks.Update(a, "foo", "bar"); err != nil {
t.Errorf("Update error: %v", err) t.Errorf("Update error: %v", err)
} }
if err := ks.Delete(a, "bar"); err != nil { if err := ks.Delete(a, "bar"); err != nil {
t.Errorf("Delete error: %v", err) t.Errorf("Delete error: %v", err)
} }
if common.FileExist(a.URL.Path) { if common.FileExist(a.URL.Path) {
t.Errorf("account file %s should be gone after Delete", a.URL) t.Errorf("account file %s should be gone after Delete", a.URL)
} }
if ks.HasAddress(a.Address) { if ks.HasAddress(a.Address) {
t.Errorf("HasAccount(%x) should've returned true after Delete", a.Address) t.Errorf("HasAccount(%x) should've returned true after Delete", a.Address)
} }
@ -73,6 +81,7 @@ func TestSign(t *testing.T) {
_, ks := tmpKeyStore(t, true) _, ks := tmpKeyStore(t, true)
pass := "" // not used but required by API pass := "" // not used but required by API
a1, err := ks.NewAccount(pass) a1, err := ks.NewAccount(pass)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -80,6 +89,7 @@ func TestSign(t *testing.T) {
if err := ks.Unlock(a1, ""); err != nil { if err := ks.Unlock(a1, ""); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil { if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -89,6 +99,7 @@ func TestSignWithPassphrase(t *testing.T) {
_, ks := tmpKeyStore(t, true) _, ks := tmpKeyStore(t, true)
pass := "passwd" pass := "passwd"
acc, err := ks.NewAccount(pass) acc, err := ks.NewAccount(pass)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -117,6 +128,7 @@ func TestTimedUnlock(t *testing.T) {
_, ks := tmpKeyStore(t, true) _, ks := tmpKeyStore(t, true)
pass := "foo" pass := "foo"
a1, err := ks.NewAccount(pass) a1, err := ks.NewAccount(pass)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -141,6 +153,7 @@ func TestTimedUnlock(t *testing.T) {
// Signing fails again after automatic locking // Signing fails again after automatic locking
time.Sleep(250 * time.Millisecond) time.Sleep(250 * time.Millisecond)
_, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
if err != ErrLocked { if err != ErrLocked {
t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
@ -152,6 +165,7 @@ func TestOverrideUnlock(t *testing.T) {
_, ks := tmpKeyStore(t, false) _, ks := tmpKeyStore(t, false)
pass := "foo" pass := "foo"
a1, err := ks.NewAccount(pass) a1, err := ks.NewAccount(pass)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -181,6 +195,7 @@ func TestOverrideUnlock(t *testing.T) {
// Signing fails again after automatic locking // Signing fails again after automatic locking
time.Sleep(250 * time.Millisecond) time.Sleep(250 * time.Millisecond)
_, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
if err != ErrLocked { if err != ErrLocked {
t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
@ -201,6 +216,7 @@ func TestSignRace(t *testing.T) {
if err := ks.TimedUnlock(a1, "", 15*time.Millisecond); err != nil { if err := ks.TimedUnlock(a1, "", 15*time.Millisecond); err != nil {
t.Fatal("could not unlock the test account", err) t.Fatal("could not unlock the test account", err)
} }
end := time.Now().Add(500 * time.Millisecond) end := time.Now().Add(500 * time.Millisecond)
for time.Now().Before(end) { for time.Now().Before(end) {
if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err == ErrLocked { if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err == ErrLocked {
@ -209,6 +225,7 @@ func TestSignRace(t *testing.T) {
t.Errorf("Sign error: %v", err) t.Errorf("Sign error: %v", err)
return return
} }
time.Sleep(1 * time.Millisecond) time.Sleep(1 * time.Millisecond)
} }
t.Errorf("Account did not lock within the timeout") t.Errorf("Account did not lock within the timeout")
@ -225,8 +242,10 @@ func waitForKsUpdating(t *testing.T, ks *KeyStore, wantStatus bool, maxTime time
if ks.isUpdating() == wantStatus { if ks.isUpdating() == wantStatus {
return true return true
} }
time.Sleep(25 * time.Millisecond) time.Sleep(25 * time.Millisecond)
} }
return false return false
} }
@ -250,6 +269,7 @@ func TestWalletNotifierLifecycle(t *testing.T) {
for i := 0; i < len(subs); i++ { for i := 0; i < len(subs); i++ {
// Create a new subscription // Create a new subscription
subs[i] = ks.Subscribe(updates) subs[i] = ks.Subscribe(updates)
if !waitForKsUpdating(t, ks, true, 250*time.Millisecond) { if !waitForKsUpdating(t, ks, true, 250*time.Millisecond) {
t.Errorf("sub %d: wallet notifier not running after subscription", i) t.Errorf("sub %d: wallet notifier not running after subscription", i)
} }
@ -267,6 +287,7 @@ func TestWalletNotifierLifecycle(t *testing.T) {
} }
// Unsubscribe the last one and ensure the updater terminates eventually. // Unsubscribe the last one and ensure the updater terminates eventually.
subs[len(subs)-1].Unsubscribe() subs[len(subs)-1].Unsubscribe()
if !waitForKsUpdating(t, ks, false, 4*time.Second) { if !waitForKsUpdating(t, ks, false, 4*time.Second) {
t.Errorf("wallet notifier didn't terminate after unsubscribe") t.Errorf("wallet notifier didn't terminate after unsubscribe")
} }
@ -288,7 +309,9 @@ func TestWalletNotifications(t *testing.T) {
updates = make(chan accounts.WalletEvent) updates = make(chan accounts.WalletEvent)
sub = ks.Subscribe(updates) sub = ks.Subscribe(updates)
) )
defer sub.Unsubscribe() defer sub.Unsubscribe()
go func() { go func() {
for { for {
select { select {
@ -306,6 +329,7 @@ func TestWalletNotifications(t *testing.T) {
live = make(map[common.Address]accounts.Account) live = make(map[common.Address]accounts.Account)
wantEvents []walletEvent wantEvents []walletEvent
) )
for i := 0; i < 1024; i++ { for i := 0; i < 1024; i++ {
if create := len(live) == 0 || rand.Int()%4 > 0; create { if create := len(live) == 0 || rand.Int()%4 > 0; create {
// Add a new account and ensure wallet notifications arrives // Add a new account and ensure wallet notifications arrives
@ -313,6 +337,7 @@ func TestWalletNotifications(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create test account: %v", err) t.Fatalf("failed to create test account: %v", err)
} }
live[account.Address] = account live[account.Address] = account
wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletArrived}, account}) wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletArrived}, account})
} else { } else {
@ -322,9 +347,11 @@ func TestWalletNotifications(t *testing.T) {
account = a account = a
break break
} }
if err := ks.Delete(account, ""); err != nil { if err := ks.Delete(account, ""); err != nil {
t.Fatalf("failed to delete test account: %v", err) t.Fatalf("failed to delete test account: %v", err)
} }
delete(live, account.Address) delete(live, account.Address)
wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletDropped}, account}) wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletDropped}, account})
} }
@ -332,9 +359,11 @@ func TestWalletNotifications(t *testing.T) {
// Shut down the event collector and check events. // Shut down the event collector and check events.
sub.Unsubscribe() sub.Unsubscribe()
for ev := range updates { for ev := range updates {
events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]}) events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]})
} }
checkAccounts(t, live, ks.Wallets()) checkAccounts(t, live, ks.Wallets())
checkEvents(t, wantEvents, events) checkEvents(t, wantEvents, events)
} }
@ -342,16 +371,20 @@ func TestWalletNotifications(t *testing.T) {
// TestImportExport tests the import functionality of a keystore. // TestImportExport tests the import functionality of a keystore.
func TestImportECDSA(t *testing.T) { func TestImportECDSA(t *testing.T) {
_, ks := tmpKeyStore(t, true) _, ks := tmpKeyStore(t, true)
key, err := crypto.GenerateKey() key, err := crypto.GenerateKey()
if err != nil { if err != nil {
t.Fatalf("failed to generate key: %v", key) t.Fatalf("failed to generate key: %v", key)
} }
if _, err = ks.ImportECDSA(key, "old"); err != nil { if _, err = ks.ImportECDSA(key, "old"); err != nil {
t.Errorf("importing failed: %v", err) t.Errorf("importing failed: %v", err)
} }
if _, err = ks.ImportECDSA(key, "old"); err == nil { if _, err = ks.ImportECDSA(key, "old"); err == nil {
t.Errorf("importing same key twice succeeded") t.Errorf("importing same key twice succeeded")
} }
if _, err = ks.ImportECDSA(key, "new"); err == nil { if _, err = ks.ImportECDSA(key, "new"); err == nil {
t.Errorf("importing same key twice succeeded") t.Errorf("importing same key twice succeeded")
} }
@ -360,25 +393,31 @@ func TestImportECDSA(t *testing.T) {
// TestImportECDSA tests the import and export functionality of a keystore. // TestImportECDSA tests the import and export functionality of a keystore.
func TestImportExport(t *testing.T) { func TestImportExport(t *testing.T) {
_, ks := tmpKeyStore(t, true) _, ks := tmpKeyStore(t, true)
acc, err := ks.NewAccount("old") acc, err := ks.NewAccount("old")
if err != nil { if err != nil {
t.Fatalf("failed to create account: %v", acc) t.Fatalf("failed to create account: %v", acc)
} }
json, err := ks.Export(acc, "old", "new") json, err := ks.Export(acc, "old", "new")
if err != nil { if err != nil {
t.Fatalf("failed to export account: %v", acc) t.Fatalf("failed to export account: %v", acc)
} }
_, ks2 := tmpKeyStore(t, true) _, ks2 := tmpKeyStore(t, true)
if _, err = ks2.Import(json, "old", "old"); err == nil { if _, err = ks2.Import(json, "old", "old"); err == nil {
t.Errorf("importing with invalid password succeeded") t.Errorf("importing with invalid password succeeded")
} }
acc2, err := ks2.Import(json, "new", "new") acc2, err := ks2.Import(json, "new", "new")
if err != nil { if err != nil {
t.Errorf("importing failed: %v", err) t.Errorf("importing failed: %v", err)
} }
if acc.Address != acc2.Address { if acc.Address != acc2.Address {
t.Error("imported account does not match exported account") t.Error("imported account does not match exported account")
} }
if _, err = ks2.Import(json, "new", "new"); err == nil { if _, err = ks2.Import(json, "new", "new"); err == nil {
t.Errorf("importing a key twice succeeded") t.Errorf("importing a key twice succeeded")
} }
@ -388,27 +427,36 @@ func TestImportExport(t *testing.T) {
// This test should fail under -race if importing races. // This test should fail under -race if importing races.
func TestImportRace(t *testing.T) { func TestImportRace(t *testing.T) {
_, ks := tmpKeyStore(t, true) _, ks := tmpKeyStore(t, true)
acc, err := ks.NewAccount("old") acc, err := ks.NewAccount("old")
if err != nil { if err != nil {
t.Fatalf("failed to create account: %v", acc) t.Fatalf("failed to create account: %v", acc)
} }
json, err := ks.Export(acc, "old", "new") json, err := ks.Export(acc, "old", "new")
if err != nil { if err != nil {
t.Fatalf("failed to export account: %v", acc) t.Fatalf("failed to export account: %v", acc)
} }
_, ks2 := tmpKeyStore(t, true) _, ks2 := tmpKeyStore(t, true)
var atom uint32 var atom uint32
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(2) wg.Add(2)
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
go func() { go func() {
defer wg.Done() defer wg.Done()
if _, err := ks2.Import(json, "new", "new"); err != nil { if _, err := ks2.Import(json, "new", "new"); err != nil {
atomic.AddUint32(&atom, 1) atomic.AddUint32(&atom, 1)
} }
}() }()
} }
wg.Wait() wg.Wait()
if atom != 1 { if atom != 1 {
t.Errorf("Import is racy") t.Errorf("Import is racy")
} }
@ -420,11 +468,14 @@ func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, walle
t.Errorf("wallet list doesn't match required accounts: have %d, want %d", len(wallets), len(live)) t.Errorf("wallet list doesn't match required accounts: have %d, want %d", len(wallets), len(live))
return return
} }
liveList := make([]accounts.Account, 0, len(live)) liveList := make([]accounts.Account, 0, len(live))
for _, account := range live { for _, account := range live {
liveList = append(liveList, account) liveList = append(liveList, account)
} }
sort.Sort(accountsByURL(liveList)) sort.Sort(accountsByURL(liveList))
for j, wallet := range wallets { for j, wallet := range wallets {
if accs := wallet.Accounts(); len(accs) != 1 { if accs := wallet.Accounts(); len(accs) != 1 {
t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs)) t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs))
@ -438,12 +489,15 @@ func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, walle
func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) { func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) {
for _, wantEv := range want { for _, wantEv := range want {
nmatch := 0 nmatch := 0
for ; len(have) > 0; nmatch++ { for ; len(have) > 0; nmatch++ {
if have[0].Kind != wantEv.Kind || have[0].a != wantEv.a { if have[0].Kind != wantEv.Kind || have[0].a != wantEv.a {
break break
} }
have = have[1:] have = have[1:]
} }
if nmatch == 0 { if nmatch == 0 {
t.Fatalf("can't find event with Kind=%v for %x", wantEv.Kind, wantEv.a.Address) t.Fatalf("can't find event with Kind=%v for %x", wantEv.Kind, wantEv.a.Address)
} }
@ -452,9 +506,11 @@ func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) {
func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) { func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) {
d := t.TempDir() d := t.TempDir()
newKs := NewPlaintextKeyStore newKs := NewPlaintextKeyStore
if encrypted { if encrypted {
newKs = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) } newKs = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) }
} }
return d, newKs(d) return d, newKs(d)
} }

View file

@ -85,6 +85,7 @@ func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string)
if err != nil { if err != nil {
return nil, err return nil, err
} }
key, err := DecryptKey(keyjson, auth) key, err := DecryptKey(keyjson, auth)
if err != nil { if err != nil {
return nil, err return nil, err
@ -93,6 +94,7 @@ func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string)
if key.Address != addr { if key.Address != addr {
return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr) return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr)
} }
return key, nil return key, nil
} }
@ -112,6 +114,7 @@ func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) er
if err != nil { if err != nil {
return err return err
} }
if !ks.skipKeyFileVerification { if !ks.skipKeyFileVerification {
// Verify that we can decrypt the file with the given password. // Verify that we can decrypt the file with the given password.
_, err = ks.GetKey(key.Address, tmpName, auth) _, err = ks.GetKey(key.Address, tmpName, auth)
@ -126,6 +129,7 @@ func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) er
return fmt.Errorf(msg, tmpName, err) return fmt.Errorf(msg, tmpName, err)
} }
} }
return os.Rename(tmpName, filename) return os.Rename(tmpName, filename)
} }
@ -133,6 +137,7 @@ func (ks keyStorePassphrase) JoinPath(filename string) string {
if filepath.IsAbs(filename) { if filepath.IsAbs(filename) {
return filename return filename
} }
return filepath.Join(ks.keysDirPath, filename) return filepath.Join(ks.keysDirPath, filename)
} }
@ -142,20 +147,24 @@ func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error)
if _, err := io.ReadFull(rand.Reader, salt); err != nil { if _, err := io.ReadFull(rand.Reader, salt); err != nil {
panic("reading from crypto/rand failed: " + err.Error()) panic("reading from crypto/rand failed: " + err.Error())
} }
derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen) derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen)
if err != nil { if err != nil {
return CryptoJSON{}, err return CryptoJSON{}, err
} }
encryptKey := derivedKey[:16] encryptKey := derivedKey[:16]
iv := make([]byte, aes.BlockSize) // 16 iv := make([]byte, aes.BlockSize) // 16
if _, err := io.ReadFull(rand.Reader, iv); err != nil { if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic("reading from crypto/rand failed: " + err.Error()) panic("reading from crypto/rand failed: " + err.Error())
} }
cipherText, err := aesCTRXOR(encryptKey, data, iv) cipherText, err := aesCTRXOR(encryptKey, data, iv)
if err != nil { if err != nil {
return CryptoJSON{}, err return CryptoJSON{}, err
} }
mac := crypto.Keccak256(derivedKey[16:32], cipherText) mac := crypto.Keccak256(derivedKey[16:32], cipherText)
scryptParamsJSON := make(map[string]interface{}, 5) scryptParamsJSON := make(map[string]interface{}, 5)
@ -176,6 +185,7 @@ func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error)
KDFParams: scryptParamsJSON, KDFParams: scryptParamsJSON,
MAC: hex.EncodeToString(mac), MAC: hex.EncodeToString(mac),
} }
return cryptoStruct, nil return cryptoStruct, nil
} }
@ -183,16 +193,19 @@ func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error)
// blob that can be decrypted later on. // blob that can be decrypted later on.
func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) { func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32) keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP) cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP)
if err != nil { if err != nil {
return nil, err return nil, err
} }
encryptedKeyJSONV3 := encryptedKeyJSONV3{ encryptedKeyJSONV3 := encryptedKeyJSONV3{
hex.EncodeToString(key.Address[:]), hex.EncodeToString(key.Address[:]),
cryptoStruct, cryptoStruct,
key.Id.String(), key.Id.String(),
version, version,
} }
return json.Marshal(encryptedKeyJSONV3) return json.Marshal(encryptedKeyJSONV3)
} }
@ -208,28 +221,34 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
keyBytes, keyId []byte keyBytes, keyId []byte
err error err error
) )
if version, ok := m["version"].(string); ok && version == "1" { if version, ok := m["version"].(string); ok && version == "1" {
k := new(encryptedKeyJSONV1) k := new(encryptedKeyJSONV1)
if err := json.Unmarshal(keyjson, k); err != nil { if err := json.Unmarshal(keyjson, k); err != nil {
return nil, err return nil, err
} }
keyBytes, keyId, err = decryptKeyV1(k, auth) keyBytes, keyId, err = decryptKeyV1(k, auth)
} else { } else {
k := new(encryptedKeyJSONV3) k := new(encryptedKeyJSONV3)
if err := json.Unmarshal(keyjson, k); err != nil { if err := json.Unmarshal(keyjson, k); err != nil {
return nil, err return nil, err
} }
keyBytes, keyId, err = decryptKeyV3(k, auth) keyBytes, keyId, err = decryptKeyV3(k, auth)
} }
// Handle any decryption errors and return the key // Handle any decryption errors and return the key
if err != nil { if err != nil {
return nil, err return nil, err
} }
key := crypto.ToECDSAUnsafe(keyBytes) key := crypto.ToECDSAUnsafe(keyBytes)
id, err := uuid.FromBytes(keyId) id, err := uuid.FromBytes(keyId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Key{ return &Key{
Id: id, Id: id,
Address: crypto.PubkeyToAddress(key.PublicKey), Address: crypto.PubkeyToAddress(key.PublicKey),
@ -241,6 +260,7 @@ func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
if cryptoJson.Cipher != "aes-128-ctr" { if cryptoJson.Cipher != "aes-128-ctr" {
return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher) return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
} }
mac, err := hex.DecodeString(cryptoJson.MAC) mac, err := hex.DecodeString(cryptoJson.MAC)
if err != nil { if err != nil {
return nil, err return nil, err
@ -270,6 +290,7 @@ func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return plainText, err return plainText, err
} }
@ -277,15 +298,19 @@ func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byt
if keyProtected.Version != version { if keyProtected.Version != version {
return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version) return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
} }
keyUUID, err := uuid.Parse(keyProtected.Id) keyUUID, err := uuid.Parse(keyProtected.Id)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
keyId = keyUUID[:] keyId = keyUUID[:]
plainText, err := DecryptDataV3(keyProtected.Crypto, auth) plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
return plainText, keyId, err return plainText, keyId, err
} }
@ -294,7 +319,9 @@ func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byt
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
keyId = keyUUID[:] keyId = keyUUID[:]
mac, err := hex.DecodeString(keyProtected.Crypto.MAC) mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -324,29 +351,36 @@ func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byt
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
return plainText, keyId, err return plainText, keyId, err
} }
func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) { func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
authArray := []byte(auth) authArray := []byte(auth)
salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string)) salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
if err != nil { if err != nil {
return nil, err return nil, err
} }
dkLen := ensureInt(cryptoJSON.KDFParams["dklen"]) dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
if cryptoJSON.KDF == keyHeaderKDF { if cryptoJSON.KDF == keyHeaderKDF {
n := ensureInt(cryptoJSON.KDFParams["n"]) n := ensureInt(cryptoJSON.KDFParams["n"])
r := ensureInt(cryptoJSON.KDFParams["r"]) r := ensureInt(cryptoJSON.KDFParams["r"])
p := ensureInt(cryptoJSON.KDFParams["p"]) p := ensureInt(cryptoJSON.KDFParams["p"])
return scrypt.Key(authArray, salt, n, r, p, dkLen) return scrypt.Key(authArray, salt, n, r, p, dkLen)
} else if cryptoJSON.KDF == "pbkdf2" { } else if cryptoJSON.KDF == "pbkdf2" {
c := ensureInt(cryptoJSON.KDFParams["c"]) c := ensureInt(cryptoJSON.KDFParams["c"])
prf := cryptoJSON.KDFParams["prf"].(string) prf := cryptoJSON.KDFParams["prf"].(string)
if prf != "hmac-sha256" { if prf != "hmac-sha256" {
return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf) return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
} }
key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New) key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
return key, nil return key, nil
} }
@ -361,5 +395,6 @@ func ensureInt(x interface{}) int {
if !ok { if !ok {
res = int(x.(float64)) res = int(x.(float64))
} }
return res return res
} }

View file

@ -34,6 +34,7 @@ func TestKeyEncryptDecrypt(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
password := "" password := ""
address := common.HexToAddress("45dea0fb0bba44f4fcf290bba71fd57d7117cbb8") address := common.HexToAddress("45dea0fb0bba44f4fcf290bba71fd57d7117cbb8")
@ -48,6 +49,7 @@ func TestKeyEncryptDecrypt(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("test %d: json key failed to decrypt: %v", i, err) t.Fatalf("test %d: json key failed to decrypt: %v", i, err)
} }
if key.Address != address { if key.Address != address {
t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address) t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address)
} }

View file

@ -34,14 +34,18 @@ func (ks keyStorePlain) GetKey(addr common.Address, filename, auth string) (*Key
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer fd.Close() defer fd.Close()
key := new(Key) key := new(Key)
if err := json.NewDecoder(fd).Decode(key); err != nil { if err := json.NewDecoder(fd).Decode(key); err != nil {
return nil, err return nil, err
} }
if key.Address != addr { if key.Address != addr {
return nil, fmt.Errorf("key content mismatch: have address %x, want %x", key.Address, addr) return nil, fmt.Errorf("key content mismatch: have address %x, want %x", key.Address, addr)
} }
return key, nil return key, nil
} }
@ -50,6 +54,7 @@ func (ks keyStorePlain) StoreKey(filename string, key *Key, auth string) error {
if err != nil { if err != nil {
return err return err
} }
return writeKeyFile(filename, content) return writeKeyFile(filename, content)
} }
@ -57,5 +62,6 @@ func (ks keyStorePlain) JoinPath(filename string) string {
if filepath.IsAbs(filename) { if filepath.IsAbs(filename) {
return filename return filename
} }
return filepath.Join(ks.keysDirPath, filename) return filepath.Join(ks.keysDirPath, filename)
} }

View file

@ -36,6 +36,7 @@ func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) {
} else { } else {
ks = &keyStorePlain{d} ks = &keyStorePlain{d}
} }
return d, ks return d, ks
} }
@ -43,17 +44,21 @@ func TestKeyStorePlain(t *testing.T) {
_, ks := tmpKeyStoreIface(t, false) _, ks := tmpKeyStoreIface(t, false)
pass := "" // not used but required by API pass := "" // not used but required by API
k1, account, err := storeNewKey(ks, rand.Reader, pass) k1, account, err := storeNewKey(ks, rand.Reader, pass)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
k2, err := ks.GetKey(k1.Address, account.URL.Path, pass) k2, err := ks.GetKey(k1.Address, account.URL.Path, pass)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !reflect.DeepEqual(k1.Address, k2.Address) { if !reflect.DeepEqual(k1.Address, k2.Address) {
t.Fatal(err) t.Fatal(err)
} }
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) { if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
t.Fatal(err) t.Fatal(err)
} }
@ -63,17 +68,21 @@ func TestKeyStorePassphrase(t *testing.T) {
_, ks := tmpKeyStoreIface(t, true) _, ks := tmpKeyStoreIface(t, true)
pass := "foo" pass := "foo"
k1, account, err := storeNewKey(ks, rand.Reader, pass) k1, account, err := storeNewKey(ks, rand.Reader, pass)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
k2, err := ks.GetKey(k1.Address, account.URL.Path, pass) k2, err := ks.GetKey(k1.Address, account.URL.Path, pass)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !reflect.DeepEqual(k1.Address, k2.Address) { if !reflect.DeepEqual(k1.Address, k2.Address) {
t.Fatal(err) t.Fatal(err)
} }
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) { if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
t.Fatal(err) t.Fatal(err)
} }
@ -83,10 +92,12 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
_, ks := tmpKeyStoreIface(t, true) _, ks := tmpKeyStoreIface(t, true)
pass := "foo" pass := "foo"
k1, account, err := storeNewKey(ks, rand.Reader, pass) k1, account, err := storeNewKey(ks, rand.Reader, pass)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if _, err = ks.GetKey(k1.Address, account.URL.Path, "bar"); err != ErrDecrypt { if _, err = ks.GetKey(k1.Address, account.URL.Path, "bar"); err != ErrDecrypt {
t.Fatalf("wrong error for invalid password\ngot %q\nwant %q", err, ErrDecrypt) t.Fatalf("wrong error for invalid password\ngot %q\nwant %q", err, ErrDecrypt)
} }
@ -100,13 +111,16 @@ func TestImportPreSaleKey(t *testing.T) {
// with password "foo" // with password "foo"
fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}" fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}"
pass := "foo" pass := "foo"
account, _, err := importPreSaleKey(ks, []byte(fileContent), pass) account, _, err := importPreSaleKey(ks, []byte(fileContent), pass)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if account.Address != common.HexToAddress("d4584b5f6229b7be90727b0fc8c6b91bb427821f") { if account.Address != common.HexToAddress("d4584b5f6229b7be90727b0fc8c6b91bb427821f") {
t.Errorf("imported account has wrong address %x", account.Address) t.Errorf("imported account has wrong address %x", account.Address)
} }
if !strings.HasPrefix(account.URL.Path, dir) { if !strings.HasPrefix(account.URL.Path, dir) {
t.Errorf("imported account file not in keystore directory: %q", account.URL) t.Errorf("imported account file not in keystore directory: %q", account.URL)
} }
@ -182,15 +196,19 @@ func TestV1_1(t *testing.T) {
func TestV1_2(t *testing.T) { func TestV1_2(t *testing.T) {
t.Parallel() t.Parallel()
ks := &keyStorePassphrase{"testdata/v1", LightScryptN, LightScryptP, true} ks := &keyStorePassphrase{"testdata/v1", LightScryptN, LightScryptP, true}
addr := common.HexToAddress("cb61d5a9c4896fb9658090b597ef0e7be6f7b67e") addr := common.HexToAddress("cb61d5a9c4896fb9658090b597ef0e7be6f7b67e")
file := "testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e" file := "testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e"
k, err := ks.GetKey(addr, file, "g") k, err := ks.GetKey(addr, file, "g")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
privHex := hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)) privHex := hex.EncodeToString(crypto.FromECDSA(k.PrivateKey))
expectedHex := "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d" expectedHex := "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d"
if privHex != expectedHex { if privHex != expectedHex {
t.Fatal(fmt.Errorf("Unexpected privkey: %v, expected %v", privHex, expectedHex)) t.Fatal(fmt.Errorf("Unexpected privkey: %v, expected %v", privHex, expectedHex))
} }
@ -201,6 +219,7 @@ func testDecryptV3(test KeyStoreTestV3, t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
privHex := hex.EncodeToString(privBytes) privHex := hex.EncodeToString(privBytes)
if test.Priv != privHex { if test.Priv != privHex {
t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex)) t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
@ -212,6 +231,7 @@ func testDecryptV1(test KeyStoreTestV1, t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
privHex := hex.EncodeToString(privBytes) privHex := hex.EncodeToString(privBytes)
if test.Priv != privHex { if test.Priv != privHex {
t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex)) t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
@ -220,24 +240,29 @@ func testDecryptV1(test KeyStoreTestV1, t *testing.T) {
func loadKeyStoreTestV3(file string, t *testing.T) map[string]KeyStoreTestV3 { func loadKeyStoreTestV3(file string, t *testing.T) map[string]KeyStoreTestV3 {
tests := make(map[string]KeyStoreTestV3) tests := make(map[string]KeyStoreTestV3)
err := common.LoadJSON(file, &tests) err := common.LoadJSON(file, &tests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
return tests return tests
} }
func loadKeyStoreTestV1(file string, t *testing.T) map[string]KeyStoreTestV1 { func loadKeyStoreTestV1(file string, t *testing.T) map[string]KeyStoreTestV1 {
tests := make(map[string]KeyStoreTestV1) tests := make(map[string]KeyStoreTestV1)
err := common.LoadJSON(file, &tests) err := common.LoadJSON(file, &tests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
return tests return tests
} }
func TestKeyForDirectICAP(t *testing.T) { func TestKeyForDirectICAP(t *testing.T) {
t.Parallel() t.Parallel()
key := NewKeyForDirectICAP(rand.Reader) key := NewKeyForDirectICAP(rand.Reader)
if !strings.HasPrefix(key.Address.Hex(), "0x00") { if !strings.HasPrefix(key.Address.Hex(), "0x00") {
t.Errorf("Expected first address byte to be zero, have: %s", key.Address.Hex()) t.Errorf("Expected first address byte to be zero, have: %s", key.Address.Hex())

View file

@ -37,10 +37,12 @@ func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accou
if err != nil { if err != nil {
return accounts.Account{}, nil, err return accounts.Account{}, nil, err
} }
key.Id, err = uuid.NewRandom() key.Id, err = uuid.NewRandom()
if err != nil { if err != nil {
return accounts.Account{}, nil, err return accounts.Account{}, nil, err
} }
a := accounts.Account{ a := accounts.Account{
Address: key.Address, Address: key.Address,
URL: accounts.URL{ URL: accounts.URL{
@ -49,6 +51,7 @@ func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accou
}, },
} }
err = keyStore.StoreKey(a.URL.Path, key, password) err = keyStore.StoreKey(a.URL.Path, key, password)
return a, key, err return a, key, err
} }
@ -59,17 +62,21 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
Email string Email string
BtcAddr string BtcAddr string
}{} }{}
err = json.Unmarshal(fileContent, &preSaleKeyStruct) err = json.Unmarshal(fileContent, &preSaleKeyStruct)
if err != nil { if err != nil {
return nil, err return nil, err
} }
encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed) encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
if err != nil { if err != nil {
return nil, errors.New("invalid hex in encSeed") return nil, errors.New("invalid hex in encSeed")
} }
if len(encSeedBytes) < 16 { if len(encSeedBytes) < 16 {
return nil, errors.New("invalid encSeed, too short") return nil, errors.New("invalid encSeed, too short")
} }
iv := encSeedBytes[:16] iv := encSeedBytes[:16]
cipherText := encSeedBytes[16:] cipherText := encSeedBytes[16:]
/* /*
@ -81,10 +88,12 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
*/ */
passBytes := []byte(password) passBytes := []byte(password)
derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New) derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv) plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ethPriv := crypto.Keccak256(plainText) ethPriv := crypto.Keccak256(plainText)
ecKey := crypto.ToECDSAUnsafe(ethPriv) ecKey := crypto.ToECDSAUnsafe(ethPriv)
@ -95,9 +104,11 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
} }
derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x" derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x"
expectedAddr := preSaleKeyStruct.EthAddr expectedAddr := preSaleKeyStruct.EthAddr
if derivedAddr != expectedAddr { if derivedAddr != expectedAddr {
err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr) err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr)
} }
return key, err return key, err
} }
@ -107,9 +118,11 @@ func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
stream := cipher.NewCTR(aesBlock, iv) stream := cipher.NewCTR(aesBlock, iv)
outText := make([]byte, len(inText)) outText := make([]byte, len(inText))
stream.XORKeyStream(outText, inText) stream.XORKeyStream(outText, inText)
return outText, err return outText, err
} }
@ -118,13 +131,16 @@ func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
decrypter := cipher.NewCBCDecrypter(aesBlock, iv) decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
paddedPlaintext := make([]byte, len(cipherText)) paddedPlaintext := make([]byte, len(cipherText))
decrypter.CryptBlocks(paddedPlaintext, cipherText) decrypter.CryptBlocks(paddedPlaintext, cipherText)
plaintext := pkcs7Unpad(paddedPlaintext) plaintext := pkcs7Unpad(paddedPlaintext)
if plaintext == nil { if plaintext == nil {
return nil, ErrDecrypt return nil, ErrDecrypt
} }
return plaintext, err return plaintext, err
} }
@ -146,5 +162,6 @@ func pkcs7Unpad(in []byte) []byte {
return nil return nil
} }
} }
return in[:len(in)-int(padding)] return in[:len(in)-int(padding)]
} }

View file

@ -46,6 +46,7 @@ func (w *keystoreWallet) Status() (string, error) {
if _, ok := w.keystore.unlocked[w.account.Address]; ok { if _, ok := w.keystore.unlocked[w.account.Address]; ok {
return "Unlocked", nil return "Unlocked", nil
} }
return "Locked", nil return "Locked", nil
} }

View file

@ -52,6 +52,7 @@ func (w *watcher) start() {
if w.starting || w.running { if w.starting || w.running {
return return
} }
w.starting = true w.starting = true
go w.loop() go w.loop()
} }
@ -68,6 +69,7 @@ func (w *watcher) loop() {
w.runEnded = true w.runEnded = true
w.ac.mu.Unlock() w.ac.mu.Unlock()
}() }()
logger := log.New("path", w.ac.keydir) logger := log.New("path", w.ac.keydir)
// Create new watcher. // Create new watcher.
@ -76,7 +78,9 @@ func (w *watcher) loop() {
log.Error("Failed to start filesystem watcher", "err", err) log.Error("Failed to start filesystem watcher", "err", err)
return return
} }
defer watcher.Close() defer watcher.Close()
if err := watcher.Add(w.ac.keydir); err != nil { if err := watcher.Add(w.ac.keydir); err != nil {
logger.Warn("Failed to watch keystore folder", "err", err) logger.Warn("Failed to watch keystore folder", "err", err)
return return
@ -102,6 +106,7 @@ func (w *watcher) loop() {
<-debounce.C <-debounce.C
} }
defer debounce.Stop() defer debounce.Stop()
for { for {
select { select {
case <-w.quit: case <-w.quit:
@ -113,6 +118,7 @@ func (w *watcher) loop() {
// Trigger the scan (with delay), if not already triggered // Trigger the scan (with delay), if not already triggered
if !rescanTriggered { if !rescanTriggered {
debounce.Reset(debounceDuration) debounce.Reset(debounceDuration)
rescanTriggered = true rescanTriggered = true
} }
// The fsnotify library does provide more granular event-info, it // The fsnotify library does provide more granular event-info, it
@ -123,9 +129,11 @@ func (w *watcher) loop() {
if !ok { if !ok {
return return
} }
log.Info("Filsystem watcher error", "err", err) log.Info("Filsystem watcher error", "err", err)
case <-debounce.C: case <-debounce.C:
w.ac.scanAccounts() w.ac.scanAccounts()
rescanTriggered = false rescanTriggered = false
} }
} }

View file

@ -87,10 +87,12 @@ func NewManager(config *Config, backends ...Backend) *Manager {
quit: make(chan chan error), quit: make(chan chan error),
term: make(chan struct{}), term: make(chan struct{}),
} }
for _, backend := range backends { for _, backend := range backends {
kind := reflect.TypeOf(backend) kind := reflect.TypeOf(backend)
am.backends[kind] = append(am.backends[kind], backend) am.backends[kind] = append(am.backends[kind], backend)
} }
go am.update() go am.update()
return am return am
@ -100,6 +102,7 @@ func NewManager(config *Config, backends ...Backend) *Manager {
func (am *Manager) Close() error { func (am *Manager) Close() error {
errc := make(chan error) errc := make(chan error)
am.quit <- errc am.quit <- errc
return <-errc return <-errc
} }
@ -113,6 +116,7 @@ func (am *Manager) Config() *Config {
func (am *Manager) AddBackend(backend Backend) { func (am *Manager) AddBackend(backend Backend) {
done := make(chan struct{}) done := make(chan struct{})
am.newBackends <- newBackendEvent{backend, done} am.newBackends <- newBackendEvent{backend, done}
<-done <-done
} }
@ -125,6 +129,7 @@ func (am *Manager) update() {
for _, sub := range am.updaters { for _, sub := range am.updaters {
sub.Unsubscribe() sub.Unsubscribe()
} }
am.updaters = nil am.updaters = nil
am.lock.Unlock() am.lock.Unlock()
}() }()
@ -161,6 +166,7 @@ func (am *Manager) update() {
// Signals event emitters the loop is not receiving values // Signals event emitters the loop is not receiving values
// to prevent them from getting stuck. // to prevent them from getting stuck.
close(am.term) close(am.term)
return return
} }
} }
@ -186,6 +192,7 @@ func (am *Manager) Wallets() []Wallet {
func (am *Manager) walletsNoLock() []Wallet { func (am *Manager) walletsNoLock() []Wallet {
cpy := make([]Wallet, len(am.wallets)) cpy := make([]Wallet, len(am.wallets))
copy(cpy, am.wallets) copy(cpy, am.wallets)
return cpy return cpy
} }
@ -198,11 +205,13 @@ func (am *Manager) Wallet(url string) (Wallet, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, wallet := range am.walletsNoLock() { for _, wallet := range am.walletsNoLock() {
if wallet.URL() == parsed { if wallet.URL() == parsed {
return wallet, nil return wallet, nil
} }
} }
return nil, ErrUnknownWallet return nil, ErrUnknownWallet
} }
@ -212,11 +221,13 @@ func (am *Manager) Accounts() []common.Address {
defer am.lock.RUnlock() defer am.lock.RUnlock()
addresses := make([]common.Address, 0) // return [] instead of nil if empty addresses := make([]common.Address, 0) // return [] instead of nil if empty
for _, wallet := range am.wallets { for _, wallet := range am.wallets {
for _, account := range wallet.Accounts() { for _, account := range wallet.Accounts() {
addresses = append(addresses, account.Address) addresses = append(addresses, account.Address)
} }
} }
return addresses return addresses
} }
@ -232,6 +243,7 @@ func (am *Manager) Find(account Account) (Wallet, error) {
return wallet, nil return wallet, nil
} }
} }
return nil, ErrUnknownAccount return nil, ErrUnknownAccount
} }
@ -252,8 +264,10 @@ func merge(slice []Wallet, wallets ...Wallet) []Wallet {
slice = append(slice, wallet) slice = append(slice, wallet)
continue continue
} }
slice = append(slice[:n], append([]Wallet{wallet}, slice[n:]...)...) slice = append(slice[:n], append([]Wallet{wallet}, slice[n:]...)...)
} }
return slice return slice
} }
@ -266,7 +280,9 @@ func drop(slice []Wallet, wallets ...Wallet) []Wallet {
// Wallet not found, may happen during startup // Wallet not found, may happen during startup
continue continue
} }
slice = append(slice[:n], slice[n+1:]...) slice = append(slice[:n], slice[n+1:]...)
} }
return slice return slice
} }

View file

@ -36,26 +36,33 @@ func (ca commandAPDU) serialize() ([]byte, error) {
if err := binary.Write(buf, binary.BigEndian, ca.Cla); err != nil { if err := binary.Write(buf, binary.BigEndian, ca.Cla); err != nil {
return nil, err return nil, err
} }
if err := binary.Write(buf, binary.BigEndian, ca.Ins); err != nil { if err := binary.Write(buf, binary.BigEndian, ca.Ins); err != nil {
return nil, err return nil, err
} }
if err := binary.Write(buf, binary.BigEndian, ca.P1); err != nil { if err := binary.Write(buf, binary.BigEndian, ca.P1); err != nil {
return nil, err return nil, err
} }
if err := binary.Write(buf, binary.BigEndian, ca.P2); err != nil { if err := binary.Write(buf, binary.BigEndian, ca.P2); err != nil {
return nil, err return nil, err
} }
if len(ca.Data) > 0 { if len(ca.Data) > 0 {
if err := binary.Write(buf, binary.BigEndian, uint8(len(ca.Data))); err != nil { if err := binary.Write(buf, binary.BigEndian, uint8(len(ca.Data))); err != nil {
return nil, err return nil, err
} }
if err := binary.Write(buf, binary.BigEndian, ca.Data); err != nil { if err := binary.Write(buf, binary.BigEndian, ca.Data); err != nil {
return nil, err return nil, err
} }
} }
if err := binary.Write(buf, binary.BigEndian, ca.Le); err != nil { if err := binary.Write(buf, binary.BigEndian, ca.Le); err != nil {
return nil, err return nil, err
} }
return buf.Bytes(), nil return buf.Bytes(), nil
} }
@ -77,11 +84,14 @@ func (ra *responseAPDU) deserialize(data []byte) error {
if err := binary.Read(buf, binary.BigEndian, &ra.Data); err != nil { if err := binary.Read(buf, binary.BigEndian, &ra.Data); err != nil {
return err return err
} }
if err := binary.Read(buf, binary.BigEndian, &ra.Sw1); err != nil { if err := binary.Read(buf, binary.BigEndian, &ra.Sw1); err != nil {
return err return err
} }
if err := binary.Read(buf, binary.BigEndian, &ra.Sw2); err != nil { if err := binary.Read(buf, binary.BigEndian, &ra.Sw2); err != nil {
return err return err
} }
return nil return nil
} }

View file

@ -88,11 +88,13 @@ type Hub struct {
func (hub *Hub) readPairings() error { func (hub *Hub) readPairings() error {
hub.pairings = make(map[string]smartcardPairing) hub.pairings = make(map[string]smartcardPairing)
pairingFile, err := os.Open(filepath.Join(hub.datadir, "smartcards.json")) pairingFile, err := os.Open(filepath.Join(hub.datadir, "smartcards.json"))
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return nil return nil
} }
return err return err
} }
@ -100,6 +102,7 @@ func (hub *Hub) readPairings() error {
if err != nil { if err != nil {
return err return err
} }
var pairings []smartcardPairing var pairings []smartcardPairing
if err := json.Unmarshal(pairingData, &pairings); err != nil { if err := json.Unmarshal(pairingData, &pairings); err != nil {
return err return err
@ -108,6 +111,7 @@ func (hub *Hub) readPairings() error {
for _, pairing := range pairings { for _, pairing := range pairings {
hub.pairings[string(pairing.PublicKey)] = pairing hub.pairings[string(pairing.PublicKey)] = pairing
} }
return nil return nil
} }
@ -139,6 +143,7 @@ func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing {
if pairing, ok := hub.pairings[string(wallet.PublicKey)]; ok { if pairing, ok := hub.pairings[string(wallet.PublicKey)]; ok {
return &pairing return &pairing
} }
return nil return nil
} }
@ -148,6 +153,7 @@ func (hub *Hub) setPairing(wallet *Wallet, pairing *smartcardPairing) error {
} else { } else {
hub.pairings[string(wallet.PublicKey)] = *pairing hub.pairings[string(wallet.PublicKey)] = *pairing
} }
return hub.writePairings() return hub.writePairings()
} }
@ -157,6 +163,7 @@ func NewHub(daemonPath string, scheme string, datadir string) (*Hub, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
hub := &Hub{ hub := &Hub{
scheme: scheme, scheme: scheme,
context: context, context: context,
@ -167,7 +174,9 @@ func NewHub(daemonPath string, scheme string, datadir string) (*Hub, error) {
if err := hub.readPairings(); err != nil { if err := hub.readPairings(); err != nil {
return nil, err return nil, err
} }
hub.refreshWallets() hub.refreshWallets()
return hub, nil return hub, nil
} }
@ -184,7 +193,9 @@ func (hub *Hub) Wallets() []accounts.Wallet {
for _, wallet := range hub.wallets { for _, wallet := range hub.wallets {
cpy = append(cpy, wallet) cpy = append(cpy, wallet)
} }
sort.Sort(accounts.WalletsByURL(cpy)) sort.Sort(accounts.WalletsByURL(cpy))
return cpy return cpy
} }
@ -225,8 +236,10 @@ func (hub *Hub) refreshWallets() {
if err := wallet.ping(); err == nil { if err := wallet.ping(); err == nil {
continue continue
} }
wallet.Close() wallet.Close()
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped}) events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
delete(hub.wallets, reader) delete(hub.wallets, reader)
} }
// New card detected, try to connect to it // New card detected, try to connect to it
@ -235,10 +248,12 @@ func (hub *Hub) refreshWallets() {
log.Debug("Failed to open smart card", "reader", reader, "err", err) log.Debug("Failed to open smart card", "reader", reader, "err", err)
continue continue
} }
wallet := NewWallet(hub, card) wallet := NewWallet(hub, card)
if err = wallet.connect(); err != nil { if err = wallet.connect(); err != nil {
log.Debug("Failed to connect to smart card", "reader", reader, "err", err) log.Debug("Failed to connect to smart card", "reader", reader, "err", err)
card.Disconnect(pcsc.LeaveCard) card.Disconnect(pcsc.LeaveCard)
continue continue
} }
// Card connected, start tracking in amongs the wallets // Card connected, start tracking in amongs the wallets
@ -250,9 +265,11 @@ func (hub *Hub) refreshWallets() {
if _, ok := seen[reader]; !ok { if _, ok := seen[reader]; !ok {
wallet.Close() wallet.Close()
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped}) events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
delete(hub.wallets, reader) delete(hub.wallets, reader)
} }
} }
hub.refreshed = time.Now() hub.refreshed = time.Now()
hub.stateLock.Unlock() hub.stateLock.Unlock()
@ -276,6 +293,7 @@ func (hub *Hub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
hub.updating = true hub.updating = true
go hub.updater() go hub.updater()
} }
return sub return sub
} }
@ -295,6 +313,7 @@ func (hub *Hub) updater() {
if hub.updateScope.Count() == 0 { if hub.updateScope.Count() == 0 {
hub.updating = false hub.updating = false
hub.stateLock.Unlock() hub.stateLock.Unlock()
return return
} }
hub.stateLock.Unlock() hub.stateLock.Unlock()

View file

@ -67,11 +67,14 @@ func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSes
if err != nil { if err != nil {
return nil, err return nil, err
} }
cardPublic, err := crypto.UnmarshalPubkey(keyData) cardPublic, err := crypto.UnmarshalPubkey(keyData)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not unmarshal public key from card: %v", err) return nil, fmt.Errorf("could not unmarshal public key from card: %v", err)
} }
secret, _ := key.Curve.ScalarMult(cardPublic.X, cardPublic.Y, key.D.Bytes()) secret, _ := key.Curve.ScalarMult(cardPublic.X, cardPublic.Y, key.D.Bytes())
return &SecureChannelSession{ return &SecureChannelSession{
card: card, card: card,
secret: secret.Bytes(), secret: secret.Bytes(),
@ -108,6 +111,7 @@ func (s *SecureChannelSession) Pair(pairingPassword []byte) error {
md.Reset() md.Reset()
md.Write(secretHash[:]) md.Write(secretHash[:])
md.Write(cardChallenge) md.Write(cardChallenge)
response, err = s.pair(pairP1LastStep, md.Sum(nil)) response, err = s.pair(pairP1LastStep, md.Sum(nil))
if err != nil { if err != nil {
return err return err
@ -132,9 +136,11 @@ func (s *SecureChannelSession) Unpair() error {
if err != nil { if err != nil {
return err return err
} }
s.PairingKey = nil s.PairingKey = nil
// Close channel // Close channel
s.iv = nil s.iv = nil
return nil return nil
} }
@ -177,6 +183,7 @@ func (s *SecureChannelSession) mutuallyAuthenticate() error {
if err != nil { if err != nil {
return err return err
} }
if response.Sw1 != 0x90 || response.Sw2 != 0x00 { if response.Sw1 != 0x90 || response.Sw2 != 0x00 {
return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: %#x%x", response.Sw1, response.Sw2) return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: %#x%x", response.Sw1, response.Sw2)
} }
@ -222,6 +229,7 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
if err != nil { if err != nil {
return nil, err return nil, err
} }
meta := [16]byte{cla, ins, p1, p2, byte(len(data) + scBlockSize)} meta := [16]byte{cla, ins, p1, p2, byte(len(data) + scBlockSize)}
if err = s.updateIV(meta[:], data); err != nil { if err = s.updateIV(meta[:], data); err != nil {
return nil, err return nil, err
@ -245,6 +253,7 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
rmeta := [16]byte{byte(len(response.Data))} rmeta := [16]byte{byte(len(response.Data))}
rmac := response.Data[:len(s.iv)] rmac := response.Data[:len(s.iv)]
rdata := response.Data[len(s.iv):] rdata := response.Data[len(s.iv):]
plainData, err := s.decryptAPDU(rdata) plainData, err := s.decryptAPDU(rdata)
if err != nil { if err != nil {
return nil, err return nil, err
@ -253,6 +262,7 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
if err = s.updateIV(rmeta[:], rdata); err != nil { if err = s.updateIV(rmeta[:], rdata); err != nil {
return nil, err return nil, err
} }
if !bytes.Equal(s.iv, rmac) { if !bytes.Equal(s.iv, rmac) {
return nil, fmt.Errorf("invalid MAC in response") return nil, fmt.Errorf("invalid MAC in response")
} }
@ -272,6 +282,7 @@ func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
if len(data) > maxPayloadSize { if len(data) > maxPayloadSize {
return nil, fmt.Errorf("payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize) return nil, fmt.Errorf("payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize)
} }
data = pad(data, 0x80) data = pad(data, 0x80)
ret := make([]byte, len(data)) ret := make([]byte, len(data))
@ -280,8 +291,10 @@ func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
crypter := cipher.NewCBCEncrypter(a, s.iv) crypter := cipher.NewCBCEncrypter(a, s.iv)
crypter.CryptBlocks(ret, data) crypter.CryptBlocks(ret, data)
return ret, nil return ret, nil
} }
@ -290,6 +303,7 @@ func pad(data []byte, terminator byte) []byte {
padded := make([]byte, (len(data)/16+1)*16) padded := make([]byte, (len(data)/16+1)*16)
copy(padded, data) copy(padded, data)
padded[len(data)] = terminator padded[len(data)] = terminator
return padded return padded
} }
@ -304,6 +318,7 @@ func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) {
crypter := cipher.NewCBCDecrypter(a, s.iv) crypter := cipher.NewCBCDecrypter(a, s.iv)
crypter.CryptBlocks(ret, data) crypter.CryptBlocks(ret, data)
return unpad(ret, 0x80) return unpad(ret, 0x80)
} }
@ -319,6 +334,7 @@ func unpad(data []byte, terminator byte) ([]byte, error) {
return nil, fmt.Errorf("expected end of padding, got %d", data[len(data)-i]) return nil, fmt.Errorf("expected end of padding, got %d", data[len(data)-i])
} }
} }
return nil, fmt.Errorf("expected end of padding, got 0") return nil, fmt.Errorf("expected end of padding, got 0")
} }
@ -326,14 +342,17 @@ func unpad(data []byte, terminator byte) ([]byte, error) {
// each message exchanged. // each message exchanged.
func (s *SecureChannelSession) updateIV(meta, data []byte) error { func (s *SecureChannelSession) updateIV(meta, data []byte) error {
data = pad(data, 0) data = pad(data, 0)
a, err := aes.NewCipher(s.sessionMacKey) a, err := aes.NewCipher(s.sessionMacKey)
if err != nil { if err != nil {
return err return err
} }
crypter := cipher.NewCBCEncrypter(a, make([]byte, 16)) crypter := cipher.NewCBCEncrypter(a, make([]byte, 16))
crypter.CryptBlocks(meta, meta) crypter.CryptBlocks(meta, meta)
crypter.CryptBlocks(data, data) crypter.CryptBlocks(data, data)
// The first 16 bytes of the last block is the MAC // The first 16 bytes of the last block is the MAC
s.iv = data[len(data)-32 : len(data)-16] s.iv = data[len(data)-32 : len(data)-16]
return nil return nil
} }

View file

@ -132,6 +132,7 @@ func NewWallet(hub *Hub, card *pcsc.Card) *Wallet {
Hub: hub, Hub: hub,
card: card, card: card,
} }
return wallet return wallet
} }
@ -202,6 +203,7 @@ func (w *Wallet) connect() error {
Wallet: w, Wallet: w,
Channel: channel, Channel: channel,
} }
return nil return nil
} }
@ -222,6 +224,7 @@ func (w *Wallet) doselect() (*applicationInfo, error) {
if _, err := asn1.UnmarshalWithParams(response.Data, appinfo, "tag:4"); err != nil { if _, err := asn1.UnmarshalWithParams(response.Data, appinfo, "tag:4"); err != nil {
return nil, err return nil, err
} }
return appinfo, nil return appinfo, nil
} }
@ -234,9 +237,11 @@ func (w *Wallet) ping() error {
if !w.session.paired() { if !w.session.paired() {
return nil return nil
} }
if _, err := w.session.walletStatus(); err != nil { if _, err := w.session.walletStatus(); err != nil {
return err return err
} }
return nil return nil
} }
@ -245,6 +250,7 @@ func (w *Wallet) release() error {
if w.session != nil { if w.session != nil {
return w.session.release() return w.session.release()
} }
return nil return nil
} }
@ -254,13 +260,16 @@ func (w *Wallet) pair(puk []byte) error {
if w.session.paired() { if w.session.paired() {
return fmt.Errorf("wallet already paired") return fmt.Errorf("wallet already paired")
} }
pairing, err := w.session.pair(puk) pairing, err := w.session.pair(puk)
if err != nil { if err != nil {
return err return err
} }
if err = w.Hub.setPairing(w, &pairing); err != nil { if err = w.Hub.setPairing(w, &pairing); err != nil {
return err return err
} }
return w.session.authenticate(pairing) return w.session.authenticate(pairing)
} }
@ -272,15 +281,19 @@ func (w *Wallet) Unpair(pin []byte) error {
if !w.session.paired() { if !w.session.paired() {
return fmt.Errorf("wallet %x not paired", w.PublicKey) return fmt.Errorf("wallet %x not paired", w.PublicKey)
} }
if err := w.session.verifyPin(pin); err != nil { if err := w.session.verifyPin(pin); err != nil {
return fmt.Errorf("failed to verify pin: %s", err) return fmt.Errorf("failed to verify pin: %s", err)
} }
if err := w.session.unpair(); err != nil { if err := w.session.unpair(); err != nil {
return fmt.Errorf("failed to unpair: %s", err) return fmt.Errorf("failed to unpair: %s", err)
} }
if err := w.Hub.setPairing(w, nil); err != nil { if err := w.Hub.setPairing(w, nil); err != nil {
return err return err
} }
return nil return nil
} }
@ -310,6 +323,7 @@ func (w *Wallet) Status() (string, error) {
if err != nil { if err != nil {
return fmt.Sprintf("Failed: %v", err), err return fmt.Sprintf("Failed: %v", err), err
} }
switch { switch {
case !w.session.verified && status.PinRetryCount == 0 && status.PukRetryCount == 0: case !w.session.verified && status.PinRetryCount == 0 && status.PukRetryCount == 0:
return "Bricked, waiting for full wipe", nil return "Bricked, waiting for full wipe", nil
@ -384,6 +398,7 @@ func (w *Wallet) Open(passphrase string) error {
w.log.Error("PIN needs to be at least 6 digits") w.log.Error("PIN needs to be at least 6 digits")
return ErrPINNeeded return ErrPINNeeded
} }
if err := w.session.verifyPin([]byte(passphrase)); err != nil { if err := w.session.verifyPin([]byte(passphrase)); err != nil {
return err return err
} }
@ -392,6 +407,7 @@ func (w *Wallet) Open(passphrase string) error {
w.log.Error("PUK needs to be at least 12 digits") w.log.Error("PUK needs to be at least 12 digits")
return ErrPINUnblockNeeded return ErrPINUnblockNeeded
} }
if err := w.session.unblockPin([]byte(passphrase)); err != nil { if err := w.session.unblockPin([]byte(passphrase)); err != nil {
return err return err
} }
@ -417,6 +433,7 @@ func (w *Wallet) Close() error {
// Terminate the self-derivations // Terminate the self-derivations
var derr error var derr error
if dQuit != nil { if dQuit != nil {
errc := make(chan error) errc := make(chan error)
dQuit <- errc dQuit <- errc
@ -432,6 +449,7 @@ func (w *Wallet) Close() error {
if err := w.release(); err != nil { if err := w.release(); err != nil {
return err return err
} }
return derr return derr
} }
@ -447,6 +465,7 @@ func (w *Wallet) selfDerive() {
errc chan error errc chan error
err error err error
) )
for errc == nil && err == nil { for errc == nil && err == nil {
// Wait until either derivation or termination is requested // Wait until either derivation or termination is requested
select { select {
@ -461,8 +480,10 @@ func (w *Wallet) selfDerive() {
if w.session == nil || w.deriveChain == nil { if w.session == nil || w.deriveChain == nil {
w.lock.Unlock() w.lock.Unlock()
reqc <- struct{}{} reqc <- struct{}{}
continue continue
} }
pairing := w.Hub.pairing(w) pairing := w.Hub.pairing(w)
// Device lock obtained, derive the next batch of accounts // Device lock obtained, derive the next batch of accounts
@ -475,6 +496,7 @@ func (w *Wallet) selfDerive() {
context = context.Background() context = context.Background()
) )
for i := 0; i < len(nextAddrs); i++ { for i := 0; i < len(nextAddrs); i++ {
for empty := false; !empty; { for empty := false; !empty; {
// Retrieve the next derived Ethereum account // Retrieve the next derived Ethereum account
@ -483,6 +505,7 @@ func (w *Wallet) selfDerive() {
w.log.Warn("Smartcard wallet account derivation failed", "err", err) w.log.Warn("Smartcard wallet account derivation failed", "err", err)
break break
} }
nextAddrs[i] = nextAcc.Address nextAddrs[i] = nextAcc.Address
} }
// Check the account's status against the current chain state // Check the account's status against the current chain state
@ -490,11 +513,13 @@ func (w *Wallet) selfDerive() {
balance *big.Int balance *big.Int
nonce uint64 nonce uint64
) )
balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil) balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil)
if err != nil { if err != nil {
w.log.Warn("Smartcard wallet balance retrieval failed", "err", err) w.log.Warn("Smartcard wallet balance retrieval failed", "err", err)
break break
} }
nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil) nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil)
if err != nil { if err != nil {
w.log.Warn("Smartcard wallet nonce retrieval failed", "err", err) w.log.Warn("Smartcard wallet nonce retrieval failed", "err", err)
@ -503,6 +528,7 @@ func (w *Wallet) selfDerive() {
// If the next account is empty, stop self-derivation, but add for the last base path // If the next account is empty, stop self-derivation, but add for the last base path
if balance.Sign() == 0 && nonce == 0 { if balance.Sign() == 0 && nonce == 0 {
empty = true empty = true
if i < len(nextAddrs)-1 { if i < len(nextAddrs)-1 {
break break
} }
@ -516,6 +542,7 @@ func (w *Wallet) selfDerive() {
if _, known := pairing.Accounts[nextAddrs[i]]; !known || !empty || nextAddrs[i] != w.deriveNextAddrs[i] { 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) w.log.Info("Smartcard wallet discovered new account", "address", nextAddrs[i], "path", path, "balance", balance, "nonce", nonce)
} }
pairing.Accounts[nextAddrs[i]] = path pairing.Accounts[nextAddrs[i]] = path
// Fetch the next potential account // Fetch the next potential account
@ -538,6 +565,7 @@ func (w *Wallet) selfDerive() {
// Notify the user of termination and loop after a bit of time (to avoid trashing) // Notify the user of termination and loop after a bit of time (to avoid trashing)
reqc <- struct{}{} reqc <- struct{}{}
if err == nil { if err == nil {
select { select {
case errc = <-w.deriveQuit: case errc = <-w.deriveQuit:
@ -577,9 +605,12 @@ func (w *Wallet) Accounts() []accounts.Account {
for address, path := range pairing.Accounts { for address, path := range pairing.Accounts {
ret = append(ret, w.makeAccount(address, path)) ret = append(ret, w.makeAccount(address, path))
} }
sort.Sort(accounts.AccountsByURL(ret)) sort.Sort(accounts.AccountsByURL(ret))
return ret return ret
} }
return nil return nil
} }
@ -599,6 +630,7 @@ func (w *Wallet) Contains(account accounts.Account) bool {
_, ok := pairing.Accounts[account.Address] _, ok := pairing.Accounts[account.Address]
return ok return ok
} }
return false return false
} }
@ -625,6 +657,7 @@ func (w *Wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
if pin { if pin {
pairing := w.Hub.pairing(w) pairing := w.Hub.pairing(w)
pairing.Accounts[account.Address] = path pairing.Accounts[account.Address] = path
if err := w.Hub.setPairing(w, pairing); err != nil { if err := w.Hub.setPairing(w, pairing); err != nil {
return accounts.Account{}, err return accounts.Account{}, err
} }
@ -656,6 +689,7 @@ func (w *Wallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.Chai
w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base)) w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base))
copy(w.deriveNextPaths[i][:], base[:]) copy(w.deriveNextPaths[i][:], base[:])
} }
w.deriveNextAddrs = make([]common.Address, len(bases)) w.deriveNextAddrs = make([]common.Address, len(bases))
w.deriveChain = chain w.deriveChain = chain
} }
@ -701,10 +735,12 @@ func (w *Wallet) signHash(account accounts.Account, hash []byte) ([]byte, error)
func (w *Wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { func (w *Wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
signer := types.LatestSignerForChainID(chainID) signer := types.LatestSignerForChainID(chainID)
hash := signer.Hash(tx) hash := signer.Hash(tx)
sig, err := w.signHash(account, hash[:]) sig, err := w.signHash(account, hash[:])
if err != nil { if err != nil {
return nil, err return nil, err
} }
return tx.WithSignature(signer, sig) return tx.WithSignature(signer, sig)
} }
@ -759,6 +795,7 @@ func (w *Wallet) SignTxWithPassphrase(account accounts.Account, passphrase strin
return nil, err return nil, err
} }
} }
return w.SignTx(account, tx, chainID) return w.SignTx(account, tx, chainID)
} }
@ -815,6 +852,7 @@ func (s *Session) unpair() error {
if !s.verified { if !s.verified {
return fmt.Errorf("unpair requires that the PIN be verified") return fmt.Errorf("unpair requires that the PIN be verified")
} }
return s.Channel.Unpair() return s.Channel.Unpair()
} }
@ -823,7 +861,9 @@ func (s *Session) verifyPin(pin []byte) error {
if _, err := s.Channel.transmitEncrypted(claSCWallet, insVerifyPin, 0, 0, pin); err != nil { if _, err := s.Channel.transmitEncrypted(claSCWallet, insVerifyPin, 0, 0, pin); err != nil {
return err return err
} }
s.verified = true s.verified = true
return nil return nil
} }
@ -833,7 +873,9 @@ func (s *Session) unblockPin(pukpin []byte) error {
if _, err := s.Channel.transmitEncrypted(claSCWallet, insUnblockPin, 0, 0, pukpin); err != nil { if _, err := s.Channel.transmitEncrypted(claSCWallet, insUnblockPin, 0, 0, pukpin); err != nil {
return err return err
} }
s.verified = true s.verified = true
return nil return nil
} }
@ -852,8 +894,10 @@ func (s *Session) authenticate(pairing smartcardPairing) error {
if !bytes.Equal(s.Wallet.PublicKey, pairing.PublicKey) { 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) return fmt.Errorf("cannot pair using another wallet's pairing; %x != %x", s.Wallet.PublicKey, pairing.PublicKey)
} }
s.Channel.PairingKey = pairing.PairingKey s.Channel.PairingKey = pairing.PairingKey
s.Channel.PairingIndex = pairing.PairingIndex s.Channel.PairingIndex = pairing.PairingIndex
return s.Channel.Open() return s.Channel.Open()
} }
@ -875,6 +919,7 @@ func (s *Session) walletStatus() (*walletStatus, error) {
if _, err := asn1.UnmarshalWithParams(response.Data, status, "tag:3"); err != nil { if _, err := asn1.UnmarshalWithParams(response.Data, status, "tag:3"); err != nil {
return nil, err return nil, err
} }
return status, nil return status, nil
} }
@ -886,8 +931,10 @@ func (s *Session) derivationPath() (accounts.DerivationPath, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
buf := bytes.NewReader(response.Data) buf := bytes.NewReader(response.Data)
path := make(accounts.DerivationPath, len(response.Data)/4) path := make(accounts.DerivationPath, len(response.Data)/4)
return path, binary.Read(buf, binary.BigEndian, &path) return path, binary.Read(buf, binary.BigEndian, &path)
} }
@ -906,6 +953,7 @@ func (s *Session) initialize(seed []byte) error {
if err != nil { if err != nil {
return err return err
} }
if status == "Online" { if status == "Online" {
return fmt.Errorf("card is already initialized, cowardly refusing to proceed") return fmt.Errorf("card is already initialized, cowardly refusing to proceed")
} }
@ -927,6 +975,7 @@ func (s *Session) initialize(seed []byte) error {
id.PublicKey = crypto.FromECDSAPub(&key.PublicKey) id.PublicKey = crypto.FromECDSAPub(&key.PublicKey)
id.PrivateKey = seed[:32] id.PrivateKey = seed[:32]
id.ChainCode = seed[32:] id.ChainCode = seed[32:]
data, err := asn1.Marshal(id) data, err := asn1.Marshal(id)
if err != nil { if err != nil {
return err return err
@ -936,6 +985,7 @@ func (s *Session) initialize(seed []byte) error {
data[0] = 0xA1 data[0] = 0xA1
_, err = s.Channel.transmitEncrypted(claSCWallet, insLoadKey, 0x02, 0, data) _, err = s.Channel.transmitEncrypted(claSCWallet, insLoadKey, 0x02, 0, data)
return err return err
} }
@ -947,6 +997,7 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
} }
var p1 uint8 var p1 uint8
switch startingPoint { switch startingPoint {
case derivationpath.StartingPointMaster: case derivationpath.StartingPointMaster:
p1 = P1DeriveKeyFromMaster p1 = P1DeriveKeyFromMaster
@ -979,6 +1030,7 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil { if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil {
return accounts.Account{}, err return accounts.Account{}, err
} }
rbytes, sbytes := sigdata.Signature.R.Bytes(), sigdata.Signature.S.Bytes() rbytes, sbytes := sigdata.Signature.R.Bytes(), sigdata.Signature.S.Bytes()
sig := make([]byte, 65) sig := make([]byte, 65)
copy(sig[32-len(rbytes):32], rbytes) copy(sig[32-len(rbytes):32], rbytes)
@ -987,10 +1039,12 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
if err := confirmPublicKey(sig, sigdata.PublicKey); err != nil { if err := confirmPublicKey(sig, sigdata.PublicKey); err != nil {
return accounts.Account{}, err return accounts.Account{}, err
} }
pub, err := crypto.UnmarshalPubkey(sigdata.PublicKey) pub, err := crypto.UnmarshalPubkey(sigdata.PublicKey)
if err != nil { if err != nil {
return accounts.Account{}, err return accounts.Account{}, err
} }
return s.Wallet.makeAccount(crypto.PubkeyToAddress(*pub), path), nil return s.Wallet.makeAccount(crypto.PubkeyToAddress(*pub), path), nil
} }
@ -1010,10 +1064,12 @@ func (s *Session) publicKey() ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
keys := new(keyExport) keys := new(keyExport)
if _, err := asn1.UnmarshalWithParams(response.Data, keys, "tag:1"); err != nil { if _, err := asn1.UnmarshalWithParams(response.Data, keys, "tag:1"); err != nil {
return nil, err return nil, err
} }
return keys.PublicKey, nil return keys.PublicKey, nil
} }
@ -1031,16 +1087,19 @@ type signatureData struct {
// recovering the v value. // recovering the v value.
func (s *Session) sign(path accounts.DerivationPath, hash []byte) ([]byte, error) { func (s *Session) sign(path accounts.DerivationPath, hash []byte) ([]byte, error) {
startTime := time.Now() startTime := time.Now()
_, err := s.derive(path) _, err := s.derive(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
deriveTime := time.Now() deriveTime := time.Now()
response, err := s.Channel.transmitEncrypted(claSCWallet, insSign, signP1PrecomputedHash, signP2OnlyBlock, hash) response, err := s.Channel.transmitEncrypted(claSCWallet, insSign, signP1PrecomputedHash, signP2OnlyBlock, hash)
if err != nil { if err != nil {
return nil, err return nil, err
} }
sigdata := new(signatureData) sigdata := new(signatureData)
if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil { if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil {
return nil, err return nil, err
@ -1056,6 +1115,7 @@ func (s *Session) sign(path accounts.DerivationPath, hash []byte) ([]byte, error
if err != nil { if err != nil {
return nil, err return nil, err
} }
log.Debug("Signed using smartcard", "deriveTime", deriveTime.Sub(startTime), "signingTime", time.Since(deriveTime)) log.Debug("Signed using smartcard", "deriveTime", deriveTime.Sub(startTime), "signingTime", time.Since(deriveTime))
return sig, nil return sig, nil
@ -1071,6 +1131,7 @@ func confirmPublicKey(sig, pubkey []byte) error {
// recover the v value and produce a recoverable signature. // recover the v value and produce a recoverable signature.
func makeRecoverableSignature(hash, sig, expectedPubkey []byte) ([]byte, error) { func makeRecoverableSignature(hash, sig, expectedPubkey []byte) ([]byte, error) {
var libraryError error var libraryError error
for v := 0; v < 2; v++ { for v := 0; v < 2; v++ {
sig[64] = byte(v) sig[64] = byte(v)
if pubkey, err := crypto.Ecrecover(hash, sig); err == nil { if pubkey, err := crypto.Ecrecover(hash, sig); err == nil {
@ -1081,8 +1142,10 @@ func makeRecoverableSignature(hash, sig, expectedPubkey []byte) ([]byte, error)
libraryError = err libraryError = err
} }
} }
if libraryError != nil { if libraryError != nil {
return nil, libraryError return nil, libraryError
} }
return nil, ErrPubkeyMismatch return nil, ErrPubkeyMismatch
} }

View file

@ -46,6 +46,7 @@ func parseURL(url string) (URL, error) {
if len(parts) != 2 || parts[0] == "" { if len(parts) != 2 || parts[0] == "" {
return URL{}, errors.New("protocol scheme missing") return URL{}, errors.New("protocol scheme missing")
} }
return URL{ return URL{
Scheme: parts[0], Scheme: parts[0],
Path: parts[1], Path: parts[1],
@ -57,6 +58,7 @@ func (u URL) String() string {
if u.Scheme != "" { if u.Scheme != "" {
return fmt.Sprintf("%s://%s", u.Scheme, u.Path) return fmt.Sprintf("%s://%s", u.Scheme, u.Path)
} }
return u.Path return u.Path
} }
@ -66,6 +68,7 @@ func (u URL) TerminalString() string {
if len(url) > 32 { if len(url) > 32 {
return url[:31] + ".." return url[:31] + ".."
} }
return url return url
} }
@ -77,16 +80,20 @@ func (u URL) MarshalJSON() ([]byte, error) {
// UnmarshalJSON parses url. // UnmarshalJSON parses url.
func (u *URL) UnmarshalJSON(input []byte) error { func (u *URL) UnmarshalJSON(input []byte) error {
var textURL string var textURL string
err := json.Unmarshal(input, &textURL) err := json.Unmarshal(input, &textURL)
if err != nil { if err != nil {
return err return err
} }
url, err := parseURL(textURL) url, err := parseURL(textURL)
if err != nil { if err != nil {
return err return err
} }
u.Scheme = url.Scheme u.Scheme = url.Scheme
u.Path = url.Path u.Path = url.Path
return nil return nil
} }
@ -99,5 +106,6 @@ func (u URL) Cmp(url URL) int {
if u.Scheme == url.Scheme { if u.Scheme == url.Scheme {
return strings.Compare(u.Path, url.Path) return strings.Compare(u.Path, url.Path)
} }
return strings.Compare(u.Scheme, url.Scheme) return strings.Compare(u.Scheme, url.Scheme)
} }

View file

@ -25,9 +25,11 @@ func TestURLParsing(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
if url.Scheme != "https" { if url.Scheme != "https" {
t.Errorf("expected: %v, got: %v", "https", url.Scheme) t.Errorf("expected: %v, got: %v", "https", url.Scheme)
} }
if url.Path != "ethereum.org" { if url.Path != "ethereum.org" {
t.Errorf("expected: %v, got: %v", "ethereum.org", url.Path) t.Errorf("expected: %v, got: %v", "ethereum.org", url.Path)
} }
@ -53,10 +55,12 @@ func TestURLString(t *testing.T) {
func TestURLMarshalJSON(t *testing.T) { func TestURLMarshalJSON(t *testing.T) {
url := URL{Scheme: "https", Path: "ethereum.org"} url := URL{Scheme: "https", Path: "ethereum.org"}
json, err := url.MarshalJSON() json, err := url.MarshalJSON()
if err != nil { if err != nil {
t.Errorf("unexpcted error: %v", err) t.Errorf("unexpcted error: %v", err)
} }
if string(json) != "\"https://ethereum.org\"" { if string(json) != "\"https://ethereum.org\"" {
t.Errorf("expected: %v, got: %v", "\"https://ethereum.org\"", string(json)) t.Errorf("expected: %v, got: %v", "\"https://ethereum.org\"", string(json))
} }
@ -64,13 +68,16 @@ func TestURLMarshalJSON(t *testing.T) {
func TestURLUnmarshalJSON(t *testing.T) { func TestURLUnmarshalJSON(t *testing.T) {
url := &URL{} url := &URL{}
err := url.UnmarshalJSON([]byte("\"https://ethereum.org\"")) err := url.UnmarshalJSON([]byte("\"https://ethereum.org\""))
if err != nil { if err != nil {
t.Errorf("unexpcted error: %v", err) t.Errorf("unexpcted error: %v", err)
} }
if url.Scheme != "https" { if url.Scheme != "https" {
t.Errorf("expected: %v, got: %v", "https", url.Scheme) t.Errorf("expected: %v, got: %v", "https", url.Scheme)
} }
if url.Path != "ethereum.org" { if url.Path != "ethereum.org" {
t.Errorf("expected: %v, got: %v", "https", url.Path) t.Errorf("expected: %v, got: %v", "https", url.Path)
} }

View file

@ -112,6 +112,7 @@ func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16,
if !usb.Supported() { if !usb.Supported() {
return nil, errors.New("unsupported platform") return nil, errors.New("unsupported platform")
} }
hub := &Hub{ hub := &Hub{
scheme: scheme, scheme: scheme,
vendorID: vendorID, vendorID: vendorID,
@ -122,6 +123,7 @@ func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16,
quit: make(chan chan error), quit: make(chan chan error),
} }
hub.refreshWallets() hub.refreshWallets()
return hub, nil return hub, nil
} }
@ -136,6 +138,7 @@ func (hub *Hub) Wallets() []accounts.Wallet {
cpy := make([]accounts.Wallet, len(hub.wallets)) cpy := make([]accounts.Wallet, len(hub.wallets))
copy(cpy, hub.wallets) copy(cpy, hub.wallets)
return cpy return cpy
} }
@ -170,17 +173,22 @@ func (hub *Hub) refreshWallets() {
return return
} }
} }
infos, err := usb.Enumerate(hub.vendorID, 0) infos, err := usb.Enumerate(hub.vendorID, 0)
if err != nil { if err != nil {
failcount := atomic.AddUint32(&hub.enumFails, 1) failcount := atomic.AddUint32(&hub.enumFails, 1)
if runtime.GOOS == "linux" { if runtime.GOOS == "linux" {
// See rationale before the enumeration why this is needed and only on Linux. // See rationale before the enumeration why this is needed and only on Linux.
hub.commsLock.Unlock() hub.commsLock.Unlock()
} }
log.Error("Failed to enumerate USB devices", "hub", hub.scheme, log.Error("Failed to enumerate USB devices", "hub", hub.scheme,
"vendor", hub.vendorID, "failcount", failcount, "err", err) "vendor", hub.vendorID, "failcount", failcount, "err", err)
return return
} }
atomic.StoreUint32(&hub.enumFails, 0) atomic.StoreUint32(&hub.enumFails, 0)
for _, info := range infos { for _, info := range infos {
@ -192,6 +200,7 @@ func (hub *Hub) refreshWallets() {
} }
} }
} }
if runtime.GOOS == "linux" { if runtime.GOOS == "linux" {
// See rationale before the enumeration why this is needed and only on Linux. // See rationale before the enumeration why this is needed and only on Linux.
hub.commsLock.Unlock() hub.commsLock.Unlock()
@ -225,12 +234,14 @@ func (hub *Hub) refreshWallets() {
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived}) events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
wallets = append(wallets, wallet) wallets = append(wallets, wallet)
continue continue
} }
// If the device is the same as the first wallet, keep it // If the device is the same as the first wallet, keep it
if hub.wallets[0].URL().Cmp(url) == 0 { if hub.wallets[0].URL().Cmp(url) == 0 {
wallets = append(wallets, hub.wallets[0]) wallets = append(wallets, hub.wallets[0])
hub.wallets = hub.wallets[1:] hub.wallets = hub.wallets[1:]
continue continue
} }
} }
@ -238,6 +249,7 @@ func (hub *Hub) refreshWallets() {
for _, wallet := range hub.wallets { for _, wallet := range hub.wallets {
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped}) events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
} }
hub.refreshed = time.Now() hub.refreshed = time.Now()
hub.wallets = wallets hub.wallets = wallets
hub.stateLock.Unlock() hub.stateLock.Unlock()
@ -263,6 +275,7 @@ func (hub *Hub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
hub.updating = true hub.updating = true
go hub.updater() go hub.updater()
} }
return sub return sub
} }
@ -282,6 +295,7 @@ func (hub *Hub) updater() {
if hub.updateScope.Count() == 0 { if hub.updateScope.Count() == 0 {
hub.updating = false hub.updating = false
hub.stateLock.Unlock() hub.stateLock.Unlock()
return return
} }
hub.stateLock.Unlock() hub.stateLock.Unlock()

View file

@ -94,12 +94,15 @@ func (w *ledgerDriver) Status() (string, error) {
if w.failure != nil { if w.failure != nil {
return fmt.Sprintf("Failed: %v", w.failure), w.failure return fmt.Sprintf("Failed: %v", w.failure), w.failure
} }
if w.browser { if w.browser {
return "Ethereum app in browser mode", w.failure return "Ethereum app in browser mode", w.failure
} }
if w.offline() { if w.offline() {
return "Ethereum app offline", w.failure 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 return fmt.Sprintf("Ethereum app v%d.%d.%d online", w.version[0], w.version[1], w.version[2]), w.failure
} }
@ -122,12 +125,14 @@ func (w *ledgerDriver) Open(device io.ReadWriter, passphrase string) error {
if err == errLedgerReplyInvalidHeader { if err == errLedgerReplyInvalidHeader {
w.browser = true w.browser = true
} }
return nil return nil
} }
// Try to resolve the Ethereum app's version, will fail prior to v1.0.2 // Try to resolve the Ethereum app's version, will fail prior to v1.0.2
if w.version, err = w.ledgerVersion(); err != nil { 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 w.version = [3]byte{1, 0, 0} // Assume worst case, can't verify if v1.0.0 or v1.0.1
} }
return nil return nil
} }
@ -145,6 +150,7 @@ func (w *ledgerDriver) Heartbeat() error {
w.failure = err w.failure = err
return err return err
} }
return nil return nil
} }
@ -215,12 +221,15 @@ func (w *ledgerDriver) ledgerVersion() ([3]byte, error) {
if err != nil { if err != nil {
return [3]byte{}, err return [3]byte{}, err
} }
if len(reply) != 4 { if len(reply) != 4 {
return [3]byte{}, errLedgerInvalidVersionReply return [3]byte{}, errLedgerInvalidVersionReply
} }
// Cache the version for future reference // Cache the version for future reference
var version [3]byte var version [3]byte
copy(version[:], reply[1:]) copy(version[:], reply[1:])
return version, nil return version, nil
} }
@ -259,6 +268,7 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
// Flatten the derivation path into the Ledger request // Flatten the derivation path into the Ledger request
path := make([]byte, 1+4*len(derivationPath)) path := make([]byte, 1+4*len(derivationPath))
path[0] = byte(len(derivationPath)) path[0] = byte(len(derivationPath))
for i, component := range derivationPath { for i, component := range derivationPath {
binary.BigEndian.PutUint32(path[1+4*i:], component) binary.BigEndian.PutUint32(path[1+4*i:], component)
} }
@ -271,12 +281,14 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
if len(reply) < 1 || len(reply) < 1+int(reply[0]) { if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
return common.Address{}, errors.New("reply lacks public key entry") return common.Address{}, errors.New("reply lacks public key entry")
} }
reply = reply[1+int(reply[0]):] reply = reply[1+int(reply[0]):]
// Extract the Ethereum hex address string // Extract the Ethereum hex address string
if len(reply) < 1 || len(reply) < 1+int(reply[0]) { if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
return common.Address{}, errors.New("reply lacks address entry") return common.Address{}, errors.New("reply lacks address entry")
} }
hexstr := reply[1 : 1+int(reply[0])] hexstr := reply[1 : 1+int(reply[0])]
// Decode the hex sting into an Ethereum address and return // Decode the hex sting into an Ethereum address and return
@ -284,6 +296,7 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
if _, err = hex.Decode(address[:], hexstr); err != nil { if _, err = hex.Decode(address[:], hexstr); err != nil {
return common.Address{}, err return common.Address{}, err
} }
return address, nil return address, nil
} }
@ -325,6 +338,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
// Flatten the derivation path into the Ledger request // Flatten the derivation path into the Ledger request
path := make([]byte, 1+4*len(derivationPath)) path := make([]byte, 1+4*len(derivationPath))
path[0] = byte(len(derivationPath)) path[0] = byte(len(derivationPath))
for i, component := range derivationPath { for i, component := range derivationPath {
binary.BigEndian.PutUint32(path[1+4*i:], component) binary.BigEndian.PutUint32(path[1+4*i:], component)
} }
@ -333,6 +347,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
txrlp []byte txrlp []byte
err error err error
) )
if chainID == nil { if chainID == nil {
if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data()}); err != 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 return common.Address{}, nil, err
@ -342,6 +357,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
return common.Address{}, nil, err return common.Address{}, nil, err
} }
} }
payload := append(path, txrlp...) payload := append(path, txrlp...)
// Send the request and wait for the response // Send the request and wait for the response
@ -374,6 +390,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
if len(reply) != crypto.SignatureLength { if len(reply) != crypto.SignatureLength {
return common.Address{}, nil, errors.New("reply lacks signature") return common.Address{}, nil, errors.New("reply lacks signature")
} }
signature := append(reply[1:], reply[0]) signature := append(reply[1:], reply[0])
// Create the correct signer and signature transform based on the chain ID // Create the correct signer and signature transform based on the chain ID
@ -384,14 +401,17 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
signer = types.NewEIP155Signer(chainID) signer = types.NewEIP155Signer(chainID)
signature[64] -= byte(chainID.Uint64()*2 + 35) signature[64] -= byte(chainID.Uint64()*2 + 35)
} }
signed, err := tx.WithSignature(signer, signature) signed, err := tx.WithSignature(signer, signature)
if err != nil { if err != nil {
return common.Address{}, nil, err return common.Address{}, nil, err
} }
sender, err := types.Sender(signer, signed) sender, err := types.Sender(signer, signed)
if err != nil { if err != nil {
return common.Address{}, nil, err return common.Address{}, nil, err
} }
return sender, signed, nil return sender, signed, nil
} }
@ -426,6 +446,7 @@ func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHas
// Flatten the derivation path into the Ledger request // Flatten the derivation path into the Ledger request
path := make([]byte, 1+4*len(derivationPath)) path := make([]byte, 1+4*len(derivationPath))
path[0] = byte(len(derivationPath)) path[0] = byte(len(derivationPath))
for i, component := range derivationPath { for i, component := range derivationPath {
binary.BigEndian.PutUint32(path[1+4*i:], component) binary.BigEndian.PutUint32(path[1+4*i:], component)
} }
@ -451,7 +472,9 @@ func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHas
if len(reply) != crypto.SignatureLength { if len(reply) != crypto.SignatureLength {
return nil, errors.New("reply lacks signature") return nil, errors.New("reply lacks signature")
} }
signature := append(reply[1:], reply[0]) signature := append(reply[1:], reply[0])
return signature, nil return signature, nil
} }
@ -515,18 +538,22 @@ func (w *ledgerDriver) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 l
} }
// Send over to the device // Send over to the device
w.log.Trace("Data chunk sent to the Ledger", "chunk", hexutil.Bytes(chunk)) w.log.Trace("Data chunk sent to the Ledger", "chunk", hexutil.Bytes(chunk))
if _, err := w.device.Write(chunk); err != nil { if _, err := w.device.Write(chunk); err != nil {
return nil, err return nil, err
} }
} }
// Stream the reply back from the wallet in 64 byte chunks // Stream the reply back from the wallet in 64 byte chunks
var reply []byte var reply []byte
chunk = chunk[:64] // Yeah, we surely have enough space chunk = chunk[:64] // Yeah, we surely have enough space
for { for {
// Read the next chunk from the Ledger wallet // Read the next chunk from the Ledger wallet
if _, err := io.ReadFull(w.device, chunk); err != nil { if _, err := io.ReadFull(w.device, chunk); err != nil {
return nil, err return nil, err
} }
w.log.Trace("Data chunk received from the Ledger", "chunk", hexutil.Bytes(chunk)) w.log.Trace("Data chunk received from the Ledger", "chunk", hexutil.Bytes(chunk))
// Make sure the transport header matches // Make sure the transport header matches
@ -550,5 +577,6 @@ func (w *ledgerDriver) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 l
break break
} }
} }
return reply[:len(reply)-2], nil return reply[:len(reply)-2], nil
} }

View file

@ -73,12 +73,15 @@ func (w *trezorDriver) Status() (string, error) {
if w.failure != nil { if w.failure != nil {
return fmt.Sprintf("Failed: %v", w.failure), w.failure return fmt.Sprintf("Failed: %v", w.failure), w.failure
} }
if w.device == nil { if w.device == nil {
return "Closed", w.failure return "Closed", w.failure
} }
if w.pinwait { 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' 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 return fmt.Sprintf("Trezor v%d.%d.%d '%s' online", w.version[0], w.version[1], w.version[2], w.label), w.failure
} }
@ -107,12 +110,14 @@ func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
if _, err := w.trezorExchange(&trezor.Initialize{}, features); err != nil { if _, err := w.trezorExchange(&trezor.Initialize{}, features); err != nil {
return err return err
} }
w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()} w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
w.label = features.GetLabel() w.label = features.GetLabel()
// Do a manual ping, forcing the device to ask for its PIN and Passphrase // Do a manual ping, forcing the device to ask for its PIN and Passphrase
askPin := true askPin := true
askPassphrase := true askPassphrase := true
res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin, PassphraseProtection: &askPassphrase}, new(trezor.PinMatrixRequest), new(trezor.PassphraseRequest), new(trezor.Success)) res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin, PassphraseProtection: &askPassphrase}, new(trezor.PinMatrixRequest), new(trezor.PassphraseRequest), new(trezor.Success))
if err != nil { if err != nil {
return err return err
@ -125,6 +130,7 @@ func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
case 1: case 1:
w.pinwait = false w.pinwait = false
w.passphrasewait = true w.passphrasewait = true
return ErrTrezorPassphraseNeeded return ErrTrezorPassphraseNeeded
case 2: case 2:
return nil // responded with trezor.Success return nil // responded with trezor.Success
@ -134,10 +140,12 @@ func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
if w.pinwait { if w.pinwait {
w.pinwait = false w.pinwait = false
res, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success), new(trezor.PassphraseRequest)) res, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success), new(trezor.PassphraseRequest))
if err != nil { if err != nil {
w.failure = err w.failure = err
return err return err
} }
if res == 1 { if res == 1 {
w.passphrasewait = true w.passphrasewait = true
return ErrTrezorPassphraseNeeded return ErrTrezorPassphraseNeeded
@ -167,6 +175,7 @@ func (w *trezorDriver) Heartbeat() error {
w.failure = err w.failure = err
return err return err
} }
return nil return nil
} }
@ -182,6 +191,7 @@ func (w *trezorDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
if w.device == nil { if w.device == nil {
return common.Address{}, nil, accounts.ErrWalletClosed return common.Address{}, nil, accounts.ErrWalletClosed
} }
return w.trezorSign(path, tx, chainID) return w.trezorSign(path, tx, chainID)
} }
@ -204,6 +214,7 @@ func (w *trezorDriver) trezorDerive(derivationPath []uint32) (common.Address, er
if addr := address.GetAddressHex(); len(addr) > 0 { // Newer firmwares use hexadecimal formats if addr := address.GetAddressHex(); len(addr) > 0 { // Newer firmwares use hexadecimal formats
return common.HexToAddress(addr), nil return common.HexToAddress(addr), nil
} }
return common.Address{}, errors.New("missing derived address") return common.Address{}, errors.New("missing derived address")
} }
@ -222,17 +233,20 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
Value: tx.Value().Bytes(), Value: tx.Value().Bytes(),
DataLength: &length, DataLength: &length,
} }
if to := tx.To(); to != nil { if to := tx.To(); to != nil {
// Non contract deploy, set recipient explicitly // Non contract deploy, set recipient explicitly
hex := to.Hex() hex := to.Hex()
request.ToHex = &hex // Newer firmwares (old will ignore) request.ToHex = &hex // Newer firmwares (old will ignore)
request.ToBin = (*to)[:] // Older firmwares (new will ignore) request.ToBin = (*to)[:] // Older firmwares (new will ignore)
} }
if length > 1024 { // Send the data chunked if that was requested if length > 1024 { // Send the data chunked if that was requested
request.DataInitialChunk, data = data[:1024], data[1024:] request.DataInitialChunk, data = data[:1024], data[1024:]
} else { } else {
request.DataInitialChunk, data = data, nil request.DataInitialChunk, data = data, nil
} }
if chainID != nil { // EIP-155 transaction, set chain ID explicitly (only 32 bit is supported!?) if chainID != nil { // EIP-155 transaction, set chain ID explicitly (only 32 bit is supported!?)
id := uint32(chainID.Int64()) id := uint32(chainID.Int64())
request.ChainId = &id request.ChainId = &id
@ -242,6 +256,7 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
if _, err := w.trezorExchange(request, response); err != nil { if _, err := w.trezorExchange(request, response); err != nil {
return common.Address{}, nil, err return common.Address{}, nil, err
} }
for response.DataLength != nil && int(*response.DataLength) <= len(data) { for response.DataLength != nil && int(*response.DataLength) <= len(data) {
chunk := data[:*response.DataLength] chunk := data[:*response.DataLength]
data = data[*response.DataLength:] data = data[*response.DataLength:]
@ -254,6 +269,7 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
if len(response.GetSignatureR()) == 0 || len(response.GetSignatureS()) == 0 || response.GetSignatureV() == 0 { if len(response.GetSignatureR()) == 0 || len(response.GetSignatureS()) == 0 || response.GetSignatureV() == 0 {
return common.Address{}, nil, errors.New("reply lacks signature") return common.Address{}, nil, errors.New("reply lacks signature")
} }
signature := append(append(response.GetSignatureR(), response.GetSignatureS()...), byte(response.GetSignatureV())) signature := append(append(response.GetSignatureR(), response.GetSignatureS()...), byte(response.GetSignatureV()))
// Create the correct signer and signature transform based on the chain ID // Create the correct signer and signature transform based on the chain ID
@ -271,10 +287,12 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
if err != nil { if err != nil {
return common.Address{}, nil, err return common.Address{}, nil, err
} }
sender, err := types.Sender(signer, signed) sender, err := types.Sender(signer, signed)
if err != nil { if err != nil {
return common.Address{}, nil, err return common.Address{}, nil, err
} }
return sender, signed, nil return sender, signed, nil
} }
@ -287,6 +305,7 @@ func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Messag
if err != nil { if err != nil {
return 0, err return 0, err
} }
payload := make([]byte, 8+len(data)) payload := make([]byte, 8+len(data))
copy(payload, []byte{0x23, 0x23}) copy(payload, []byte{0x23, 0x23})
binary.BigEndian.PutUint16(payload[2:], trezor.Type(req)) binary.BigEndian.PutUint16(payload[2:], trezor.Type(req))
@ -309,6 +328,7 @@ func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Messag
} }
// Send over to the device // Send over to the device
w.log.Trace("Data chunk sent to the Trezor", "chunk", hexutil.Bytes(chunk)) w.log.Trace("Data chunk sent to the Trezor", "chunk", hexutil.Bytes(chunk))
if _, err := w.device.Write(chunk); err != nil { if _, err := w.device.Write(chunk); err != nil {
return 0, err return 0, err
} }
@ -318,11 +338,13 @@ func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Messag
kind uint16 kind uint16
reply []byte reply []byte
) )
for { for {
// Read the next chunk from the Trezor wallet // Read the next chunk from the Trezor wallet
if _, err := io.ReadFull(w.device, chunk); err != nil { if _, err := io.ReadFull(w.device, chunk); err != nil {
return 0, err return 0, err
} }
w.log.Trace("Data chunk received from the Trezor", "chunk", hexutil.Bytes(chunk)) w.log.Trace("Data chunk received from the Trezor", "chunk", hexutil.Bytes(chunk))
// Make sure the transport header matches // Make sure the transport header matches
@ -354,20 +376,25 @@ func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Messag
if err := proto.Unmarshal(reply, failure); err != nil { if err := proto.Unmarshal(reply, failure); err != nil {
return 0, err return 0, err
} }
return 0, errors.New("trezor: " + failure.GetMessage()) return 0, errors.New("trezor: " + failure.GetMessage())
} }
if kind == uint16(trezor.MessageType_MessageType_ButtonRequest) { if kind == uint16(trezor.MessageType_MessageType_ButtonRequest) {
// Trezor is waiting for user confirmation, ack and wait for the next message // Trezor is waiting for user confirmation, ack and wait for the next message
return w.trezorExchange(&trezor.ButtonAck{}, results...) return w.trezorExchange(&trezor.ButtonAck{}, results...)
} }
for i, res := range results { for i, res := range results {
if trezor.Type(res) == kind { if trezor.Type(res) == kind {
return i, proto.Unmarshal(reply, res) return i, proto.Unmarshal(reply, res)
} }
} }
expected := make([]string, len(results)) expected := make([]string, len(results))
for i, res := range results { for i, res := range results {
expected[i] = trezor.Name(trezor.Type(res)) expected[i] = trezor.Name(trezor.Type(res))
} }
return 0, fmt.Errorf("trezor: expected reply types %s, got %s", expected, trezor.Name(kind)) return 0, fmt.Errorf("trezor: expected reply types %s, got %s", expected, trezor.Name(kind))
} }

View file

@ -74,6 +74,7 @@ var Failure_FailureType_value = map[string]int32{
func (x Failure_FailureType) Enum() *Failure_FailureType { func (x Failure_FailureType) Enum() *Failure_FailureType {
p := new(Failure_FailureType) p := new(Failure_FailureType)
*p = x *p = x
return p return p
} }
@ -86,7 +87,9 @@ func (x *Failure_FailureType) UnmarshalJSON(data []byte) error {
if err != nil { if err != nil {
return err return err
} }
*x = Failure_FailureType(value) *x = Failure_FailureType(value)
return nil return nil
} }
@ -155,6 +158,7 @@ var ButtonRequest_ButtonRequestType_value = map[string]int32{
func (x ButtonRequest_ButtonRequestType) Enum() *ButtonRequest_ButtonRequestType { func (x ButtonRequest_ButtonRequestType) Enum() *ButtonRequest_ButtonRequestType {
p := new(ButtonRequest_ButtonRequestType) p := new(ButtonRequest_ButtonRequestType)
*p = x *p = x
return p return p
} }
@ -167,7 +171,9 @@ func (x *ButtonRequest_ButtonRequestType) UnmarshalJSON(data []byte) error {
if err != nil { if err != nil {
return err return err
} }
*x = ButtonRequest_ButtonRequestType(value) *x = ButtonRequest_ButtonRequestType(value)
return nil return nil
} }
@ -200,6 +206,7 @@ var PinMatrixRequest_PinMatrixRequestType_value = map[string]int32{
func (x PinMatrixRequest_PinMatrixRequestType) Enum() *PinMatrixRequest_PinMatrixRequestType { func (x PinMatrixRequest_PinMatrixRequestType) Enum() *PinMatrixRequest_PinMatrixRequestType {
p := new(PinMatrixRequest_PinMatrixRequestType) p := new(PinMatrixRequest_PinMatrixRequestType)
*p = x *p = x
return p return p
} }
@ -212,7 +219,9 @@ func (x *PinMatrixRequest_PinMatrixRequestType) UnmarshalJSON(data []byte) error
if err != nil { if err != nil {
return err return err
} }
*x = PinMatrixRequest_PinMatrixRequestType(value) *x = PinMatrixRequest_PinMatrixRequestType(value)
return nil return nil
} }
@ -259,6 +268,7 @@ func (m *Success) GetMessage() string {
if m != nil && m.Message != nil { if m != nil && m.Message != nil {
return *m.Message return *m.Message
} }
return "" return ""
} }
@ -302,6 +312,7 @@ func (m *Failure) GetCode() Failure_FailureType {
if m != nil && m.Code != nil { if m != nil && m.Code != nil {
return *m.Code return *m.Code
} }
return Failure_Failure_UnexpectedMessage return Failure_Failure_UnexpectedMessage
} }
@ -309,6 +320,7 @@ func (m *Failure) GetMessage() string {
if m != nil && m.Message != nil { if m != nil && m.Message != nil {
return *m.Message return *m.Message
} }
return "" return ""
} }
@ -353,6 +365,7 @@ func (m *ButtonRequest) GetCode() ButtonRequest_ButtonRequestType {
if m != nil && m.Code != nil { if m != nil && m.Code != nil {
return *m.Code return *m.Code
} }
return ButtonRequest_ButtonRequest_Other return ButtonRequest_ButtonRequest_Other
} }
@ -360,6 +373,7 @@ func (m *ButtonRequest) GetData() string {
if m != nil && m.Data != nil { if m != nil && m.Data != nil {
return *m.Data return *m.Data
} }
return "" return ""
} }
@ -437,6 +451,7 @@ func (m *PinMatrixRequest) GetType() PinMatrixRequest_PinMatrixRequestType {
if m != nil && m.Type != nil { if m != nil && m.Type != nil {
return *m.Type return *m.Type
} }
return PinMatrixRequest_PinMatrixRequestType_Current return PinMatrixRequest_PinMatrixRequestType_Current
} }
@ -479,6 +494,7 @@ func (m *PinMatrixAck) GetPin() string {
if m != nil && m.Pin != nil { if m != nil && m.Pin != nil {
return *m.Pin return *m.Pin
} }
return "" return ""
} }
@ -522,6 +538,7 @@ func (m *PassphraseRequest) GetOnDevice() bool {
if m != nil && m.OnDevice != nil { if m != nil && m.OnDevice != nil {
return *m.OnDevice return *m.OnDevice
} }
return false return false
} }
@ -565,6 +582,7 @@ func (m *PassphraseAck) GetPassphrase() string {
if m != nil && m.Passphrase != nil { if m != nil && m.Passphrase != nil {
return *m.Passphrase return *m.Passphrase
} }
return "" return ""
} }
@ -572,6 +590,7 @@ func (m *PassphraseAck) GetState() []byte {
if m != nil { if m != nil {
return m.State return m.State
} }
return nil return nil
} }
@ -614,6 +633,7 @@ func (m *PassphraseStateRequest) GetState() []byte {
if m != nil { if m != nil {
return m.State return m.State
} }
return nil return nil
} }
@ -696,6 +716,7 @@ func (m *HDNodeType) GetDepth() uint32 {
if m != nil && m.Depth != nil { if m != nil && m.Depth != nil {
return *m.Depth return *m.Depth
} }
return 0 return 0
} }
@ -703,6 +724,7 @@ func (m *HDNodeType) GetFingerprint() uint32 {
if m != nil && m.Fingerprint != nil { if m != nil && m.Fingerprint != nil {
return *m.Fingerprint return *m.Fingerprint
} }
return 0 return 0
} }
@ -710,6 +732,7 @@ func (m *HDNodeType) GetChildNum() uint32 {
if m != nil && m.ChildNum != nil { if m != nil && m.ChildNum != nil {
return *m.ChildNum return *m.ChildNum
} }
return 0 return 0
} }
@ -717,6 +740,7 @@ func (m *HDNodeType) GetChainCode() []byte {
if m != nil { if m != nil {
return m.ChainCode return m.ChainCode
} }
return nil return nil
} }
@ -724,6 +748,7 @@ func (m *HDNodeType) GetPrivateKey() []byte {
if m != nil { if m != nil {
return m.PrivateKey return m.PrivateKey
} }
return nil return nil
} }
@ -731,6 +756,7 @@ func (m *HDNodeType) GetPublicKey() []byte {
if m != nil { if m != nil {
return m.PublicKey return m.PublicKey
} }
return nil return nil
} }

View file

@ -63,6 +63,7 @@ func (m *EthereumGetPublicKey) GetAddressN() []uint32 {
if m != nil { if m != nil {
return m.AddressN return m.AddressN
} }
return nil return nil
} }
@ -70,6 +71,7 @@ func (m *EthereumGetPublicKey) GetShowDisplay() bool {
if m != nil && m.ShowDisplay != nil { if m != nil && m.ShowDisplay != nil {
return *m.ShowDisplay return *m.ShowDisplay
} }
return false return false
} }
@ -113,6 +115,7 @@ func (m *EthereumPublicKey) GetNode() *HDNodeType {
if m != nil { if m != nil {
return m.Node return m.Node
} }
return nil return nil
} }
@ -120,6 +123,7 @@ func (m *EthereumPublicKey) GetXpub() string {
if m != nil && m.Xpub != nil { if m != nil && m.Xpub != nil {
return *m.Xpub return *m.Xpub
} }
return "" return ""
} }
@ -165,6 +169,7 @@ func (m *EthereumGetAddress) GetAddressN() []uint32 {
if m != nil { if m != nil {
return m.AddressN return m.AddressN
} }
return nil return nil
} }
@ -172,6 +177,7 @@ func (m *EthereumGetAddress) GetShowDisplay() bool {
if m != nil && m.ShowDisplay != nil { if m != nil && m.ShowDisplay != nil {
return *m.ShowDisplay return *m.ShowDisplay
} }
return false return false
} }
@ -215,6 +221,7 @@ func (m *EthereumAddress) GetAddressBin() []byte {
if m != nil { if m != nil {
return m.AddressBin return m.AddressBin
} }
return nil return nil
} }
@ -222,6 +229,7 @@ func (m *EthereumAddress) GetAddressHex() string {
if m != nil && m.AddressHex != nil { if m != nil && m.AddressHex != nil {
return *m.AddressHex return *m.AddressHex
} }
return "" return ""
} }
@ -278,6 +286,7 @@ func (m *EthereumSignTx) GetAddressN() []uint32 {
if m != nil { if m != nil {
return m.AddressN return m.AddressN
} }
return nil return nil
} }
@ -285,6 +294,7 @@ func (m *EthereumSignTx) GetNonce() []byte {
if m != nil { if m != nil {
return m.Nonce return m.Nonce
} }
return nil return nil
} }
@ -292,6 +302,7 @@ func (m *EthereumSignTx) GetGasPrice() []byte {
if m != nil { if m != nil {
return m.GasPrice return m.GasPrice
} }
return nil return nil
} }
@ -299,6 +310,7 @@ func (m *EthereumSignTx) GetGasLimit() []byte {
if m != nil { if m != nil {
return m.GasLimit return m.GasLimit
} }
return nil return nil
} }
@ -306,6 +318,7 @@ func (m *EthereumSignTx) GetToBin() []byte {
if m != nil { if m != nil {
return m.ToBin return m.ToBin
} }
return nil return nil
} }
@ -313,6 +326,7 @@ func (m *EthereumSignTx) GetToHex() string {
if m != nil && m.ToHex != nil { if m != nil && m.ToHex != nil {
return *m.ToHex return *m.ToHex
} }
return "" return ""
} }
@ -320,6 +334,7 @@ func (m *EthereumSignTx) GetValue() []byte {
if m != nil { if m != nil {
return m.Value return m.Value
} }
return nil return nil
} }
@ -327,6 +342,7 @@ func (m *EthereumSignTx) GetDataInitialChunk() []byte {
if m != nil { if m != nil {
return m.DataInitialChunk return m.DataInitialChunk
} }
return nil return nil
} }
@ -334,6 +350,7 @@ func (m *EthereumSignTx) GetDataLength() uint32 {
if m != nil && m.DataLength != nil { if m != nil && m.DataLength != nil {
return *m.DataLength return *m.DataLength
} }
return 0 return 0
} }
@ -341,6 +358,7 @@ func (m *EthereumSignTx) GetChainId() uint32 {
if m != nil && m.ChainId != nil { if m != nil && m.ChainId != nil {
return *m.ChainId return *m.ChainId
} }
return 0 return 0
} }
@ -348,6 +366,7 @@ func (m *EthereumSignTx) GetTxType() uint32 {
if m != nil && m.TxType != nil { if m != nil && m.TxType != nil {
return *m.TxType return *m.TxType
} }
return 0 return 0
} }
@ -396,6 +415,7 @@ func (m *EthereumTxRequest) GetDataLength() uint32 {
if m != nil && m.DataLength != nil { if m != nil && m.DataLength != nil {
return *m.DataLength return *m.DataLength
} }
return 0 return 0
} }
@ -403,6 +423,7 @@ func (m *EthereumTxRequest) GetSignatureV() uint32 {
if m != nil && m.SignatureV != nil { if m != nil && m.SignatureV != nil {
return *m.SignatureV return *m.SignatureV
} }
return 0 return 0
} }
@ -410,6 +431,7 @@ func (m *EthereumTxRequest) GetSignatureR() []byte {
if m != nil { if m != nil {
return m.SignatureR return m.SignatureR
} }
return nil return nil
} }
@ -417,6 +439,7 @@ func (m *EthereumTxRequest) GetSignatureS() []byte {
if m != nil { if m != nil {
return m.SignatureS return m.SignatureS
} }
return nil return nil
} }
@ -459,6 +482,7 @@ func (m *EthereumTxAck) GetDataChunk() []byte {
if m != nil { if m != nil {
return m.DataChunk return m.DataChunk
} }
return nil return nil
} }
@ -504,6 +528,7 @@ func (m *EthereumSignMessage) GetAddressN() []uint32 {
if m != nil { if m != nil {
return m.AddressN return m.AddressN
} }
return nil return nil
} }
@ -511,6 +536,7 @@ func (m *EthereumSignMessage) GetMessage() []byte {
if m != nil { if m != nil {
return m.Message return m.Message
} }
return nil return nil
} }
@ -555,6 +581,7 @@ func (m *EthereumMessageSignature) GetAddressBin() []byte {
if m != nil { if m != nil {
return m.AddressBin return m.AddressBin
} }
return nil return nil
} }
@ -562,6 +589,7 @@ func (m *EthereumMessageSignature) GetSignature() []byte {
if m != nil { if m != nil {
return m.Signature return m.Signature
} }
return nil return nil
} }
@ -569,6 +597,7 @@ func (m *EthereumMessageSignature) GetAddressHex() string {
if m != nil && m.AddressHex != nil { if m != nil && m.AddressHex != nil {
return *m.AddressHex return *m.AddressHex
} }
return "" return ""
} }
@ -616,6 +645,7 @@ func (m *EthereumVerifyMessage) GetAddressBin() []byte {
if m != nil { if m != nil {
return m.AddressBin return m.AddressBin
} }
return nil return nil
} }
@ -623,6 +653,7 @@ func (m *EthereumVerifyMessage) GetSignature() []byte {
if m != nil { if m != nil {
return m.Signature return m.Signature
} }
return nil return nil
} }
@ -630,6 +661,7 @@ func (m *EthereumVerifyMessage) GetMessage() []byte {
if m != nil { if m != nil {
return m.Message return m.Message
} }
return nil return nil
} }
@ -637,6 +669,7 @@ func (m *EthereumVerifyMessage) GetAddressHex() string {
if m != nil && m.AddressHex != nil { if m != nil && m.AddressHex != nil {
return *m.AddressHex return *m.AddressHex
} }
return "" return ""
} }

View file

@ -46,6 +46,7 @@ var ApplySettings_PassphraseSourceType_value = map[string]int32{
func (x ApplySettings_PassphraseSourceType) Enum() *ApplySettings_PassphraseSourceType { func (x ApplySettings_PassphraseSourceType) Enum() *ApplySettings_PassphraseSourceType {
p := new(ApplySettings_PassphraseSourceType) p := new(ApplySettings_PassphraseSourceType)
*p = x *p = x
return p return p
} }
@ -58,7 +59,9 @@ func (x *ApplySettings_PassphraseSourceType) UnmarshalJSON(data []byte) error {
if err != nil { if err != nil {
return err return err
} }
*x = ApplySettings_PassphraseSourceType(value) *x = ApplySettings_PassphraseSourceType(value)
return nil return nil
} }
@ -94,6 +97,7 @@ var RecoveryDevice_RecoveryDeviceType_value = map[string]int32{
func (x RecoveryDevice_RecoveryDeviceType) Enum() *RecoveryDevice_RecoveryDeviceType { func (x RecoveryDevice_RecoveryDeviceType) Enum() *RecoveryDevice_RecoveryDeviceType {
p := new(RecoveryDevice_RecoveryDeviceType) p := new(RecoveryDevice_RecoveryDeviceType)
*p = x *p = x
return p return p
} }
@ -106,7 +110,9 @@ func (x *RecoveryDevice_RecoveryDeviceType) UnmarshalJSON(data []byte) error {
if err != nil { if err != nil {
return err return err
} }
*x = RecoveryDevice_RecoveryDeviceType(value) *x = RecoveryDevice_RecoveryDeviceType(value)
return nil return nil
} }
@ -139,6 +145,7 @@ var WordRequest_WordRequestType_value = map[string]int32{
func (x WordRequest_WordRequestType) Enum() *WordRequest_WordRequestType { func (x WordRequest_WordRequestType) Enum() *WordRequest_WordRequestType {
p := new(WordRequest_WordRequestType) p := new(WordRequest_WordRequestType)
*p = x *p = x
return p return p
} }
@ -151,7 +158,9 @@ func (x *WordRequest_WordRequestType) UnmarshalJSON(data []byte) error {
if err != nil { if err != nil {
return err return err
} }
*x = WordRequest_WordRequestType(value) *x = WordRequest_WordRequestType(value)
return nil return nil
} }
@ -200,6 +209,7 @@ func (m *Initialize) GetState() []byte {
if m != nil { if m != nil {
return m.State return m.State
} }
return nil return nil
} }
@ -207,6 +217,7 @@ func (m *Initialize) GetSkipPassphrase() bool {
if m != nil && m.SkipPassphrase != nil { if m != nil && m.SkipPassphrase != nil {
return *m.SkipPassphrase return *m.SkipPassphrase
} }
return false return false
} }
@ -310,6 +321,7 @@ func (m *Features) GetVendor() string {
if m != nil && m.Vendor != nil { if m != nil && m.Vendor != nil {
return *m.Vendor return *m.Vendor
} }
return "" return ""
} }
@ -317,6 +329,7 @@ func (m *Features) GetMajorVersion() uint32 {
if m != nil && m.MajorVersion != nil { if m != nil && m.MajorVersion != nil {
return *m.MajorVersion return *m.MajorVersion
} }
return 0 return 0
} }
@ -324,6 +337,7 @@ func (m *Features) GetMinorVersion() uint32 {
if m != nil && m.MinorVersion != nil { if m != nil && m.MinorVersion != nil {
return *m.MinorVersion return *m.MinorVersion
} }
return 0 return 0
} }
@ -331,6 +345,7 @@ func (m *Features) GetPatchVersion() uint32 {
if m != nil && m.PatchVersion != nil { if m != nil && m.PatchVersion != nil {
return *m.PatchVersion return *m.PatchVersion
} }
return 0 return 0
} }
@ -338,6 +353,7 @@ func (m *Features) GetBootloaderMode() bool {
if m != nil && m.BootloaderMode != nil { if m != nil && m.BootloaderMode != nil {
return *m.BootloaderMode return *m.BootloaderMode
} }
return false return false
} }
@ -345,6 +361,7 @@ func (m *Features) GetDeviceId() string {
if m != nil && m.DeviceId != nil { if m != nil && m.DeviceId != nil {
return *m.DeviceId return *m.DeviceId
} }
return "" return ""
} }
@ -352,6 +369,7 @@ func (m *Features) GetPinProtection() bool {
if m != nil && m.PinProtection != nil { if m != nil && m.PinProtection != nil {
return *m.PinProtection return *m.PinProtection
} }
return false return false
} }
@ -359,6 +377,7 @@ func (m *Features) GetPassphraseProtection() bool {
if m != nil && m.PassphraseProtection != nil { if m != nil && m.PassphraseProtection != nil {
return *m.PassphraseProtection return *m.PassphraseProtection
} }
return false return false
} }
@ -366,6 +385,7 @@ func (m *Features) GetLanguage() string {
if m != nil && m.Language != nil { if m != nil && m.Language != nil {
return *m.Language return *m.Language
} }
return "" return ""
} }
@ -373,6 +393,7 @@ func (m *Features) GetLabel() string {
if m != nil && m.Label != nil { if m != nil && m.Label != nil {
return *m.Label return *m.Label
} }
return "" return ""
} }
@ -380,6 +401,7 @@ func (m *Features) GetInitialized() bool {
if m != nil && m.Initialized != nil { if m != nil && m.Initialized != nil {
return *m.Initialized return *m.Initialized
} }
return false return false
} }
@ -387,6 +409,7 @@ func (m *Features) GetRevision() []byte {
if m != nil { if m != nil {
return m.Revision return m.Revision
} }
return nil return nil
} }
@ -394,6 +417,7 @@ func (m *Features) GetBootloaderHash() []byte {
if m != nil { if m != nil {
return m.BootloaderHash return m.BootloaderHash
} }
return nil return nil
} }
@ -401,6 +425,7 @@ func (m *Features) GetImported() bool {
if m != nil && m.Imported != nil { if m != nil && m.Imported != nil {
return *m.Imported return *m.Imported
} }
return false return false
} }
@ -408,6 +433,7 @@ func (m *Features) GetPinCached() bool {
if m != nil && m.PinCached != nil { if m != nil && m.PinCached != nil {
return *m.PinCached return *m.PinCached
} }
return false return false
} }
@ -415,6 +441,7 @@ func (m *Features) GetPassphraseCached() bool {
if m != nil && m.PassphraseCached != nil { if m != nil && m.PassphraseCached != nil {
return *m.PassphraseCached return *m.PassphraseCached
} }
return false return false
} }
@ -422,6 +449,7 @@ func (m *Features) GetFirmwarePresent() bool {
if m != nil && m.FirmwarePresent != nil { if m != nil && m.FirmwarePresent != nil {
return *m.FirmwarePresent return *m.FirmwarePresent
} }
return false return false
} }
@ -429,6 +457,7 @@ func (m *Features) GetNeedsBackup() bool {
if m != nil && m.NeedsBackup != nil { if m != nil && m.NeedsBackup != nil {
return *m.NeedsBackup return *m.NeedsBackup
} }
return false return false
} }
@ -436,6 +465,7 @@ func (m *Features) GetFlags() uint32 {
if m != nil && m.Flags != nil { if m != nil && m.Flags != nil {
return *m.Flags return *m.Flags
} }
return 0 return 0
} }
@ -443,6 +473,7 @@ func (m *Features) GetModel() string {
if m != nil && m.Model != nil { if m != nil && m.Model != nil {
return *m.Model return *m.Model
} }
return "" return ""
} }
@ -450,6 +481,7 @@ func (m *Features) GetFwMajor() uint32 {
if m != nil && m.FwMajor != nil { if m != nil && m.FwMajor != nil {
return *m.FwMajor return *m.FwMajor
} }
return 0 return 0
} }
@ -457,6 +489,7 @@ func (m *Features) GetFwMinor() uint32 {
if m != nil && m.FwMinor != nil { if m != nil && m.FwMinor != nil {
return *m.FwMinor return *m.FwMinor
} }
return 0 return 0
} }
@ -464,6 +497,7 @@ func (m *Features) GetFwPatch() uint32 {
if m != nil && m.FwPatch != nil { if m != nil && m.FwPatch != nil {
return *m.FwPatch return *m.FwPatch
} }
return 0 return 0
} }
@ -471,6 +505,7 @@ func (m *Features) GetFwVendor() string {
if m != nil && m.FwVendor != nil { if m != nil && m.FwVendor != nil {
return *m.FwVendor return *m.FwVendor
} }
return "" return ""
} }
@ -478,6 +513,7 @@ func (m *Features) GetFwVendorKeys() []byte {
if m != nil { if m != nil {
return m.FwVendorKeys return m.FwVendorKeys
} }
return nil return nil
} }
@ -485,6 +521,7 @@ func (m *Features) GetUnfinishedBackup() bool {
if m != nil && m.UnfinishedBackup != nil { if m != nil && m.UnfinishedBackup != nil {
return *m.UnfinishedBackup return *m.UnfinishedBackup
} }
return false return false
} }
@ -492,6 +529,7 @@ func (m *Features) GetNoBackup() bool {
if m != nil && m.NoBackup != nil { if m != nil && m.NoBackup != nil {
return *m.NoBackup return *m.NoBackup
} }
return false return false
} }
@ -577,6 +615,7 @@ func (m *ApplySettings) GetLanguage() string {
if m != nil && m.Language != nil { if m != nil && m.Language != nil {
return *m.Language return *m.Language
} }
return "" return ""
} }
@ -584,6 +623,7 @@ func (m *ApplySettings) GetLabel() string {
if m != nil && m.Label != nil { if m != nil && m.Label != nil {
return *m.Label return *m.Label
} }
return "" return ""
} }
@ -591,6 +631,7 @@ func (m *ApplySettings) GetUsePassphrase() bool {
if m != nil && m.UsePassphrase != nil { if m != nil && m.UsePassphrase != nil {
return *m.UsePassphrase return *m.UsePassphrase
} }
return false return false
} }
@ -598,6 +639,7 @@ func (m *ApplySettings) GetHomescreen() []byte {
if m != nil { if m != nil {
return m.Homescreen return m.Homescreen
} }
return nil return nil
} }
@ -605,6 +647,7 @@ func (m *ApplySettings) GetPassphraseSource() ApplySettings_PassphraseSourceType
if m != nil && m.PassphraseSource != nil { if m != nil && m.PassphraseSource != nil {
return *m.PassphraseSource return *m.PassphraseSource
} }
return ApplySettings_ASK return ApplySettings_ASK
} }
@ -612,6 +655,7 @@ func (m *ApplySettings) GetAutoLockDelayMs() uint32 {
if m != nil && m.AutoLockDelayMs != nil { if m != nil && m.AutoLockDelayMs != nil {
return *m.AutoLockDelayMs return *m.AutoLockDelayMs
} }
return 0 return 0
} }
@ -619,6 +663,7 @@ func (m *ApplySettings) GetDisplayRotation() uint32 {
if m != nil && m.DisplayRotation != nil { if m != nil && m.DisplayRotation != nil {
return *m.DisplayRotation return *m.DisplayRotation
} }
return 0 return 0
} }
@ -663,6 +708,7 @@ func (m *ApplyFlags) GetFlags() uint32 {
if m != nil && m.Flags != nil { if m != nil && m.Flags != nil {
return *m.Flags return *m.Flags
} }
return 0 return 0
} }
@ -707,6 +753,7 @@ func (m *ChangePin) GetRemove() bool {
if m != nil && m.Remove != nil { if m != nil && m.Remove != nil {
return *m.Remove return *m.Remove
} }
return false return false
} }
@ -753,6 +800,7 @@ func (m *Ping) GetMessage() string {
if m != nil && m.Message != nil { if m != nil && m.Message != nil {
return *m.Message return *m.Message
} }
return "" return ""
} }
@ -760,6 +808,7 @@ func (m *Ping) GetButtonProtection() bool {
if m != nil && m.ButtonProtection != nil { if m != nil && m.ButtonProtection != nil {
return *m.ButtonProtection return *m.ButtonProtection
} }
return false return false
} }
@ -767,6 +816,7 @@ func (m *Ping) GetPinProtection() bool {
if m != nil && m.PinProtection != nil { if m != nil && m.PinProtection != nil {
return *m.PinProtection return *m.PinProtection
} }
return false return false
} }
@ -774,6 +824,7 @@ func (m *Ping) GetPassphraseProtection() bool {
if m != nil && m.PassphraseProtection != nil { if m != nil && m.PassphraseProtection != nil {
return *m.PassphraseProtection return *m.PassphraseProtection
} }
return false return false
} }
@ -853,6 +904,7 @@ func (m *GetEntropy) GetSize() uint32 {
if m != nil && m.Size != nil { if m != nil && m.Size != nil {
return *m.Size return *m.Size
} }
return 0 return 0
} }
@ -895,6 +947,7 @@ func (m *Entropy) GetEntropy() []byte {
if m != nil { if m != nil {
return m.Entropy return m.Entropy
} }
return nil return nil
} }
@ -984,6 +1037,7 @@ func (m *LoadDevice) GetMnemonic() string {
if m != nil && m.Mnemonic != nil { if m != nil && m.Mnemonic != nil {
return *m.Mnemonic return *m.Mnemonic
} }
return "" return ""
} }
@ -991,6 +1045,7 @@ func (m *LoadDevice) GetNode() *HDNodeType {
if m != nil { if m != nil {
return m.Node return m.Node
} }
return nil return nil
} }
@ -998,6 +1053,7 @@ func (m *LoadDevice) GetPin() string {
if m != nil && m.Pin != nil { if m != nil && m.Pin != nil {
return *m.Pin return *m.Pin
} }
return "" return ""
} }
@ -1005,6 +1061,7 @@ func (m *LoadDevice) GetPassphraseProtection() bool {
if m != nil && m.PassphraseProtection != nil { if m != nil && m.PassphraseProtection != nil {
return *m.PassphraseProtection return *m.PassphraseProtection
} }
return false return false
} }
@ -1012,6 +1069,7 @@ func (m *LoadDevice) GetLanguage() string {
if m != nil && m.Language != nil { if m != nil && m.Language != nil {
return *m.Language return *m.Language
} }
return Default_LoadDevice_Language return Default_LoadDevice_Language
} }
@ -1019,6 +1077,7 @@ func (m *LoadDevice) GetLabel() string {
if m != nil && m.Label != nil { if m != nil && m.Label != nil {
return *m.Label return *m.Label
} }
return "" return ""
} }
@ -1026,6 +1085,7 @@ func (m *LoadDevice) GetSkipChecksum() bool {
if m != nil && m.SkipChecksum != nil { if m != nil && m.SkipChecksum != nil {
return *m.SkipChecksum return *m.SkipChecksum
} }
return false return false
} }
@ -1033,6 +1093,7 @@ func (m *LoadDevice) GetU2FCounter() uint32 {
if m != nil && m.U2FCounter != nil { if m != nil && m.U2FCounter != nil {
return *m.U2FCounter return *m.U2FCounter
} }
return 0 return 0
} }
@ -1088,6 +1149,7 @@ func (m *ResetDevice) GetDisplayRandom() bool {
if m != nil && m.DisplayRandom != nil { if m != nil && m.DisplayRandom != nil {
return *m.DisplayRandom return *m.DisplayRandom
} }
return false return false
} }
@ -1095,6 +1157,7 @@ func (m *ResetDevice) GetStrength() uint32 {
if m != nil && m.Strength != nil { if m != nil && m.Strength != nil {
return *m.Strength return *m.Strength
} }
return Default_ResetDevice_Strength return Default_ResetDevice_Strength
} }
@ -1102,6 +1165,7 @@ func (m *ResetDevice) GetPassphraseProtection() bool {
if m != nil && m.PassphraseProtection != nil { if m != nil && m.PassphraseProtection != nil {
return *m.PassphraseProtection return *m.PassphraseProtection
} }
return false return false
} }
@ -1109,6 +1173,7 @@ func (m *ResetDevice) GetPinProtection() bool {
if m != nil && m.PinProtection != nil { if m != nil && m.PinProtection != nil {
return *m.PinProtection return *m.PinProtection
} }
return false return false
} }
@ -1116,6 +1181,7 @@ func (m *ResetDevice) GetLanguage() string {
if m != nil && m.Language != nil { if m != nil && m.Language != nil {
return *m.Language return *m.Language
} }
return Default_ResetDevice_Language return Default_ResetDevice_Language
} }
@ -1123,6 +1189,7 @@ func (m *ResetDevice) GetLabel() string {
if m != nil && m.Label != nil { if m != nil && m.Label != nil {
return *m.Label return *m.Label
} }
return "" return ""
} }
@ -1130,6 +1197,7 @@ func (m *ResetDevice) GetU2FCounter() uint32 {
if m != nil && m.U2FCounter != nil { if m != nil && m.U2FCounter != nil {
return *m.U2FCounter return *m.U2FCounter
} }
return 0 return 0
} }
@ -1137,6 +1205,7 @@ func (m *ResetDevice) GetSkipBackup() bool {
if m != nil && m.SkipBackup != nil { if m != nil && m.SkipBackup != nil {
return *m.SkipBackup return *m.SkipBackup
} }
return false return false
} }
@ -1144,6 +1213,7 @@ func (m *ResetDevice) GetNoBackup() bool {
if m != nil && m.NoBackup != nil { if m != nil && m.NoBackup != nil {
return *m.NoBackup return *m.NoBackup
} }
return false return false
} }
@ -1255,6 +1325,7 @@ func (m *EntropyAck) GetEntropy() []byte {
if m != nil { if m != nil {
return m.Entropy return m.Entropy
} }
return nil return nil
} }
@ -1310,6 +1381,7 @@ func (m *RecoveryDevice) GetWordCount() uint32 {
if m != nil && m.WordCount != nil { if m != nil && m.WordCount != nil {
return *m.WordCount return *m.WordCount
} }
return 0 return 0
} }
@ -1317,6 +1389,7 @@ func (m *RecoveryDevice) GetPassphraseProtection() bool {
if m != nil && m.PassphraseProtection != nil { if m != nil && m.PassphraseProtection != nil {
return *m.PassphraseProtection return *m.PassphraseProtection
} }
return false return false
} }
@ -1324,6 +1397,7 @@ func (m *RecoveryDevice) GetPinProtection() bool {
if m != nil && m.PinProtection != nil { if m != nil && m.PinProtection != nil {
return *m.PinProtection return *m.PinProtection
} }
return false return false
} }
@ -1331,6 +1405,7 @@ func (m *RecoveryDevice) GetLanguage() string {
if m != nil && m.Language != nil { if m != nil && m.Language != nil {
return *m.Language return *m.Language
} }
return Default_RecoveryDevice_Language return Default_RecoveryDevice_Language
} }
@ -1338,6 +1413,7 @@ func (m *RecoveryDevice) GetLabel() string {
if m != nil && m.Label != nil { if m != nil && m.Label != nil {
return *m.Label return *m.Label
} }
return "" return ""
} }
@ -1345,6 +1421,7 @@ func (m *RecoveryDevice) GetEnforceWordlist() bool {
if m != nil && m.EnforceWordlist != nil { if m != nil && m.EnforceWordlist != nil {
return *m.EnforceWordlist return *m.EnforceWordlist
} }
return false return false
} }
@ -1352,6 +1429,7 @@ func (m *RecoveryDevice) GetType() RecoveryDevice_RecoveryDeviceType {
if m != nil && m.Type != nil { if m != nil && m.Type != nil {
return *m.Type return *m.Type
} }
return RecoveryDevice_RecoveryDeviceType_ScrambledWords return RecoveryDevice_RecoveryDeviceType_ScrambledWords
} }
@ -1359,6 +1437,7 @@ func (m *RecoveryDevice) GetU2FCounter() uint32 {
if m != nil && m.U2FCounter != nil { if m != nil && m.U2FCounter != nil {
return *m.U2FCounter return *m.U2FCounter
} }
return 0 return 0
} }
@ -1366,6 +1445,7 @@ func (m *RecoveryDevice) GetDryRun() bool {
if m != nil && m.DryRun != nil { if m != nil && m.DryRun != nil {
return *m.DryRun return *m.DryRun
} }
return false return false
} }
@ -1409,6 +1489,7 @@ func (m *WordRequest) GetType() WordRequest_WordRequestType {
if m != nil && m.Type != nil { if m != nil && m.Type != nil {
return *m.Type return *m.Type
} }
return WordRequest_WordRequestType_Plain return WordRequest_WordRequestType_Plain
} }
@ -1453,6 +1534,7 @@ func (m *WordAck) GetWord() string {
if m != nil && m.Word != nil { if m != nil && m.Word != nil {
return *m.Word return *m.Word
} }
return "" return ""
} }
@ -1496,6 +1578,7 @@ func (m *SetU2FCounter) GetU2FCounter() uint32 {
if m != nil && m.U2FCounter != nil { if m != nil && m.U2FCounter != nil {
return *m.U2FCounter return *m.U2FCounter
} }
return 0 return 0
} }

View file

@ -636,6 +636,7 @@ var MessageType_value = map[string]int32{
func (x MessageType) Enum() *MessageType { func (x MessageType) Enum() *MessageType {
p := new(MessageType) p := new(MessageType)
*p = x *p = x
return p return p
} }
@ -648,7 +649,9 @@ func (x *MessageType) UnmarshalJSON(data []byte) error {
if err != nil { if err != nil {
return err return err
} }
*x = MessageType(value) *x = MessageType(value)
return nil return nil
} }

View file

@ -66,5 +66,6 @@ func Name(kind uint16) string {
if len(name) < 12 { if len(name) < 12 {
return name return name
} }
return name[12:] return name[12:]
} }

View file

@ -133,6 +133,7 @@ func (w *wallet) Status() (string, error) {
if w.device == nil { if w.device == nil {
return "Closed", failure return "Closed", failure
} }
return status, failure return status, failure
} }
@ -152,6 +153,7 @@ func (w *wallet) Open(passphrase string) error {
if err != nil { if err != nil {
return err return err
} }
w.device = device w.device = device
w.commsLock = make(chan struct{}, 1) w.commsLock = make(chan struct{}, 1)
w.commsLock <- struct{}{} // Enable lock w.commsLock <- struct{}{} // Enable lock
@ -187,6 +189,7 @@ func (w *wallet) heartbeat() {
errc chan error errc chan error
err error err error
) )
for errc == nil && err == nil { for errc == nil && err == nil {
// Wait until termination is requested or the heartbeat cycle arrives // Wait until termination is requested or the heartbeat cycle arrives
select { select {
@ -203,6 +206,7 @@ func (w *wallet) heartbeat() {
w.stateLock.RUnlock() w.stateLock.RUnlock()
continue continue
} }
<-w.commsLock // Don't lock state while resolving version <-w.commsLock // Don't lock state while resolving version
err = w.driver.Heartbeat() err = w.driver.Heartbeat()
w.commsLock <- struct{}{} w.commsLock <- struct{}{}
@ -233,6 +237,7 @@ func (w *wallet) Close() error {
// Terminate the health checks // Terminate the health checks
var herr error var herr error
if hQuit != nil { if hQuit != nil {
errc := make(chan error) errc := make(chan error)
hQuit <- errc hQuit <- errc
@ -240,6 +245,7 @@ func (w *wallet) Close() error {
} }
// Terminate the self-derivations // Terminate the self-derivations
var derr error var derr error
if dQuit != nil { if dQuit != nil {
errc := make(chan error) errc := make(chan error)
dQuit <- errc dQuit <- errc
@ -256,9 +262,11 @@ func (w *wallet) Close() error {
if err := w.close(); err != nil { if err := w.close(); err != nil {
return err return err
} }
if herr != nil { if herr != nil {
return herr return herr
} }
return derr return derr
} }
@ -276,6 +284,7 @@ func (w *wallet) close() error {
w.device = nil w.device = nil
w.accounts, w.paths = nil, nil w.accounts, w.paths = nil, nil
return w.driver.Close() return w.driver.Close()
} }
@ -298,6 +307,7 @@ func (w *wallet) Accounts() []accounts.Account {
cpy := make([]accounts.Account, len(w.accounts)) cpy := make([]accounts.Account, len(w.accounts))
copy(cpy, w.accounts) copy(cpy, w.accounts)
return cpy return cpy
} }
@ -313,6 +323,7 @@ func (w *wallet) selfDerive() {
errc chan error errc chan error
err error err error
) )
for errc == nil && err == nil { for errc == nil && err == nil {
// Wait until either derivation or termination is requested // Wait until either derivation or termination is requested
select { select {
@ -327,6 +338,7 @@ func (w *wallet) selfDerive() {
if w.device == nil || w.deriveChain == nil { if w.device == nil || w.deriveChain == nil {
w.stateLock.RUnlock() w.stateLock.RUnlock()
reqc <- struct{}{} reqc <- struct{}{}
continue continue
} }
select { select {
@ -334,6 +346,7 @@ func (w *wallet) selfDerive() {
default: default:
w.stateLock.RUnlock() w.stateLock.RUnlock()
reqc <- struct{}{} reqc <- struct{}{}
continue continue
} }
// Device lock obtained, derive the next batch of accounts // Device lock obtained, derive the next batch of accounts
@ -346,6 +359,7 @@ func (w *wallet) selfDerive() {
context = context.Background() context = context.Background()
) )
for i := 0; i < len(nextAddrs); i++ { for i := 0; i < len(nextAddrs); i++ {
for empty := false; !empty; { for empty := false; !empty; {
// Retrieve the next derived Ethereum account // Retrieve the next derived Ethereum account
@ -360,11 +374,13 @@ func (w *wallet) selfDerive() {
balance *big.Int balance *big.Int
nonce uint64 nonce uint64
) )
balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil) balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil)
if err != nil { if err != nil {
w.log.Warn("USB wallet balance retrieval failed", "err", err) w.log.Warn("USB wallet balance retrieval failed", "err", err)
break break
} }
nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil) nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil)
if err != nil { if err != nil {
w.log.Warn("USB wallet nonce retrieval failed", "err", err) w.log.Warn("USB wallet nonce retrieval failed", "err", err)
@ -374,6 +390,7 @@ func (w *wallet) selfDerive() {
// unless the account was empty. // unless the account was empty.
path := make(accounts.DerivationPath, len(nextPaths[i])) path := make(accounts.DerivationPath, len(nextPaths[i]))
copy(path[:], nextPaths[i][:]) copy(path[:], nextPaths[i][:])
if balance.Sign() == 0 && nonce == 0 { if balance.Sign() == 0 && nonce == 0 {
empty = true empty = true
// If it indeed was empty, make a log output for it anyway. In the case // If it indeed was empty, make a log output for it anyway. In the case
@ -385,6 +402,7 @@ func (w *wallet) selfDerive() {
break break
} }
} }
paths = append(paths, path) paths = append(paths, path)
account := accounts.Account{ account := accounts.Account{
Address: nextAddrs[i], Address: nextAddrs[i],
@ -423,6 +441,7 @@ func (w *wallet) selfDerive() {
// Notify the user of termination and loop after a bit of time (to avoid trashing) // Notify the user of termination and loop after a bit of time (to avoid trashing)
reqc <- struct{}{} reqc <- struct{}{}
if err == nil { if err == nil {
select { select {
case errc = <-w.deriveQuit: case errc = <-w.deriveQuit:
@ -448,6 +467,7 @@ func (w *wallet) Contains(account accounts.Account) bool {
defer w.stateLock.RUnlock() defer w.stateLock.RUnlock()
_, exists := w.paths[account.Address] _, exists := w.paths[account.Address]
return exists return exists
} }
@ -462,6 +482,7 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
w.stateLock.RUnlock() w.stateLock.RUnlock()
return accounts.Account{}, accounts.ErrWalletClosed return accounts.Account{}, accounts.ErrWalletClosed
} }
<-w.commsLock // Avoid concurrent hardware access <-w.commsLock // Avoid concurrent hardware access
address, err := w.driver.Derive(path) address, err := w.driver.Derive(path)
w.commsLock <- struct{}{} w.commsLock <- struct{}{}
@ -472,6 +493,7 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
if err != nil { if err != nil {
return accounts.Account{}, err return accounts.Account{}, err
} }
account := accounts.Account{ account := accounts.Account{
Address: address, Address: address,
URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)}, URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
@ -488,6 +510,7 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
w.paths[address] = make(accounts.DerivationPath, len(path)) w.paths[address] = make(accounts.DerivationPath, len(path))
copy(w.paths[address], path) copy(w.paths[address], path)
} }
return account, nil return account, nil
} }
@ -514,6 +537,7 @@ func (w *wallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.Chai
w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base)) w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base))
copy(w.deriveNextPaths[i][:], base[:]) copy(w.deriveNextPaths[i][:], base[:])
} }
w.deriveNextAddrs = make([]common.Address, len(bases)) w.deriveNextAddrs = make([]common.Address, len(bases))
w.deriveChain = chain w.deriveChain = chain
} }
@ -546,6 +570,7 @@ func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte
} }
// All infos gathered and metadata checks out, request signing // All infos gathered and metadata checks out, request signing
<-w.commsLock <-w.commsLock
defer func() { w.commsLock <- struct{}{} }() defer func() { w.commsLock <- struct{}{} }()
// Ensure the device isn't screwed with while user confirmation is pending // Ensure the device isn't screwed with while user confirmation is pending
@ -564,6 +589,7 @@ func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte
if err != nil { if err != nil {
return nil, err return nil, err
} }
return signature, nil return signature, nil
} }
@ -600,6 +626,7 @@ func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID
} }
// All infos gathered and metadata checks out, request signing // All infos gathered and metadata checks out, request signing
<-w.commsLock <-w.commsLock
defer func() { w.commsLock <- struct{}{} }() defer func() { w.commsLock <- struct{}{} }()
// Ensure the device isn't screwed with while user confirmation is pending // Ensure the device isn't screwed with while user confirmation is pending
@ -618,9 +645,11 @@ func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID
if err != nil { if err != nil {
return nil, err return nil, err
} }
if sender != account.Address { if sender != account.Address {
return nil, fmt.Errorf("signer mismatch: expected %s, got %s", account.Address.Hex(), sender.Hex()) return nil, fmt.Errorf("signer mismatch: expected %s, got %s", account.Address.Hex(), sender.Hex())
} }
return signed, nil return signed, nil
} }

View file

@ -35,6 +35,7 @@ func (e *EngineAPIError) ErrorData() interface{} {
if e.err == nil { if e.err == nil {
return nil return nil
} }
return struct { return struct {
Error string `json:"err"` Error string `json:"err"`
}{e.err.Error()} }{e.err.Error()}

View file

@ -21,11 +21,13 @@ func (p PayloadAttributes) MarshalJSON() ([]byte, error) {
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
} }
var enc PayloadAttributes var enc PayloadAttributes
enc.Timestamp = hexutil.Uint64(p.Timestamp) enc.Timestamp = hexutil.Uint64(p.Timestamp)
enc.Random = p.Random enc.Random = p.Random
enc.SuggestedFeeRecipient = p.SuggestedFeeRecipient enc.SuggestedFeeRecipient = p.SuggestedFeeRecipient
enc.Withdrawals = p.Withdrawals enc.Withdrawals = p.Withdrawals
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -37,24 +39,32 @@ func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"` SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
} }
var dec PayloadAttributes var dec PayloadAttributes
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
return err return err
} }
if dec.Timestamp == nil { if dec.Timestamp == nil {
return errors.New("missing required field 'timestamp' for PayloadAttributes") return errors.New("missing required field 'timestamp' for PayloadAttributes")
} }
p.Timestamp = uint64(*dec.Timestamp) p.Timestamp = uint64(*dec.Timestamp)
if dec.Random == nil { if dec.Random == nil {
return errors.New("missing required field 'prevRandao' for PayloadAttributes") return errors.New("missing required field 'prevRandao' for PayloadAttributes")
} }
p.Random = *dec.Random p.Random = *dec.Random
if dec.SuggestedFeeRecipient == nil { if dec.SuggestedFeeRecipient == nil {
return errors.New("missing required field 'suggestedFeeRecipient' for PayloadAttributes") return errors.New("missing required field 'suggestedFeeRecipient' for PayloadAttributes")
} }
p.SuggestedFeeRecipient = *dec.SuggestedFeeRecipient p.SuggestedFeeRecipient = *dec.SuggestedFeeRecipient
if dec.Withdrawals != nil { if dec.Withdrawals != nil {
p.Withdrawals = dec.Withdrawals p.Withdrawals = dec.Withdrawals
} }
return nil return nil
} }

View file

@ -33,6 +33,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
} }
var enc ExecutableData var enc ExecutableData
enc.ParentHash = e.ParentHash enc.ParentHash = e.ParentHash
enc.FeeRecipient = e.FeeRecipient enc.FeeRecipient = e.FeeRecipient
@ -47,13 +48,16 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
enc.ExtraData = e.ExtraData enc.ExtraData = e.ExtraData
enc.BaseFeePerGas = (*hexutil.Big)(e.BaseFeePerGas) enc.BaseFeePerGas = (*hexutil.Big)(e.BaseFeePerGas)
enc.BlockHash = e.BlockHash enc.BlockHash = e.BlockHash
if e.Transactions != nil { if e.Transactions != nil {
enc.Transactions = make([]hexutil.Bytes, len(e.Transactions)) enc.Transactions = make([]hexutil.Bytes, len(e.Transactions))
for k, v := range e.Transactions { for k, v := range e.Transactions {
enc.Transactions[k] = v enc.Transactions[k] = v
} }
} }
enc.Withdrawals = e.Withdrawals enc.Withdrawals = e.Withdrawals
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -76,71 +80,102 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
} }
var dec ExecutableData var dec ExecutableData
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
return err return err
} }
if dec.ParentHash == nil { if dec.ParentHash == nil {
return errors.New("missing required field 'parentHash' for ExecutableData") return errors.New("missing required field 'parentHash' for ExecutableData")
} }
e.ParentHash = *dec.ParentHash e.ParentHash = *dec.ParentHash
if dec.FeeRecipient == nil { if dec.FeeRecipient == nil {
return errors.New("missing required field 'feeRecipient' for ExecutableData") return errors.New("missing required field 'feeRecipient' for ExecutableData")
} }
e.FeeRecipient = *dec.FeeRecipient e.FeeRecipient = *dec.FeeRecipient
if dec.StateRoot == nil { if dec.StateRoot == nil {
return errors.New("missing required field 'stateRoot' for ExecutableData") return errors.New("missing required field 'stateRoot' for ExecutableData")
} }
e.StateRoot = *dec.StateRoot e.StateRoot = *dec.StateRoot
if dec.ReceiptsRoot == nil { if dec.ReceiptsRoot == nil {
return errors.New("missing required field 'receiptsRoot' for ExecutableData") return errors.New("missing required field 'receiptsRoot' for ExecutableData")
} }
e.ReceiptsRoot = *dec.ReceiptsRoot e.ReceiptsRoot = *dec.ReceiptsRoot
if dec.LogsBloom == nil { if dec.LogsBloom == nil {
return errors.New("missing required field 'logsBloom' for ExecutableData") return errors.New("missing required field 'logsBloom' for ExecutableData")
} }
e.LogsBloom = *dec.LogsBloom e.LogsBloom = *dec.LogsBloom
if dec.Random == nil { if dec.Random == nil {
return errors.New("missing required field 'prevRandao' for ExecutableData") return errors.New("missing required field 'prevRandao' for ExecutableData")
} }
e.Random = *dec.Random e.Random = *dec.Random
if dec.Number == nil { if dec.Number == nil {
return errors.New("missing required field 'blockNumber' for ExecutableData") return errors.New("missing required field 'blockNumber' for ExecutableData")
} }
e.Number = uint64(*dec.Number) e.Number = uint64(*dec.Number)
if dec.GasLimit == nil { if dec.GasLimit == nil {
return errors.New("missing required field 'gasLimit' for ExecutableData") return errors.New("missing required field 'gasLimit' for ExecutableData")
} }
e.GasLimit = uint64(*dec.GasLimit) e.GasLimit = uint64(*dec.GasLimit)
if dec.GasUsed == nil { if dec.GasUsed == nil {
return errors.New("missing required field 'gasUsed' for ExecutableData") return errors.New("missing required field 'gasUsed' for ExecutableData")
} }
e.GasUsed = uint64(*dec.GasUsed) e.GasUsed = uint64(*dec.GasUsed)
if dec.Timestamp == nil { if dec.Timestamp == nil {
return errors.New("missing required field 'timestamp' for ExecutableData") return errors.New("missing required field 'timestamp' for ExecutableData")
} }
e.Timestamp = uint64(*dec.Timestamp) e.Timestamp = uint64(*dec.Timestamp)
if dec.ExtraData == nil { if dec.ExtraData == nil {
return errors.New("missing required field 'extraData' for ExecutableData") return errors.New("missing required field 'extraData' for ExecutableData")
} }
e.ExtraData = *dec.ExtraData e.ExtraData = *dec.ExtraData
if dec.BaseFeePerGas == nil { if dec.BaseFeePerGas == nil {
return errors.New("missing required field 'baseFeePerGas' for ExecutableData") return errors.New("missing required field 'baseFeePerGas' for ExecutableData")
} }
e.BaseFeePerGas = (*big.Int)(dec.BaseFeePerGas) e.BaseFeePerGas = (*big.Int)(dec.BaseFeePerGas)
if dec.BlockHash == nil { if dec.BlockHash == nil {
return errors.New("missing required field 'blockHash' for ExecutableData") return errors.New("missing required field 'blockHash' for ExecutableData")
} }
e.BlockHash = *dec.BlockHash e.BlockHash = *dec.BlockHash
if dec.Transactions == nil { if dec.Transactions == nil {
return errors.New("missing required field 'transactions' for ExecutableData") return errors.New("missing required field 'transactions' for ExecutableData")
} }
e.Transactions = make([][]byte, len(dec.Transactions)) e.Transactions = make([][]byte, len(dec.Transactions))
for k, v := range dec.Transactions { for k, v := range dec.Transactions {
e.Transactions[k] = v e.Transactions[k] = v
} }
if dec.Withdrawals != nil { if dec.Withdrawals != nil {
e.Withdrawals = dec.Withdrawals e.Withdrawals = dec.Withdrawals
} }
return nil return nil
} }

View file

@ -18,9 +18,11 @@ func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) {
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"` BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
} }
var enc ExecutionPayloadEnvelope var enc ExecutionPayloadEnvelope
enc.ExecutionPayload = e.ExecutionPayload enc.ExecutionPayload = e.ExecutionPayload
enc.BlockValue = (*hexutil.Big)(e.BlockValue) enc.BlockValue = (*hexutil.Big)(e.BlockValue)
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -30,17 +32,23 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error {
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"` BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
} }
var dec ExecutionPayloadEnvelope var dec ExecutionPayloadEnvelope
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
return err return err
} }
if dec.ExecutionPayload == nil { if dec.ExecutionPayload == nil {
return errors.New("missing required field 'executionPayload' for ExecutionPayloadEnvelope") return errors.New("missing required field 'executionPayload' for ExecutionPayloadEnvelope")
} }
e.ExecutionPayload = dec.ExecutionPayload e.ExecutionPayload = dec.ExecutionPayload
if dec.BlockValue == nil { if dec.BlockValue == nil {
return errors.New("missing required field 'blockValue' for ExecutionPayloadEnvelope") return errors.New("missing required field 'blockValue' for ExecutionPayloadEnvelope")
} }
e.BlockValue = (*big.Int)(dec.BlockValue) e.BlockValue = (*big.Int)(dec.BlockValue)
return nil return nil
} }

View file

@ -115,6 +115,7 @@ func (b *PayloadID) UnmarshalText(input []byte) error {
if err != nil { if err != nil {
return fmt.Errorf("invalid payload id %q: %w", input, err) return fmt.Errorf("invalid payload id %q: %w", input, err)
} }
return nil return nil
} }
@ -134,18 +135,22 @@ func encodeTransactions(txs []*types.Transaction) [][]byte {
for i, tx := range txs { for i, tx := range txs {
enc[i], _ = tx.MarshalBinary() enc[i], _ = tx.MarshalBinary()
} }
return enc return enc
} }
func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
var txs = make([]*types.Transaction, len(enc)) var txs = make([]*types.Transaction, len(enc))
for i, encTx := range enc { for i, encTx := range enc {
var tx types.Transaction var tx types.Transaction
if err := tx.UnmarshalBinary(encTx); err != nil { if err := tx.UnmarshalBinary(encTx); err != nil {
return nil, fmt.Errorf("invalid transaction %d: %v", i, err) return nil, fmt.Errorf("invalid transaction %d: %v", i, err)
} }
txs[i] = &tx txs[i] = &tx
} }
return txs, nil return txs, nil
} }
@ -164,9 +169,11 @@ func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(params.ExtraData) > 32 { if len(params.ExtraData) > 32 {
return nil, fmt.Errorf("invalid extradata length: %v", len(params.ExtraData)) return nil, fmt.Errorf("invalid extradata length: %v", len(params.ExtraData))
} }
if len(params.LogsBloom) != 256 { if len(params.LogsBloom) != 256 {
return nil, fmt.Errorf("invalid logsBloom length: %v", len(params.LogsBloom)) return nil, fmt.Errorf("invalid logsBloom length: %v", len(params.LogsBloom))
} }
@ -178,10 +185,12 @@ func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) {
// ExecutableData before withdrawals are enabled by marshaling // ExecutableData before withdrawals are enabled by marshaling
// Withdrawals as the json null value. // Withdrawals as the json null value.
var withdrawalsRoot *common.Hash var withdrawalsRoot *common.Hash
if params.Withdrawals != nil { if params.Withdrawals != nil {
h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil)) h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil))
withdrawalsRoot = &h withdrawalsRoot = &h
} }
header := &types.Header{ header := &types.Header{
ParentHash: params.ParentHash, ParentHash: params.ParentHash,
UncleHash: types.EmptyUncleHash, UncleHash: types.EmptyUncleHash,
@ -200,10 +209,12 @@ func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) {
MixDigest: params.Random, MixDigest: params.Random,
WithdrawalsHash: withdrawalsRoot, WithdrawalsHash: withdrawalsRoot,
} }
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals) block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals)
if block.Hash() != params.BlockHash { if block.Hash() != params.BlockHash {
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash()) return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash())
} }
return block, nil return block, nil
} }
@ -227,6 +238,7 @@ func BlockToExecutableData(block *types.Block, fees *big.Int) *ExecutionPayloadE
ExtraData: block.Extra(), ExtraData: block.Extra(),
Withdrawals: block.Withdrawals(), Withdrawals: block.Withdrawals(),
} }
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees} return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees}
} }

View file

@ -41,8 +41,10 @@ func parse(data []byte) {
if err != nil { if err != nil {
die(err) die(err)
} }
messages := apitypes.ValidationMessages{} messages := apitypes.ValidationMessages{}
db.ValidateCallData(nil, data, &messages) db.ValidateCallData(nil, data, &messages)
for _, m := range messages.Messages { for _, m := range messages.Messages {
fmt.Printf("%v: %v\n", m.Typ, m.Message) fmt.Printf("%v: %v\n", m.Typ, m.Message)
} }
@ -56,10 +58,12 @@ func main() {
switch { switch {
case flag.NArg() == 1: case flag.NArg() == 1:
hexdata := flag.Arg(0) hexdata := flag.Arg(0)
data, err := hex.DecodeString(strings.TrimPrefix(hexdata, "0x")) data, err := hex.DecodeString(strings.TrimPrefix(hexdata, "0x"))
if err != nil { if err != nil {
die(err) die(err)
} }
parse(data) parse(data)
default: default:
fmt.Fprintln(os.Stderr, "Error: one argument needed") fmt.Fprintln(os.Stderr, "Error: one argument needed")

View file

@ -99,6 +99,7 @@ func abigen(c *cli.Context) error {
if c.String(pkgFlag.Name) == "" { if c.String(pkgFlag.Name) == "" {
utils.Fatalf("No destination package specified (--pkg)") utils.Fatalf("No destination package specified (--pkg)")
} }
var lang bind.Lang var lang bind.Lang
switch c.String(langFlag.Name) { switch c.String(langFlag.Name) {
@ -116,6 +117,7 @@ func abigen(c *cli.Context) error {
libs = make(map[string]string) libs = make(map[string]string)
aliases = make(map[string]string) aliases = make(map[string]string)
) )
if c.String(abiFlag.Name) != "" { if c.String(abiFlag.Name) != "" {
// Load up the ABI, optional bytecode and type name from the parameters // Load up the ABI, optional bytecode and type name from the parameters
var ( var (
@ -129,9 +131,11 @@ func abigen(c *cli.Context) error {
} else { } else {
abi, err = os.ReadFile(input) abi, err = os.ReadFile(input)
} }
if err != nil { if err != nil {
utils.Fatalf("Failed to read input ABI: %v", err) utils.Fatalf("Failed to read input ABI: %v", err)
} }
abis = append(abis, string(abi)) abis = append(abis, string(abi))
var bin []byte var bin []byte
@ -140,26 +144,31 @@ func abigen(c *cli.Context) error {
if bin, err = os.ReadFile(binFile); err != nil { if bin, err = os.ReadFile(binFile); err != nil {
utils.Fatalf("Failed to read input bytecode: %v", err) utils.Fatalf("Failed to read input bytecode: %v", err)
} }
if strings.Contains(string(bin), "//") { if strings.Contains(string(bin), "//") {
utils.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos") utils.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos")
} }
} }
bins = append(bins, string(bin)) bins = append(bins, string(bin))
kind := c.String(typeFlag.Name) kind := c.String(typeFlag.Name)
if kind == "" { if kind == "" {
kind = c.String(pkgFlag.Name) kind = c.String(pkgFlag.Name)
} }
types = append(types, kind) types = append(types, kind)
} else { } else {
// Generate the list of types to exclude from binding // Generate the list of types to exclude from binding
var exclude *nameFilter var exclude *nameFilter
if c.IsSet(excFlag.Name) { if c.IsSet(excFlag.Name) {
var err error var err error
if exclude, err = newNameFilter(strings.Split(c.String(excFlag.Name), ",")...); err != nil { if exclude, err = newNameFilter(strings.Split(c.String(excFlag.Name), ",")...); err != nil {
utils.Fatalf("Failed to parse excludes: %v", err) utils.Fatalf("Failed to parse excludes: %v", err)
} }
} }
var contracts map[string]*compiler.Contract var contracts map[string]*compiler.Contract
if c.IsSet(jsonFlag.Name) { if c.IsSet(jsonFlag.Name) {
@ -168,14 +177,17 @@ func abigen(c *cli.Context) error {
jsonOutput []byte jsonOutput []byte
err error err error
) )
if input == "-" { if input == "-" {
jsonOutput, err = io.ReadAll(os.Stdin) jsonOutput, err = io.ReadAll(os.Stdin)
} else { } else {
jsonOutput, err = os.ReadFile(input) jsonOutput, err = os.ReadFile(input)
} }
if err != nil { if err != nil {
utils.Fatalf("Failed to read combined-json: %v", err) utils.Fatalf("Failed to read combined-json: %v", err)
} }
contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "") contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "")
if err != nil { if err != nil {
utils.Fatalf("Failed to read contract information from json output: %v", err) utils.Fatalf("Failed to read contract information from json output: %v", err)
@ -186,14 +198,17 @@ func abigen(c *cli.Context) error {
// fully qualified name is of the form <solFilePath>:<type> // fully qualified name is of the form <solFilePath>:<type>
nameParts := strings.Split(name, ":") nameParts := strings.Split(name, ":")
typeName := nameParts[len(nameParts)-1] typeName := nameParts[len(nameParts)-1]
if exclude != nil && exclude.Matches(name) { if exclude != nil && exclude.Matches(name) {
fmt.Fprintf(os.Stderr, "excluding: %v\n", name) fmt.Fprintf(os.Stderr, "excluding: %v\n", name)
continue continue
} }
abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse
if err != nil { if err != nil {
utils.Fatalf("Failed to parse ABIs from compiler output: %v", err) utils.Fatalf("Failed to parse ABIs from compiler output: %v", err)
} }
abis = append(abis, string(abi)) abis = append(abis, string(abi))
bins = append(bins, contract.Code) bins = append(bins, contract.Code)
sigs = append(sigs, contract.Hashes) sigs = append(sigs, contract.Hashes)
@ -214,6 +229,7 @@ func abigen(c *cli.Context) error {
// foo=bar,foo2=bar2 // foo=bar,foo2=bar2
// foo:bar,foo2:bar2 // foo:bar,foo2:bar2
re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`) re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`)
submatches := re.FindAllStringSubmatch(c.String(aliasFlag.Name), -1) submatches := re.FindAllStringSubmatch(c.String(aliasFlag.Name), -1)
for _, match := range submatches { for _, match := range submatches {
aliases[match[1]] = match[2] aliases[match[1]] = match[2]
@ -233,6 +249,7 @@ func abigen(c *cli.Context) error {
if err := os.WriteFile(c.String(outFlag.Name), []byte(code), 0600); err != nil { if err := os.WriteFile(c.String(outFlag.Name), []byte(code), 0600); err != nil {
utils.Fatalf("Failed to write ABI binding: %v", err) utils.Fatalf("Failed to write ABI binding: %v", err)
} }
return nil return nil
} }

View file

@ -49,6 +49,7 @@ func main() {
nodeKey *ecdsa.PrivateKey nodeKey *ecdsa.PrivateKey
err error err error
) )
flag.Parse() flag.Parse()
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
@ -60,15 +61,18 @@ func main() {
if err != nil { if err != nil {
utils.Fatalf("-nat: %v", err) utils.Fatalf("-nat: %v", err)
} }
switch { switch {
case *genKey != "": case *genKey != "":
nodeKey, err = crypto.GenerateKey() nodeKey, err = crypto.GenerateKey()
if err != nil { if err != nil {
utils.Fatalf("could not generate key: %v", err) utils.Fatalf("could not generate key: %v", err)
} }
if err = crypto.SaveECDSA(*genKey, nodeKey); err != nil { if err = crypto.SaveECDSA(*genKey, nodeKey); err != nil {
utils.Fatalf("%v", err) utils.Fatalf("%v", err)
} }
if !*writeAddr { if !*writeAddr {
return return
} }
@ -103,6 +107,7 @@ func main() {
if err != nil { if err != nil {
utils.Fatalf("-ResolveUDPAddr: %v", err) utils.Fatalf("-ResolveUDPAddr: %v", err)
} }
conn, err := net.ListenUDP("udp", addr) conn, err := net.ListenUDP("udp", addr)
if err != nil { if err != nil {
utils.Fatalf("-ListenUDP: %v", err) utils.Fatalf("-ListenUDP: %v", err)
@ -113,6 +118,7 @@ func main() {
if !realaddr.IP.IsLoopback() { if !realaddr.IP.IsLoopback() {
go nat.Map(natm, nil, "udp", realaddr.Port, realaddr.Port, "ethereum discovery") go nat.Map(natm, nil, "udp", realaddr.Port, realaddr.Port, "ethereum discovery")
} }
if ext, err := natm.ExternalIP(); err == nil { if ext, err := natm.ExternalIP(); err == nil {
realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port} realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port}
} }
@ -126,6 +132,7 @@ func main() {
PrivateKey: nodeKey, PrivateKey: nodeKey,
NetRestrict: restrictList, NetRestrict: restrictList,
} }
if *runv5 { if *runv5 {
if _, err := discover.ListenV5(conn, ln, cfg); err != nil { if _, err := discover.ListenV5(conn, ln, cfg); err != nil {
utils.Fatalf("%v", err) utils.Fatalf("%v", err)
@ -143,6 +150,7 @@ func printNotice(nodeKey *ecdsa.PublicKey, addr net.UDPAddr) {
if addr.IP.IsUnspecified() { if addr.IP.IsUnspecified() {
addr.IP = net.IP{127, 0, 0, 1} addr.IP = net.IP{127, 0, 0, 1}
} }
n := enode.NewV4(nodeKey, addr.IP, 0, addr.Port) n := enode.NewV4(nodeKey, addr.IP, 0, addr.Port)
fmt.Println(n.URLv4()) fmt.Println(n.URLv4())
fmt.Println("Note: you're using cmd/bootnode, a developer tool.") fmt.Println("Note: you're using cmd/bootnode, a developer tool.")

View file

@ -17,6 +17,7 @@
package main package main
import ( import (
"github.com/urfave/cli/v2"
"strconv" "strconv"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
@ -36,6 +37,7 @@ func newClient(ctx *cli.Context) *ethclient.Client {
if err != nil { if err != nil {
utils.Fatalf("Failed to connect to Ethereum node: %v", err) utils.Fatalf("Failed to connect to Ethereum node: %v", err)
} }
return client return client
} }
@ -45,6 +47,7 @@ func newRPCClient(url string) *rpc.Client {
if err != nil { if err != nil {
utils.Fatalf("Failed to connect to Ethereum node: %v", err) utils.Fatalf("Failed to connect to Ethereum node: %v", err)
} }
return client return client
} }
@ -55,6 +58,7 @@ func getContractAddr(client *rpc.Client) common.Address {
if err := client.Call(&addr, "les_getCheckpointContractAddress"); err != nil { if err := client.Call(&addr, "les_getCheckpointContractAddress"); err != nil {
utils.Fatalf("Failed to fetch checkpoint oracle address: %v", err) utils.Fatalf("Failed to fetch checkpoint oracle address: %v", err)
} }
return common.HexToAddress(addr) return common.HexToAddress(addr)
} }
@ -70,6 +74,7 @@ func getCheckpoint(ctx *cli.Context, client *rpc.Client) *params.TrustedCheckpoi
if err := client.Call(&result, "les_getCheckpoint", index); err != nil { if err := client.Call(&result, "les_getCheckpoint", index); err != nil {
utils.Fatalf("Failed to get local checkpoint %v, please ensure the les API is exposed", err) utils.Fatalf("Failed to get local checkpoint %v, please ensure the les API is exposed", err)
} }
checkpoint = &params.TrustedCheckpoint{ checkpoint = &params.TrustedCheckpoint{
SectionIndex: index, SectionIndex: index,
SectionHead: common.HexToHash(result[0]), SectionHead: common.HexToHash(result[0]),
@ -78,14 +83,17 @@ func getCheckpoint(ctx *cli.Context, client *rpc.Client) *params.TrustedCheckpoi
} }
} else { } else {
var result [4]string var result [4]string
err := client.Call(&result, "les_latestCheckpoint") err := client.Call(&result, "les_latestCheckpoint")
if err != nil { if err != nil {
utils.Fatalf("Failed to get local checkpoint %v, please ensure the les API is exposed", err) utils.Fatalf("Failed to get local checkpoint %v, please ensure the les API is exposed", err)
} }
index, err := strconv.ParseUint(result[0], 0, 64) index, err := strconv.ParseUint(result[0], 0, 64)
if err != nil { if err != nil {
utils.Fatalf("Failed to parse checkpoint index %v", err) utils.Fatalf("Failed to parse checkpoint index %v", err)
} }
checkpoint = &params.TrustedCheckpoint{ checkpoint = &params.TrustedCheckpoint{
SectionIndex: index, SectionIndex: index,
SectionHead: common.HexToHash(result[1]), SectionHead: common.HexToHash(result[1]),
@ -93,6 +101,7 @@ func getCheckpoint(ctx *cli.Context, client *rpc.Client) *params.TrustedCheckpoi
BloomRoot: common.HexToHash(result[3]), BloomRoot: common.HexToHash(result[3]),
} }
} }
return checkpoint return checkpoint
} }
@ -103,10 +112,12 @@ func newContract(client *rpc.Client) (common.Address, *checkpointoracle.Checkpoi
if addr == (common.Address{}) { if addr == (common.Address{}) {
utils.Fatalf("No specified registrar contract address") utils.Fatalf("No specified registrar contract address")
} }
contract, err := checkpointoracle.NewCheckpointOracle(addr, ethclient.NewClient(client)) contract, err := checkpointoracle.NewCheckpointOracle(addr, ethclient.NewClient(client))
if err != nil { if err != nil {
utils.Fatalf("Failed to setup registrar contract %s: %v", addr, err) utils.Fatalf("Failed to setup registrar contract %s: %v", addr, err)
} }
return addr, contract return addr, contract
} }
@ -116,5 +127,6 @@ func newClefSigner(ctx *cli.Context) *bind.TransactOpts {
if err != nil { if err != nil {
utils.Fatalf("Failed to create clef signer %v", err) utils.Fatalf("Failed to create clef signer %v", err)
} }
return bind.NewClefTransactor(clef, accounts.Account{Address: common.HexToAddress(ctx.String(signerFlag.Name))}) return bind.NewClefTransactor(clef, accounts.Account{Address: common.HexToAddress(ctx.String(signerFlag.Name))})
} }

View file

@ -87,10 +87,12 @@ var commandPublish = &cli.Command{
func deploy(ctx *cli.Context) error { func deploy(ctx *cli.Context) error {
// Gather all the addresses that should be permitted to sign // Gather all the addresses that should be permitted to sign
var addrs []common.Address var addrs []common.Address
for _, account := range strings.Split(ctx.String(signersFlag.Name), ",") { for _, account := range strings.Split(ctx.String(signersFlag.Name), ",") {
if trimmed := strings.TrimSpace(account); !common.IsHexAddress(trimmed) { if trimmed := strings.TrimSpace(account); !common.IsHexAddress(trimmed) {
utils.Fatalf("Invalid account in --signers: '%s'", trimmed) utils.Fatalf("Invalid account in --signers: '%s'", trimmed)
} }
addrs = append(addrs, common.HexToAddress(account)) addrs = append(addrs, common.HexToAddress(account))
} }
// Retrieve and validate the signing threshold // Retrieve and validate the signing threshold
@ -100,9 +102,11 @@ func deploy(ctx *cli.Context) error {
} }
// Print a summary to ensure the user understands what they're signing // Print a summary to ensure the user understands what they're signing
fmt.Printf("Deploying new checkpoint oracle:\n\n") fmt.Printf("Deploying new checkpoint oracle:\n\n")
for i, addr := range addrs { for i, addr := range addrs {
fmt.Printf("Admin %d => %s\n", i+1, addr.Hex()) fmt.Printf("Admin %d => %s\n", i+1, addr.Hex())
} }
fmt.Printf("\nSignatures needed to publish: %d\n", needed) fmt.Printf("\nSignatures needed to publish: %d\n", needed)
// setup clef signer, create an abigen transactor and an RPC client // setup clef signer, create an abigen transactor and an RPC client
@ -110,11 +114,13 @@ func deploy(ctx *cli.Context) error {
// Deploy the checkpoint oracle // Deploy the checkpoint oracle
fmt.Println("Sending deploy request to Clef...") fmt.Println("Sending deploy request to Clef...")
oracle, tx, _, err := contract.DeployCheckpointOracle(transactor, client, addrs, big.NewInt(int64(params.CheckpointFrequency)), oracle, tx, _, err := contract.DeployCheckpointOracle(transactor, client, addrs, big.NewInt(int64(params.CheckpointFrequency)),
big.NewInt(int64(params.CheckpointProcessConfirmations)), big.NewInt(int64(needed))) big.NewInt(int64(params.CheckpointProcessConfirmations)), big.NewInt(int64(needed)))
if err != nil { if err != nil {
utils.Fatalf("Failed to deploy checkpoint oracle %v", err) utils.Fatalf("Failed to deploy checkpoint oracle %v", err)
} }
log.Info("Deployed checkpoint oracle", "address", oracle, "tx", tx.Hash().Hex()) log.Info("Deployed checkpoint oracle", "address", oracle, "tx", tx.Hash().Hex())
return nil return nil
@ -133,22 +139,27 @@ func sign(ctx *cli.Context) error {
node *rpc.Client node *rpc.Client
oracle *checkpointoracle.CheckpointOracle oracle *checkpointoracle.CheckpointOracle
) )
if !ctx.IsSet(nodeURLFlag.Name) { if !ctx.IsSet(nodeURLFlag.Name) {
// Offline mode signing // Offline mode signing
offline = true offline = true
if !ctx.IsSet(hashFlag.Name) { if !ctx.IsSet(hashFlag.Name) {
utils.Fatalf("Please specify the checkpoint hash (--hash) to sign in offline mode") utils.Fatalf("Please specify the checkpoint hash (--hash) to sign in offline mode")
} }
chash = common.HexToHash(ctx.String(hashFlag.Name)) chash = common.HexToHash(ctx.String(hashFlag.Name))
if !ctx.IsSet(indexFlag.Name) { if !ctx.IsSet(indexFlag.Name) {
utils.Fatalf("Please specify checkpoint index (--index) to sign in offline mode") utils.Fatalf("Please specify checkpoint index (--index) to sign in offline mode")
} }
cindex = ctx.Uint64(indexFlag.Name) cindex = ctx.Uint64(indexFlag.Name)
if !ctx.IsSet(oracleFlag.Name) { if !ctx.IsSet(oracleFlag.Name) {
utils.Fatalf("Please specify oracle address (--oracle) to sign in offline mode") utils.Fatalf("Please specify oracle address (--oracle) to sign in offline mode")
} }
address = common.HexToAddress(ctx.String(oracleFlag.Name)) address = common.HexToAddress(ctx.String(oracleFlag.Name))
} else { } else {
// Interactive mode signing, retrieve the data from the remote node // Interactive mode signing, retrieve the data from the remote node
@ -165,22 +176,28 @@ func sign(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
num := head.Number.Uint64() num := head.Number.Uint64()
if num < ((cindex+1)*params.CheckpointFrequency + params.CheckpointProcessConfirmations) { if num < ((cindex+1)*params.CheckpointFrequency + params.CheckpointProcessConfirmations) {
utils.Fatalf("Invalid future checkpoint") utils.Fatalf("Invalid future checkpoint")
} }
_, oracle = newContract(node) _, oracle = newContract(node)
latest, _, h, err := oracle.Contract().GetLatestCheckpoint(nil) latest, _, h, err := oracle.Contract().GetLatestCheckpoint(nil)
if err != nil { if err != nil {
return err return err
} }
if cindex < latest { if cindex < latest {
utils.Fatalf("Checkpoint is too old") utils.Fatalf("Checkpoint is too old")
} }
if cindex == latest && (latest != 0 || h.Uint64() != 0) { if cindex == latest && (latest != 0 || h.Uint64() != 0) {
utils.Fatalf("Stale checkpoint, latest registered %d, given %d", latest, cindex) utils.Fatalf("Stale checkpoint, latest registered %d, given %d", latest, cindex)
} }
} }
var ( var (
signature string signature string
signer string signer string
@ -191,11 +208,13 @@ func sign(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
for _, s := range signers { for _, s := range signers {
if s == addr { if s == addr {
return nil return nil
} }
} }
return fmt.Errorf("signer %v is not the admin", addr.Hex()) return fmt.Errorf("signer %v is not the admin", addr.Hex())
} }
// Print to the user the data thy are about to sign // Print to the user the data thy are about to sign
@ -210,19 +229,24 @@ func sign(ctx *cli.Context) error {
return err return err
} }
} }
clef := newRPCClient(ctx.String(clefURLFlag.Name)) clef := newRPCClient(ctx.String(clefURLFlag.Name))
p := make(map[string]string) p := make(map[string]string)
buf := make([]byte, 8) buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, cindex) binary.BigEndian.PutUint64(buf, cindex)
p["address"] = address.Hex() p["address"] = address.Hex()
p["message"] = hexutil.Encode(append(buf, chash.Bytes()...)) p["message"] = hexutil.Encode(append(buf, chash.Bytes()...))
fmt.Println("Sending signing request to Clef...") fmt.Println("Sending signing request to Clef...")
if err := clef.Call(&signature, "account_signData", accounts.MimetypeDataWithValidator, signer, p); err != nil { if err := clef.Call(&signature, "account_signData", accounts.MimetypeDataWithValidator, signer, p); err != nil {
utils.Fatalf("Failed to sign checkpoint, err %v", err) utils.Fatalf("Failed to sign checkpoint, err %v", err)
} }
fmt.Printf("Signer => %s\n", signer) fmt.Printf("Signer => %s\n", signer)
fmt.Printf("Signature => %s\n", signature) fmt.Printf("Signature => %s\n", signature)
return nil return nil
} }
@ -232,6 +256,7 @@ func sighash(index uint64, oracle common.Address, hash common.Hash) []byte {
binary.BigEndian.PutUint64(buf, index) binary.BigEndian.PutUint64(buf, index)
data := append([]byte{0x19, 0x00}, append(oracle[:], append(buf, hash[:]...)...)...) data := append([]byte{0x19, 0x00}, append(oracle[:], append(buf, hash[:]...)...)...)
return crypto.Keccak256(data) return crypto.Keccak256(data)
} }
@ -244,6 +269,7 @@ func ecrecover(sighash []byte, sig []byte) common.Address {
if err != nil { if err != nil {
utils.Fatalf("Failed to recover sender from signature %x: %v", sig, err) utils.Fatalf("Failed to recover sender from signature %x: %v", sig, err)
} }
return crypto.PubkeyToAddress(*signer) return crypto.PubkeyToAddress(*signer)
} }
@ -256,6 +282,7 @@ func publish(ctx *cli.Context) error {
// Gather the signatures from the CLI // Gather the signatures from the CLI
var sigs [][]byte var sigs [][]byte
for _, sig := range strings.Split(ctx.String(signaturesFlag.Name), ",") { for _, sig := range strings.Split(ctx.String(signaturesFlag.Name), ",") {
trimmed := strings.TrimPrefix(strings.TrimSpace(sig), "0x") trimmed := strings.TrimPrefix(strings.TrimSpace(sig), "0x")
if len(trimmed) != 130 { if len(trimmed) != 130 {
@ -271,10 +298,12 @@ func publish(ctx *cli.Context) error {
checkpoint = getCheckpoint(ctx, client) checkpoint = getCheckpoint(ctx, client)
sighash = sighash(checkpoint.SectionIndex, addr, checkpoint.Hash()) sighash = sighash(checkpoint.SectionIndex, addr, checkpoint.Hash())
) )
for i := 0; i < len(sigs); i++ { for i := 0; i < len(sigs); i++ {
for j := i + 1; j < len(sigs); j++ { for j := i + 1; j < len(sigs); j++ {
signerA := ecrecover(sighash, sigs[i]) signerA := ecrecover(sighash, sigs[i])
signerB := ecrecover(sighash, sigs[j]) signerB := ecrecover(sighash, sigs[j])
if bytes.Compare(signerA.Bytes(), signerB.Bytes()) > 0 { if bytes.Compare(signerA.Bytes(), signerB.Bytes()) > 0 {
sigs[i], sigs[j] = sigs[j], sigs[i] sigs[i], sigs[j] = sigs[j], sigs[i]
} }
@ -288,25 +317,32 @@ func publish(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
num := head.Number.Uint64() num := head.Number.Uint64()
recent, err := ethclient.NewClient(client).HeaderByNumber(reqCtx, big.NewInt(int64(num-128))) recent, err := ethclient.NewClient(client).HeaderByNumber(reqCtx, big.NewInt(int64(num-128)))
if err != nil { if err != nil {
return err return err
} }
// Print a summary of the operation that's going to be performed // Print a summary of the operation that's going to be performed
fmt.Printf("Publishing %d => %s:\n\n", checkpoint.SectionIndex, checkpoint.Hash().Hex()) fmt.Printf("Publishing %d => %s:\n\n", checkpoint.SectionIndex, checkpoint.Hash().Hex())
for i, sig := range sigs { for i, sig := range sigs {
fmt.Printf("Signer %d => %s\n", i+1, ecrecover(sighash, sig).Hex()) fmt.Printf("Signer %d => %s\n", i+1, ecrecover(sighash, sig).Hex())
} }
fmt.Println() fmt.Println()
fmt.Printf("Sentry number => %d\nSentry hash => %s\n", recent.Number, recent.Hash().Hex()) fmt.Printf("Sentry number => %d\nSentry hash => %s\n", recent.Number, recent.Hash().Hex())
// Publish the checkpoint into the oracle // Publish the checkpoint into the oracle
fmt.Println("Sending publish request to Clef...") fmt.Println("Sending publish request to Clef...")
tx, err := oracle.RegisterCheckpoint(newClefSigner(ctx), checkpoint.SectionIndex, checkpoint.Hash().Bytes(), recent.Number, recent.Hash(), sigs) tx, err := oracle.RegisterCheckpoint(newClefSigner(ctx), checkpoint.SectionIndex, checkpoint.Hash().Bytes(), recent.Number, recent.Hash(), sigs)
if err != nil { if err != nil {
utils.Fatalf("Register contract failed %v", err) utils.Fatalf("Register contract failed %v", err)
} }
log.Info("Successfully registered checkpoint", "tx", tx.Hash().Hex()) log.Info("Successfully registered checkpoint", "tx", tx.Hash().Hex())
return nil return nil
} }

View file

@ -45,9 +45,11 @@ func status(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
for i, admin := range admins { for i, admin := range admins {
fmt.Printf("Admin %d => %s\n", i+1, admin.Hex()) fmt.Printf("Admin %d => %s\n", i+1, admin.Hex())
} }
fmt.Println() fmt.Println()
// Retrieve the latest checkpoint // Retrieve the latest checkpoint
@ -55,6 +57,7 @@ func status(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("Checkpoint (published at #%d) %d => %s\n", height, index, common.Hash(checkpoint).Hex()) fmt.Printf("Checkpoint (published at #%d) %d => %s\n", height, index, common.Hash(checkpoint).Hex())
return nil return nil

View file

@ -37,6 +37,7 @@ func TestImportRaw(t *testing.T) {
// Run clef importraw // Run clef importraw
clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "importraw", keyPath) clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "importraw", keyPath)
clef.input("myverylongpassword").input("myverylongpassword") clef.input("myverylongpassword").input("myverylongpassword")
if out := string(clef.Output()); !strings.Contains(out, if out := string(clef.Output()); !strings.Contains(out,
"Key imported:\n Address 0x9160DC9105f7De5dC5E7f3d97ef11DA47269BdA6") { "Key imported:\n Address 0x9160DC9105f7De5dC5E7f3d97ef11DA47269BdA6") {
t.Logf("Output\n%v", out) t.Logf("Output\n%v", out)
@ -49,6 +50,7 @@ func TestImportRaw(t *testing.T) {
// Run clef importraw // Run clef importraw
clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "importraw", keyPath) clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "importraw", keyPath)
clef.input("myverylongpassword1").input("myverylongpassword2").WaitExit() clef.input("myverylongpassword1").input("myverylongpassword2").WaitExit()
if have, want := clef.StderrText(), "Passwords do not match\n"; have != want { if have, want := clef.StderrText(), "Passwords do not match\n"; have != want {
t.Errorf("have %q, want %q", have, want) t.Errorf("have %q, want %q", have, want)
} }
@ -59,6 +61,7 @@ func TestImportRaw(t *testing.T) {
// Run clef importraw // Run clef importraw
clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "importraw", keyPath) clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "importraw", keyPath)
clef.input("shorty").input("shorty").WaitExit() clef.input("shorty").input("shorty").WaitExit()
if have, want := clef.StderrText(), if have, want := clef.StderrText(),
"password requirements not met: password too short (<10 characters)\n"; have != want { "password requirements not met: password too short (<10 characters)\n"; have != want {
t.Errorf("have %q, want %q", have, want) t.Errorf("have %q, want %q", have, want)
@ -76,6 +79,7 @@ func TestListAccounts(t *testing.T) {
t.Run("no-accounts", func(t *testing.T) { t.Run("no-accounts", func(t *testing.T) {
t.Parallel() t.Parallel()
clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "list-accounts") clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "list-accounts")
if out := string(clef.Output()); !strings.Contains(out, "The keystore is empty.") { if out := string(clef.Output()); !strings.Contains(out, "The keystore is empty.") {
t.Logf("Output\n%v", out) t.Logf("Output\n%v", out)
@ -106,6 +110,7 @@ func TestListWallets(t *testing.T) {
t.Run("no-accounts", func(t *testing.T) { t.Run("no-accounts", func(t *testing.T) {
t.Parallel() t.Parallel()
clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "list-wallets") clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "list-wallets")
if out := string(clef.Output()); !strings.Contains(out, "There are no wallets.") { if out := string(clef.Output()); !strings.Contains(out, "There are no wallets.") {
t.Logf("Output\n%v", out) t.Logf("Output\n%v", out)

View file

@ -315,26 +315,33 @@ func initializeSecrets(c *cli.Context) error {
if err := os.Mkdir(configDir, 0700); err != nil && !os.IsExist(err) { if err := os.Mkdir(configDir, 0700); err != nil && !os.IsExist(err) {
return err return err
} }
location := filepath.Join(configDir, "masterseed.json") location := filepath.Join(configDir, "masterseed.json")
if _, err := os.Stat(location); err == nil { if _, err := os.Stat(location); err == nil {
return fmt.Errorf("master key %v already exists, will not overwrite", location) return fmt.Errorf("master key %v already exists, will not overwrite", location)
} }
// Key file does not exist yet, generate a new one and encrypt it // Key file does not exist yet, generate a new one and encrypt it
masterSeed := make([]byte, 256) masterSeed := make([]byte, 256)
num, err := io.ReadFull(rand.Reader, masterSeed) num, err := io.ReadFull(rand.Reader, masterSeed)
if err != nil { if err != nil {
return err return err
} }
if num != len(masterSeed) { if num != len(masterSeed) {
return fmt.Errorf("failed to read enough random") return fmt.Errorf("failed to read enough random")
} }
n, p := keystore.StandardScryptN, keystore.StandardScryptP n, p := keystore.StandardScryptN, keystore.StandardScryptP
if c.Bool(utils.LightKDFFlag.Name) { if c.Bool(utils.LightKDFFlag.Name) {
n, p = keystore.LightScryptN, keystore.LightScryptP n, p = keystore.LightScryptN, keystore.LightScryptP
} }
text := "The master seed of clef will be locked with a password.\nPlease specify a password. Do not forget this password!" text := "The master seed of clef will be locked with a password.\nPlease specify a password. Do not forget this password!"
var password string var password string
for { for {
password = utils.GetPassPhrase(text, true) password = utils.GetPassPhrase(text, true)
if err := core.ValidatePasswordFormat(password); err != nil { if err := core.ValidatePasswordFormat(password); err != nil {
@ -344,6 +351,7 @@ func initializeSecrets(c *cli.Context) error {
break break
} }
} }
cipherSeed, err := encryptSeed(masterSeed, []byte(password), n, p) cipherSeed, err := encryptSeed(masterSeed, []byte(password), n, p)
if err != nil { if err != nil {
return fmt.Errorf("failed to encrypt master seed: %v", err) return fmt.Errorf("failed to encrypt master seed: %v", err)
@ -352,6 +360,7 @@ func initializeSecrets(c *cli.Context) error {
if err = os.Mkdir(configDir, 0700); err != nil && !os.IsExist(err) { if err = os.Mkdir(configDir, 0700); err != nil && !os.IsExist(err) {
return err return err
} }
if _, err := os.Stat(location); err == nil { if _, err := os.Stat(location); err == nil {
return fmt.Errorf("master key %v already exists, will not overwrite", location) return fmt.Errorf("master key %v already exists, will not overwrite", location)
} }
@ -359,6 +368,7 @@ func initializeSecrets(c *cli.Context) error {
if err = os.WriteFile(location, cipherSeed, 0400); err != nil { if err = os.WriteFile(location, cipherSeed, 0400); err != nil {
return err return err
} }
fmt.Printf("A master seed has been generated into %s\n", location) fmt.Printf("A master seed has been generated into %s\n", location)
fmt.Printf(` fmt.Printf(`
This is required to be able to store credentials, such as: This is required to be able to store credentials, such as:
@ -371,6 +381,7 @@ You should treat 'masterseed.json' with utmost secrecy and make a backup of it!
* The master seed does not contain your accounts, those need to be backed up separately! * The master seed does not contain your accounts, those need to be backed up separately!
`) `)
return nil return nil
} }
@ -378,6 +389,7 @@ func attestFile(ctx *cli.Context) error {
if ctx.NArg() < 1 { if ctx.NArg() < 1 {
utils.Fatalf("This command requires an argument.") utils.Fatalf("This command requires an argument.")
} }
if err := initialize(ctx); err != nil { if err := initialize(ctx); err != nil {
return err return err
} }
@ -396,6 +408,7 @@ func attestFile(ctx *cli.Context) error {
val := ctx.Args().First() val := ctx.Args().First()
configStorage.Put("ruleset_sha256", val) configStorage.Put("ruleset_sha256", val)
log.Info("Ruleset attestation updated", "sha256", val) log.Info("Ruleset attestation updated", "sha256", val)
return nil return nil
} }
@ -422,15 +435,19 @@ func setCredential(ctx *cli.Context) error {
if ctx.NArg() < 1 { if ctx.NArg() < 1 {
utils.Fatalf("This command requires an address to be passed as an argument") utils.Fatalf("This command requires an address to be passed as an argument")
} }
if err := initialize(ctx); err != nil { if err := initialize(ctx); err != nil {
return err return err
} }
addr := ctx.Args().First() addr := ctx.Args().First()
if !common.IsHexAddress(addr) { if !common.IsHexAddress(addr) {
utils.Fatalf("Invalid address specified: %s", addr) utils.Fatalf("Invalid address specified: %s", addr)
} }
address := common.HexToAddress(addr) address := common.HexToAddress(addr)
password := utils.GetPassPhrase("Please enter a password to store for this address:", true) password := utils.GetPassPhrase("Please enter a password to store for this address:", true)
fmt.Println() fmt.Println()
stretchedKey, err := readMasterKey(ctx, nil) stretchedKey, err := readMasterKey(ctx, nil)
@ -446,6 +463,7 @@ func setCredential(ctx *cli.Context) error {
pwStorage.Put(address.Hex(), password) pwStorage.Put(address.Hex(), password)
log.Info("Credential store updated", "set", address) log.Info("Credential store updated", "set", address)
return nil return nil
} }
@ -453,13 +471,16 @@ func removeCredential(ctx *cli.Context) error {
if ctx.NArg() < 1 { if ctx.NArg() < 1 {
utils.Fatalf("This command requires an address to be passed as an argument") utils.Fatalf("This command requires an address to be passed as an argument")
} }
if err := initialize(ctx); err != nil { if err := initialize(ctx); err != nil {
return err return err
} }
addr := ctx.Args().First() addr := ctx.Args().First()
if !common.IsHexAddress(addr) { if !common.IsHexAddress(addr) {
utils.Fatalf("Invalid address specified: %s", addr) utils.Fatalf("Invalid address specified: %s", addr)
} }
address := common.HexToAddress(addr) address := common.HexToAddress(addr)
stretchedKey, err := readMasterKey(ctx, nil) stretchedKey, err := readMasterKey(ctx, nil)
@ -475,6 +496,7 @@ func removeCredential(ctx *cli.Context) error {
pwStorage.Del(address.Hex()) pwStorage.Del(address.Hex())
log.Info("Credential store updated", "unset", address) log.Info("Credential store updated", "unset", address)
return nil return nil
} }
@ -491,13 +513,17 @@ func initialize(c *cli.Context) error {
if !confirm(legalWarning) { if !confirm(legalWarning) {
return fmt.Errorf("aborted by user") return fmt.Errorf("aborted by user")
} }
fmt.Println() fmt.Println()
} }
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
output := io.Writer(logOutput) output := io.Writer(logOutput)
if usecolor { if usecolor {
output = colorable.NewColorable(logOutput) output = colorable.NewColorable(logOutput)
} }
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(output, log.TerminalFormat(usecolor)))) log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(output, log.TerminalFormat(usecolor))))
return nil return nil
@ -644,6 +670,7 @@ func ipcEndpoint(ipcPath, datadir string) string {
if strings.HasPrefix(ipcPath, `\\.\pipe\`) { if strings.HasPrefix(ipcPath, `\\.\pipe\`) {
return ipcPath return ipcPath
} }
return `\\.\pipe\` + ipcPath return `\\.\pipe\` + ipcPath
} }
// Resolve names into the data directory full paths otherwise // Resolve names into the data directory full paths otherwise
@ -651,8 +678,10 @@ func ipcEndpoint(ipcPath, datadir string) string {
if datadir == "" { if datadir == "" {
return filepath.Join(os.TempDir(), ipcPath) return filepath.Join(os.TempDir(), ipcPath)
} }
return filepath.Join(datadir, ipcPath) return filepath.Join(datadir, ipcPath)
} }
return ipcPath return ipcPath
} }
@ -661,26 +690,32 @@ func signer(c *cli.Context) error {
if c.NArg() > 0 { if c.NArg() > 0 {
return fmt.Errorf("invalid command: %q", c.Args().First()) return fmt.Errorf("invalid command: %q", c.Args().First())
} }
if err := initialize(c); err != nil { if err := initialize(c); err != nil {
return err return err
} }
var ( var (
ui core.UIClientAPI ui core.UIClientAPI
) )
if c.Bool(stdiouiFlag.Name) { if c.Bool(stdiouiFlag.Name) {
log.Info("Using stdin/stdout as UI-channel") log.Info("Using stdin/stdout as UI-channel")
ui = core.NewStdIOUI() ui = core.NewStdIOUI()
} else { } else {
log.Info("Using CLI as UI-channel") log.Info("Using CLI as UI-channel")
ui = core.NewCommandlineUI() ui = core.NewCommandlineUI()
} }
// 4bytedb data // 4bytedb data
fourByteLocal := c.String(customDBFlag.Name) fourByteLocal := c.String(customDBFlag.Name)
db, err := fourbyte.NewWithFile(fourByteLocal) db, err := fourbyte.NewWithFile(fourByteLocal)
if err != nil { if err != nil {
utils.Fatalf(err.Error()) utils.Fatalf(err.Error())
} }
embeds, locals := db.Size() embeds, locals := db.Size()
log.Info("Loaded 4byte database", "embeds", embeds, "locals", locals, "local", fourByteLocal) log.Info("Loaded 4byte database", "embeds", embeds, "locals", locals, "local", fourByteLocal)
@ -690,6 +725,7 @@ func signer(c *cli.Context) error {
) )
configDir := c.String(configdirFlag.Name) configDir := c.String(configdirFlag.Name)
if stretchedKey, err := readMasterKey(c, ui); err != nil { if stretchedKey, err := readMasterKey(c, ui); err != nil {
log.Warn("Failed to open master, rules disabled", "err", err) log.Warn("Failed to open master, rules disabled", "err", err)
} else { } else {
@ -714,6 +750,7 @@ func signer(c *cli.Context) error {
shasum := sha256.Sum256(ruleJS) shasum := sha256.Sum256(ruleJS)
foundShaSum := hex.EncodeToString(shasum[:]) foundShaSum := hex.EncodeToString(shasum[:])
storedShasum, _ := configStorage.Get("ruleset_sha256") storedShasum, _ := configStorage.Get("ruleset_sha256")
if storedShasum != foundShaSum { if storedShasum != foundShaSum {
log.Warn("Rule hash not attested, disabling", "hash", foundShaSum, "attested", storedShasum) log.Warn("Rule hash not attested, disabling", "hash", foundShaSum, "attested", storedShasum)
} else { } else {
@ -722,13 +759,16 @@ func signer(c *cli.Context) error {
if err != nil { if err != nil {
utils.Fatalf(err.Error()) utils.Fatalf(err.Error())
} }
ruleEngine.Init(string(ruleJS)) ruleEngine.Init(string(ruleJS))
ui = ruleEngine ui = ruleEngine
log.Info("Rule engine configured", "file", c.String(ruleFlag.Name)) log.Info("Rule engine configured", "file", c.String(ruleFlag.Name))
} }
} }
} }
} }
var ( var (
chainId = c.Int64(chainIdFlag.Name) chainId = c.Int64(chainIdFlag.Name)
ksLoc = c.String(keystoreFlag.Name) ksLoc = c.String(keystoreFlag.Name)
@ -737,8 +777,10 @@ func signer(c *cli.Context) error {
nousb = c.Bool(utils.NoUSBFlag.Name) nousb = c.Bool(utils.NoUSBFlag.Name)
scpath = c.String(utils.SmartCardDaemonPathFlag.Name) scpath = c.String(utils.SmartCardDaemonPathFlag.Name)
) )
log.Info("Starting signer", "chainid", chainId, "keystore", ksLoc, log.Info("Starting signer", "chainid", chainId, "keystore", ksLoc,
"light-kdf", lightKdf, "advanced", advanced) "light-kdf", lightKdf, "advanced", advanced)
am := core.StartClefAccountManager(ksLoc, nousb, lightKdf, scpath) am := core.StartClefAccountManager(ksLoc, nousb, lightKdf, scpath)
apiImpl := core.NewSignerAPI(am, chainId, nousb, ui, db, advanced, pwStorage) apiImpl := core.NewSignerAPI(am, chainId, nousb, ui, db, advanced, pwStorage)
@ -753,6 +795,7 @@ func signer(c *cli.Context) error {
if err != nil { if err != nil {
utils.Fatalf(err.Error()) utils.Fatalf(err.Error())
} }
log.Info("Audit logs configured", "file", logfile) log.Info("Audit logs configured", "file", logfile)
} }
// register signer API with server // register signer API with server
@ -760,6 +803,7 @@ func signer(c *cli.Context) error {
extapiURL = "n/a" extapiURL = "n/a"
ipcapiURL = "n/a" ipcapiURL = "n/a"
) )
rpcAPI := []rpc.API{ rpcAPI := []rpc.API{
{ {
Namespace: "account", Namespace: "account",
@ -772,10 +816,12 @@ func signer(c *cli.Context) error {
cors := utils.SplitAndTrim(c.String(utils.HTTPCORSDomainFlag.Name)) cors := utils.SplitAndTrim(c.String(utils.HTTPCORSDomainFlag.Name))
srv := rpc.NewServer(0, 0) srv := rpc.NewServer(0, 0)
err := node.RegisterApis(rpcAPI, []string{"account"}, srv) err := node.RegisterApis(rpcAPI, []string{"account"}, srv)
if err != nil { if err != nil {
utils.Fatalf("Could not register API: %w", err) utils.Fatalf("Could not register API: %w", err)
} }
handler := node.NewHTTPHandlerStack(srv, cors, vhosts, nil) handler := node.NewHTTPHandlerStack(srv, cors, vhosts, nil)
// set port // set port
@ -783,10 +829,12 @@ func signer(c *cli.Context) error {
// start http server // start http server
httpEndpoint := fmt.Sprintf("%s:%d", c.String(utils.HTTPListenAddrFlag.Name), port) httpEndpoint := fmt.Sprintf("%s:%d", c.String(utils.HTTPListenAddrFlag.Name), port)
httpServer, addr, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler) httpServer, addr, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler)
if err != nil { if err != nil {
utils.Fatalf("Could not start RPC api: %v", err) utils.Fatalf("Could not start RPC api: %v", err)
} }
extapiURL = fmt.Sprintf("http://%v/", addr) extapiURL = fmt.Sprintf("http://%v/", addr)
log.Info("HTTP endpoint opened", "url", extapiURL) log.Info("HTTP endpoint opened", "url", extapiURL)
@ -800,11 +848,14 @@ func signer(c *cli.Context) error {
if !c.Bool(utils.IPCDisabledFlag.Name) { if !c.Bool(utils.IPCDisabledFlag.Name) {
givenPath := c.String(utils.IPCPathFlag.Name) givenPath := c.String(utils.IPCPathFlag.Name)
ipcapiURL = ipcEndpoint(filepath.Join(givenPath, "clef.ipc"), configDir) ipcapiURL = ipcEndpoint(filepath.Join(givenPath, "clef.ipc"), configDir)
listener, _, err := rpc.StartIPCEndpoint(ipcapiURL, rpcAPI) listener, _, err := rpc.StartIPCEndpoint(ipcapiURL, rpcAPI)
if err != nil { if err != nil {
utils.Fatalf("Could not start IPC api: %v", err) utils.Fatalf("Could not start IPC api: %v", err)
} }
log.Info("IPC endpoint opened", "url", ipcapiURL) log.Info("IPC endpoint opened", "url", ipcapiURL)
defer func() { defer func() {
listener.Close() listener.Close()
log.Info("IPC endpoint closed", "url", ipcapiURL) log.Info("IPC endpoint closed", "url", ipcapiURL)
@ -813,8 +864,10 @@ func signer(c *cli.Context) error {
if c.Bool(testFlag.Name) { if c.Bool(testFlag.Name) {
log.Info("Performing UI test") log.Info("Performing UI test")
go testExternalUI(apiImpl) go testExternalUI(apiImpl)
} }
ui.OnSignerStartup(core.StartupInfo{ ui.OnSignerStartup(core.StartupInfo{
Info: map[string]interface{}{ Info: map[string]interface{}{
"intapi_version": core.InternalAPIVersion, "intapi_version": core.InternalAPIVersion,
@ -845,8 +898,10 @@ func DefaultConfigDir() string {
if appdata != "" { if appdata != "" {
return filepath.Join(appdata, "Signer") return filepath.Join(appdata, "Signer")
} }
return filepath.Join(home, "AppData", "Roaming", "Signer") return filepath.Join(home, "AppData", "Roaming", "Signer")
} }
return filepath.Join(home, ".clef") return filepath.Join(home, ".clef")
} }
// As we cannot guess a stable location, return empty and handle later // As we cannot guess a stable location, return empty and handle later
@ -864,6 +919,7 @@ func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) {
} else { } else {
file = filepath.Join(configDir, "masterseed.json") file = filepath.Join(configDir, "masterseed.json")
} }
if err := checkFile(file); err != nil { if err := checkFile(file); err != nil {
return nil, err return nil, err
} }
@ -872,6 +928,7 @@ func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
var password string var password string
// If ui is not nil, get the password from ui. // If ui is not nil, get the password from ui.
if ui != nil { if ui != nil {
@ -882,23 +939,28 @@ func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
password = resp.Text password = resp.Text
} else { } else {
password = utils.GetPassPhrase("Decrypt master seed of clef", false) password = utils.GetPassPhrase("Decrypt master seed of clef", false)
} }
masterSeed, err := decryptSeed(cipherKey, password) masterSeed, err := decryptSeed(cipherKey, password)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to decrypt the master seed of clef") return nil, fmt.Errorf("failed to decrypt the master seed of clef")
} }
if len(masterSeed) < 256 { if len(masterSeed) < 256 {
return nil, fmt.Errorf("master seed of insufficient length, expected >255 bytes, got %d", len(masterSeed)) return nil, fmt.Errorf("master seed of insufficient length, expected >255 bytes, got %d", len(masterSeed))
} }
// Create vault location // Create vault location
vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), masterSeed)[:10])) vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), masterSeed)[:10]))
err = os.Mkdir(vaultLocation, 0700) err = os.Mkdir(vaultLocation, 0700)
if err != nil && !os.IsExist(err) { if err != nil && !os.IsExist(err) {
return nil, err return nil, err
} }
return masterSeed, nil return masterSeed, nil
} }
@ -916,6 +978,7 @@ func checkFile(filename string) error {
if runtime.GOOS != "windows" && info.Mode().Perm()&0377 != 0 { if runtime.GOOS != "windows" && info.Mode().Perm()&0377 != 0 {
return fmt.Errorf("file (%v) has insecure file permissions (%v)", filename, info.Mode().String()) return fmt.Errorf("file (%v) has insecure file permissions (%v)", filename, info.Mode().String())
} }
return nil return nil
} }
@ -928,9 +991,11 @@ func confirm(text string) bool {
if err != nil { if err != nil {
log.Crit("Failed to read user input", "err", err) log.Crit("Failed to read user input", "err", err)
} }
if text := strings.TrimSpace(text); text == "ok" { if text := strings.TrimSpace(text); text == "ok" {
return true return true
} }
return false return false
} }
@ -954,6 +1019,7 @@ func testExternalUI(api *core.SignerAPI) {
if err != nil { if err != nil {
addErr(err.Error()) addErr(err.Error())
} }
return resp.Text return resp.Text
} }
expectResponse := func(testcase, question, expect string) { expectResponse := func(testcase, question, expect string) {
@ -965,6 +1031,7 @@ func testExternalUI(api *core.SignerAPI) {
if err == nil || err == accounts.ErrUnknownAccount { if err == nil || err == accounts.ErrUnknownAccount {
return return
} }
addErr(fmt.Sprintf("%v: expected no error, got %v", testcase, err.Error())) addErr(fmt.Sprintf("%v: expected no error, got %v", testcase, err.Error()))
} }
expectDeny := func(testcase string, err error) { expectDeny := func(testcase string, err error) {
@ -972,6 +1039,7 @@ func testExternalUI(api *core.SignerAPI) {
addErr(fmt.Sprintf("%v: expected ErrRequestDenied, got %v", testcase, err)) addErr(fmt.Sprintf("%v: expected ErrRequestDenied, got %v", testcase, err))
} }
} }
var delay = 1 * time.Second var delay = 1 * time.Second
// Test display of info and error // Test display of info and error
{ {
@ -985,6 +1053,7 @@ func testExternalUI(api *core.SignerAPI) {
{ // Sign data test - clique header { // Sign data test - clique header
api.UI.ShowInfo("Please approve the next request for signing a clique header") api.UI.ShowInfo("Please approve the next request for signing a clique header")
time.Sleep(delay) time.Sleep(delay)
cliqueHeader := types.Header{ cliqueHeader := types.Header{
ParentHash: common.HexToHash("0000H45H"), ParentHash: common.HexToHash("0000H45H"),
UncleHash: common.HexToHash("0000H45H"), UncleHash: common.HexToHash("0000H45H"),
@ -1000,10 +1069,12 @@ func testExternalUI(api *core.SignerAPI) {
Extra: []byte("Extra data Extra data Extra data Extra data Extra data Extra data Extra data Extra data"), Extra: []byte("Extra data Extra data Extra data Extra data Extra data Extra data Extra data Extra data"),
MixDigest: common.HexToHash("0x0000H45H"), MixDigest: common.HexToHash("0x0000H45H"),
} }
cliqueRlp, err := rlp.EncodeToBytes(cliqueHeader) cliqueRlp, err := rlp.EncodeToBytes(cliqueHeader)
if err != nil { if err != nil {
utils.Fatalf("Should not error: %v", err) utils.Fatalf("Should not error: %v", err)
} }
addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
_, err = api.SignData(ctx, accounts.MimetypeClique, *addr, hexutil.Encode(cliqueRlp)) _, err = api.SignData(ctx, accounts.MimetypeClique, *addr, hexutil.Encode(cliqueRlp))
expectApprove("signdata - clique header", err) expectApprove("signdata - clique header", err)
@ -1011,10 +1082,12 @@ func testExternalUI(api *core.SignerAPI) {
{ // Sign data test - typed data { // Sign data test - typed data
api.UI.ShowInfo("Please approve the next request for signing EIP-712 typed data") api.UI.ShowInfo("Please approve the next request for signing EIP-712 typed data")
time.Sleep(delay) time.Sleep(delay)
addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
data := `{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Person":[{"name":"name","type":"string"},{"name":"test","type":"uint8"},{"name":"wallet","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person"},{"name":"contents","type":"string"}]},"primaryType":"Mail","domain":{"name":"Ether Mail","version":"1","chainId":"1","verifyingContract":"0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"},"message":{"from":{"name":"Cow","test":"3","wallet":"0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB","test":"2"},"contents":"Hello, Bob!"}}` data := `{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Person":[{"name":"name","type":"string"},{"name":"test","type":"uint8"},{"name":"wallet","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person"},{"name":"contents","type":"string"}]},"primaryType":"Mail","domain":{"name":"Ether Mail","version":"1","chainId":"1","verifyingContract":"0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"},"message":{"from":{"name":"Cow","test":"3","wallet":"0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB","test":"2"},"contents":"Hello, Bob!"}}`
//_, err := api.SignData(ctx, accounts.MimetypeTypedData, *addr, hexutil.Encode([]byte(data))) //_, err := api.SignData(ctx, accounts.MimetypeTypedData, *addr, hexutil.Encode([]byte(data)))
var typedData apitypes.TypedData var typedData apitypes.TypedData
json.Unmarshal([]byte(data), &typedData) json.Unmarshal([]byte(data), &typedData)
_, err := api.SignTypedData(ctx, *addr, typedData) _, err := api.SignTypedData(ctx, *addr, typedData)
expectApprove("sign 712 typed data", err) expectApprove("sign 712 typed data", err)
@ -1022,6 +1095,7 @@ func testExternalUI(api *core.SignerAPI) {
{ // Sign data test - plain text { // Sign data test - plain text
api.UI.ShowInfo("Please approve the next request for signing text") api.UI.ShowInfo("Please approve the next request for signing text")
time.Sleep(delay) time.Sleep(delay)
addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
_, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world"))) _, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world")))
expectApprove("signdata - text", err) expectApprove("signdata - text", err)
@ -1029,6 +1103,7 @@ func testExternalUI(api *core.SignerAPI) {
{ // Sign data test - plain text reject { // Sign data test - plain text reject
api.UI.ShowInfo("Please deny the next request for signing text") api.UI.ShowInfo("Please deny the next request for signing text")
time.Sleep(delay) time.Sleep(delay)
addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
_, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world"))) _, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world")))
expectDeny("signdata - text", err) expectDeny("signdata - text", err)
@ -1036,6 +1111,7 @@ func testExternalUI(api *core.SignerAPI) {
{ // Sign transaction { // Sign transaction
api.UI.ShowInfo("Please reject next transaction") api.UI.ShowInfo("Please reject next transaction")
time.Sleep(delay) time.Sleep(delay)
data := hexutil.Bytes([]byte{}) data := hexutil.Bytes([]byte{})
to := common.NewMixedcaseAddress(a) to := common.NewMixedcaseAddress(a)
tx := apitypes.SendTxArgs{ tx := apitypes.SendTxArgs{
@ -1055,12 +1131,14 @@ func testExternalUI(api *core.SignerAPI) {
{ // Listing { // Listing
api.UI.ShowInfo("Please reject listing-request") api.UI.ShowInfo("Please reject listing-request")
time.Sleep(delay) time.Sleep(delay)
_, err := api.List(ctx) _, err := api.List(ctx)
expectDeny("list", err) expectDeny("list", err)
} }
{ // Import { // Import
api.UI.ShowInfo("Please reject new account-request") api.UI.ShowInfo("Please reject new account-request")
time.Sleep(delay) time.Sleep(delay)
_, err := api.New(ctx) _, err := api.New(ctx)
expectDeny("newaccount", err) expectDeny("newaccount", err)
} }
@ -1074,6 +1152,7 @@ func testExternalUI(api *core.SignerAPI) {
for _, e := range errs { for _, e := range errs {
log.Error(e) log.Error(e)
} }
result := fmt.Sprintf("Tests completed. %d errors:\n%s\n", len(errs), strings.Join(errs, "\n")) result := fmt.Sprintf("Tests completed. %d errors:\n%s\n", len(errs), strings.Join(errs, "\n"))
api.UI.ShowInfo(result) api.UI.ShowInfo(result)
} }
@ -1091,6 +1170,7 @@ func encryptSeed(seed []byte, auth []byte, scryptN, scryptP int) ([]byte, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return json.Marshal(&encryptedSeedStorage{"Clef seed", 1, cryptoStruct}) return json.Marshal(&encryptedSeedStorage{"Clef seed", 1, cryptoStruct})
} }
@ -1100,13 +1180,16 @@ func decryptSeed(keyjson []byte, auth string) ([]byte, error) {
if err := json.Unmarshal(keyjson, &encSeed); err != nil { if err := json.Unmarshal(keyjson, &encSeed); err != nil {
return nil, err return nil, err
} }
if encSeed.Version != 1 { if encSeed.Version != 1 {
log.Warn(fmt.Sprintf("unsupported encryption format of seed: %d, operation will likely fail", encSeed.Version)) log.Warn(fmt.Sprintf("unsupported encryption format of seed: %d, operation will likely fail", encSeed.Version))
} }
seed, err := keystore.DecryptDataV3(encSeed.Params, auth) seed, err := keystore.DecryptDataV3(encSeed.Params, auth)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return seed, err return seed, err
} }
@ -1218,7 +1301,9 @@ func GenDoc(ctx *cli.Context) error {
"The `OnApproved` method cannot be responded to, it's purely informative" "The `OnApproved` method cannot be responded to, it's purely informative"
rlpdata := common.FromHex("0xf85d640101948a8eafb1cf62bfbeb1741769dae1a9dd47996192018026a0716bd90515acb1e68e5ac5867aa11a1e65399c3349d479f5fb698554ebc6f293a04e8a4ebfff434e971e0ef12c5bf3a881b06fd04fc3f8b8a7291fb67a26a1d4ed") rlpdata := common.FromHex("0xf85d640101948a8eafb1cf62bfbeb1741769dae1a9dd47996192018026a0716bd90515acb1e68e5ac5867aa11a1e65399c3349d479f5fb698554ebc6f293a04e8a4ebfff434e971e0ef12c5bf3a881b06fd04fc3f8b8a7291fb67a26a1d4ed")
var tx types.Transaction var tx types.Transaction
tx.UnmarshalBinary(rlpdata) tx.UnmarshalBinary(rlpdata)
add("OnApproved - SignTransactionResult", desc, &ethapi.SignTransactionResult{Raw: rlpdata, Tx: &tx}) add("OnApproved - SignTransactionResult", desc, &ethapi.SignTransactionResult{Raw: rlpdata, Tx: &tx})
} }
@ -1256,6 +1341,7 @@ func GenDoc(ctx *cli.Context) error {
fmt.Println(`## UI Client interface fmt.Println(`## UI Client interface
These data types are defined in the channel between clef and the UI`) These data types are defined in the channel between clef and the UI`)
for _, elem := range output { for _, elem := range output {
fmt.Println(elem) fmt.Println(elem)
} }

View file

@ -42,6 +42,7 @@ func init() {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(1) os.Exit(1)
} }
os.Exit(0) os.Exit(0)
}) })
} }

View file

@ -67,6 +67,7 @@ func newCrawler(input nodeSet, disc resolver, iters ...enode.Iterator) *crawler
for id, n := range input { for id, n := range input {
c.output[id] = n c.output[id] = n
} }
return c return c
} }
@ -82,8 +83,10 @@ func (c *crawler) run(timeout time.Duration, nthreads int) nodeSet {
if nthreads < 1 { if nthreads < 1 {
nthreads = 1 nthreads = 1
} }
defer timeoutTimer.Stop() defer timeoutTimer.Stop()
defer statusTicker.Stop() defer statusTicker.Stop()
for _, it := range c.iters { for _, it := range c.iters {
go c.runIterator(doneCh, it) go c.runIterator(doneCh, it)
} }
@ -152,18 +155,22 @@ loop:
} }
close(c.closed) close(c.closed)
for _, it := range c.iters { for _, it := range c.iters {
it.Close() it.Close()
} }
for ; liveIters > 0; liveIters-- { for ; liveIters > 0; liveIters-- {
<-doneCh <-doneCh
} }
wg.Wait() wg.Wait()
return c.output return c.output
} }
func (c *crawler) runIterator(done chan<- enode.Iterator, it enode.Iterator) { func (c *crawler) runIterator(done chan<- enode.Iterator, it enode.Iterator) {
defer func() { done <- it }() defer func() { done <- it }()
for it.Next() { for it.Next() {
select { select {
case c.ch <- it.Node(): case c.ch <- it.Node():
@ -195,20 +202,24 @@ func (c *crawler) updateNode(n *enode.Node) int {
log.Debug("Skipping node", "id", n.ID()) log.Debug("Skipping node", "id", n.ID())
return nodeSkipIncompat return nodeSkipIncompat
} }
node.Score /= 2 node.Score /= 2
} else { } else {
node.N = nn node.N = nn
node.Seq = nn.Seq() node.Seq = nn.Seq()
node.Score++ node.Score++
if node.FirstResponse.IsZero() { if node.FirstResponse.IsZero() {
node.FirstResponse = node.LastCheck node.FirstResponse = node.LastCheck
status = nodeAdded status = nodeAdded
} }
node.LastResponse = node.LastCheck node.LastResponse = node.LastCheck
} }
// Store/update node in output set. // Store/update node in output set.
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
if node.Score <= 0 { if node.Score <= 0 {
log.Debug("Removing node", "id", n.ID()) log.Debug("Removing node", "id", n.ID())
delete(c.output, n.ID()) delete(c.output, n.ID())

View file

@ -143,19 +143,24 @@ var discoveryNodeFlags = []cli.Flag{
func discv4Ping(ctx *cli.Context) error { func discv4Ping(ctx *cli.Context) error {
n := getNodeArg(ctx) n := getNodeArg(ctx)
disc := startV4(ctx) disc := startV4(ctx)
defer disc.Close() defer disc.Close()
start := time.Now() start := time.Now()
if err := disc.Ping(n); err != nil { if err := disc.Ping(n); err != nil {
return fmt.Errorf("node didn't respond: %v", err) return fmt.Errorf("node didn't respond: %v", err)
} }
fmt.Printf("node responded to ping (RTT %v).\n", time.Since(start)) fmt.Printf("node responded to ping (RTT %v).\n", time.Since(start))
return nil return nil
} }
func discv4RequestRecord(ctx *cli.Context) error { func discv4RequestRecord(ctx *cli.Context) error {
n := getNodeArg(ctx) n := getNodeArg(ctx)
disc := startV4(ctx) disc := startV4(ctx)
defer disc.Close() defer disc.Close()
@ -163,16 +168,20 @@ func discv4RequestRecord(ctx *cli.Context) error {
if err != nil { if err != nil {
return fmt.Errorf("can't retrieve record: %v", err) return fmt.Errorf("can't retrieve record: %v", err)
} }
fmt.Println(respN.String()) fmt.Println(respN.String())
return nil return nil
} }
func discv4Resolve(ctx *cli.Context) error { func discv4Resolve(ctx *cli.Context) error {
n := getNodeArg(ctx) n := getNodeArg(ctx)
disc := startV4(ctx) disc := startV4(ctx)
defer disc.Close() defer disc.Close()
fmt.Println(disc.Resolve(n).String()) fmt.Println(disc.Resolve(n).String())
return nil return nil
} }
@ -180,19 +189,23 @@ func discv4ResolveJSON(ctx *cli.Context) error {
if ctx.NArg() < 1 { if ctx.NArg() < 1 {
return fmt.Errorf("need nodes file as argument") return fmt.Errorf("need nodes file as argument")
} }
nodesFile := ctx.Args().Get(0) nodesFile := ctx.Args().Get(0)
inputSet := make(nodeSet) inputSet := make(nodeSet)
if common.FileExist(nodesFile) { if common.FileExist(nodesFile) {
inputSet = loadNodesJSON(nodesFile) inputSet = loadNodesJSON(nodesFile)
} }
// Add extra nodes from command line arguments. // Add extra nodes from command line arguments.
var nodeargs []*enode.Node var nodeargs []*enode.Node
for i := 1; i < ctx.NArg(); i++ { for i := 1; i < ctx.NArg(); i++ {
n, err := parseNode(ctx.Args().Get(i)) n, err := parseNode(ctx.Args().Get(i))
if err != nil { if err != nil {
exit(err) exit(err)
} }
nodeargs = append(nodeargs, n) nodeargs = append(nodeargs, n)
} }
@ -203,6 +216,7 @@ func discv4ResolveJSON(ctx *cli.Context) error {
c.revalidateInterval = 0 c.revalidateInterval = 0
output := c.run(0, 1) output := c.run(0, 1)
writeNodesJSON(nodesFile, output) writeNodesJSON(nodesFile, output)
return nil return nil
} }
@ -210,8 +224,11 @@ func discv4Crawl(ctx *cli.Context) error {
if ctx.NArg() < 1 { if ctx.NArg() < 1 {
return fmt.Errorf("need nodes file as argument") return fmt.Errorf("need nodes file as argument")
} }
nodesFile := ctx.Args().First() nodesFile := ctx.Args().First()
var inputSet nodeSet var inputSet nodeSet
if common.FileExist(nodesFile) { if common.FileExist(nodesFile) {
inputSet = loadNodesJSON(nodesFile) inputSet = loadNodesJSON(nodesFile)
} }
@ -222,6 +239,7 @@ func discv4Crawl(ctx *cli.Context) error {
c.revalidateInterval = 10 * time.Minute c.revalidateInterval = 10 * time.Minute
output := c.run(ctx.Duration(crawlTimeoutFlag.Name), ctx.Int(crawlParallelismFlag.Name)) output := c.run(ctx.Duration(crawlTimeoutFlag.Name), ctx.Int(crawlParallelismFlag.Name))
writeNodesJSON(nodesFile, output) writeNodesJSON(nodesFile, output)
return nil return nil
} }
@ -231,9 +249,11 @@ func discv4Test(ctx *cli.Context) error {
if !ctx.IsSet(remoteEnodeFlag.Name) { if !ctx.IsSet(remoteEnodeFlag.Name) {
return fmt.Errorf("Missing -%v", remoteEnodeFlag.Name) return fmt.Errorf("Missing -%v", remoteEnodeFlag.Name)
} }
v4test.Remote = ctx.String(remoteEnodeFlag.Name) v4test.Remote = ctx.String(remoteEnodeFlag.Name)
v4test.Listen1 = ctx.String(testListen1Flag.Name) v4test.Listen1 = ctx.String(testListen1Flag.Name)
v4test.Listen2 = ctx.String(testListen2Flag.Name) v4test.Listen2 = ctx.String(testListen2Flag.Name)
return runTests(ctx, v4test.AllTests) return runTests(ctx, v4test.AllTests)
} }
@ -241,10 +261,12 @@ func discv4Test(ctx *cli.Context) error {
func startV4(ctx *cli.Context) *discover.UDPv4 { func startV4(ctx *cli.Context) *discover.UDPv4 {
ln, config := makeDiscoveryConfig(ctx) ln, config := makeDiscoveryConfig(ctx)
socket := listen(ctx, ln) socket := listen(ctx, ln)
disc, err := discover.ListenV4(socket, ln, config) disc, err := discover.ListenV4(socket, ln, config)
if err != nil { if err != nil {
exit(err) exit(err)
} }
return disc return disc
} }
@ -256,6 +278,7 @@ func makeDiscoveryConfig(ctx *cli.Context) (*enode.LocalNode, discover.Config) {
if err != nil { if err != nil {
exit(fmt.Errorf("-%s: %v", nodekeyFlag.Name, err)) exit(fmt.Errorf("-%s: %v", nodekeyFlag.Name, err))
} }
cfg.PrivateKey = key cfg.PrivateKey = key
} else { } else {
cfg.PrivateKey, _ = crypto.GenerateKey() cfg.PrivateKey, _ = crypto.GenerateKey()
@ -266,15 +289,19 @@ func makeDiscoveryConfig(ctx *cli.Context) (*enode.LocalNode, discover.Config) {
if err != nil { if err != nil {
exit(err) exit(err)
} }
cfg.Bootnodes = bn cfg.Bootnodes = bn
} }
dbpath := ctx.String(nodedbFlag.Name) dbpath := ctx.String(nodedbFlag.Name)
db, err := enode.OpenDB(dbpath) db, err := enode.OpenDB(dbpath)
if err != nil { if err != nil {
exit(err) exit(err)
} }
ln := enode.NewLocalNode(db, cfg.PrivateKey) ln := enode.NewLocalNode(db, cfg.PrivateKey)
return ln, cfg return ln, cfg
} }
@ -310,6 +337,7 @@ func listen(ctx *cli.Context, ln *enode.LocalNode) *net.UDPConn {
if addr == "" { if addr == "" {
addr = "0.0.0.0:0" addr = "0.0.0.0:0"
} }
socket, err := net.ListenPacket("udp4", addr) socket, err := net.ListenPacket("udp4", addr)
if err != nil { if err != nil {
exit(err) exit(err)
@ -317,12 +345,14 @@ func listen(ctx *cli.Context, ln *enode.LocalNode) *net.UDPConn {
// Configure UDP endpoint in ENR from listener address. // Configure UDP endpoint in ENR from listener address.
usocket := socket.(*net.UDPConn) usocket := socket.(*net.UDPConn)
uaddr := socket.LocalAddr().(*net.UDPAddr) uaddr := socket.LocalAddr().(*net.UDPAddr)
if uaddr.IP.IsUnspecified() { if uaddr.IP.IsUnspecified() {
ln.SetFallbackIP(net.IP{127, 0, 0, 1}) ln.SetFallbackIP(net.IP{127, 0, 0, 1})
} else { } else {
ln.SetFallbackIP(uaddr.IP) ln.SetFallbackIP(uaddr.IP)
} }
ln.SetFallbackUDP(uaddr.Port) ln.SetFallbackUDP(uaddr.Port)
// If an ENR endpoint is set explicitly on the command-line, override // If an ENR endpoint is set explicitly on the command-line, override
@ -347,20 +377,26 @@ func listen(ctx *cli.Context, ln *enode.LocalNode) *net.UDPConn {
func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) { func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) {
s := params.RinkebyBootnodes s := params.RinkebyBootnodes
if ctx.IsSet(bootnodesFlag.Name) { if ctx.IsSet(bootnodesFlag.Name) {
input := ctx.String(bootnodesFlag.Name) input := ctx.String(bootnodesFlag.Name)
if input == "" { if input == "" {
return nil, nil return nil, nil
} }
s = strings.Split(input, ",") s = strings.Split(input, ",")
} }
nodes := make([]*enode.Node, len(s)) nodes := make([]*enode.Node, len(s))
var err error var err error
for i, record := range s { for i, record := range s {
nodes[i], err = parseNode(record) nodes[i], err = parseNode(record)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid bootstrap node: %v", err) return nil, fmt.Errorf("invalid bootstrap node: %v", err)
} }
} }
return nodes, nil return nodes, nil
} }

View file

@ -81,19 +81,23 @@ var (
func discv5Ping(ctx *cli.Context) error { func discv5Ping(ctx *cli.Context) error {
n := getNodeArg(ctx) n := getNodeArg(ctx)
disc := startV5(ctx) disc := startV5(ctx)
defer disc.Close() defer disc.Close()
fmt.Println(disc.Ping(n)) fmt.Println(disc.Ping(n))
return nil return nil
} }
func discv5Resolve(ctx *cli.Context) error { func discv5Resolve(ctx *cli.Context) error {
n := getNodeArg(ctx) n := getNodeArg(ctx)
disc := startV5(ctx) disc := startV5(ctx)
defer disc.Close() defer disc.Close()
fmt.Println(disc.Resolve(n)) fmt.Println(disc.Resolve(n))
return nil return nil
} }
@ -101,8 +105,11 @@ func discv5Crawl(ctx *cli.Context) error {
if ctx.NArg() < 1 { if ctx.NArg() < 1 {
return fmt.Errorf("need nodes file as argument") return fmt.Errorf("need nodes file as argument")
} }
nodesFile := ctx.Args().First() nodesFile := ctx.Args().First()
var inputSet nodeSet var inputSet nodeSet
if common.FileExist(nodesFile) { if common.FileExist(nodesFile) {
inputSet = loadNodesJSON(nodesFile) inputSet = loadNodesJSON(nodesFile)
} }
@ -113,6 +120,7 @@ func discv5Crawl(ctx *cli.Context) error {
c.revalidateInterval = 10 * time.Minute c.revalidateInterval = 10 * time.Minute
output := c.run(ctx.Duration(crawlTimeoutFlag.Name), ctx.Int(crawlParallelismFlag.Name)) output := c.run(ctx.Duration(crawlTimeoutFlag.Name), ctx.Int(crawlParallelismFlag.Name))
writeNodesJSON(nodesFile, output) writeNodesJSON(nodesFile, output)
return nil return nil
} }
@ -123,6 +131,7 @@ func discv5Test(ctx *cli.Context) error {
Listen1: ctx.String(testListen1Flag.Name), Listen1: ctx.String(testListen1Flag.Name),
Listen2: ctx.String(testListen2Flag.Name), Listen2: ctx.String(testListen2Flag.Name),
} }
return runTests(ctx, suite.AllTests()) return runTests(ctx, suite.AllTests())
} }
@ -138,9 +147,11 @@ func discv5Listen(ctx *cli.Context) error {
func startV5(ctx *cli.Context) *discover.UDPv5 { func startV5(ctx *cli.Context) *discover.UDPv5 {
ln, config := makeDiscoveryConfig(ctx) ln, config := makeDiscoveryConfig(ctx)
socket := listen(ctx, ln) socket := listen(ctx, ln)
disc, err := discover.ListenV5(socket, ln, config) disc, err := discover.ListenV5(socket, ln, config)
if err != nil { if err != nil {
exit(err) exit(err)
} }
return disc return disc
} }

View file

@ -51,10 +51,12 @@ func newCloudflareClient(ctx *cli.Context) *cloudflareClient {
if token == "" { if token == "" {
exit(fmt.Errorf("need cloudflare API token to proceed")) exit(fmt.Errorf("need cloudflare API token to proceed"))
} }
api, err := cloudflare.NewWithAPIToken(token) api, err := cloudflare.NewWithAPIToken(token)
if err != nil { if err != nil {
exit(fmt.Errorf("can't create Cloudflare client: %v", err)) exit(fmt.Errorf("can't create Cloudflare client: %v", err))
} }
return &cloudflareClient{ return &cloudflareClient{
API: api, API: api,
zoneID: ctx.String(cloudflareZoneIDFlag.Name), zoneID: ctx.String(cloudflareZoneIDFlag.Name),
@ -66,7 +68,9 @@ func (c *cloudflareClient) deploy(name string, t *dnsdisc.Tree) error {
if err := c.checkZone(name); err != nil { if err := c.checkZone(name); err != nil {
return err return err
} }
records := t.ToTXT(name) records := t.ToTXT(name)
return c.uploadRecords(name, records) return c.uploadRecords(name, records)
} }
@ -74,31 +78,39 @@ func (c *cloudflareClient) deploy(name string, t *dnsdisc.Tree) error {
func (c *cloudflareClient) checkZone(name string) error { func (c *cloudflareClient) checkZone(name string) error {
if c.zoneID == "" { if c.zoneID == "" {
log.Info(fmt.Sprintf("Finding CloudFlare zone ID for %s", name)) log.Info(fmt.Sprintf("Finding CloudFlare zone ID for %s", name))
id, err := c.ZoneIDByName(name) id, err := c.ZoneIDByName(name)
if err != nil { if err != nil {
return err return err
} }
c.zoneID = id c.zoneID = id
} }
log.Info(fmt.Sprintf("Checking Permissions on zone %s", c.zoneID)) log.Info(fmt.Sprintf("Checking Permissions on zone %s", c.zoneID))
zone, err := c.ZoneDetails(context.Background(), c.zoneID) zone, err := c.ZoneDetails(context.Background(), c.zoneID)
if err != nil { if err != nil {
return err return err
} }
if !strings.HasSuffix(name, "."+zone.Name) { if !strings.HasSuffix(name, "."+zone.Name) {
return fmt.Errorf("CloudFlare zone name %q does not match name %q to be deployed", zone.Name, name) return fmt.Errorf("CloudFlare zone name %q does not match name %q to be deployed", zone.Name, name)
} }
needPerms := map[string]bool{"#zone:edit": false, "#zone:read": false} needPerms := map[string]bool{"#zone:edit": false, "#zone:read": false}
for _, perm := range zone.Permissions { for _, perm := range zone.Permissions {
if _, ok := needPerms[perm]; ok { if _, ok := needPerms[perm]; ok {
needPerms[perm] = true needPerms[perm] = true
} }
} }
for _, ok := range needPerms { for _, ok := range needPerms {
if !ok { if !ok {
return fmt.Errorf("wrong permissions on zone %s: %v", c.zoneID, needPerms) return fmt.Errorf("wrong permissions on zone %s: %v", c.zoneID, needPerms)
} }
} }
return nil return nil
} }
@ -111,18 +123,23 @@ func (c *cloudflareClient) uploadRecords(name string, records map[string]string)
for name, r := range records { for name, r := range records {
lrecords[strings.ToLower(name)] = r lrecords[strings.ToLower(name)] = r
} }
records = lrecords records = lrecords
log.Info(fmt.Sprintf("Retrieving existing TXT records on %s", name)) log.Info(fmt.Sprintf("Retrieving existing TXT records on %s", name))
entries, err := c.DNSRecords(context.Background(), c.zoneID, cloudflare.DNSRecord{Type: "TXT"}) entries, err := c.DNSRecords(context.Background(), c.zoneID, cloudflare.DNSRecord{Type: "TXT"})
if err != nil { if err != nil {
return err return err
} }
existing := make(map[string]cloudflare.DNSRecord) existing := make(map[string]cloudflare.DNSRecord)
for _, entry := range entries { for _, entry := range entries {
if !strings.HasSuffix(entry.Name, name) { if !strings.HasSuffix(entry.Name, name) {
continue continue
} }
existing[strings.ToLower(entry.Name)] = entry existing[strings.ToLower(entry.Name)] = entry
} }
@ -132,28 +149,35 @@ func (c *cloudflareClient) uploadRecords(name string, records map[string]string)
created := 0 created := 0
updated := 0 updated := 0
skipped := 0 skipped := 0
for path, val := range records { for path, val := range records {
old, exists := existing[path] old, exists := existing[path]
if !exists { if !exists {
// Entry is unknown, push a new one to Cloudflare. // Entry is unknown, push a new one to Cloudflare.
log.Debug(fmt.Sprintf("Creating %s = %q", path, val)) log.Debug(fmt.Sprintf("Creating %s = %q", path, val))
created++ created++
ttl := rootTTL ttl := rootTTL
if path != name { if path != name {
ttl = treeNodeTTLCloudflare // Max TTL permitted by Cloudflare ttl = treeNodeTTLCloudflare // Max TTL permitted by Cloudflare
} }
record := cloudflare.DNSRecord{Type: "TXT", Name: path, Content: val, TTL: ttl} record := cloudflare.DNSRecord{Type: "TXT", Name: path, Content: val, TTL: ttl}
_, err = c.CreateDNSRecord(context.Background(), c.zoneID, record) _, err = c.CreateDNSRecord(context.Background(), c.zoneID, record)
} else if old.Content != val { } else if old.Content != val {
// Entry already exists, only change its content. // Entry already exists, only change its content.
log.Info(fmt.Sprintf("Updating %s from %q to %q", path, old.Content, val)) log.Info(fmt.Sprintf("Updating %s from %q to %q", path, old.Content, val))
updated++ updated++
old.Content = val old.Content = val
err = c.UpdateDNSRecord(context.Background(), c.zoneID, old.ID, old) err = c.UpdateDNSRecord(context.Background(), c.zoneID, old.ID, old)
} else { } else {
skipped++ skipped++
log.Debug(fmt.Sprintf("Skipping %s = %q", path, val)) log.Debug(fmt.Sprintf("Skipping %s = %q", path, val))
} }
if err != nil { if err != nil {
return fmt.Errorf("failed to publish %s: %v", path, err) return fmt.Errorf("failed to publish %s: %v", path, err)
} }
@ -164,18 +188,22 @@ func (c *cloudflareClient) uploadRecords(name string, records map[string]string)
deleted := 0 deleted := 0
log.Info("Deleting stale DNS entries") log.Info("Deleting stale DNS entries")
for path, entry := range existing { for path, entry := range existing {
if _, ok := records[path]; ok { if _, ok := records[path]; ok {
continue continue
} }
// Stale entry, nuke it. // Stale entry, nuke it.
log.Debug(fmt.Sprintf("Deleting %s = %q", path, entry.Content)) log.Debug(fmt.Sprintf("Deleting %s = %q", path, entry.Content))
deleted++ deleted++
if err := c.DeleteDNSRecord(context.Background(), c.zoneID, entry.ID); err != nil { if err := c.DeleteDNSRecord(context.Background(), c.zoneID, entry.ID); err != nil {
return fmt.Errorf("failed to delete %s: %v", path, err) return fmt.Errorf("failed to delete %s: %v", path, err)
} }
} }
log.Info("Deleted stale DNS entries", "count", deleted) log.Info("Deleted stale DNS entries", "count", deleted)
return nil return nil
} }

View file

@ -81,15 +81,20 @@ type recordSet struct {
func newRoute53Client(ctx *cli.Context) *route53Client { func newRoute53Client(ctx *cli.Context) *route53Client {
akey := ctx.String(route53AccessKeyFlag.Name) akey := ctx.String(route53AccessKeyFlag.Name)
asec := ctx.String(route53AccessSecretFlag.Name) asec := ctx.String(route53AccessSecretFlag.Name)
if akey == "" || asec == "" { if akey == "" || asec == "" {
exit(fmt.Errorf("need Route53 Access Key ID and secret to proceed")) exit(fmt.Errorf("need Route53 Access Key ID and secret to proceed"))
} }
creds := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(akey, asec, "")) creds := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(akey, asec, ""))
cfg, err := config.LoadDefaultConfig(context.Background(), config.WithCredentialsProvider(creds)) cfg, err := config.LoadDefaultConfig(context.Background(), config.WithCredentialsProvider(creds))
if err != nil { if err != nil {
exit(fmt.Errorf("can't initialize AWS configuration: %v", err)) exit(fmt.Errorf("can't initialize AWS configuration: %v", err))
} }
cfg.Region = ctx.String(route53RegionFlag.Name) cfg.Region = ctx.String(route53RegionFlag.Name)
return &route53Client{ return &route53Client{
api: route53.NewFromConfig(cfg), api: route53.NewFromConfig(cfg),
zoneID: ctx.String(route53ZoneIDFlag.Name), zoneID: ctx.String(route53ZoneIDFlag.Name),
@ -107,12 +112,15 @@ func (c *route53Client) deploy(name string, t *dnsdisc.Tree) error {
if err != nil { if err != nil {
return err return err
} }
log.Info(fmt.Sprintf("Found %d TXT records", len(existing))) log.Info(fmt.Sprintf("Found %d TXT records", len(existing)))
records := t.ToTXT(name) records := t.ToTXT(name)
changes := c.computeChanges(name, records, existing) changes := c.computeChanges(name, records, existing)
// Submit to API. // Submit to API.
comment := fmt.Sprintf("enrtree update of %s at seq %d", name, t.Seq()) comment := fmt.Sprintf("enrtree update of %s at seq %d", name, t.Seq())
return c.submitChanges(changes, comment) return c.submitChanges(changes, comment)
} }
@ -127,11 +135,13 @@ func (c *route53Client) deleteDomain(name string) error {
if err != nil { if err != nil {
return err return err
} }
log.Info(fmt.Sprintf("Found %d TXT records", len(existing))) log.Info(fmt.Sprintf("Found %d TXT records", len(existing)))
changes := makeDeletionChanges(existing, nil) changes := makeDeletionChanges(existing, nil)
// Submit to API. // Submit to API.
comment := "enrtree delete of " + name comment := "enrtree delete of " + name
return c.submitChanges(changes, comment) return c.submitChanges(changes, comment)
} }
@ -143,8 +153,10 @@ func (c *route53Client) submitChanges(changes []types.Change, comment string) er
} }
var err error var err error
batches := splitChanges(changes, route53ChangeSizeLimit, route53ChangeCountLimit) batches := splitChanges(changes, route53ChangeSizeLimit, route53ChangeCountLimit)
changesToCheck := make([]*route53.ChangeResourceRecordSetsOutput, len(batches)) changesToCheck := make([]*route53.ChangeResourceRecordSetsOutput, len(batches))
for i, changes := range batches { for i, changes := range batches {
log.Info(fmt.Sprintf("Submitting %d changes to Route53", len(changes))) log.Info(fmt.Sprintf("Submitting %d changes to Route53", len(changes)))
batch := &types.ChangeBatch{ batch := &types.ChangeBatch{
@ -152,6 +164,7 @@ func (c *route53Client) submitChanges(changes []types.Change, comment string) er
Comment: aws.String(fmt.Sprintf("%s (%d/%d)", comment, i+1, len(batches))), Comment: aws.String(fmt.Sprintf("%s (%d/%d)", comment, i+1, len(batches))),
} }
req := &route53.ChangeResourceRecordSetsInput{HostedZoneId: &c.zoneID, ChangeBatch: batch} req := &route53.ChangeResourceRecordSetsInput{HostedZoneId: &c.zoneID, ChangeBatch: batch}
changesToCheck[i], err = c.api.ChangeResourceRecordSets(context.TODO(), req) changesToCheck[i], err = c.api.ChangeResourceRecordSets(context.TODO(), req)
if err != nil { if err != nil {
return err return err
@ -162,7 +175,9 @@ func (c *route53Client) submitChanges(changes []types.Change, comment string) er
for _, change := range changesToCheck { for _, change := range changesToCheck {
log.Info(fmt.Sprintf("Waiting for change request %s", *change.ChangeInfo.Id)) log.Info(fmt.Sprintf("Waiting for change request %s", *change.ChangeInfo.Id))
wreq := &route53.GetChangeInput{Id: change.ChangeInfo.Id} wreq := &route53.GetChangeInput{Id: change.ChangeInfo.Id}
var count int var count int
for { for {
wresp, err := c.api.GetChange(context.TODO(), wreq) wresp, err := c.api.GetChange(context.TODO(), wreq)
if err != nil { if err != nil {
@ -178,6 +193,7 @@ func (c *route53Client) submitChanges(changes []types.Change, comment string) er
time.Sleep(30 * time.Second) time.Sleep(30 * time.Second)
} }
} }
return nil return nil
} }
@ -186,29 +202,36 @@ func (c *route53Client) checkZone(name string) (err error) {
if c.zoneID == "" { if c.zoneID == "" {
c.zoneID, err = c.findZoneID(name) c.zoneID, err = c.findZoneID(name)
} }
return err return err
} }
// findZoneID searches for the Zone ID containing the given domain. // findZoneID searches for the Zone ID containing the given domain.
func (c *route53Client) findZoneID(name string) (string, error) { func (c *route53Client) findZoneID(name string) (string, error) {
log.Info(fmt.Sprintf("Finding Route53 Zone ID for %s", name)) log.Info(fmt.Sprintf("Finding Route53 Zone ID for %s", name))
var req route53.ListHostedZonesByNameInput var req route53.ListHostedZonesByNameInput
for { for {
resp, err := c.api.ListHostedZonesByName(context.TODO(), &req) resp, err := c.api.ListHostedZonesByName(context.TODO(), &req)
if err != nil { if err != nil {
return "", err return "", err
} }
for _, zone := range resp.HostedZones { for _, zone := range resp.HostedZones {
if isSubdomain(name, *zone.Name) { if isSubdomain(name, *zone.Name) {
return *zone.Id, nil return *zone.Id, nil
} }
} }
if !resp.IsTruncated { if !resp.IsTruncated {
break break
} }
req.DNSName = resp.NextDNSName req.DNSName = resp.NextDNSName
req.HostedZoneId = resp.NextHostedZoneId req.HostedZoneId = resp.NextHostedZoneId
} }
return "", errors.New("can't find zone ID for " + name) return "", errors.New("can't find zone ID for " + name)
} }
@ -220,6 +243,7 @@ func (c *route53Client) computeChanges(name string, records map[string]string, e
for name, r := range records { for name, r := range records {
lrecords[strings.ToLower(name)] = r lrecords[strings.ToLower(name)] = r
} }
records = lrecords records = lrecords
var ( var (
@ -254,6 +278,7 @@ func (c *route53Client) computeChanges(name string, records map[string]string, e
upserts++ upserts++
} else { } else {
log.Debug(fmt.Sprintf("Skipping %s = %s", path, newValue)) log.Debug(fmt.Sprintf("Skipping %s = %s", path, newValue))
skips++ skips++
} }
} }
@ -270,12 +295,14 @@ func (c *route53Client) computeChanges(name string, records map[string]string, e
"upserts", upserts) "upserts", upserts)
// Ensure changes are in the correct order. // Ensure changes are in the correct order.
sortChanges(changes) sortChanges(changes)
return changes return changes
} }
// makeDeletionChanges creates record changes which delete all records not contained in 'keep'. // makeDeletionChanges creates record changes which delete all records not contained in 'keep'.
func makeDeletionChanges(records map[string]recordSet, keep map[string]string) []types.Change { func makeDeletionChanges(records map[string]recordSet, keep map[string]string) []types.Change {
var changes []types.Change var changes []types.Change
for path, set := range records { for path, set := range records {
if _, ok := keep[path]; ok { if _, ok := keep[path]; ok {
continue continue
@ -284,16 +311,19 @@ func makeDeletionChanges(records map[string]recordSet, keep map[string]string) [
log.Debug(fmt.Sprintf("Deleting %s = %s", path, strings.Join(set.values, ""))) log.Debug(fmt.Sprintf("Deleting %s = %s", path, strings.Join(set.values, "")))
changes = append(changes, newTXTChange("DELETE", path, set.ttl, set.values...)) changes = append(changes, newTXTChange("DELETE", path, set.ttl, set.values...))
} }
return changes return changes
} }
// sortChanges ensures DNS changes are in leaf-added -> root-changed -> leaf-deleted order. // sortChanges ensures DNS changes are in leaf-added -> root-changed -> leaf-deleted order.
func sortChanges(changes []types.Change) { func sortChanges(changes []types.Change) {
score := map[string]int{"CREATE": 1, "UPSERT": 2, "DELETE": 3} score := map[string]int{"CREATE": 1, "UPSERT": 2, "DELETE": 3}
sort.Slice(changes, func(i, j int) bool { sort.Slice(changes, func(i, j int) bool {
if changes[i].Action == changes[j].Action { if changes[i].Action == changes[j].Action {
return *changes[i].ResourceRecordSet.Name < *changes[j].ResourceRecordSet.Name return *changes[i].ResourceRecordSet.Name < *changes[j].ResourceRecordSet.Name
} }
return score[string(changes[i].Action)] < score[string(changes[j].Action)] return score[string(changes[i].Action)] < score[string(changes[j].Action)]
}) })
} }
@ -306,32 +336,38 @@ func splitChanges(changes []types.Change, sizeLimit, countLimit int) [][]types.C
batchSize int batchSize int
batchCount int batchCount int
) )
for _, ch := range changes { for _, ch := range changes {
// Start new batch if this change pushes the current one over the limit. // Start new batch if this change pushes the current one over the limit.
count := changeCount(ch) count := changeCount(ch)
size := changeSize(ch) * count size := changeSize(ch) * count
overSize := batchSize+size > sizeLimit overSize := batchSize+size > sizeLimit
overCount := batchCount+count > countLimit overCount := batchCount+count > countLimit
if len(batches) == 0 || overSize || overCount { if len(batches) == 0 || overSize || overCount {
batches = append(batches, nil) batches = append(batches, nil)
batchSize = 0 batchSize = 0
batchCount = 0 batchCount = 0
} }
batches[len(batches)-1] = append(batches[len(batches)-1], ch) batches[len(batches)-1] = append(batches[len(batches)-1], ch)
batchSize += size batchSize += size
batchCount += count batchCount += count
} }
return batches return batches
} }
// changeSize returns the RDATA size of a DNS change. // changeSize returns the RDATA size of a DNS change.
func changeSize(ch types.Change) int { func changeSize(ch types.Change) int {
size := 0 size := 0
for _, rr := range ch.ResourceRecordSet.ResourceRecords { for _, rr := range ch.ResourceRecordSet.ResourceRecords {
if rr.Value != nil { if rr.Value != nil {
size += len(*rr.Value) size += len(*rr.Value)
} }
} }
return size return size
} }
@ -339,6 +375,7 @@ func changeCount(ch types.Change) int {
if ch.Action == types.ChangeActionUpsert { if ch.Action == types.ChangeActionUpsert {
return 2 return 2
} }
return 1 return 1
} }
@ -349,20 +386,25 @@ func (c *route53Client) collectRecords(name string) (map[string]recordSet, error
existing := make(map[string]recordSet) existing := make(map[string]recordSet)
log.Info("Loading existing TXT records", "name", name, "zone", c.zoneID) log.Info("Loading existing TXT records", "name", name, "zone", c.zoneID)
for page := 0; ; page++ { for page := 0; ; page++ {
log.Debug("Loading existing TXT records", "name", name, "zone", c.zoneID, "page", page) log.Debug("Loading existing TXT records", "name", name, "zone", c.zoneID, "page", page)
resp, err := c.api.ListResourceRecordSets(context.TODO(), &req) resp, err := c.api.ListResourceRecordSets(context.TODO(), &req)
if err != nil { if err != nil {
return existing, err return existing, err
} }
for _, set := range resp.ResourceRecordSets { for _, set := range resp.ResourceRecordSets {
if !isSubdomain(*set.Name, name) || set.Type != types.RRTypeTxt { if !isSubdomain(*set.Name, name) || set.Type != types.RRTypeTxt {
continue continue
} }
s := recordSet{ttl: *set.TTL} s := recordSet{ttl: *set.TTL}
for _, rec := range set.ResourceRecords { for _, rec := range set.ResourceRecords {
s.values = append(s.values, *rec.Value) s.values = append(s.values, *rec.Value)
} }
name := strings.TrimSuffix(*set.Name, ".") name := strings.TrimSuffix(*set.Name, ".")
existing[name] = s existing[name] = s
} }
@ -381,6 +423,7 @@ func (c *route53Client) collectRecords(name string) (map[string]recordSet, error
req.StartRecordType = resp.NextRecordType req.StartRecordType = resp.NextRecordType
} }
log.Info("Loaded existing TXT records", "name", name, "zone", c.zoneID, "records", len(existing)) log.Info("Loaded existing TXT records", "name", name, "zone", c.zoneID, "records", len(existing))
return existing, nil return existing, nil
} }
@ -391,7 +434,9 @@ func newTXTChange(action, name string, ttl int64, values ...string) types.Change
Name: &name, Name: &name,
TTL: &ttl, TTL: &ttl,
} }
var rrs []types.ResourceRecord var rrs []types.ResourceRecord
for _, val := range values { for _, val := range values {
var rr types.ResourceRecord var rr types.ResourceRecord
rr.Value = aws.String(val) rr.Value = aws.String(val)
@ -410,19 +455,23 @@ func newTXTChange(action, name string, ttl int64, values ...string) types.Change
func isSubdomain(name, domain string) bool { func isSubdomain(name, domain string) bool {
domain = strings.TrimSuffix(domain, ".") domain = strings.TrimSuffix(domain, ".")
name = strings.TrimSuffix(name, ".") name = strings.TrimSuffix(name, ".")
return strings.HasSuffix("."+name, "."+domain) return strings.HasSuffix("."+name, "."+domain)
} }
// splitTXT splits value into a list of quoted 255-character strings. // splitTXT splits value into a list of quoted 255-character strings.
func splitTXT(value string) string { func splitTXT(value string) string {
var result strings.Builder var result strings.Builder
for len(value) > 0 { for len(value) > 0 {
rlen := len(value) rlen := len(value)
if rlen > 253 { if rlen > 253 {
rlen = 253 rlen = 253
} }
result.WriteString(strconv.Quote(value[:rlen])) result.WriteString(strconv.Quote(value[:rlen]))
value = value[rlen:] value = value[rlen:]
} }
return result.String() return result.String()
} }

View file

@ -135,6 +135,7 @@ func TestRoute53ChangeSort(t *testing.T) {
} }
var client route53Client var client route53Client
changes := client.computeChanges("n", testTree1, testTree0) changes := client.computeChanges("n", testTree1, testTree0)
if !reflect.DeepEqual(changes, wantChanges) { if !reflect.DeepEqual(changes, wantChanges) {
t.Fatalf("wrong changes (got %d, want %d)", len(changes), len(wantChanges)) t.Fatalf("wrong changes (got %d, want %d)", len(changes), len(wantChanges))
@ -147,6 +148,7 @@ func TestRoute53ChangeSort(t *testing.T) {
wantChanges[6:], wantChanges[6:],
} }
split := splitChanges(changes, 600, 4000) split := splitChanges(changes, 600, 4000)
if !reflect.DeepEqual(split, wantSplit) { if !reflect.DeepEqual(split, wantSplit) {
t.Fatalf("wrong split batches: got %d, want %d", len(split), len(wantSplit)) t.Fatalf("wrong split batches: got %d, want %d", len(split), len(wantSplit))
} }
@ -157,6 +159,7 @@ func TestRoute53ChangeSort(t *testing.T) {
wantChanges[5:], wantChanges[5:],
} }
split = splitChanges(changes, 10000, 6) split = splitChanges(changes, 10000, 6)
if !reflect.DeepEqual(split, wantSplit) { if !reflect.DeepEqual(split, wantSplit) {
t.Fatalf("wrong split batches: got %d, want %d", len(split), len(wantSplit)) t.Fatalf("wrong split batches: got %d, want %d", len(split), len(wantSplit))
} }
@ -180,6 +183,7 @@ func TestRoute53NoChange(t *testing.T) {
} }
var client route53Client var client route53Client
changes := client.computeChanges("n", testTree1, testTree0) changes := client.computeChanges("n", testTree1, testTree0)
if len(changes) > 0 { if len(changes) > 0 {
t.Fatalf("wrong changes (got %d, want 0)", len(changes)) t.Fatalf("wrong changes (got %d, want 0)", len(changes))

View file

@ -127,10 +127,12 @@ func dnsSync(ctx *cli.Context) error {
url = ctx.Args().Get(0) url = ctx.Args().Get(0)
outdir = ctx.Args().Get(1) outdir = ctx.Args().Get(1)
) )
domain, _, err := dnsdisc.ParseURL(url) domain, _, err := dnsdisc.ParseURL(url)
if err != nil { if err != nil {
return err return err
} }
if outdir == "" { if outdir == "" {
outdir = domain outdir = domain
} }
@ -139,10 +141,12 @@ func dnsSync(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
def := treeToDefinition(url, t) def := treeToDefinition(url, t)
def.Meta.LastModified = time.Now() def.Meta.LastModified = time.Now()
writeTreeMetadata(outdir, def) writeTreeMetadata(outdir, def)
writeTreeNodes(outdir, def) writeTreeNodes(outdir, def)
return nil return nil
} }
@ -150,33 +154,40 @@ func dnsSign(ctx *cli.Context) error {
if ctx.NArg() < 2 { if ctx.NArg() < 2 {
return fmt.Errorf("need tree definition directory and key file as arguments") return fmt.Errorf("need tree definition directory and key file as arguments")
} }
var ( var (
defdir = ctx.Args().Get(0) defdir = ctx.Args().Get(0)
keyfile = ctx.Args().Get(1) keyfile = ctx.Args().Get(1)
def = loadTreeDefinition(defdir) def = loadTreeDefinition(defdir)
domain = directoryName(defdir) domain = directoryName(defdir)
) )
if def.Meta.URL != "" { if def.Meta.URL != "" {
d, _, err := dnsdisc.ParseURL(def.Meta.URL) d, _, err := dnsdisc.ParseURL(def.Meta.URL)
if err != nil { if err != nil {
return fmt.Errorf("invalid 'url' field: %v", err) return fmt.Errorf("invalid 'url' field: %v", err)
} }
domain = d domain = d
} }
if ctx.IsSet(dnsDomainFlag.Name) { if ctx.IsSet(dnsDomainFlag.Name) {
domain = ctx.String(dnsDomainFlag.Name) domain = ctx.String(dnsDomainFlag.Name)
} }
if ctx.IsSet(dnsSeqFlag.Name) { if ctx.IsSet(dnsSeqFlag.Name) {
def.Meta.Seq = ctx.Uint(dnsSeqFlag.Name) def.Meta.Seq = ctx.Uint(dnsSeqFlag.Name)
} else { } else {
def.Meta.Seq++ // Auto-bump sequence number if not supplied via flag. def.Meta.Seq++ // Auto-bump sequence number if not supplied via flag.
} }
t, err := dnsdisc.MakeTree(def.Meta.Seq, def.Nodes, def.Meta.Links) t, err := dnsdisc.MakeTree(def.Meta.Seq, def.Nodes, def.Meta.Links)
if err != nil { if err != nil {
return err return err
} }
key := loadSigningKey(keyfile) key := loadSigningKey(keyfile)
url, err := t.Sign(key, domain) url, err := t.Sign(key, domain)
if err != nil { if err != nil {
return fmt.Errorf("can't sign: %v", err) return fmt.Errorf("can't sign: %v", err)
@ -185,6 +196,7 @@ func dnsSign(ctx *cli.Context) error {
def = treeToDefinition(url, t) def = treeToDefinition(url, t)
def.Meta.LastModified = time.Now() def.Meta.LastModified = time.Now()
writeTreeMetadata(defdir, def) writeTreeMetadata(defdir, def)
return nil return nil
} }
@ -196,6 +208,7 @@ func directoryName(dir string) string {
if err != nil { if err != nil {
exit(err) exit(err)
} }
return filepath.Base(abs) return filepath.Base(abs)
} }
@ -204,15 +217,19 @@ func dnsToTXT(ctx *cli.Context) error {
if ctx.NArg() < 1 { if ctx.NArg() < 1 {
return fmt.Errorf("need tree definition directory as argument") return fmt.Errorf("need tree definition directory as argument")
} }
output := ctx.Args().Get(1) output := ctx.Args().Get(1)
if output == "" { if output == "" {
output = "-" // default to stdout output = "-" // default to stdout
} }
domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0)) domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0))
if err != nil { if err != nil {
return err return err
} }
writeTXTJSON(output, t.ToTXT(domain)) writeTXTJSON(output, t.ToTXT(domain))
return nil return nil
} }
@ -221,11 +238,14 @@ func dnsToCloudflare(ctx *cli.Context) error {
if ctx.NArg() != 1 { if ctx.NArg() != 1 {
return fmt.Errorf("need tree definition directory as argument") return fmt.Errorf("need tree definition directory as argument")
} }
domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0)) domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0))
if err != nil { if err != nil {
return err return err
} }
client := newCloudflareClient(ctx) client := newCloudflareClient(ctx)
return client.deploy(domain, t) return client.deploy(domain, t)
} }
@ -234,11 +254,14 @@ func dnsToRoute53(ctx *cli.Context) error {
if ctx.NArg() != 1 { if ctx.NArg() != 1 {
return fmt.Errorf("need tree definition directory as argument") return fmt.Errorf("need tree definition directory as argument")
} }
domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0)) domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0))
if err != nil { if err != nil {
return err return err
} }
client := newRoute53Client(ctx) client := newRoute53Client(ctx)
return client.deploy(domain, t) return client.deploy(domain, t)
} }
@ -247,7 +270,9 @@ func dnsNukeRoute53(ctx *cli.Context) error {
if ctx.NArg() != 1 { if ctx.NArg() != 1 {
return fmt.Errorf("need domain name as argument") return fmt.Errorf("need domain name as argument")
} }
client := newRoute53Client(ctx) client := newRoute53Client(ctx)
return client.deleteDomain(ctx.Args().First()) return client.deleteDomain(ctx.Args().First())
} }
@ -257,11 +282,14 @@ func loadSigningKey(keyfile string) *ecdsa.PrivateKey {
if err != nil { if err != nil {
exit(fmt.Errorf("failed to read the keyfile at '%s': %v", keyfile, err)) exit(fmt.Errorf("failed to read the keyfile at '%s': %v", keyfile, err))
} }
password, _ := prompt.Stdin.PromptPassword("Please enter the password for '" + keyfile + "': ") password, _ := prompt.Stdin.PromptPassword("Please enter the password for '" + keyfile + "': ")
key, err := keystore.DecryptKey(keyjson, password) key, err := keystore.DecryptKey(keyjson, password)
if err != nil { if err != nil {
exit(fmt.Errorf("error decrypting key: %v", err)) exit(fmt.Errorf("error decrypting key: %v", err))
} }
return key.PrivateKey return key.PrivateKey
} }
@ -271,6 +299,7 @@ func dnsClient(ctx *cli.Context) *dnsdisc.Client {
if commandHasFlag(ctx, dnsTimeoutFlag) { if commandHasFlag(ctx, dnsTimeoutFlag) {
cfg.Timeout = ctx.Duration(dnsTimeoutFlag.Name) cfg.Timeout = ctx.Duration(dnsTimeoutFlag.Name)
} }
return dnsdisc.NewClient(cfg) return dnsdisc.NewClient(cfg)
} }
@ -311,17 +340,21 @@ func treeToDefinition(url string, t *dnsdisc.Tree) *dnsDefinition {
if meta.Links == nil { if meta.Links == nil {
meta.Links = []string{} meta.Links = []string{}
} }
return &dnsDefinition{Meta: meta, Nodes: t.Nodes()} return &dnsDefinition{Meta: meta, Nodes: t.Nodes()}
} }
// loadTreeDefinition loads a directory in 'definition' format. // loadTreeDefinition loads a directory in 'definition' format.
func loadTreeDefinition(directory string) *dnsDefinition { func loadTreeDefinition(directory string) *dnsDefinition {
metaFile, nodesFile := treeDefinitionFiles(directory) metaFile, nodesFile := treeDefinitionFiles(directory)
var def dnsDefinition var def dnsDefinition
err := common.LoadJSON(metaFile, &def.Meta) err := common.LoadJSON(metaFile, &def.Meta)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
exit(err) exit(err)
} }
if def.Meta.Links == nil { if def.Meta.Links == nil {
def.Meta.Links = []string{} def.Meta.Links = []string{}
} }
@ -336,7 +369,9 @@ func loadTreeDefinition(directory string) *dnsDefinition {
if err := nodes.verify(); err != nil { if err := nodes.verify(); err != nil {
exit(err) exit(err)
} }
def.Nodes = nodes.nodes() def.Nodes = nodes.nodes()
return &def return &def
} }
@ -344,19 +379,24 @@ func loadTreeDefinition(directory string) *dnsDefinition {
func loadTreeDefinitionForExport(dir string) (domain string, t *dnsdisc.Tree, err error) { func loadTreeDefinitionForExport(dir string) (domain string, t *dnsdisc.Tree, err error) {
metaFile, _ := treeDefinitionFiles(dir) metaFile, _ := treeDefinitionFiles(dir)
def := loadTreeDefinition(dir) def := loadTreeDefinition(dir)
if def.Meta.URL == "" { if def.Meta.URL == "" {
return "", nil, fmt.Errorf("missing 'url' field in %v", metaFile) return "", nil, fmt.Errorf("missing 'url' field in %v", metaFile)
} }
domain, pubkey, err := dnsdisc.ParseURL(def.Meta.URL) domain, pubkey, err := dnsdisc.ParseURL(def.Meta.URL)
if err != nil { if err != nil {
return "", nil, fmt.Errorf("invalid 'url' field in %v: %v", metaFile, err) return "", nil, fmt.Errorf("invalid 'url' field in %v: %v", metaFile, err)
} }
if t, err = dnsdisc.MakeTree(def.Meta.Seq, def.Nodes, def.Meta.Links); err != nil { if t, err = dnsdisc.MakeTree(def.Meta.Seq, def.Nodes, def.Meta.Links); err != nil {
return "", nil, err return "", nil, err
} }
if err := ensureValidTreeSignature(t, pubkey, def.Meta.Sig); err != nil { if err := ensureValidTreeSignature(t, pubkey, def.Meta.Sig); err != nil {
return "", nil, err return "", nil, err
} }
return domain, t, nil return domain, t, nil
} }
@ -366,9 +406,11 @@ func ensureValidTreeSignature(t *dnsdisc.Tree, pubkey *ecdsa.PublicKey, sig stri
if sig == "" { if sig == "" {
return fmt.Errorf("missing signature, run 'devp2p dns sign' first") return fmt.Errorf("missing signature, run 'devp2p dns sign' first")
} }
if err := t.SetSignature(pubkey, sig); err != nil { if err := t.SetSignature(pubkey, sig); err != nil {
return fmt.Errorf("invalid signature on tree, run 'devp2p dns sign' to update it") return fmt.Errorf("invalid signature on tree, run 'devp2p dns sign' to update it")
} }
return nil return nil
} }
@ -378,9 +420,11 @@ func writeTreeMetadata(directory string, def *dnsDefinition) {
if err != nil { if err != nil {
exit(err) exit(err)
} }
if err := os.Mkdir(directory, 0744); err != nil && !os.IsExist(err) { if err := os.Mkdir(directory, 0744); err != nil && !os.IsExist(err) {
exit(err) exit(err)
} }
metaFile, _ := treeDefinitionFiles(directory) metaFile, _ := treeDefinitionFiles(directory)
if err := os.WriteFile(metaFile, metaJSON, 0644); err != nil { if err := os.WriteFile(metaFile, metaJSON, 0644); err != nil {
@ -391,6 +435,7 @@ func writeTreeMetadata(directory string, def *dnsDefinition) {
func writeTreeNodes(directory string, def *dnsDefinition) { func writeTreeNodes(directory string, def *dnsDefinition) {
ns := make(nodeSet, len(def.Nodes)) ns := make(nodeSet, len(def.Nodes))
ns.add(def.Nodes...) ns.add(def.Nodes...)
_, nodesFile := treeDefinitionFiles(directory) _, nodesFile := treeDefinitionFiles(directory)
writeNodesJSON(nodesFile, ns) writeNodesJSON(nodesFile, ns)
} }
@ -398,6 +443,7 @@ func writeTreeNodes(directory string, def *dnsDefinition) {
func treeDefinitionFiles(directory string) (string, string) { func treeDefinitionFiles(directory string) (string, string) {
meta := filepath.Join(directory, "enrtree-info.json") meta := filepath.Join(directory, "enrtree-info.json")
nodes := filepath.Join(directory, "nodes.json") nodes := filepath.Join(directory, "nodes.json")
return meta, nodes return meta, nodes
} }
@ -407,9 +453,11 @@ func writeTXTJSON(file string, txt map[string]string) {
if err != nil { if err != nil {
exit(err) exit(err)
} }
if file == "-" { if file == "-" {
os.Stdout.Write(txtJSON) os.Stdout.Write(txtJSON)
fmt.Println() fmt.Println()
return return
} }

View file

@ -47,20 +47,25 @@ var enrdumpCommand = &cli.Command{
func enrdump(ctx *cli.Context) error { func enrdump(ctx *cli.Context) error {
var source string var source string
if file := ctx.String(fileFlag.Name); file != "" { if file := ctx.String(fileFlag.Name); file != "" {
if ctx.NArg() != 0 { if ctx.NArg() != 0 {
return fmt.Errorf("can't dump record from command-line argument in -file mode") return fmt.Errorf("can't dump record from command-line argument in -file mode")
} }
var b []byte var b []byte
var err error var err error
if file == "-" { if file == "-" {
b, err = io.ReadAll(os.Stdin) b, err = io.ReadAll(os.Stdin)
} else { } else {
b, err = os.ReadFile(file) b, err = os.ReadFile(file)
} }
if err != nil { if err != nil {
return err return err
} }
source = string(b) source = string(b)
} else if ctx.NArg() == 1 { } else if ctx.NArg() == 1 {
source = ctx.Args().First() source = ctx.Args().First()
@ -72,7 +77,9 @@ func enrdump(ctx *cli.Context) error {
if err != nil { if err != nil {
return fmt.Errorf("INVALID: %v", err) return fmt.Errorf("INVALID: %v", err)
} }
dumpRecord(os.Stdout, r) dumpRecord(os.Stdout, r)
return nil return nil
} }
@ -85,6 +92,7 @@ func dumpRecord(out io.Writer, r *enr.Record) {
fmt.Fprintf(out, "Node ID: %v\n", n.ID()) fmt.Fprintf(out, "Node ID: %v\n", n.ID())
dumpNodeURL(out, n) dumpNodeURL(out, n)
} }
kv := r.AppendElements(nil)[1:] kv := r.AppendElements(nil)[1:]
fmt.Fprintf(out, "Record has sequence number %d and %d key/value pairs.\n", r.Seq(), len(kv)/2) fmt.Fprintf(out, "Record has sequence number %d and %d key/value pairs.\n", r.Seq(), len(kv)/2)
fmt.Fprint(out, dumpRecordKV(kv, 2)) fmt.Fprint(out, dumpRecordKV(kv, 2))
@ -95,13 +103,16 @@ func dumpNodeURL(out io.Writer, n *enode.Node) {
if n.Load(&key) != nil { if n.Load(&key) != nil {
return // no secp256k1 public key return // no secp256k1 public key
} }
fmt.Fprintf(out, "URLv4: %s\n", n.URLv4()) fmt.Fprintf(out, "URLv4: %s\n", n.URLv4())
} }
func dumpRecordKV(kv []interface{}, indent int) string { func dumpRecordKV(kv []interface{}, indent int) string {
// Determine the longest key name for alignment. // Determine the longest key name for alignment.
var out string var out string
var longestKey = 0 var longestKey = 0
for i := 0; i < len(kv); i += 2 { for i := 0; i < len(kv); i += 2 {
key := kv[i].(string) key := kv[i].(string)
if len(key) > longestKey { if len(key) > longestKey {
@ -114,10 +125,12 @@ func dumpRecordKV(kv []interface{}, indent int) string {
val := kv[i+1].(rlp.RawValue) val := kv[i+1].(rlp.RawValue)
pad := longestKey - len(key) pad := longestKey - len(key)
out += strings.Repeat(" ", indent) + strconv.Quote(key) + strings.Repeat(" ", pad+1) out += strings.Repeat(" ", indent) + strconv.Quote(key) + strings.Repeat(" ", pad+1)
formatter := attrFormatters[key] formatter := attrFormatters[key]
if formatter == nil { if formatter == nil {
formatter = formatAttrRaw formatter = formatAttrRaw
} }
fmtval, ok := formatter(val) fmtval, ok := formatter(val)
if ok { if ok {
out += fmtval + "\n" out += fmtval + "\n"
@ -125,6 +138,7 @@ func dumpRecordKV(kv []interface{}, indent int) string {
out += hex.EncodeToString(val) + " (!)\n" out += hex.EncodeToString(val) + " (!)\n"
} }
} }
return out return out
} }
@ -133,10 +147,12 @@ func parseNode(source string) (*enode.Node, error) {
if strings.HasPrefix(source, "enode://") { if strings.HasPrefix(source, "enode://") {
return enode.ParseV4(source) return enode.ParseV4(source)
} }
r, err := parseRecord(source) r, err := parseRecord(source)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return enode.New(enode.ValidSchemes, r) return enode.New(enode.ValidSchemes, r)
} }
@ -148,8 +164,10 @@ func parseRecord(source string) (*enr.Record, error) {
} else if d, ok := decodeRecordBase64(bytes.TrimSpace(bin)); ok { } else if d, ok := decodeRecordBase64(bytes.TrimSpace(bin)); ok {
bin = d bin = d
} }
var r enr.Record var r enr.Record
err := rlp.DecodeBytes(bin, &r) err := rlp.DecodeBytes(bin, &r)
return &r, err return &r, err
} }
@ -157,8 +175,10 @@ func decodeRecordHex(b []byte) ([]byte, bool) {
if bytes.HasPrefix(b, []byte("0x")) { if bytes.HasPrefix(b, []byte("0x")) {
b = b[2:] b = b[2:]
} }
dec := make([]byte, hex.DecodedLen(len(b))) dec := make([]byte, hex.DecodedLen(len(b)))
_, err := hex.Decode(dec, b) _, err := hex.Decode(dec, b)
return dec, err == nil return dec, err == nil
} }
@ -166,8 +186,10 @@ func decodeRecordBase64(b []byte) ([]byte, bool) {
if bytes.HasPrefix(b, []byte("enr:")) { if bytes.HasPrefix(b, []byte("enr:")) {
b = b[4:] b = b[4:]
} }
dec := make([]byte, base64.RawURLEncoding.DecodedLen(len(b))) dec := make([]byte, base64.RawURLEncoding.DecodedLen(len(b)))
n, err := base64.RawURLEncoding.Decode(dec, b) n, err := base64.RawURLEncoding.Decode(dec, b)
return dec[:n], err == nil return dec[:n], err == nil
} }
@ -197,6 +219,7 @@ func formatAttrIP(v rlp.RawValue) (string, bool) {
if err != nil || len(content) != 4 && len(content) != 6 { if err != nil || len(content) != 4 && len(content) != 6 {
return "", false return "", false
} }
return net.IP(content).String(), true return net.IP(content).String(), true
} }
@ -205,5 +228,6 @@ func formatAttrUint(v rlp.RawValue) (string, bool) {
if err := rlp.DecodeBytes(v, &x); err != nil { if err := rlp.DecodeBytes(v, &x); err != nil {
return "", false return "", false
} }
return strconv.FormatUint(x, 10), true return strconv.FormatUint(x, 10), true
} }

View file

@ -51,6 +51,7 @@ func (c *Chain) TD() *big.Int {
for _, block := range c.blocks[:c.Len()] { for _, block := range c.blocks[:c.Len()] {
sum.Add(sum, block.Difficulty()) sum.Add(sum, block.Difficulty())
} }
return sum return sum
} }
@ -61,9 +62,11 @@ func (c *Chain) TotalDifficultyAt(height int) *big.Int {
if height >= c.Len() { if height >= c.Len() {
return sum return sum
} }
for _, block := range c.blocks[:height+1] { for _, block := range c.blocks[:height+1] {
sum.Add(sum, block.Difficulty()) sum.Add(sum, block.Difficulty())
} }
return sum return sum
} }
@ -71,6 +74,7 @@ func (c *Chain) RootAt(height int) common.Hash {
if height < c.Len() { if height < c.Len() {
return c.blocks[height].Root() return c.blocks[height].Root()
} }
return common.Hash{} return common.Hash{}
} }
@ -85,6 +89,7 @@ func (c *Chain) Shorten(height int) *Chain {
copy(blocks, c.blocks[:height]) copy(blocks, c.blocks[:height])
config := *c.chainConfig config := *c.chainConfig
return &Chain{ return &Chain{
blocks: blocks, blocks: blocks,
chainConfig: &config, chainConfig: &config,
@ -103,6 +108,7 @@ func (c *Chain) GetHeaders(req *GetBlockHeaders) ([]*types.Header, error) {
} }
headers := make([]*types.Header, req.Amount) headers := make([]*types.Header, req.Amount)
var blockNumber uint64 var blockNumber uint64
// range over blocks to check if our chain has the requested header // range over blocks to check if our chain has the requested header
@ -112,6 +118,7 @@ func (c *Chain) GetHeaders(req *GetBlockHeaders) ([]*types.Header, error) {
blockNumber = block.Number().Uint64() blockNumber = block.Number().Uint64()
} }
} }
if headers[0] == nil { if headers[0] == nil {
return nil, fmt.Errorf("no headers found for given origin number %v, hash %v", req.Origin.Number, req.Origin.Hash) return nil, fmt.Errorf("no headers found for given origin number %v, hash %v", req.Origin.Number, req.Origin.Hash)
} }
@ -149,6 +156,7 @@ func loadChain(chainfile string, genesis string) (*Chain, error) {
} }
c := &Chain{genesis: gen, blocks: blocks, chainConfig: gen.Config} c := &Chain{genesis: gen, blocks: blocks, chainConfig: gen.Config}
return c, nil return c, nil
} }
@ -157,10 +165,12 @@ func loadGenesis(genesisFile string) (core.Genesis, error) {
if err != nil { if err != nil {
return core.Genesis{}, err return core.Genesis{}, err
} }
var gen core.Genesis var gen core.Genesis
if err := json.Unmarshal(chainConfig, &gen); err != nil { if err := json.Unmarshal(chainConfig, &gen); err != nil {
return core.Genesis{}, err return core.Genesis{}, err
} }
return gen, nil return gen, nil
} }
@ -170,16 +180,21 @@ func blocksFromFile(chainfile string, gblock *types.Block) ([]*types.Block, erro
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer fh.Close() defer fh.Close()
var reader io.Reader = fh var reader io.Reader = fh
if strings.HasSuffix(chainfile, ".gz") { if strings.HasSuffix(chainfile, ".gz") {
if reader, err = gzip.NewReader(reader); err != nil { if reader, err = gzip.NewReader(reader); err != nil {
return nil, err return nil, err
} }
} }
stream := rlp.NewStream(reader, 0) stream := rlp.NewStream(reader, 0)
var blocks = make([]*types.Block, 1) var blocks = make([]*types.Block, 1)
blocks[0] = gblock blocks[0] = gblock
for i := 0; ; i++ { for i := 0; ; i++ {
var b types.Block var b types.Block
if err := stream.Decode(&b); err == io.EOF { if err := stream.Decode(&b); err == io.EOF {
@ -187,10 +202,13 @@ func blocksFromFile(chainfile string, gblock *types.Block) ([]*types.Block, erro
} else if err != nil { } else if err != nil {
return nil, fmt.Errorf("at block index %d: %v", i, err) return nil, fmt.Errorf("at block index %d: %v", i, err)
} }
if b.NumberU64() != uint64(i+1) { if b.NumberU64() != uint64(i+1) {
return nil, fmt.Errorf("block at index %d has wrong number %d", i, b.NumberU64()) return nil, fmt.Errorf("block at index %d has wrong number %d", i, b.NumberU64())
} }
blocks = append(blocks, &b) blocks = append(blocks, &b)
} }
return blocks, nil return blocks, nil
} }

View file

@ -129,6 +129,7 @@ func TestChain_GetHeaders(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
genesisFile, err := filepath.Abs("./testdata/genesis.json") genesisFile, err := filepath.Abs("./testdata/genesis.json")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -196,6 +197,7 @@ func TestChain_GetHeaders(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
assert.Equal(t, headers, tt.expected) assert.Equal(t, headers, tt.expected)
}) })
} }

View file

@ -51,10 +51,12 @@ func (s *Suite) dial() (*Conn, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
conn := Conn{Conn: rlpx.NewConn(fd, s.Dest.Pubkey())} conn := Conn{Conn: rlpx.NewConn(fd, s.Dest.Pubkey())}
// do encHandshake // do encHandshake
conn.ourKey, _ = crypto.GenerateKey() conn.ourKey, _ = crypto.GenerateKey()
_, err = conn.Handshake(conn.ourKey) _, err = conn.Handshake(conn.ourKey)
if err != nil { if err != nil {
conn.Close() conn.Close()
return nil, err return nil, err
@ -66,6 +68,7 @@ func (s *Suite) dial() (*Conn, error) {
{Name: "eth", Version: 68}, {Name: "eth", Version: 68},
} }
conn.ourHighestProtoVersion = 68 conn.ourHighestProtoVersion = 68
return &conn, nil return &conn, nil
} }
@ -75,8 +78,10 @@ func (s *Suite) dialSnap() (*Conn, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("dial failed: %v", err) return nil, fmt.Errorf("dial failed: %v", err)
} }
conn.caps = append(conn.caps, p2p.Cap{Name: "snap", Version: 1}) conn.caps = append(conn.caps, p2p.Cap{Name: "snap", Version: 1})
conn.ourHighestSnapProtoVersion = 1 conn.ourHighestSnapProtoVersion = 1
return conn, nil return conn, nil
} }
@ -86,9 +91,11 @@ func (c *Conn) peer(chain *Chain, status *Status) error {
if err := c.handshake(); err != nil { if err := c.handshake(); err != nil {
return fmt.Errorf("handshake failed: %v", err) return fmt.Errorf("handshake failed: %v", err)
} }
if _, err := c.statusExchange(chain, status); err != nil { if _, err := c.statusExchange(chain, status); err != nil {
return fmt.Errorf("status exchange failed: %v", err) return fmt.Errorf("status exchange failed: %v", err)
} }
return nil return nil
} }
@ -98,6 +105,7 @@ func (c *Conn) handshake() error {
c.SetDeadline(time.Now().Add(10 * time.Second)) c.SetDeadline(time.Now().Add(10 * time.Second))
// write hello to client // write hello to client
pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:] pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:]
ourHandshake := &Hello{ ourHandshake := &Hello{
Version: 5, Version: 5,
Caps: c.caps, Caps: c.caps,
@ -113,7 +121,9 @@ func (c *Conn) handshake() error {
if msg.Version >= 5 { if msg.Version >= 5 {
c.SetSnappy(true) c.SetSnappy(true)
} }
c.negotiateEthProtocol(msg.Caps) c.negotiateEthProtocol(msg.Caps)
if c.negotiatedProtoVersion == 0 { if c.negotiatedProtoVersion == 0 {
return fmt.Errorf("could not negotiate eth protocol (remote caps: %v, local eth version: %v)", msg.Caps, c.ourHighestProtoVersion) return fmt.Errorf("could not negotiate eth protocol (remote caps: %v, local eth version: %v)", msg.Caps, c.ourHighestProtoVersion)
} }
@ -121,6 +131,7 @@ func (c *Conn) handshake() error {
if c.ourHighestSnapProtoVersion != c.negotiatedSnapProtoVersion { if c.ourHighestSnapProtoVersion != c.negotiatedSnapProtoVersion {
return fmt.Errorf("could not negotiate snap protocol (remote caps: %v, local snap version: %v)", msg.Caps, c.ourHighestSnapProtoVersion) return fmt.Errorf("could not negotiate snap protocol (remote caps: %v, local snap version: %v)", msg.Caps, c.ourHighestSnapProtoVersion)
} }
return nil return nil
default: default:
return fmt.Errorf("bad handshake: %#v", msg) return fmt.Errorf("bad handshake: %#v", msg)
@ -131,7 +142,9 @@ func (c *Conn) handshake() error {
// advertised capability from peer. // advertised capability from peer.
func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) { func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) {
var highestEthVersion uint var highestEthVersion uint
var highestSnapVersion uint var highestSnapVersion uint
for _, capability := range caps { for _, capability := range caps {
switch capability.Name { switch capability.Name {
case "eth": case "eth":
@ -144,6 +157,7 @@ func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) {
} }
} }
} }
c.negotiatedProtoVersion = highestEthVersion c.negotiatedProtoVersion = highestEthVersion
c.negotiatedSnapProtoVersion = highestSnapVersion c.negotiatedSnapProtoVersion = highestSnapVersion
} }
@ -187,6 +201,7 @@ loop:
if c.negotiatedProtoVersion == 0 { if c.negotiatedProtoVersion == 0 {
return nil, fmt.Errorf("eth protocol version must be set in Conn") return nil, fmt.Errorf("eth protocol version must be set in Conn")
} }
if status == nil { if status == nil {
// default status message // default status message
status = &Status{ status = &Status{
@ -198,9 +213,11 @@ loop:
ForkID: chain.ForkID(), ForkID: chain.ForkID(),
} }
} }
if err := c.Write(status); err != nil { if err := c.Write(status); err != nil {
return nil, fmt.Errorf("write to connection failed: %v", err) return nil, fmt.Errorf("write to connection failed: %v", err)
} }
return message, nil return message, nil
} }
@ -283,9 +300,11 @@ func (c *Conn) headersRequest(request *GetBlockHeaders, chain *Chain, reqID uint
func (c *Conn) snapRequest(msg Message, id uint64, chain *Chain) (Message, error) { func (c *Conn) snapRequest(msg Message, id uint64, chain *Chain) (Message, error) {
defer c.SetReadDeadline(time.Time{}) defer c.SetReadDeadline(time.Time{})
c.SetReadDeadline(time.Now().Add(5 * time.Second)) c.SetReadDeadline(time.Now().Add(5 * time.Second))
if err := c.Write(msg); err != nil { if err := c.Write(msg); err != nil {
return nil, fmt.Errorf("could not write to connection: %v", err) return nil, fmt.Errorf("could not write to connection: %v", err)
} }
return c.ReadSnap(id) return c.ReadSnap(id)
} }
@ -313,11 +332,14 @@ func (s *Suite) sendNextBlock() error {
if err != nil { if err != nil {
return err return err
} }
defer sendConn.Close() defer sendConn.Close()
defer recvConn.Close() defer recvConn.Close()
if err = sendConn.peer(s.chain, nil); err != nil { if err = sendConn.peer(s.chain, nil); err != nil {
return fmt.Errorf("peering failed: %v", err) return fmt.Errorf("peering failed: %v", err)
} }
if err = recvConn.peer(s.chain, nil); err != nil { if err = recvConn.peer(s.chain, nil); err != nil {
return fmt.Errorf("peering failed: %v", err) return fmt.Errorf("peering failed: %v", err)
} }
@ -337,6 +359,7 @@ func (s *Suite) sendNextBlock() error {
} }
// update test suite chain // update test suite chain
s.chain.blocks = append(s.chain.blocks, nextBlock) s.chain.blocks = append(s.chain.blocks, nextBlock)
return nil return nil
} }
@ -346,6 +369,7 @@ func (s *Suite) testAnnounce(sendConn, receiveConn *Conn, blockAnnouncement *New
if err := sendConn.Write(blockAnnouncement); err != nil { if err := sendConn.Write(blockAnnouncement); err != nil {
return fmt.Errorf("could not write to connection: %v", err) return fmt.Errorf("could not write to connection: %v", err)
} }
return s.waitAnnounce(receiveConn, blockAnnouncement) return s.waitAnnounce(receiveConn, blockAnnouncement)
} }
@ -358,15 +382,18 @@ func (s *Suite) waitAnnounce(conn *Conn, blockAnnouncement *NewBlock) error {
return fmt.Errorf("wrong header in block announcement: \nexpected %v "+ return fmt.Errorf("wrong header in block announcement: \nexpected %v "+
"\ngot %v", blockAnnouncement.Block.Header(), msg.Block.Header()) "\ngot %v", blockAnnouncement.Block.Header(), msg.Block.Header())
} }
if !reflect.DeepEqual(blockAnnouncement.TD, msg.TD) { if !reflect.DeepEqual(blockAnnouncement.TD, msg.TD) {
return fmt.Errorf("wrong TD in announcement: expected %v, got %v", blockAnnouncement.TD, msg.TD) return fmt.Errorf("wrong TD in announcement: expected %v, got %v", blockAnnouncement.TD, msg.TD)
} }
return nil return nil
case *NewBlockHashes: case *NewBlockHashes:
hashes := *msg hashes := *msg
if blockAnnouncement.Block.Hash() != hashes[0].Hash { if blockAnnouncement.Block.Hash() != hashes[0].Hash {
return fmt.Errorf("wrong block hash in announcement: expected %v, got %v", blockAnnouncement.Block.Hash(), hashes[0].Hash) return fmt.Errorf("wrong block hash in announcement: expected %v, got %v", blockAnnouncement.Block.Hash(), hashes[0].Hash)
} }
return nil return nil
// ignore tx announcements from previous tests // ignore tx announcements from previous tests
@ -398,6 +425,7 @@ func (s *Suite) waitForBlockImport(conn *Conn, block *types.Block) error {
// node imported the block // node imported the block
for { for {
requestID := uint64(54) requestID := uint64(54)
headers, err := conn.headersRequest(req, s.chain, requestID) headers, err := conn.headersRequest(req, s.chain, requestID)
if err != nil { if err != nil {
return fmt.Errorf("GetBlockHeader request failed: %v", err) return fmt.Errorf("GetBlockHeader request failed: %v", err)
@ -407,9 +435,11 @@ func (s *Suite) waitForBlockImport(conn *Conn, block *types.Block) error {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
continue continue
} }
if !reflect.DeepEqual(block.Header(), headers[0]) { if !reflect.DeepEqual(block.Header(), headers[0]) {
return fmt.Errorf("wrong header returned: wanted %v, got %v", block.Header(), headers[0]) return fmt.Errorf("wrong header returned: wanted %v, got %v", block.Header(), headers[0])
} }
return nil return nil
} }
} }
@ -419,11 +449,14 @@ func (s *Suite) oldAnnounce() error {
if err != nil { if err != nil {
return err return err
} }
defer sendConn.Close() defer sendConn.Close()
defer receiveConn.Close() defer receiveConn.Close()
if err := sendConn.peer(s.chain, nil); err != nil { if err := sendConn.peer(s.chain, nil); err != nil {
return fmt.Errorf("peering failed: %v", err) return fmt.Errorf("peering failed: %v", err)
} }
if err := receiveConn.peer(s.chain, nil); err != nil { if err := receiveConn.peer(s.chain, nil); err != nil {
return fmt.Errorf("peering failed: %v", err) return fmt.Errorf("peering failed: %v", err)
} }
@ -458,6 +491,7 @@ func (s *Suite) oldAnnounce() error {
default: default:
return fmt.Errorf("unexpected: %s", pretty.Sdump(msg)) return fmt.Errorf("unexpected: %s", pretty.Sdump(msg))
} }
return nil return nil
} }
@ -470,6 +504,7 @@ func (s *Suite) maliciousHandshakes(t *utesting.T) error {
// write hello to client // write hello to client
pub0 := crypto.FromECDSAPub(&conn.ourKey.PublicKey)[1:] pub0 := crypto.FromECDSAPub(&conn.ourKey.PublicKey)[1:]
handshakes := []*Hello{ handshakes := []*Hello{
{ {
Version: 5, Version: 5,
@ -512,6 +547,7 @@ func (s *Suite) maliciousHandshakes(t *utesting.T) error {
} }
for i, handshake := range handshakes { for i, handshake := range handshakes {
t.Logf("Testing malicious handshake %v\n", i) t.Logf("Testing malicious handshake %v\n", i)
if err := conn.Write(handshake); err != nil { if err := conn.Write(handshake); err != nil {
return fmt.Errorf("could not write to connection: %v", err) return fmt.Errorf("could not write to connection: %v", err)
} }
@ -533,6 +569,7 @@ func (s *Suite) maliciousHandshakes(t *utesting.T) error {
return fmt.Errorf("dial failed: %v", err) return fmt.Errorf("dial failed: %v", err)
} }
} }
return nil return nil
} }
@ -540,6 +577,7 @@ func (s *Suite) maliciousStatus(conn *Conn) error {
if err := conn.handshake(); err != nil { if err := conn.handshake(); err != nil {
return fmt.Errorf("handshake failed: %v", err) return fmt.Errorf("handshake failed: %v", err)
} }
status := &Status{ status := &Status{
ProtocolVersion: uint32(conn.negotiatedProtoVersion), ProtocolVersion: uint32(conn.negotiatedProtoVersion),
NetworkID: s.chain.chainConfig.ChainID.Uint64(), NetworkID: s.chain.chainConfig.ChainID.Uint64(),
@ -554,6 +592,7 @@ func (s *Suite) maliciousStatus(conn *Conn) error {
if err != nil { if err != nil {
return fmt.Errorf("status exchange failed: %v", err) return fmt.Errorf("status exchange failed: %v", err)
} }
switch msg := msg.(type) { switch msg := msg.(type) {
case *Status: case *Status:
default: default:
@ -578,11 +617,14 @@ func (s *Suite) hashAnnounce() error {
if err != nil { if err != nil {
return fmt.Errorf("failed to create connections: %v", err) return fmt.Errorf("failed to create connections: %v", err)
} }
defer sendConn.Close() defer sendConn.Close()
defer recvConn.Close() defer recvConn.Close()
if err := sendConn.peer(s.chain, nil); err != nil { if err := sendConn.peer(s.chain, nil); err != nil {
return fmt.Errorf("peering failed: %v", err) return fmt.Errorf("peering failed: %v", err)
} }
if err := recvConn.peer(s.chain, nil); err != nil { if err := recvConn.peer(s.chain, nil); err != nil {
return fmt.Errorf("peering failed: %v", err) return fmt.Errorf("peering failed: %v", err)
} }
@ -592,8 +634,10 @@ func (s *Suite) hashAnnounce() error {
Hash common.Hash // Hash of one particular block being announced Hash common.Hash // Hash of one particular block being announced
Number uint64 // Number of one particular block being announced Number uint64 // Number of one particular block being announced
} }
nextBlock := s.fullChain.blocks[s.chain.Len()] nextBlock := s.fullChain.blocks[s.chain.Len()]
announcement := anno{Hash: nextBlock.Hash(), Number: nextBlock.Number().Uint64()} announcement := anno{Hash: nextBlock.Hash(), Number: nextBlock.Number().Uint64()}
newBlockHash := &NewBlockHashes{announcement} newBlockHash := &NewBlockHashes{announcement}
if err := sendConn.Write(newBlockHash); err != nil { if err := sendConn.Write(newBlockHash); err != nil {
return fmt.Errorf("failed to write to connection: %v", err) return fmt.Errorf("failed to write to connection: %v", err)
@ -634,6 +678,7 @@ func (s *Suite) hashAnnounce() error {
if len(hashes) != 1 { if len(hashes) != 1 {
return fmt.Errorf("unexpected new block hash announcement: wanted 1 announcement, got %d", len(hashes)) return fmt.Errorf("unexpected new block hash announcement: wanted 1 announcement, got %d", len(hashes))
} }
if nextBlock.Hash() != hashes[0].Hash { if nextBlock.Hash() != hashes[0].Hash {
return fmt.Errorf("unexpected block hash announcement, wanted %v, got %v", nextBlock.Hash(), return fmt.Errorf("unexpected block hash announcement, wanted %v, got %v", nextBlock.Hash(),
hashes[0].Hash) hashes[0].Hash)
@ -645,6 +690,7 @@ func (s *Suite) hashAnnounce() error {
if len(nextBlockBody.Transactions) != 0 || len(nextBlockBody.Uncles) != 0 { if len(nextBlockBody.Transactions) != 0 || len(nextBlockBody.Uncles) != 0 {
return fmt.Errorf("unexpected non-empty new block propagated: %s", pretty.Sdump(msg)) return fmt.Errorf("unexpected non-empty new block propagated: %s", pretty.Sdump(msg))
} }
if msg.Block.Hash() != nextBlock.Hash() { if msg.Block.Hash() != nextBlock.Hash() {
return fmt.Errorf("mismatched hash of propagated new block: wanted %v, got %v", return fmt.Errorf("mismatched hash of propagated new block: wanted %v, got %v",
nextBlock.Hash(), msg.Block.Hash()) nextBlock.Hash(), msg.Block.Hash())
@ -662,5 +708,6 @@ func (s *Suite) hashAnnounce() error {
} }
// update the chain // update the chain
s.chain.blocks = append(s.chain.blocks, nextBlock) s.chain.blocks = append(s.chain.blocks, nextBlock)
return nil return nil
} }

View file

@ -29,8 +29,10 @@ import (
func largeNumber(megabytes int) *big.Int { func largeNumber(megabytes int) *big.Int {
buf := make([]byte, megabytes*1024*1024) buf := make([]byte, megabytes*1024*1024)
rand.Read(buf) rand.Read(buf)
bigint := new(big.Int) bigint := new(big.Int)
bigint.SetBytes(buf) bigint.SetBytes(buf)
return bigint return bigint
} }
@ -38,6 +40,7 @@ func largeNumber(megabytes int) *big.Int {
func largeBuffer(megabytes int) []byte { func largeBuffer(megabytes int) []byte {
buf := make([]byte, megabytes*1024*1024) buf := make([]byte, megabytes*1024*1024)
rand.Read(buf) rand.Read(buf)
return buf return buf
} }
@ -45,6 +48,7 @@ func largeBuffer(megabytes int) []byte {
func largeString(megabytes int) string { func largeString(megabytes int) string {
buf := make([]byte, megabytes*1024*1024) buf := make([]byte, megabytes*1024*1024)
rand.Read(buf) rand.Read(buf)
return hexutil.Encode(buf) return hexutil.Encode(buf)
} }
@ -55,7 +59,9 @@ func largeBlock() *types.Block {
// Returns a random hash // Returns a random hash
func randHash() common.Hash { func randHash() common.Hash {
var h common.Hash var h common.Hash
rand.Read(h[:]) rand.Read(h[:])
return h return h
} }

View file

@ -37,7 +37,9 @@ func (s *Suite) TestSnapStatus(t *utesting.T) {
if err != nil { if err != nil {
t.Fatalf("dial failed: %v", err) t.Fatalf("dial failed: %v", err)
} }
defer conn.Close() defer conn.Close()
if err := conn.peer(s.chain, nil); err != nil { if err := conn.peer(s.chain, nil); err != nil {
t.Fatalf("peering failed: %v", err) t.Fatalf("peering failed: %v", err)
} }
@ -66,6 +68,7 @@ func (s *Suite) TestSnapGetAccountRange(t *utesting.T) {
secondKey = common.HexToHash("0x09e47cd5056a689e708f22fe1f932709a320518e444f5f7d8d46a3da523d6606") secondKey = common.HexToHash("0x09e47cd5056a689e708f22fe1f932709a320518e444f5f7d8d46a3da523d6606")
storageRoot = common.HexToHash("0xbe3d75a1729be157e79c3b77f00206db4d54e3ea14375a015451c88ec067c790") storageRoot = common.HexToHash("0xbe3d75a1729be157e79c3b77f00206db4d54e3ea14375a015451c88ec067c790")
) )
for i, tc := range []accRangeTest{ for i, tc := range []accRangeTest{
// Tests decreasing the number of bytes // Tests decreasing the number of bytes
{4000, root, zero, ffHash, 76, firstKey, common.HexToHash("0xd2669dcf3858e7f1eecb8b5fedbf22fbea3e9433848a75035f79d68422c2dcda")}, {4000, root, zero, ffHash, 76, firstKey, common.HexToHash("0xd2669dcf3858e7f1eecb8b5fedbf22fbea3e9433848a75035f79d68422c2dcda")},
@ -130,6 +133,7 @@ func (s *Suite) TestSnapGetStorageRanges(t *utesting.T) {
firstKey = common.HexToHash("0x00bf49f440a1cd0527e4d06e2765654c0f56452257516d793a9b8d604dcfdf2a") firstKey = common.HexToHash("0x00bf49f440a1cd0527e4d06e2765654c0f56452257516d793a9b8d604dcfdf2a")
secondKey = common.HexToHash("0x09e47cd5056a689e708f22fe1f932709a320518e444f5f7d8d46a3da523d6606") secondKey = common.HexToHash("0x09e47cd5056a689e708f22fe1f932709a320518e444f5f7d8d46a3da523d6606")
) )
for i, tc := range []stRangesTest{ for i, tc := range []stRangesTest{
{ {
root: s.chain.RootAt(999), root: s.chain.RootAt(999),
@ -316,12 +320,15 @@ func hasTerm(s []byte) bool {
func keybytesToHex(str []byte) []byte { func keybytesToHex(str []byte) []byte {
l := len(str)*2 + 1 l := len(str)*2 + 1
var nibbles = make([]byte, l) var nibbles = make([]byte, l)
for i, b := range str { for i, b := range str {
nibbles[i*2] = b / 16 nibbles[i*2] = b / 16
nibbles[i*2+1] = b % 16 nibbles[i*2+1] = b % 16
} }
nibbles[l-1] = 16 nibbles[l-1] = 16
return nibbles return nibbles
} }
@ -331,14 +338,18 @@ func hexToCompact(hex []byte) []byte {
terminator = 1 terminator = 1
hex = hex[:len(hex)-1] hex = hex[:len(hex)-1]
} }
buf := make([]byte, len(hex)/2+1) buf := make([]byte, len(hex)/2+1)
buf[0] = terminator << 5 // the flag byte buf[0] = terminator << 5 // the flag byte
if len(hex)&1 == 1 { if len(hex)&1 == 1 {
buf[0] |= 1 << 4 // odd flag buf[0] |= 1 << 4 // odd flag
buf[0] |= hex[0] // first nibble is contained in the first byte buf[0] |= hex[0] // first nibble is contained in the first byte
hex = hex[1:] hex = hex[1:]
} }
decodeNibbles(hex, buf[1:]) decodeNibbles(hex, buf[1:])
return buf return buf
} }
@ -351,9 +362,12 @@ func (s *Suite) TestSnapTrieNodes(t *utesting.T) {
hex := keybytesToHex(key)[:length] hex := keybytesToHex(key)[:length]
hex[len(hex)-1] = 0 // remove term flag hex[len(hex)-1] = 0 // remove term flag
hKey := hexToCompact(hex) hKey := hexToCompact(hex)
return snap.TrieNodePathSet{hKey} return snap.TrieNodePathSet{hKey}
} }
var accPaths []snap.TrieNodePathSet var accPaths []snap.TrieNodePathSet
for i := 1; i <= 65; i++ { for i := 1; i <= 65; i++ {
accPaths = append(accPaths, pathTo(i)) accPaths = append(accPaths, pathTo(i))
} }
@ -475,7 +489,9 @@ func (s *Suite) snapGetAccountRange(t *utesting.T, tc *accRangeTest) error {
if err != nil { if err != nil {
t.Fatalf("dial failed: %v", err) t.Fatalf("dial failed: %v", err)
} }
defer conn.Close() defer conn.Close()
if err = conn.peer(s.chain, nil); err != nil { if err = conn.peer(s.chain, nil); err != nil {
t.Fatalf("peering failed: %v", err) t.Fatalf("peering failed: %v", err)
} }
@ -487,16 +503,20 @@ func (s *Suite) snapGetAccountRange(t *utesting.T, tc *accRangeTest) error {
Limit: tc.limit, Limit: tc.limit,
Bytes: tc.nBytes, Bytes: tc.nBytes,
} }
resp, err := conn.snapRequest(req, req.ID, s.chain) resp, err := conn.snapRequest(req, req.ID, s.chain)
if err != nil { if err != nil {
return fmt.Errorf("account range request failed: %v", err) return fmt.Errorf("account range request failed: %v", err)
} }
var res *snap.AccountRangePacket var res *snap.AccountRangePacket
if r, ok := resp.(*AccountRange); !ok { if r, ok := resp.(*AccountRange); !ok {
return fmt.Errorf("account range response wrong: %T %v", resp, resp) return fmt.Errorf("account range response wrong: %T %v", resp, resp)
} else { } else {
res = (*snap.AccountRangePacket)(r) res = (*snap.AccountRangePacket)(r)
} }
if exp, got := tc.expAccounts, len(res.Accounts); exp != got { if exp, got := tc.expAccounts, len(res.Accounts); exp != got {
return fmt.Errorf("expected %d accounts, got %d", exp, got) return fmt.Errorf("expected %d accounts, got %d", exp, got)
} }
@ -506,22 +526,27 @@ func (s *Suite) snapGetAccountRange(t *utesting.T, tc *accRangeTest) error {
return fmt.Errorf("accounts not monotonically increasing: #%d [%x] vs #%d [%x]", i-1, res.Accounts[i-1].Hash[:], i, res.Accounts[i].Hash[:]) return fmt.Errorf("accounts not monotonically increasing: #%d [%x] vs #%d [%x]", i-1, res.Accounts[i-1].Hash[:], i, res.Accounts[i].Hash[:])
} }
} }
var ( var (
hashes []common.Hash hashes []common.Hash
accounts [][]byte accounts [][]byte
proof = res.Proof proof = res.Proof
) )
hashes, accounts, err = res.Unpack() hashes, accounts, err = res.Unpack()
if err != nil { if err != nil {
return err return err
} }
if len(hashes) == 0 && len(accounts) == 0 && len(proof) == 0 { if len(hashes) == 0 && len(accounts) == 0 && len(proof) == 0 {
return nil return nil
} }
if len(hashes) > 0 { if len(hashes) > 0 {
if exp, got := tc.expFirst, res.Accounts[0].Hash; exp != got { if exp, got := tc.expFirst, res.Accounts[0].Hash; exp != got {
return fmt.Errorf("expected first account %#x, got %#x", exp, got) return fmt.Errorf("expected first account %#x, got %#x", exp, got)
} }
if exp, got := tc.expLast, res.Accounts[len(res.Accounts)-1].Hash; exp != got { if exp, got := tc.expLast, res.Accounts[len(res.Accounts)-1].Hash; exp != got {
return fmt.Errorf("expected last account %#x, got %#x", exp, got) return fmt.Errorf("expected last account %#x, got %#x", exp, got)
} }
@ -531,17 +556,21 @@ func (s *Suite) snapGetAccountRange(t *utesting.T, tc *accRangeTest) error {
for i, key := range hashes { for i, key := range hashes {
keys[i] = common.CopyBytes(key[:]) keys[i] = common.CopyBytes(key[:])
} }
nodes := make(light.NodeList, len(proof)) nodes := make(light.NodeList, len(proof))
for i, node := range proof { for i, node := range proof {
nodes[i] = node nodes[i] = node
} }
proofdb := nodes.NodeSet() proofdb := nodes.NodeSet()
var end []byte var end []byte
if len(keys) > 0 { if len(keys) > 0 {
end = keys[len(keys)-1] end = keys[len(keys)-1]
} }
_, err = trie.VerifyRangeProof(tc.root, tc.origin[:], end, keys, accounts, proofdb) _, err = trie.VerifyRangeProof(tc.root, tc.origin[:], end, keys, accounts, proofdb)
return err return err
} }
@ -550,7 +579,9 @@ func (s *Suite) snapGetStorageRanges(t *utesting.T, tc *stRangesTest) error {
if err != nil { if err != nil {
t.Fatalf("dial failed: %v", err) t.Fatalf("dial failed: %v", err)
} }
defer conn.Close() defer conn.Close()
if err = conn.peer(s.chain, nil); err != nil { if err = conn.peer(s.chain, nil); err != nil {
t.Fatalf("peering failed: %v", err) t.Fatalf("peering failed: %v", err)
} }
@ -563,29 +594,36 @@ func (s *Suite) snapGetStorageRanges(t *utesting.T, tc *stRangesTest) error {
Limit: tc.limit, Limit: tc.limit,
Bytes: tc.nBytes, Bytes: tc.nBytes,
} }
resp, err := conn.snapRequest(req, req.ID, s.chain) resp, err := conn.snapRequest(req, req.ID, s.chain)
if err != nil { if err != nil {
return fmt.Errorf("account range request failed: %v", err) return fmt.Errorf("account range request failed: %v", err)
} }
var res *snap.StorageRangesPacket var res *snap.StorageRangesPacket
if r, ok := resp.(*StorageRanges); !ok { if r, ok := resp.(*StorageRanges); !ok {
return fmt.Errorf("account range response wrong: %T %v", resp, resp) return fmt.Errorf("account range response wrong: %T %v", resp, resp)
} else { } else {
res = (*snap.StorageRangesPacket)(r) res = (*snap.StorageRangesPacket)(r)
} }
gotSlots := 0 gotSlots := 0
// Ensure the ranges are monotonically increasing // Ensure the ranges are monotonically increasing
for i, slots := range res.Slots { for i, slots := range res.Slots {
gotSlots += len(slots) gotSlots += len(slots)
for j := 1; j < len(slots); j++ { for j := 1; j < len(slots); j++ {
if bytes.Compare(slots[j-1].Hash[:], slots[j].Hash[:]) >= 0 { if bytes.Compare(slots[j-1].Hash[:], slots[j].Hash[:]) >= 0 {
return fmt.Errorf("storage slots not monotonically increasing for account #%d: #%d [%x] vs #%d [%x]", i, j-1, slots[j-1].Hash[:], j, slots[j].Hash[:]) return fmt.Errorf("storage slots not monotonically increasing for account #%d: #%d [%x] vs #%d [%x]", i, j-1, slots[j-1].Hash[:], j, slots[j].Hash[:])
} }
} }
} }
if exp, got := tc.expSlots, gotSlots; exp != got { if exp, got := tc.expSlots, gotSlots; exp != got {
return fmt.Errorf("expected %d slots, got %d", exp, got) return fmt.Errorf("expected %d slots, got %d", exp, got)
} }
return nil return nil
} }
@ -594,7 +632,9 @@ func (s *Suite) snapGetByteCodes(t *utesting.T, tc *byteCodesTest) error {
if err != nil { if err != nil {
t.Fatalf("dial failed: %v", err) t.Fatalf("dial failed: %v", err)
} }
defer conn.Close() defer conn.Close()
if err = conn.peer(s.chain, nil); err != nil { if err = conn.peer(s.chain, nil); err != nil {
t.Fatalf("peering failed: %v", err) t.Fatalf("peering failed: %v", err)
} }
@ -604,20 +644,25 @@ func (s *Suite) snapGetByteCodes(t *utesting.T, tc *byteCodesTest) error {
Hashes: tc.hashes, Hashes: tc.hashes,
Bytes: tc.nBytes, Bytes: tc.nBytes,
} }
resp, err := conn.snapRequest(req, req.ID, s.chain) resp, err := conn.snapRequest(req, req.ID, s.chain)
if err != nil { if err != nil {
return fmt.Errorf("getBytecodes request failed: %v", err) return fmt.Errorf("getBytecodes request failed: %v", err)
} }
var res *snap.ByteCodesPacket var res *snap.ByteCodesPacket
if r, ok := resp.(*ByteCodes); !ok { if r, ok := resp.(*ByteCodes); !ok {
return fmt.Errorf("bytecodes response wrong: %T %v", resp, resp) return fmt.Errorf("bytecodes response wrong: %T %v", resp, resp)
} else { } else {
res = (*snap.ByteCodesPacket)(r) res = (*snap.ByteCodesPacket)(r)
} }
if exp, got := tc.expHashes, len(res.Codes); exp != got { if exp, got := tc.expHashes, len(res.Codes); exp != got {
for i, c := range res.Codes { for i, c := range res.Codes {
fmt.Printf("%d. %#x\n", i, c) fmt.Printf("%d. %#x\n", i, c)
} }
return fmt.Errorf("expected %d bytecodes, got %d", exp, got) return fmt.Errorf("expected %d bytecodes, got %d", exp, got)
} }
// Cross reference the requested bytecodes with the response to find gaps // Cross reference the requested bytecodes with the response to find gaps
@ -638,9 +683,11 @@ func (s *Suite) snapGetByteCodes(t *utesting.T, tc *byteCodesTest) error {
for j < len(req.Hashes) && !bytes.Equal(hash, req.Hashes[j][:]) { for j < len(req.Hashes) && !bytes.Equal(hash, req.Hashes[j][:]) {
j++ j++
} }
if j < len(req.Hashes) { if j < len(req.Hashes) {
codes[j] = bytecodes[i] codes[j] = bytecodes[i]
j++ j++
continue continue
} }
// We've either ran out of hashes, or got unrequested data // We've either ran out of hashes, or got unrequested data
@ -655,7 +702,9 @@ func (s *Suite) snapGetTrieNodes(t *utesting.T, tc *trieNodesTest) error {
if err != nil { if err != nil {
t.Fatalf("dial failed: %v", err) t.Fatalf("dial failed: %v", err)
} }
defer conn.Close() defer conn.Close()
if err = conn.peer(s.chain, nil); err != nil { if err = conn.peer(s.chain, nil); err != nil {
t.Fatalf("peering failed: %v", err) t.Fatalf("peering failed: %v", err)
} }
@ -666,14 +715,18 @@ func (s *Suite) snapGetTrieNodes(t *utesting.T, tc *trieNodesTest) error {
Paths: tc.paths, Paths: tc.paths,
Bytes: tc.nBytes, Bytes: tc.nBytes,
} }
resp, err := conn.snapRequest(req, req.ID, s.chain) resp, err := conn.snapRequest(req, req.ID, s.chain)
if err != nil { if err != nil {
if tc.expReject { if tc.expReject {
return nil return nil
} }
return fmt.Errorf("trienodes request failed: %v", err) return fmt.Errorf("trienodes request failed: %v", err)
} }
var res *snap.TrieNodesPacket var res *snap.TrieNodesPacket
if r, ok := resp.(*TrieNodes); !ok { if r, ok := resp.(*TrieNodes); !ok {
return fmt.Errorf("trienodes response wrong: %T %v", resp, resp) return fmt.Errorf("trienodes response wrong: %T %v", resp, resp)
} else { } else {
@ -686,18 +739,22 @@ func (s *Suite) snapGetTrieNodes(t *utesting.T, tc *trieNodesTest) error {
// that the serving node is missing // that the serving node is missing
hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState) hasher := sha3.NewLegacyKeccak256().(crypto.KeccakState)
hash := make([]byte, 32) hash := make([]byte, 32)
trienodes := res.Nodes trienodes := res.Nodes
if got, want := len(trienodes), len(tc.expHashes); got != want { if got, want := len(trienodes), len(tc.expHashes); got != want {
return fmt.Errorf("wrong trienode count, got %d, want %d\n", got, want) return fmt.Errorf("wrong trienode count, got %d, want %d\n", got, want)
} }
for i, trienode := range trienodes { for i, trienode := range trienodes {
hasher.Reset() hasher.Reset()
hasher.Write(trienode) hasher.Write(trienode)
hasher.Read(hash) hasher.Read(hash)
if got, want := hash, tc.expHashes[i]; !bytes.Equal(got, want[:]) { if got, want := hash, tc.expHashes[i]; !bytes.Equal(got, want[:]) {
fmt.Printf("hash %d wrong, got %#x, want %#x\n", i, got, want) fmt.Printf("hash %d wrong, got %#x, want %#x\n", i, got, want)
err = fmt.Errorf("hash %d wrong, got %#x, want %#x", i, got, want) err = fmt.Errorf("hash %d wrong, got %#x, want %#x", i, got, want)
} }
} }
return err return err
} }

View file

@ -42,6 +42,7 @@ func NewSuite(dest *enode.Node, chainfile string, genesisfile string) (*Suite, e
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Suite{ return &Suite{
Dest: dest, Dest: dest,
chain: chain.Shorten(1000), chain: chain.Shorten(1000),
@ -93,7 +94,9 @@ func (s *Suite) TestStatus(t *utesting.T) {
if err != nil { if err != nil {
t.Fatalf("dial failed: %v", err) t.Fatalf("dial failed: %v", err)
} }
defer conn.Close() defer conn.Close()
if err := conn.peer(s.chain, nil); err != nil { if err := conn.peer(s.chain, nil); err != nil {
t.Fatalf("peering failed: %v", err) t.Fatalf("peering failed: %v", err)
} }
@ -106,7 +109,9 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
if err != nil { if err != nil {
t.Fatalf("dial failed: %v", err) t.Fatalf("dial failed: %v", err)
} }
defer conn.Close() defer conn.Close()
if err = conn.peer(s.chain, nil); err != nil { if err = conn.peer(s.chain, nil); err != nil {
t.Fatalf("peering failed: %v", err) t.Fatalf("peering failed: %v", err)
} }
@ -119,6 +124,7 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
Reverse: false, Reverse: false,
}, },
} }
headers, err := conn.headersRequest(req, s.chain, 33) headers, err := conn.headersRequest(req, s.chain, 33)
if err != nil { if err != nil {
t.Fatalf("could not get block headers: %v", err) t.Fatalf("could not get block headers: %v", err)
@ -128,6 +134,7 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
if err != nil { if err != nil {
t.Fatalf("failed to get headers for given request: %v", err) t.Fatalf("failed to get headers for given request: %v", err)
} }
if !headersMatch(expected, headers) { if !headersMatch(expected, headers) {
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers) t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers)
} }
@ -143,7 +150,9 @@ func (s *Suite) TestSimultaneousRequests(t *utesting.T) {
if err != nil { if err != nil {
t.Fatalf("dial failed: %v", err) t.Fatalf("dial failed: %v", err)
} }
defer conn.Close() defer conn.Close()
if err := conn.peer(s.chain, nil); err != nil { if err := conn.peer(s.chain, nil); err != nil {
t.Fatalf("peering failed: %v", err) t.Fatalf("peering failed: %v", err)
} }
@ -184,11 +193,14 @@ func (s *Suite) TestSimultaneousRequests(t *utesting.T) {
// wait for responses // wait for responses
msg := conn.waitForResponse(s.chain, timeout, req1.RequestId) msg := conn.waitForResponse(s.chain, timeout, req1.RequestId)
headers1, ok := msg.(*BlockHeaders) headers1, ok := msg.(*BlockHeaders)
if !ok { if !ok {
t.Fatalf("unexpected %s", pretty.Sdump(msg)) t.Fatalf("unexpected %s", pretty.Sdump(msg))
} }
msg = conn.waitForResponse(s.chain, timeout, req2.RequestId) msg = conn.waitForResponse(s.chain, timeout, req2.RequestId)
headers2, ok := msg.(*BlockHeaders) headers2, ok := msg.(*BlockHeaders)
if !ok { if !ok {
t.Fatalf("unexpected %s", pretty.Sdump(msg)) t.Fatalf("unexpected %s", pretty.Sdump(msg))
} }
@ -221,7 +233,9 @@ func (s *Suite) TestSameRequestID(t *utesting.T) {
if err != nil { if err != nil {
t.Fatalf("dial failed: %v", err) t.Fatalf("dial failed: %v", err)
} }
defer conn.Close() defer conn.Close()
if err := conn.peer(s.chain, nil); err != nil { if err := conn.peer(s.chain, nil); err != nil {
t.Fatalf("peering failed: %v", err) t.Fatalf("peering failed: %v", err)
} }
@ -258,11 +272,14 @@ func (s *Suite) TestSameRequestID(t *utesting.T) {
// wait for responses // wait for responses
msg := conn.waitForResponse(s.chain, timeout, reqID) msg := conn.waitForResponse(s.chain, timeout, reqID)
headers1, ok := msg.(*BlockHeaders) headers1, ok := msg.(*BlockHeaders)
if !ok { if !ok {
t.Fatalf("unexpected %s", pretty.Sdump(msg)) t.Fatalf("unexpected %s", pretty.Sdump(msg))
} }
msg = conn.waitForResponse(s.chain, timeout, reqID) msg = conn.waitForResponse(s.chain, timeout, reqID)
headers2, ok := msg.(*BlockHeaders) headers2, ok := msg.(*BlockHeaders)
if !ok { if !ok {
t.Fatalf("unexpected %s", pretty.Sdump(msg)) t.Fatalf("unexpected %s", pretty.Sdump(msg))
} }
@ -294,16 +311,20 @@ func (s *Suite) TestZeroRequestID(t *utesting.T) {
if err != nil { if err != nil {
t.Fatalf("dial failed: %v", err) t.Fatalf("dial failed: %v", err)
} }
defer conn.Close() defer conn.Close()
if err := conn.peer(s.chain, nil); err != nil { if err := conn.peer(s.chain, nil); err != nil {
t.Fatalf("peering failed: %v", err) t.Fatalf("peering failed: %v", err)
} }
req := &GetBlockHeaders{ req := &GetBlockHeaders{
GetBlockHeadersPacket: &eth.GetBlockHeadersPacket{ GetBlockHeadersPacket: &eth.GetBlockHeadersPacket{
Origin: eth.HashOrNumber{Number: 0}, Origin: eth.HashOrNumber{Number: 0},
Amount: 2, Amount: 2,
}, },
} }
headers, err := conn.headersRequest(req, s.chain, 0) headers, err := conn.headersRequest(req, s.chain, 0)
if err != nil { if err != nil {
t.Fatalf("failed to get block headers: %v", err) t.Fatalf("failed to get block headers: %v", err)
@ -313,6 +334,7 @@ func (s *Suite) TestZeroRequestID(t *utesting.T) {
if err != nil { if err != nil {
t.Fatalf("failed to get expected block headers: %v", err) t.Fatalf("failed to get expected block headers: %v", err)
} }
if !headersMatch(expected, headers) { if !headersMatch(expected, headers) {
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers) t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers)
} }
@ -325,7 +347,9 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) {
if err != nil { if err != nil {
t.Fatalf("dial failed: %v", err) t.Fatalf("dial failed: %v", err)
} }
defer conn.Close() defer conn.Close()
if err := conn.peer(s.chain, nil); err != nil { if err := conn.peer(s.chain, nil); err != nil {
t.Fatalf("peering failed: %v", err) t.Fatalf("peering failed: %v", err)
} }
@ -343,6 +367,7 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) {
// wait for block bodies response // wait for block bodies response
msg := conn.waitForResponse(s.chain, timeout, req.RequestId) msg := conn.waitForResponse(s.chain, timeout, req.RequestId)
resp, ok := msg.(*BlockBodies) resp, ok := msg.(*BlockBodies)
if !ok { if !ok {
t.Fatalf("unexpected: %s", pretty.Sdump(msg)) t.Fatalf("unexpected: %s", pretty.Sdump(msg))
} }
@ -389,9 +414,11 @@ func (s *Suite) TestLargeAnnounce(t *utesting.T) {
if err != nil { if err != nil {
t.Fatalf("dial failed: %v", err) t.Fatalf("dial failed: %v", err)
} }
if err := conn.peer(s.chain, nil); err != nil { if err := conn.peer(s.chain, nil); err != nil {
t.Fatalf("peering failed: %v", err) t.Fatalf("peering failed: %v", err)
} }
if err := conn.Write(blockAnnouncement); err != nil { if err := conn.Write(blockAnnouncement); err != nil {
t.Fatalf("could not write to connection: %v", err) t.Fatalf("could not write to connection: %v", err)
} }
@ -476,6 +503,7 @@ func (s *Suite) TestLargeTxRequest(t *utesting.T) {
if err != nil { if err != nil {
t.Fatalf("failed to generate transactions: %v", err) t.Fatalf("failed to generate transactions: %v", err)
} }
if err = sendMultipleSuccessfulTxs(t, s, txs); err != nil { if err = sendMultipleSuccessfulTxs(t, s, txs); err != nil {
t.Fatalf("failed to send multiple txs: %v", err) t.Fatalf("failed to send multiple txs: %v", err)
} }
@ -485,7 +513,9 @@ func (s *Suite) TestLargeTxRequest(t *utesting.T) {
if err != nil { if err != nil {
t.Fatalf("dial failed: %v", err) t.Fatalf("dial failed: %v", err)
} }
defer conn.Close() defer conn.Close()
if err = conn.peer(s.chain, nil); err != nil { if err = conn.peer(s.chain, nil); err != nil {
t.Fatalf("peering failed: %v", err) t.Fatalf("peering failed: %v", err)
} }
@ -546,7 +576,9 @@ func (s *Suite) TestNewPooledTxs(t *utesting.T) {
if err != nil { if err != nil {
t.Fatalf("dial failed: %v", err) t.Fatalf("dial failed: %v", err)
} }
defer conn.Close() defer conn.Close()
if err = conn.peer(s.chain, nil); err != nil { if err = conn.peer(s.chain, nil); err != nil {
t.Fatalf("peering failed: %v", err) t.Fatalf("peering failed: %v", err)
} }
@ -571,6 +603,7 @@ func (s *Suite) TestNewPooledTxs(t *utesting.T) {
if len(msg.GetPooledTransactionsPacket) != len(hashes) { if len(msg.GetPooledTransactionsPacket) != len(hashes) {
t.Fatalf("unexpected number of txs requested: wanted %d, got %d", len(hashes), len(msg.GetPooledTransactionsPacket)) t.Fatalf("unexpected number of txs requested: wanted %d, got %d", len(hashes), len(msg.GetPooledTransactionsPacket))
} }
return return
// ignore propagated txs from previous tests // ignore propagated txs from previous tests

View file

@ -50,6 +50,7 @@ func TestEthSuite(t *testing.T) {
test := test test := test
t.Run(test.Name, func(t *testing.T) { t.Run(test.Name, func(t *testing.T) {
t.Parallel() t.Parallel()
result := utesting.RunTAP([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout) result := utesting.RunTAP([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout)
if result[0].Failed { if result[0].Failed {
t.Fatal() t.Fatal()
@ -69,6 +70,7 @@ func TestSnapSuite(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("could not create new test suite: %v", err) t.Fatalf("could not create new test suite: %v", err)
} }
for _, test := range suite.SnapTests() { for _, test := range suite.SnapTests() {
t.Run(test.Name, func(t *testing.T) { t.Run(test.Name, func(t *testing.T) {
result := utesting.RunTAP([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout) result := utesting.RunTAP([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout)
@ -98,10 +100,12 @@ func runGeth() (*node.Node, error) {
stack.Close() stack.Close()
return nil, err return nil, err
} }
if err = stack.Start(); err != nil { if err = stack.Start(); err != nil {
stack.Close() stack.Close()
return nil, err return nil, err
} }
return stack, nil return stack, nil
} }
@ -127,5 +131,6 @@ func setupGeth(stack *node.Node) error {
} }
_, err = backend.BlockChain().InsertChain(chain.blocks[1:]) _, err = backend.BlockChain().InsertChain(chain.blocks[1:])
return err return err
} }

View file

@ -41,6 +41,7 @@ func (s *Suite) sendSuccessfulTxs(t *utesting.T) error {
if tx == nil { if tx == nil {
return fmt.Errorf("could not find tx to send") return fmt.Errorf("could not find tx to send")
} }
t.Logf("Testing tx propagation %d: sending tx %v %v %v\n", i, tx.Hash().String(), tx.GasPrice(), tx.Gas()) t.Logf("Testing tx propagation %d: sending tx %v %v %v\n", i, tx.Hash().String(), tx.GasPrice(), tx.Gas())
// get previous tx if exists for reference in case of old tx propagation // get previous tx if exists for reference in case of old tx propagation
var prevTx *types.Transaction var prevTx *types.Transaction
@ -52,6 +53,7 @@ func (s *Suite) sendSuccessfulTxs(t *utesting.T) error {
return fmt.Errorf("send successful tx test failed: %v", err) return fmt.Errorf("send successful tx test failed: %v", err)
} }
} }
return nil return nil
} }
@ -61,8 +63,10 @@ func sendSuccessfulTx(s *Suite, tx *types.Transaction, prevTx *types.Transaction
if err != nil { if err != nil {
return err return err
} }
defer sendConn.Close() defer sendConn.Close()
defer recvConn.Close() defer recvConn.Close()
if err = sendConn.peer(s.chain, nil); err != nil { if err = sendConn.peer(s.chain, nil); err != nil {
return fmt.Errorf("peering failed: %v", err) return fmt.Errorf("peering failed: %v", err)
} }
@ -89,12 +93,14 @@ func sendSuccessfulTx(s *Suite, tx *types.Transaction, prevTx *types.Transaction
continue continue
} }
} }
for _, gotTx := range recTxs { for _, gotTx := range recTxs {
if gotTx.Hash() == tx.Hash() { if gotTx.Hash() == tx.Hash() {
// Ok // Ok
return nil return nil
} }
} }
return fmt.Errorf("missing transaction: got %v missing %v", recTxs, tx.Hash()) return fmt.Errorf("missing transaction: got %v missing %v", recTxs, tx.Hash())
case *NewPooledTransactionHashes66: case *NewPooledTransactionHashes66:
txHashes := *msg txHashes := *msg
@ -104,12 +110,14 @@ func sendSuccessfulTx(s *Suite, tx *types.Transaction, prevTx *types.Transaction
continue continue
} }
} }
for _, gotHash := range txHashes { for _, gotHash := range txHashes {
if gotHash == tx.Hash() { if gotHash == tx.Hash() {
// Ok // Ok
return nil return nil
} }
} }
return fmt.Errorf("missing transaction announcement: got %v missing %v", txHashes, tx.Hash()) return fmt.Errorf("missing transaction announcement: got %v missing %v", txHashes, tx.Hash())
case *NewPooledTransactionHashes: case *NewPooledTransactionHashes:
txHashes := msg.Hashes txHashes := msg.Hashes
@ -163,7 +171,9 @@ func (s *Suite) sendMaliciousTxs(t *utesting.T) error {
if err != nil { if err != nil {
return fmt.Errorf("dial failed: %v", err) return fmt.Errorf("dial failed: %v", err)
} }
defer recvConn.Close() defer recvConn.Close()
if err = recvConn.peer(s.chain, nil); err != nil { if err = recvConn.peer(s.chain, nil); err != nil {
return fmt.Errorf("peering failed: %v", err) return fmt.Errorf("peering failed: %v", err)
} }
@ -184,7 +194,9 @@ func sendMaliciousTx(s *Suite, tx *types.Transaction) error {
if err != nil { if err != nil {
return fmt.Errorf("dial failed: %v", err) return fmt.Errorf("dial failed: %v", err)
} }
defer conn.Close() defer conn.Close()
if err = conn.peer(s.chain, nil); err != nil { if err = conn.peer(s.chain, nil); err != nil {
return fmt.Errorf("peering failed: %v", err) return fmt.Errorf("peering failed: %v", err)
} }
@ -193,6 +205,7 @@ func sendMaliciousTx(s *Suite, tx *types.Transaction) error {
if err = conn.Write(&Transactions{tx}); err != nil { if err = conn.Write(&Transactions{tx}); err != nil {
return fmt.Errorf("failed to write to connection: %v", err) return fmt.Errorf("failed to write to connection: %v", err)
} }
return nil return nil
} }
@ -208,11 +221,14 @@ func sendMultipleSuccessfulTxs(t *utesting.T, s *Suite, txs []*types.Transaction
if err != nil { if err != nil {
return err return err
} }
defer sendConn.Close() defer sendConn.Close()
defer recvConn.Close() defer recvConn.Close()
if err = sendConn.peer(s.chain, nil); err != nil { if err = sendConn.peer(s.chain, nil); err != nil {
return fmt.Errorf("peering failed: %v", err) return fmt.Errorf("peering failed: %v", err)
} }
if err = recvConn.peer(s.chain, nil); err != nil { if err = recvConn.peer(s.chain, nil); err != nil {
return fmt.Errorf("peering failed: %v", err) return fmt.Errorf("peering failed: %v", err)
} }
@ -248,6 +264,7 @@ func sendMultipleSuccessfulTxs(t *utesting.T, s *Suite, txs []*types.Transaction
if len(recvHashes) == 2000 { if len(recvHashes) == 2000 {
break break
} }
if len(recvHashes) > 0 { if len(recvHashes) > 0 {
_, missingTxs := compareReceivedTxs(recvHashes, txs) _, missingTxs := compareReceivedTxs(recvHashes, txs)
if len(missingTxs) > 0 { if len(missingTxs) > 0 {
@ -258,13 +275,16 @@ func sendMultipleSuccessfulTxs(t *utesting.T, s *Suite, txs []*types.Transaction
} }
} }
} }
_, missingTxs := compareReceivedTxs(recvHashes, txs) _, missingTxs := compareReceivedTxs(recvHashes, txs)
if len(missingTxs) > 0 { if len(missingTxs) > 0 {
for _, missing := range missingTxs { for _, missing := range missingTxs {
t.Logf("missing tx: %v", missing.Hash()) t.Logf("missing tx: %v", missing.Hash())
} }
return fmt.Errorf("missing %d txs", len(missingTxs)) return fmt.Errorf("missing %d txs", len(missingTxs))
} }
return nil return nil
} }
@ -279,6 +299,7 @@ func checkMaliciousTxPropagation(s *Suite, txs []*types.Transaction, conn *Conn)
for i, recvTx := range *msg { for i, recvTx := range *msg {
recvTxs[i] = recvTx.Hash() recvTxs[i] = recvTx.Hash()
} }
badTxs, _ := compareReceivedTxs(recvTxs, txs) badTxs, _ := compareReceivedTxs(recvTxs, txs)
if len(badTxs) > 0 { if len(badTxs) > 0 {
return fmt.Errorf("received %d bad txs: \n%v", len(badTxs), badTxs) return fmt.Errorf("received %d bad txs: \n%v", len(badTxs), badTxs)
@ -299,6 +320,7 @@ func checkMaliciousTxPropagation(s *Suite, txs []*types.Transaction, conn *Conn)
default: default:
return fmt.Errorf("unexpected message in sendFailingTx: %s", pretty.Sdump(msg)) return fmt.Errorf("unexpected message in sendFailingTx: %s", pretty.Sdump(msg))
} }
return nil return nil
} }
@ -315,6 +337,7 @@ func compareReceivedTxs(recvTxs []common.Hash, txs []*types.Transaction) (presen
// collect present txs and missing txs separately // collect present txs and missing txs separately
present = make([]*types.Transaction, 0) present = make([]*types.Transaction, 0)
missing = make([]*types.Transaction, 0) missing = make([]*types.Transaction, 0)
for _, tx := range txs { for _, tx := range txs {
if _, exists := recvHashes[tx.Hash()]; exists { if _, exists := recvHashes[tx.Hash()]; exists {
present = append(present, tx) present = append(present, tx)
@ -322,6 +345,7 @@ func compareReceivedTxs(recvTxs []common.Hash, txs []*types.Transaction) (presen
missing = append(missing, tx) missing = append(missing, tx)
} }
} }
return present, missing return present, missing
} }
@ -330,11 +354,14 @@ func unknownTx(s *Suite) *types.Transaction {
if tx == nil { if tx == nil {
return nil return nil
} }
var to common.Address var to common.Address
if tx.To() != nil { if tx.To() != nil {
to = *tx.To() to = *tx.To()
} }
txNew := types.NewTransaction(tx.Nonce()+1, to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.Data()) txNew := types.NewTransaction(tx.Nonce()+1, to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.Data())
return signWithFaucet(s.chain.chainConfig, txNew) return signWithFaucet(s.chain.chainConfig, txNew)
} }
@ -346,6 +373,7 @@ func getNextTxFromChain(s *Suite) *types.Transaction {
return txs[0] return txs[0]
} }
} }
return nil return nil
} }
@ -357,6 +385,7 @@ func generateTxs(s *Suite, numTxs int) (map[common.Hash]common.Hash, []*types.Tr
if nextTx == nil { if nextTx == nil {
return nil, nil, fmt.Errorf("failed to get the next transaction") return nil, nil, fmt.Errorf("failed to get the next transaction")
} }
gas := nextTx.Gas() gas := nextTx.Gas()
nonce = nonce + 1 nonce = nonce + 1
@ -366,16 +395,19 @@ func generateTxs(s *Suite, numTxs int) (map[common.Hash]common.Hash, []*types.Tr
if tx == nil { if tx == nil {
return nil, nil, fmt.Errorf("failed to get the next transaction") return nil, nil, fmt.Errorf("failed to get the next transaction")
} }
txHashMap[tx.Hash()] = tx.Hash() txHashMap[tx.Hash()] = tx.Hash()
txs[i] = tx txs[i] = tx
nonce = nonce + 1 nonce = nonce + 1
} }
return txHashMap, txs, nil return txHashMap, txs, nil
} }
func generateTx(chainConfig *params.ChainConfig, nonce uint64, gas uint64) *types.Transaction { func generateTx(chainConfig *params.ChainConfig, nonce uint64, gas uint64) *types.Transaction {
var to common.Address var to common.Address
tx := types.NewTransaction(nonce, to, big.NewInt(1), gas, big.NewInt(1), []byte{}) tx := types.NewTransaction(nonce, to, big.NewInt(1), gas, big.NewInt(1), []byte{})
return signWithFaucet(chainConfig, tx) return signWithFaucet(chainConfig, tx)
} }
@ -386,6 +418,7 @@ func getOldTxFromChain(s *Suite) *types.Transaction {
return txs[0] return txs[0]
} }
} }
return nil return nil
} }
@ -394,11 +427,14 @@ func invalidNonceTx(s *Suite) *types.Transaction {
if tx == nil { if tx == nil {
return nil return nil
} }
var to common.Address var to common.Address
if tx.To() != nil { if tx.To() != nil {
to = *tx.To() to = *tx.To()
} }
txNew := types.NewTransaction(tx.Nonce()-2, to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.Data()) txNew := types.NewTransaction(tx.Nonce()-2, to, tx.Value(), tx.Gas(), tx.GasPrice(), tx.Data())
return signWithFaucet(s.chain.chainConfig, txNew) return signWithFaucet(s.chain.chainConfig, txNew)
} }
@ -407,12 +443,16 @@ func hugeAmount(s *Suite) *types.Transaction {
if tx == nil { if tx == nil {
return nil return nil
} }
amount := largeNumber(2) amount := largeNumber(2)
var to common.Address var to common.Address
if tx.To() != nil { if tx.To() != nil {
to = *tx.To() to = *tx.To()
} }
txNew := types.NewTransaction(tx.Nonce(), to, amount, tx.Gas(), tx.GasPrice(), tx.Data()) txNew := types.NewTransaction(tx.Nonce(), to, amount, tx.Gas(), tx.GasPrice(), tx.Data())
return signWithFaucet(s.chain.chainConfig, txNew) return signWithFaucet(s.chain.chainConfig, txNew)
} }
@ -421,12 +461,16 @@ func hugeGasPrice(s *Suite) *types.Transaction {
if tx == nil { if tx == nil {
return nil return nil
} }
gasPrice := largeNumber(2) gasPrice := largeNumber(2)
var to common.Address var to common.Address
if tx.To() != nil { if tx.To() != nil {
to = *tx.To() to = *tx.To()
} }
txNew := types.NewTransaction(tx.Nonce(), to, tx.Value(), tx.Gas(), gasPrice, tx.Data()) txNew := types.NewTransaction(tx.Nonce(), to, tx.Value(), tx.Gas(), gasPrice, tx.Data())
return signWithFaucet(s.chain.chainConfig, txNew) return signWithFaucet(s.chain.chainConfig, txNew)
} }
@ -435,19 +479,24 @@ func hugeData(s *Suite) *types.Transaction {
if tx == nil { if tx == nil {
return nil return nil
} }
var to common.Address var to common.Address
if tx.To() != nil { if tx.To() != nil {
to = *tx.To() to = *tx.To()
} }
txNew := types.NewTransaction(tx.Nonce(), to, tx.Value(), tx.Gas(), tx.GasPrice(), largeBuffer(2)) txNew := types.NewTransaction(tx.Nonce(), to, tx.Value(), tx.Gas(), tx.GasPrice(), largeBuffer(2))
return signWithFaucet(s.chain.chainConfig, txNew) return signWithFaucet(s.chain.chainConfig, txNew)
} }
func signWithFaucet(chainConfig *params.ChainConfig, tx *types.Transaction) *types.Transaction { func signWithFaucet(chainConfig *params.ChainConfig, tx *types.Transaction) *types.Transaction {
signer := types.LatestSigner(chainConfig) signer := types.LatestSigner(chainConfig)
signedTx, err := types.SignTx(tx, signer, faucetKey) signedTx, err := types.SignTx(tx, signer, faucetKey)
if err != nil { if err != nil {
return nil return nil
} }
return signedTx return signedTx
} }

View file

@ -167,6 +167,7 @@ func (c *Conn) Read() Message {
} }
var msg Message var msg Message
switch int(code) { switch int(code) {
case (Hello{}).Code(): case (Hello{}).Code():
msg = new(Hello) msg = new(Hello)
@ -255,7 +256,9 @@ func (c *Conn) Write(msg Message) error {
if err != nil { if err != nil {
return err return err
} }
_, err = c.Conn.Write(uint64(msg.Code()), payload) _, err = c.Conn.Write(uint64(msg.Code()), payload)
return err return err
} }
@ -263,11 +266,13 @@ func (c *Conn) Write(msg Message) error {
func (c *Conn) ReadSnap(id uint64) (Message, error) { func (c *Conn) ReadSnap(id uint64) (Message, error) {
respId := id + 1 respId := id + 1
start := time.Now() start := time.Now()
for respId != id && time.Since(start) < timeout { for respId != id && time.Since(start) < timeout {
code, rawData, _, err := c.Conn.Read() code, rawData, _, err := c.Conn.Read()
if err != nil { if err != nil {
return nil, fmt.Errorf("could not read from connection: %v", err) return nil, fmt.Errorf("could not read from connection: %v", err)
} }
var snpMsg interface{} var snpMsg interface{}
switch int(code) { switch int(code) {
case (GetAccountRange{}).Code(): case (GetAccountRange{}).Code():
@ -290,10 +295,13 @@ func (c *Conn) ReadSnap(id uint64) (Message, error) {
//return nil, fmt.Errorf("invalid message code: %d", code) //return nil, fmt.Errorf("invalid message code: %d", code)
continue continue
} }
if err := rlp.DecodeBytes(rawData, snpMsg); err != nil { if err := rlp.DecodeBytes(rawData, snpMsg); err != nil {
return nil, fmt.Errorf("could not rlp decode message: %v", err) return nil, fmt.Errorf("could not rlp decode message: %v", err)
} }
return snpMsg.(Message), nil return snpMsg.(Message), nil
} }
return nil, fmt.Errorf("request timed out") return nil, fmt.Errorf("request timed out")
} }

View file

@ -92,16 +92,19 @@ func (te *testenv) checkPingPong(pingHash []byte) error {
pings int pings int
pongs int pongs int
) )
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
reply, _, err := te.read(te.l1) reply, _, err := te.read(te.l1)
if err != nil { if err != nil {
return err return err
} }
switch reply.Kind() { switch reply.Kind() {
case v4wire.PongPacket: case v4wire.PongPacket:
if err := te.checkPong(reply, pingHash); err != nil { if err := te.checkPong(reply, pingHash); err != nil {
return err return err
} }
pongs++ pongs++
case v4wire.PingPacket: case v4wire.PingPacket:
pings++ pings++
@ -109,9 +112,11 @@ func (te *testenv) checkPingPong(pingHash []byte) error {
return fmt.Errorf("expected PING or PONG, got %v %v", reply.Name(), reply) return fmt.Errorf("expected PING or PONG, got %v %v", reply.Name(), reply)
} }
} }
if pongs == 1 && pings == 1 { if pongs == 1 && pings == 1 {
return nil return nil
} }
return fmt.Errorf("expected 1 PING (got %d) and 1 PONG (got %d)", pings, pongs) return fmt.Errorf("expected 1 PING (got %d) and 1 PONG (got %d)", pings, pongs)
} }
@ -121,19 +126,24 @@ func (te *testenv) checkPong(reply v4wire.Packet, pingHash []byte) error {
if reply == nil { if reply == nil {
return fmt.Errorf("expected PONG reply, got nil") return fmt.Errorf("expected PONG reply, got nil")
} }
if reply.Kind() != v4wire.PongPacket { if reply.Kind() != v4wire.PongPacket {
return fmt.Errorf("expected PONG reply, got %v %v", reply.Name(), reply) return fmt.Errorf("expected PONG reply, got %v %v", reply.Name(), reply)
} }
pong := reply.(*v4wire.Pong) pong := reply.(*v4wire.Pong)
if !bytes.Equal(pong.ReplyTok, pingHash) { if !bytes.Equal(pong.ReplyTok, pingHash) {
return fmt.Errorf("PONG reply token mismatch: got %x, want %x", pong.ReplyTok, pingHash) return fmt.Errorf("PONG reply token mismatch: got %x, want %x", pong.ReplyTok, pingHash)
} }
if want := te.localEndpoint(te.l1); !want.IP.Equal(pong.To.IP) || want.UDP != pong.To.UDP { if want := te.localEndpoint(te.l1); !want.IP.Equal(pong.To.IP) || want.UDP != pong.To.UDP {
return fmt.Errorf("PONG 'to' endpoint mismatch: got %+v, want %+v", pong.To, want) return fmt.Errorf("PONG 'to' endpoint mismatch: got %+v, want %+v", pong.To, want)
} }
if v4wire.Expired(pong.Expiration) { if v4wire.Expired(pong.Expiration) {
return fmt.Errorf("PONG is expired (%v)", pong.Expiration) return fmt.Errorf("PONG is expired (%v)", pong.Expiration)
} }
return nil return nil
} }
@ -143,6 +153,7 @@ func PingWrongTo(t *utesting.T) {
defer te.close() defer te.close()
wrongEndpoint := v4wire.Endpoint{IP: net.ParseIP("192.0.2.0")} wrongEndpoint := v4wire.Endpoint{IP: net.ParseIP("192.0.2.0")}
pingHash := te.send(te.l1, &v4wire.Ping{ pingHash := te.send(te.l1, &v4wire.Ping{
Version: 4, Version: 4,
From: te.localEndpoint(te.l1), From: te.localEndpoint(te.l1),
@ -208,6 +219,7 @@ func PingExtraDataWrongFrom(t *utesting.T) {
JunkData1: 42, JunkData1: 42,
JunkData2: []byte{9, 8, 7, 6, 5, 4, 3, 2, 1}, JunkData2: []byte{9, 8, 7, 6, 5, 4, 3, 2, 1},
} }
pingHash := te.send(te.l1, &req) pingHash := te.send(te.l1, &req)
if err := te.checkPingPong(pingHash); err != nil { if err := te.checkPingPong(pingHash); err != nil {
t.Fatal(err) t.Fatal(err)
@ -304,9 +316,11 @@ func FindnodeWithoutEndpointProof(t *utesting.T) {
// No response, all good // No response, all good
break break
} }
if reply.Kind() == v4wire.PingPacket { if reply.Kind() == v4wire.PingPacket {
continue // A ping is ok, just ignore it continue // A ping is ok, just ignore it
} }
t.Fatalf("Expected no reply, got %v %v", reply.Name(), reply) t.Fatalf("Expected no reply, got %v %v", reply.Name(), reply)
} }
} }
@ -326,6 +340,7 @@ func BasicFindnode(t *utesting.T) {
if err != nil { if err != nil {
t.Fatal("read find nodes", err) t.Fatal("read find nodes", err)
} }
if reply.Kind() != v4wire.NeighborsPacket { if reply.Kind() != v4wire.NeighborsPacket {
t.Fatalf("Expected neighbors, got %v %v", reply.Name(), reply) t.Fatalf("Expected neighbors, got %v %v", reply.Name(), reply)
} }
@ -363,9 +378,11 @@ func UnsolicitedNeighbors(t *utesting.T) {
if err != nil { if err != nil {
t.Fatal("read find nodes", err) t.Fatal("read find nodes", err)
} }
if reply.Kind() != v4wire.NeighborsPacket { if reply.Kind() != v4wire.NeighborsPacket {
t.Fatalf("Expected neighbors, got %v %v", reply.Name(), reply) t.Fatalf("Expected neighbors, got %v %v", reply.Name(), reply)
} }
nodes := reply.(*v4wire.Neighbors).Nodes nodes := reply.(*v4wire.Neighbors).Nodes
if contains(nodes, encFakeKey) { if contains(nodes, encFakeKey) {
t.Fatal("neighbors response contains node from earlier unsolicited neighbors response") t.Fatal("neighbors response contains node from earlier unsolicited neighbors response")
@ -408,6 +425,7 @@ func bond(t *utesting.T, te *testenv) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
switch req.(type) { switch req.(type) {
case *v4wire.Ping: case *v4wire.Ping:
te.send(te.l1, &v4wire.Pong{ te.send(te.l1, &v4wire.Pong{
@ -415,11 +433,13 @@ func bond(t *utesting.T, te *testenv) {
ReplyTok: hash, ReplyTok: hash,
Expiration: futureExpiration(), Expiration: futureExpiration(),
}) })
gotPing = true gotPing = true
case *v4wire.Pong: case *v4wire.Pong:
if err := te.checkPong(req, pingHash); err != nil { if err := te.checkPong(req, pingHash); err != nil {
t.Fatal(err) t.Fatal(err)
} }
gotPong = true gotPong = true
} }
} }
@ -450,6 +470,7 @@ func FindnodeAmplificationInvalidPongHash(t *utesting.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
switch req.(type) { switch req.(type) {
case *v4wire.Ping: case *v4wire.Ping:
// Send PONG from this node ID, but with invalid ReplyTok. // Send PONG from this node ID, but with invalid ReplyTok.
@ -458,6 +479,7 @@ func FindnodeAmplificationInvalidPongHash(t *utesting.T) {
ReplyTok: make([]byte, macSize), ReplyTok: make([]byte, macSize),
Expiration: futureExpiration(), Expiration: futureExpiration(),
}) })
gotPing = true gotPing = true
case *v4wire.Pong: case *v4wire.Pong:
gotPong = true gotPong = true

View file

@ -41,33 +41,44 @@ func newTestEnv(remote string, listen1, listen2 string) *testenv {
if err != nil { if err != nil {
panic(err) panic(err)
} }
l2, err := net.ListenPacket("udp", fmt.Sprintf("%v:0", listen2)) l2, err := net.ListenPacket("udp", fmt.Sprintf("%v:0", listen2))
if err != nil { if err != nil {
panic(err) panic(err)
} }
key, err := crypto.GenerateKey() key, err := crypto.GenerateKey()
if err != nil { if err != nil {
panic(err) panic(err)
} }
node, err := enode.Parse(enode.ValidSchemes, remote) node, err := enode.Parse(enode.ValidSchemes, remote)
if err != nil { if err != nil {
panic(err) panic(err)
} }
if node.IP() == nil || node.UDP() == 0 { if node.IP() == nil || node.UDP() == 0 {
var ip net.IP var ip net.IP
var tcpPort, udpPort int var tcpPort, udpPort int
if ip = node.IP(); ip == nil { if ip = node.IP(); ip == nil {
ip = net.ParseIP("127.0.0.1") ip = net.ParseIP("127.0.0.1")
} }
if tcpPort = node.TCP(); tcpPort == 0 { if tcpPort = node.TCP(); tcpPort == 0 {
tcpPort = 30303 tcpPort = 30303
} }
if udpPort = node.TCP(); udpPort == 0 { if udpPort = node.TCP(); udpPort == 0 {
udpPort = 30303 udpPort = 30303
} }
node = enode.NewV4(node.Pubkey(), ip, tcpPort, udpPort) node = enode.NewV4(node.Pubkey(), ip, tcpPort, udpPort)
} }
addr := &net.UDPAddr{IP: node.IP(), Port: node.UDP()} addr := &net.UDPAddr{IP: node.IP(), Port: node.UDP()}
return &testenv{l1, l2, key, node, addr} return &testenv{l1, l2, key, node, addr}
} }
@ -81,27 +92,34 @@ func (te *testenv) send(c net.PacketConn, req v4wire.Packet) []byte {
if err != nil { if err != nil {
panic(fmt.Errorf("can't encode %v packet: %v", req.Name(), err)) panic(fmt.Errorf("can't encode %v packet: %v", req.Name(), err))
} }
if _, err := c.WriteTo(packet, te.remoteAddr); err != nil { if _, err := c.WriteTo(packet, te.remoteAddr); err != nil {
panic(fmt.Errorf("can't send %v: %v", req.Name(), err)) panic(fmt.Errorf("can't send %v: %v", req.Name(), err))
} }
return hash return hash
} }
func (te *testenv) read(c net.PacketConn) (v4wire.Packet, []byte, error) { func (te *testenv) read(c net.PacketConn) (v4wire.Packet, []byte, error) {
buf := make([]byte, 2048) buf := make([]byte, 2048)
if err := c.SetReadDeadline(time.Now().Add(waitTime)); err != nil { if err := c.SetReadDeadline(time.Now().Add(waitTime)); err != nil {
return nil, nil, err return nil, nil, err
} }
n, _, err := c.ReadFrom(buf) n, _, err := c.ReadFrom(buf)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
p, _, hash, err := v4wire.Decode(buf[:n]) p, _, hash, err := v4wire.Decode(buf[:n])
return p, hash, err return p, hash, err
} }
func (te *testenv) localEndpoint(c net.PacketConn) v4wire.Endpoint { func (te *testenv) localEndpoint(c net.PacketConn) v4wire.Endpoint {
addr := c.LocalAddr().(*net.UDPAddr) addr := c.LocalAddr().(*net.UDPAddr)
return v4wire.Endpoint{ return v4wire.Endpoint{
IP: addr.IP.To4(), IP: addr.IP.To4(),
UDP: uint16(addr.Port), UDP: uint16(addr.Port),
@ -119,5 +137,6 @@ func contains(ns []v4wire.Node, key v4wire.Pubkey) bool {
return true return true
} }
} }
return false return false
} }

View file

@ -37,12 +37,14 @@ type Suite struct {
func (s *Suite) listen1(log logger) (*conn, net.PacketConn) { func (s *Suite) listen1(log logger) (*conn, net.PacketConn) {
c := newConn(s.Dest, log) c := newConn(s.Dest, log)
l := c.listen(s.Listen1) l := c.listen(s.Listen1)
return c, l return c, l
} }
func (s *Suite) listen2(log logger) (*conn, net.PacketConn, net.PacketConn) { func (s *Suite) listen2(log logger) (*conn, net.PacketConn, net.PacketConn) {
c := newConn(s.Dest, log) c := newConn(s.Dest, log)
l1, l2 := c.listen(s.Listen1), c.listen(s.Listen2) l1, l2 := c.listen(s.Listen1), c.listen(s.Listen2)
return c, l1, l2 return c, l1, l2
} }
@ -76,9 +78,11 @@ func checkPong(t *utesting.T, pong *v5wire.Pong, ping *v5wire.Ping, c net.Packet
if !bytes.Equal(pong.ReqID, ping.ReqID) { if !bytes.Equal(pong.ReqID, ping.ReqID) {
t.Fatalf("wrong request ID %x in PONG, want %x", pong.ReqID, ping.ReqID) t.Fatalf("wrong request ID %x in PONG, want %x", pong.ReqID, ping.ReqID)
} }
if !pong.ToIP.Equal(laddr(c).IP) { if !pong.ToIP.Equal(laddr(c).IP) {
t.Fatalf("wrong destination IP %v in PONG, want %v", pong.ToIP, laddr(c).IP) t.Fatalf("wrong destination IP %v in PONG, want %v", pong.ToIP, laddr(c).IP)
} }
if int(pong.ToPort) != laddr(c).Port { if int(pong.ToPort) != laddr(c).Port {
t.Fatalf("wrong destination port %v in PONG, want %v", pong.ToPort, laddr(c).Port) t.Fatalf("wrong destination port %v in PONG, want %v", pong.ToPort, laddr(c).Port)
} }
@ -112,20 +116,24 @@ func (s *Suite) TestPingMultiIP(t *utesting.T) {
// Create the session on l1. // Create the session on l1.
ping := &v5wire.Ping{ReqID: conn.nextReqID()} ping := &v5wire.Ping{ReqID: conn.nextReqID()}
resp := conn.reqresp(l1, ping) resp := conn.reqresp(l1, ping)
if resp.Kind() != v5wire.PongMsg { if resp.Kind() != v5wire.PongMsg {
t.Fatal("expected PONG, got", resp) t.Fatal("expected PONG, got", resp)
} }
checkPong(t, resp.(*v5wire.Pong), ping, l1) checkPong(t, resp.(*v5wire.Pong), ping, l1)
// Send on l2. This reuses the session because there is only one codec. // Send on l2. This reuses the session because there is only one codec.
ping2 := &v5wire.Ping{ReqID: conn.nextReqID()} ping2 := &v5wire.Ping{ReqID: conn.nextReqID()}
conn.write(l2, ping2, nil) conn.write(l2, ping2, nil)
switch resp := conn.read(l2).(type) { switch resp := conn.read(l2).(type) {
case *v5wire.Pong: case *v5wire.Pong:
t.Fatalf("remote responded to PING from %v for session on IP %v", laddr(l2).IP, laddr(l1).IP) t.Fatalf("remote responded to PING from %v for session on IP %v", laddr(l2).IP, laddr(l1).IP)
case *v5wire.Whoareyou: case *v5wire.Whoareyou:
t.Logf("got WHOAREYOU for new session as expected") t.Logf("got WHOAREYOU for new session as expected")
resp.Node = s.Dest resp.Node = s.Dest
conn.write(l2, ping2, resp) conn.write(l2, ping2, resp)
default: default:
@ -143,6 +151,7 @@ func (s *Suite) TestPingMultiIP(t *utesting.T) {
// Try on l1 again. // Try on l1 again.
ping3 := &v5wire.Ping{ReqID: conn.nextReqID()} ping3 := &v5wire.Ping{ReqID: conn.nextReqID()}
conn.write(l1, ping3, nil) conn.write(l1, ping3, nil)
switch resp := conn.read(l1).(type) { switch resp := conn.read(l1).(type) {
case *v5wire.Pong: case *v5wire.Pong:
t.Fatalf("remote responded to PING from %v for session on IP %v", laddr(l1).IP, laddr(l2).IP) t.Fatalf("remote responded to PING from %v for session on IP %v", laddr(l1).IP, laddr(l2).IP)
@ -163,6 +172,7 @@ func (s *Suite) TestPingHandshakeInterrupted(t *utesting.T) {
// First PING triggers challenge. // First PING triggers challenge.
ping := &v5wire.Ping{ReqID: conn.nextReqID()} ping := &v5wire.Ping{ReqID: conn.nextReqID()}
conn.write(l1, ping, nil) conn.write(l1, ping, nil)
switch resp := conn.read(l1).(type) { switch resp := conn.read(l1).(type) {
case *v5wire.Whoareyou: case *v5wire.Whoareyou:
t.Logf("got WHOAREYOU for PING") t.Logf("got WHOAREYOU for PING")
@ -188,11 +198,13 @@ func (s *Suite) TestTalkRequest(t *utesting.T) {
// Non-empty request ID. // Non-empty request ID.
id := conn.nextReqID() id := conn.nextReqID()
resp := conn.reqresp(l1, &v5wire.TalkRequest{ReqID: id, Protocol: "test-protocol"}) resp := conn.reqresp(l1, &v5wire.TalkRequest{ReqID: id, Protocol: "test-protocol"})
switch resp := resp.(type) { switch resp := resp.(type) {
case *v5wire.TalkResponse: case *v5wire.TalkResponse:
if !bytes.Equal(resp.ReqID, id) { if !bytes.Equal(resp.ReqID, id) {
t.Fatalf("wrong request ID %x in TALKRESP, want %x", resp.ReqID, id) t.Fatalf("wrong request ID %x in TALKRESP, want %x", resp.ReqID, id)
} }
if len(resp.Message) > 0 { if len(resp.Message) > 0 {
t.Fatalf("non-empty message %x in TALKRESP", resp.Message) t.Fatalf("non-empty message %x in TALKRESP", resp.Message)
} }
@ -207,6 +219,7 @@ func (s *Suite) TestTalkRequest(t *utesting.T) {
if len(resp.ReqID) > 0 { if len(resp.ReqID) > 0 {
t.Fatalf("wrong request ID %x in TALKRESP, want empty byte array", resp.ReqID) t.Fatalf("wrong request ID %x in TALKRESP, want empty byte array", resp.ReqID)
} }
if len(resp.Message) > 0 { if len(resp.Message) > 0 {
t.Fatalf("non-empty message %x in TALKRESP", resp.Message) t.Fatalf("non-empty message %x in TALKRESP", resp.Message)
} }
@ -224,9 +237,11 @@ func (s *Suite) TestFindnodeZeroDistance(t *utesting.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(nodes) != 1 { if len(nodes) != 1 {
t.Fatalf("remote returned more than one node for FINDNODE [0]") t.Fatalf("remote returned more than one node for FINDNODE [0]")
} }
if nodes[0].ID() != conn.remote.ID() { if nodes[0].ID() != conn.remote.ID() {
t.Errorf("ID of response node is %v, want %v", nodes[0].ID(), conn.remote.ID()) t.Errorf("ID of response node is %v, want %v", nodes[0].ID(), conn.remote.ID())
} }
@ -238,6 +253,7 @@ func (s *Suite) TestFindnodeResults(t *utesting.T) {
// Create bystanders. // Create bystanders.
nodes := make([]*bystander, 5) nodes := make([]*bystander, 5)
added := make(chan enode.ID, len(nodes)) added := make(chan enode.ID, len(nodes))
for i := range nodes { for i := range nodes {
nodes[i] = newBystander(t, s, added) nodes[i] = newBystander(t, s, added)
defer nodes[i].close() defer nodes[i].close()
@ -246,14 +262,17 @@ func (s *Suite) TestFindnodeResults(t *utesting.T) {
// Get them added to the remote table. // Get them added to the remote table.
timeout := 60 * time.Second timeout := 60 * time.Second
timeoutCh := time.After(timeout) timeoutCh := time.After(timeout)
for count := 0; count < len(nodes); { for count := 0; count < len(nodes); {
select { select {
case id := <-added: case id := <-added:
t.Logf("bystander node %v added to remote table", id) t.Logf("bystander node %v added to remote table", id)
count++ count++
case <-timeoutCh: case <-timeoutCh:
t.Errorf("remote added %d bystander nodes in %v, need %d to continue", count, timeout, len(nodes)) t.Errorf("remote added %d bystander nodes in %v, need %d to continue", count, timeout, len(nodes))
t.Logf("this can happen if the node has a non-empty table from previous runs") t.Logf("this can happen if the node has a non-empty table from previous runs")
return return
} }
} }
@ -261,10 +280,13 @@ func (s *Suite) TestFindnodeResults(t *utesting.T) {
// Collect our nodes by distance. // Collect our nodes by distance.
var dists []uint var dists []uint
expect := make(map[enode.ID]*enode.Node) expect := make(map[enode.ID]*enode.Node)
for _, bn := range nodes { for _, bn := range nodes {
n := bn.conn.localNode.Node() n := bn.conn.localNode.Node()
expect[n.ID()] = n expect[n.ID()] = n
d := uint(enode.LogDist(n.ID(), s.Dest.ID())) d := uint(enode.LogDist(n.ID(), s.Dest.ID()))
if !containsUint(dists, d) { if !containsUint(dists, d) {
dists = append(dists, d) dists = append(dists, d)
@ -274,14 +296,18 @@ func (s *Suite) TestFindnodeResults(t *utesting.T) {
// Send FINDNODE for all distances. // Send FINDNODE for all distances.
conn, l1 := s.listen1(t) conn, l1 := s.listen1(t)
defer conn.close() defer conn.close()
foundNodes, err := conn.findnode(l1, dists) foundNodes, err := conn.findnode(l1, dists)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Logf("remote returned %d nodes for distance list %v", len(foundNodes), dists) t.Logf("remote returned %d nodes for distance list %v", len(foundNodes), dists)
for _, n := range foundNodes { for _, n := range foundNodes {
delete(expect, n.ID()) delete(expect, n.ID())
} }
if len(expect) > 0 { if len(expect) > 0 {
t.Errorf("missing %d nodes in FINDNODE result", len(expect)) t.Errorf("missing %d nodes in FINDNODE result", len(expect))
t.Logf("this can happen if the test is run multiple times in quick succession") t.Logf("this can happen if the test is run multiple times in quick succession")
@ -311,7 +337,9 @@ func newBystander(t *utesting.T, s *Suite, added chan enode.ID) *bystander {
addedCh: added, addedCh: added,
} }
bn.done.Add(1) bn.done.Add(1)
go bn.loop() go bn.loop()
return bn return bn
} }
@ -334,6 +362,7 @@ func (bn *bystander) loop() {
lastPing time.Time lastPing time.Time
wasAdded bool wasAdded bool
) )
for { for {
// Ping the remote node. // Ping the remote node.
if !wasAdded && time.Since(lastPing) > 10*time.Second { if !wasAdded && time.Since(lastPing) > 10*time.Second {
@ -341,6 +370,7 @@ func (bn *bystander) loop() {
ReqID: bn.conn.nextReqID(), ReqID: bn.conn.nextReqID(),
ENRSeq: bn.dest.Seq(), ENRSeq: bn.dest.Seq(),
}) })
lastPing = time.Now() lastPing = time.Now()
} }
// Answer packets. // Answer packets.
@ -352,11 +382,15 @@ func (bn *bystander) loop() {
ToIP: bn.dest.IP(), ToIP: bn.dest.IP(),
ToPort: uint16(bn.dest.UDP()), ToPort: uint16(bn.dest.UDP()),
}, nil) }, nil)
wasAdded = true wasAdded = true
bn.notifyAdded() bn.notifyAdded()
case *v5wire.Findnode: case *v5wire.Findnode:
bn.conn.write(bn.l, &v5wire.Nodes{ReqID: p.ReqID, RespCount: 1}, nil) bn.conn.write(bn.l, &v5wire.Nodes{ReqID: p.ReqID, RespCount: 1}, nil)
wasAdded = true wasAdded = true
bn.notifyAdded() bn.notifyAdded()
case *v5wire.TalkRequest: case *v5wire.TalkRequest:
bn.conn.write(bn.l, &v5wire.TalkResponse{ReqID: p.ReqID}, nil) bn.conn.write(bn.l, &v5wire.TalkResponse{ReqID: p.ReqID}, nil)

Some files were not shown because too many files have changed in this diff Show more