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
- unconvert
- unparam
# - wsl
- wsl
- asasalint
#- 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 {
return ABI{}, err
}
return abi, nil
}
@ -67,12 +68,15 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
if err != nil {
return nil, err
}
return arguments, nil
}
method, exist := abi.Methods[name]
if !exist {
return nil, fmt.Errorf("method '%s' not found", name)
}
arguments, err := method.Inputs.Pack(args...)
if err != nil {
return nil, err
@ -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,
// we need to decide whether we're calling a method or an event
var args Arguments
if method, ok := abi.Methods[name]; ok {
if len(data)%32 != 0 {
return nil, fmt.Errorf("abi: improperly formatted output: %q - Bytes: %+v", data, data)
}
args = method.Outputs
}
if event, ok := abi.Events[name]; ok {
args = event.Inputs
}
if args == nil {
return nil, fmt.Errorf("abi: could not locate named method or event: %s", name)
}
return args, nil
}
@ -106,6 +115,7 @@ func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) {
if err != nil {
return nil, err
}
return args.Unpack(data)
}
@ -117,10 +127,12 @@ func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) erro
if err != nil {
return err
}
unpacked, err := args.Unpack(data)
if err != nil {
return err
}
return args.Copy(v, unpacked)
}
@ -130,6 +142,7 @@ func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte)
if err != nil {
return err
}
return args.UnpackIntoMap(v, data)
}
@ -153,12 +166,15 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
// declared as anonymous.
Anonymous bool
}
if err := json.Unmarshal(data, &fields); err != nil {
return err
}
abi.Methods = make(map[string]Method)
abi.Events = make(map[string]Event)
abi.Errors = make(map[string]Error)
for _, field := range fields {
switch field.Type {
case "constructor":
@ -172,6 +188,7 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
if abi.HasFallback() {
return errors.New("only single fallback is allowed")
}
abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
case "receive":
// 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() {
return errors.New("only single receive is allowed")
}
if field.StateMutability != "payable" {
return errors.New("the statemutability of receive can only be payable")
}
abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
case "event":
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 nil
}
@ -203,11 +223,13 @@ func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
if len(sigdata) < 4 {
return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
}
for _, method := range abi.Methods {
if bytes.Equal(method.ID, sigdata[:4]) {
return &method, nil
}
}
return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
}
@ -219,6 +241,7 @@ func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
return &event, nil
}
}
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 {
return "", errors.New("invalid data for unpacking")
}
if !bytes.Equal(data[:4], revertSelector) {
return "", errors.New("invalid data for unpacking")
}
@ -252,9 +276,11 @@ func UnpackRevert(data []byte) (string, error) {
if err != nil {
return "", err
}
unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
if err != nil {
return "", err
}
return unpacked[0].(string), nil
}

View file

@ -134,6 +134,7 @@ func TestReader(t *testing.T) {
if !exist {
t.Errorf("Missing expected method %v", name)
}
if !reflect.DeepEqual(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 {
t.Errorf("Found extra method %v", name)
}
if !reflect.DeepEqual(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) {
json := `[{ "type" : "function", "name" : "", "constant" : fals }]`
_, err := JSON(strings.NewReader(json))
if err == nil {
t.Fatal("invalid json should produce error")
}
json2 := `[{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "typ" : "uint256" } ] }]`
_, err = JSON(strings.NewReader(json2))
if err == nil {
t.Fatal("invalid json should produce error")
@ -177,6 +182,7 @@ func TestConstructor(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(abi.Constructor, method) {
t.Error("Missing expected constructor")
}
@ -185,6 +191,7 @@ func TestConstructor(t *testing.T) {
if err != nil {
t.Error(err)
}
unpacked, err := abi.Constructor.Inputs.Unpack(packed)
if err != nil {
t.Error(err)
@ -193,6 +200,7 @@ func TestConstructor(t *testing.T) {
if !reflect.DeepEqual(unpacked[0], big.NewInt(1)) {
t.Error("Unable to pack/unpack from constructor")
}
if !reflect.DeepEqual(unpacked[1], big.NewInt(2)) {
t.Error("Unable to pack/unpack from constructor")
}
@ -226,6 +234,7 @@ func TestTestNumbers(t *testing.T) {
i := new(int)
*i = 1000
if _, err := abi.Pack("send", i); err == nil {
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) {
m := NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil)
exp := "foo(string,string)"
if m.Sig != exp {
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)
exp = "foo(uint256)"
if m.Sig != exp {
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)
exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
if m.Sig != exp {
t.Error("signature mismatch", exp, "!=", m.Sig)
}
@ -275,10 +287,12 @@ func TestMethodSignature(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"}]`
abi, err := JSON(strings.NewReader(json))
if err != nil {
t.Fatal(err)
}
check := func(name string, expect string, method bool) {
if method {
if abi.Methods[name].Sig != expect {
@ -298,10 +312,12 @@ func TestOverloadedMethodSignature(t *testing.T) {
func TestCustomErrors(t *testing.T) {
json := `[{ "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ],"name": "MyError", "type": "error"} ]`
abi, err := JSON(strings.NewReader(json))
if err != nil {
t.Fatal(err)
}
check := func(name string, expect string) {
if abi.Errors[name].Sig != expect {
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 {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
@ -337,6 +354,7 @@ func ExampleJSON() {
if err != nil {
panic(err)
}
out, err := abi.Pack("isBar", common.HexToAddress("01"))
if err != nil {
panic(err)
@ -361,6 +379,7 @@ func TestInputVariableInputLength(t *testing.T) {
// test one string
strin := "hello world"
strpack, err := abi.Pack("strOne", strin)
if err != nil {
t.Error(err)
@ -393,6 +412,7 @@ func TestInputVariableInputLength(t *testing.T) {
// test two strings
str1 := "hello"
str2 := "world"
str2pack, err := abi.Pack("strTwo", str1, str2)
if err != nil {
t.Error(err)
@ -422,6 +442,7 @@ func TestInputVariableInputLength(t *testing.T) {
// test two strings, first > 32, second < 32
str1 = strings.Repeat("a", 33)
str2pack, err = abi.Pack("strTwo", str1, str2)
if err != nil {
t.Error(err)
@ -447,6 +468,7 @@ func TestInputVariableInputLength(t *testing.T) {
// test two strings, first > 32, second >32
str1 = strings.Repeat("a", 33)
str2 = strings.Repeat("a", 33)
str2pack, err = abi.Pack("strTwo", str1, str2)
if err != nil {
t.Error(err)
@ -484,6 +506,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
// test string, fixed array uint256[2]
strin := "hello world"
arrin := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
fixedArrStrPack, err := abi.Pack("fixedArrStr", strin, arrin)
if err != nil {
t.Error(err)
@ -497,6 +520,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
strvalue := common.RightPadBytes([]byte(strin), 32)
arrinvalue1 := common.LeftPadBytes(arrin[0].Bytes(), 32)
arrinvalue2 := common.LeftPadBytes(arrin[1].Bytes(), 32)
exp := append(offset, arrinvalue1...)
exp = append(exp, arrinvalue2...)
exp = append(exp, append(length, strvalue...)...)
@ -510,6 +534,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
// test byte array, fixed array uint256[2]
bytesin := []byte(strin)
arrin = [2]*big.Int{big.NewInt(1), big.NewInt(2)}
fixedArrBytesPack, err := abi.Pack("fixedArrBytes", bytesin, arrin)
if err != nil {
t.Error(err)
@ -523,6 +548,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
strvalue = common.RightPadBytes([]byte(strin), 32)
arrinvalue1 = common.LeftPadBytes(arrin[0].Bytes(), 32)
arrinvalue2 = common.LeftPadBytes(arrin[1].Bytes(), 32)
exp = append(offset, arrinvalue1...)
exp = append(exp, arrinvalue2...)
exp = append(exp, append(length, strvalue...)...)
@ -537,6 +563,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
strin = "hello world"
fixedarrin := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
dynarrin := []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
mixedArrStrPack, err := abi.Pack("mixedArrStr", strin, fixedarrin, dynarrin)
if err != nil {
t.Error(err)
@ -557,6 +584,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
dynarrinvalue1 := common.LeftPadBytes(dynarrin[0].Bytes(), 32)
dynarrinvalue2 := common.LeftPadBytes(dynarrin[1].Bytes(), 32)
dynarrinvalue3 := common.LeftPadBytes(dynarrin[2].Bytes(), 32)
exp = append(stroffset, fixedarrinvalue1...)
exp = append(exp, fixedarrinvalue2...)
exp = append(exp, dynarroffset...)
@ -576,6 +604,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
strin = "hello world"
fixedarrin1 := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
fixedarrin2 := [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
doubleFixedArrStrPack, err := abi.Pack("doubleFixedArrStr", strin, fixedarrin1, fixedarrin2)
if err != nil {
t.Error(err)
@ -592,6 +621,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
fixedarrin2value1 := common.LeftPadBytes(fixedarrin2[0].Bytes(), 32)
fixedarrin2value2 := common.LeftPadBytes(fixedarrin2[1].Bytes(), 32)
fixedarrin2value3 := common.LeftPadBytes(fixedarrin2[2].Bytes(), 32)
exp = append(stroffset, fixedarrin1value1...)
exp = append(exp, fixedarrin1value2...)
exp = append(exp, fixedarrin2value1...)
@ -610,6 +640,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
fixedarrin1 = [2]*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)}
multipleMixedArrStrPack, err := abi.Pack("multipleMixedArrStr", strin, fixedarrin1, dynarrin, fixedarrin2)
if err != nil {
t.Error(err)
@ -631,6 +662,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
fixedarrin2value1 = common.LeftPadBytes(fixedarrin2[0].Bytes(), 32)
fixedarrin2value2 = common.LeftPadBytes(fixedarrin2[1].Bytes(), 32)
fixedarrin2value3 = common.LeftPadBytes(fixedarrin2[2].Bytes(), 32)
exp = append(stroffset, fixedarrin1value1...)
exp = append(exp, fixedarrin1value2...)
exp = append(exp, dynarroffset...)
@ -703,20 +735,25 @@ func TestBareEvents(t *testing.T) {
t.Errorf("could not found event %s", name)
continue
}
if got.Anonymous != exp.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) {
t.Errorf("invalid number of args, want %d, got %d", len(exp.Args), len(got.Inputs))
continue
}
for i, arg := range exp.Args {
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)
}
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)
}
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)
}
@ -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]}
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"}]`
abi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
t.Fatal(err)
}
const hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
data, err := hex.DecodeString(hexdata)
if err != nil {
t.Fatal(err)
}
if len(data)%32 == 0 {
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
Memo []byte
}
var ev ReceivedEvent
err = abi.UnpackIntoInterface(&ev, "received", data)
@ -769,7 +810,9 @@ func TestUnpackEvent(t *testing.T) {
type ReceivedAddrEvent struct {
Sender common.Address
}
var receivedAddrEv ReceivedAddrEvent
err = abi.UnpackIntoInterface(&receivedAddrEv, "receivedAddr", data)
if err != nil {
t.Error(err)
@ -778,16 +821,19 @@ func TestUnpackEvent(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"}]`
abi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
t.Fatal(err)
}
const hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
data, err := hex.DecodeString(hexdata)
if err != nil {
t.Fatal(err)
}
if len(data)%32 == 0 {
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),
"memo": []byte{88},
}
if err := abi.UnpackIntoMap(receivedMap, "received", data); err != nil {
t.Error(err)
}
if len(receivedMap) != 3 {
t.Error("unpacked `received` map expected to have length 3")
}
if receivedMap["sender"] != expectedReceivedMap["sender"] {
t.Error("unpacked `received` map does not match expected map")
}
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
t.Error("unpacked `received` map does not match expected map")
}
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
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 {
t.Error(err)
}
if len(receivedAddrMap) != 1 {
t.Error("unpacked `receivedAddr` map expected to have length 1")
}
if receivedAddrMap["sender"] != expectedReceivedMap["sender"] {
t.Error("unpacked `receivedAddr` map does not match expected map")
}
@ -828,15 +881,19 @@ func TestUnpackEventIntoMap(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"}]`
abi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
t.Fatal(err)
}
const hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000015800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000158000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001580000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000015800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000158`
data, err := hex.DecodeString(hexdata)
if err != nil {
t.Fatal(err)
}
if len(data)%32 != 0 {
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 {
t.Error(err)
}
if len(receiveMap) > 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 {
t.Error(err)
}
if len(sendMap) != 1 {
t.Error("unpacked `send` map expected to have length 1")
}
if sendMap["amount"].(*big.Int).Cmp(big.NewInt(1)) != 0 {
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 {
t.Error(err)
}
if len(getMap) != 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}
if !bytes.Equal(getMap["hash"].([]byte), 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) {
// 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"}]`
abi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
t.Fatal(err)
}
var hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
data, err := hex.DecodeString(hexdata)
if err != nil {
t.Fatal(err)
}
if len(data)%32 == 0 {
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
}
getMap := map[string]interface{}{}
if err = abi.UnpackIntoMap(getMap, "get", data); err == nil {
t.Error("naming conflict between two methods; error expected")
@ -898,18 +965,23 @@ func TestUnpackIntoMapNamingConflict(t *testing.T) {
// 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"}]`
abi, err = JSON(strings.NewReader(abiJSON))
if err != nil {
t.Fatal(err)
}
hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
data, err = hex.DecodeString(hexdata)
if err != nil {
t.Fatal(err)
}
if len(data)%32 == 0 {
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
}
receivedMap := map[string]interface{}{}
if err = abi.UnpackIntoMap(receivedMap, "received", data); err != nil {
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
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))
if err != nil {
t.Fatal(err)
}
if len(data)%32 == 0 {
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
}
if err = abi.UnpackIntoMap(receivedMap, "received", data); err == nil {
t.Error("naming conflict between an event and a method; error expected")
}
// 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"}]`
abi, err = JSON(strings.NewReader(abiJSON))
if err != nil {
t.Fatal(err)
}
if len(data)%32 == 0 {
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
}
expectedReceivedMap := map[string]interface{}{
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
"amount": big.NewInt(1),
"memo": []byte{88},
}
if err = abi.UnpackIntoMap(receivedMap, "Received", data); err != nil {
t.Error(err)
}
if len(receivedMap) != 3 {
t.Error("unpacked `received` map expected to have length 3")
}
if receivedMap["sender"] != expectedReceivedMap["sender"] {
t.Error("unpacked `received` map does not match expected map")
}
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
t.Error("unpacked `received` map does not match expected map")
}
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
t.Error("unpacked `received` map does not match expected map")
}
@ -964,12 +1047,15 @@ func TestABI_MethodById(t *testing.T) {
if err != nil {
t.Fatal(err)
}
for name, m := range abi.Methods {
a := fmt.Sprintf("%v", m)
m2, err := abi.MethodById(m.ID)
if err != nil {
t.Fatalf("Failed to look up ABI method: %v", err)
}
b := fmt.Sprintf("%v", m2)
if a != b {
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 {
t.Errorf("Expected error, too short to decode data")
}
if _, err := abi.MethodById([]byte{}); err == nil {
t.Errorf("Expected error, too short to decode data")
}
if _, err := abi.MethodById(nil); err == nil {
t.Errorf("Expected error, nil is short to decode data")
}
@ -1040,6 +1128,7 @@ func TestABI_EventById(t *testing.T) {
if err != nil {
t.Fatalf("Failed to look up ABI method: %v, test #%d", err, testnum)
}
if event == nil {
t.Errorf("We should find a event for topic %s, test #%d", topicID.Hex(), testnum)
} else if event.ID != topicID {
@ -1047,10 +1136,12 @@ func TestABI_EventById(t *testing.T) {
}
unknowntopicID := crypto.Keccak256Hash([]byte("unknownEvent"))
unknownEvent, err := abi.EventByID(unknowntopicID)
if err == nil {
t.Errorf("EventByID should return an error if a topic is not found, test #%d", testnum)
}
if unknownEvent != nil {
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.
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"}]`
contractAbi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
t.Fatal(err)
}
if _, ok := contractAbi.Methods["transfer"]; !ok {
t.Fatalf("Could not find original method")
}
if _, ok := contractAbi.Methods["transfer0"]; !ok {
t.Fatalf("Could not find duplicate method")
}
if _, ok := contractAbi.Methods["transfer1"]; !ok {
t.Fatalf("Could not find duplicate method")
}
if _, ok := contractAbi.Methods["transfer2"]; ok {
t.Fatalf("Should not have found extra method")
}
@ -1090,19 +1186,24 @@ func TestDoubleDuplicateMethodNames(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"}]`
contractAbi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
t.Fatal(err)
}
if _, ok := contractAbi.Events["send"]; !ok {
t.Fatalf("Could not find original event")
}
if _, ok := contractAbi.Events["send0"]; !ok {
t.Fatalf("Could not find duplicate event")
}
if _, ok := contractAbi.Events["send1"]; !ok {
t.Fatalf("Could not find duplicate event")
}
if _, ok := contractAbi.Events["send2"]; ok {
t.Fatalf("Should not have found extra event")
}
@ -1117,6 +1218,7 @@ func TestDoubleDuplicateEventNames(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"}]`
contractAbi, err := JSON(strings.NewReader(abiJSON))
if err != nil {
t.Fatal(err)
@ -1126,9 +1228,11 @@ func TestUnnamedEventParam(t *testing.T) {
if !ok {
t.Fatalf("Could not find event")
}
if event.Inputs[0].Name != "arg0" {
t.Fatalf("Could not find input")
}
if event.Inputs[1].Name != "arg1" {
t.Fatalf("Could not find input")
}
@ -1146,6 +1250,7 @@ func TestUnpackRevert(t *testing.T) {
{"08c379a1", "", errors.New("invalid data for unpacking")},
{"08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000", "revert reason", nil},
}
for index, c := range cases {
t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) {
got, err := UnpackRevert(common.Hex2Bytes(c.input))
@ -1153,11 +1258,14 @@ func TestUnpackRevert(t *testing.T) {
if err == nil {
t.Fatalf("Expected non-nil error")
}
if err.Error() != c.expectErr.Error() {
t.Fatalf("Expected error mismatch, want %v, got %v", c.expectErr, err)
}
return
}
if 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.
func (argument *Argument) UnmarshalJSON(data []byte) error {
var arg ArgumentMarshaling
err := json.Unmarshal(data, &arg)
if err != nil {
return fmt.Errorf("argument json err: %v", err)
@ -54,6 +55,7 @@ func (argument *Argument) UnmarshalJSON(data []byte) error {
if err != nil {
return err
}
argument.Name = arg.Name
argument.Indexed = arg.Indexed
@ -63,11 +65,13 @@ func (argument *Argument) UnmarshalJSON(data []byte) error {
// NonIndexed returns the arguments with indexed arguments filtered out.
func (arguments Arguments) NonIndexed() Arguments {
var ret []Argument
for _, arg := range arguments {
if !arg.Indexed {
ret = append(ret, arg)
}
}
return ret
}
@ -82,8 +86,10 @@ func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
if len(arguments.NonIndexed()) != 0 {
return nil, errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
}
return make([]interface{}, 0), nil
}
return arguments.UnpackValues(data)
}
@ -93,19 +99,24 @@ func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte)
if v == nil {
return errors.New("abi: cannot unpack into a nil map")
}
if len(data) == 0 {
if len(arguments.NonIndexed()) != 0 {
return errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
}
return nil // Nothing to unmarshal, return
}
marshalledValues, err := arguments.UnpackValues(data)
if err != nil {
return err
}
for i, arg := range arguments.NonIndexed() {
v[arg.Name] = marshalledValues[i]
}
return nil
}
@ -115,15 +126,19 @@ func (arguments Arguments) Copy(v interface{}, values []interface{}) error {
if reflect.Ptr != reflect.ValueOf(v).Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
}
if len(values) == 0 {
if len(arguments.NonIndexed()) != 0 {
return errors.New("abi: attempting to copy no values while arguments are expected")
}
return nil // Nothing to copy, return
}
if arguments.isTuple() {
return arguments.copyTuple(v, values)
}
return arguments.copyAtomic(v, values[0])
}
@ -135,6 +150,7 @@ func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{
if dst.Kind() == reflect.Struct {
return set(dst.Field(0), src)
}
return set(dst, src)
}
@ -149,16 +165,20 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
for i, arg := range nonIndexedArgs {
argNames[i] = arg.Name
}
var err error
abi2struct, err := mapArgNamesToStructFields(argNames, value)
if err != nil {
return err
}
for i, arg := range nonIndexedArgs {
field := value.FieldByName(abi2struct[arg.Name])
if !field.IsValid() {
return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
}
if err := set(field, reflect.ValueOf(marshalledValues[i])); err != nil {
return err
}
@ -167,6 +187,7 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
if value.Len() < len(marshalledValues) {
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
}
for i := range nonIndexedArgs {
if err := set(value.Index(i), reflect.ValueOf(marshalledValues[i])); err != nil {
return err
@ -175,6 +196,7 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
default:
return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", value.Type())
}
return nil
}
@ -184,12 +206,14 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
nonIndexedArgs := arguments.NonIndexed()
retval := make([]interface{}, 0, len(nonIndexedArgs))
virtualArgs := 0
for index, arg := range nonIndexedArgs {
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
if err != nil {
return nil, err
}
if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
// If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256.
@ -207,8 +231,10 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
// coded as just like uint256,bool,uint256
virtualArgs += getTypeSize(arg.Type)/32 - 1
}
retval = append(retval, marshalledValue)
}
return retval, nil
}
@ -234,7 +260,9 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
for _, abiArg := range abiArgs {
inputOffset += getTypeSize(abiArg.Type)
}
var ret []byte
for i, a := range args {
input := abiArgs[i]
// pack the input
@ -269,5 +297,6 @@ func ToCamelCase(input string) string {
parts[i] = strings.ToUpper(s[:1]) + s[1:]
}
}
return strings.Join(parts, "")
}

View file

@ -44,14 +44,17 @@ var ErrNotAuthorized = errors.New("not authorized to sign this account")
// Deprecated: Use NewTransactorWithChainID instead.
func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID")
json, err := io.ReadAll(keyin)
if err != nil {
return nil, err
}
key, err := keystore.DecryptKey(json, passphrase)
if err != nil {
return nil, err
}
return NewKeyedTransactor(key.PrivateKey), nil
}
@ -61,7 +64,9 @@ func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
// Deprecated: Use NewKeyStoreTransactorWithChainID instead.
func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) {
log.Warn("WARNING: NewKeyStoreTransactor has been deprecated in favour of NewTransactorWithChainID")
signer := types.HomesteadSigner{}
return &TransactOpts{
From: account.Address,
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.
func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
log.Warn("WARNING: NewKeyedTransactor has been deprecated in favour of NewKeyedTransactorWithChainID")
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
signer := types.HomesteadSigner{}
return &TransactOpts{
From: keyAddr,
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 {
return nil, err
}
key, err := keystore.DecryptKey(json, passphrase)
if err != nil {
return nil, err
}
return NewKeyedTransactorWithChainID(key.PrivateKey, chainID)
}
@ -122,7 +131,9 @@ func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accou
if chainID == nil {
return nil, ErrNoChainID
}
signer := types.LatestSignerForChainID(chainID)
return &TransactOpts{
From: account.Address,
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.
func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) {
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
if chainID == nil {
return nil, ErrNoChainID
}
signer := types.LatestSignerForChainID(chainID)
return &TransactOpts{
From: keyAddr,
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 {
return nil, nil
}
receipt := rawdb.ReadRawBorReceipt(fb.db, hash, *number)
if receipt == 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())
backend.rollback(block)
return backend
}
@ -171,11 +172,14 @@ func (b *SimulatedBackend) Fork(ctx context.Context, parent common.Hash) error {
if len(b.pendingBlock.Transactions()) != 0 {
return errors.New("pending block dirty")
}
block, err := b.blockByHash(ctx, parent)
if err != nil {
return err
}
b.rollback(block)
return nil
}
@ -184,10 +188,12 @@ func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *
if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number) == 0 {
return b.blockchain.State()
}
block, err := b.blockByNumber(ctx, blockNumber)
if err != nil {
return nil, err
}
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)
return val[:], nil
}
@ -253,6 +260,7 @@ func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common
if receipt == nil {
return nil, ethereum.NotFound
}
return receipt, nil
}
@ -268,10 +276,12 @@ func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.
if tx != nil {
return tx, true, nil
}
tx, _, _, _ = rawdb.ReadTransaction(b.database, txHash)
if tx != nil {
return tx, false, nil
}
return nil, false, ethereum.NotFound
}
@ -406,9 +416,11 @@ func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Ad
func newRevertError(result *core.ExecutionResult) *revertError {
reason, errUnpack := abi.UnpackRevert(result.Revert())
err := errors.New("execution reverted")
if errUnpack == nil {
err = fmt.Errorf("execution reverted: %v", reason)
}
return &revertError{
error: err,
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 {
return nil, errBlockNumberUnsupported
}
stateDB, err := b.blockchain.State()
if err != nil {
return nil, err
}
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), stateDB)
if err != nil {
return nil, err
@ -453,6 +467,7 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
if len(res.Revert()) > 0 {
return nil, newRevertError(res)
}
return res.Return(), res.Err
}
@ -470,6 +485,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
if len(res.Revert()) > 0 {
return nil, newRevertError(res)
}
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 {
return b.pendingBlock.Header().BaseFee, nil
}
return big.NewInt(1), nil
}
@ -512,6 +529,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
hi uint64
cap uint64
)
if call.Gas >= params.TxGas {
hi = call.Gas
} 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.
var feeCap *big.Int
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
} 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.
if feeCap.BitLen() != 0 {
balance := b.pendingState.GetBalance(call.From) // from can't be nil
available := new(big.Int).Set(balance)
if call.Value != nil {
if call.Value.Cmp(available) >= 0 {
return 0, core.ErrInsufficientFundsForTransfer
}
available.Sub(available, call.Value)
}
allowance := new(big.Int).Div(available, feeCap)
if allowance.IsUint64() && hi > allowance.Uint64() {
transfer := call.Value
if transfer == nil {
transfer = new(big.Int)
}
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
"sent", transfer, "feecap", feeCap, "fundable", allowance)
hi = allowance.Uint64()
}
}
cap = hi
// 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) {
return true, nil, nil // Special case, raise gas limit
}
return true, nil, err // Bail out
}
return res.Failed(), res, nil
}
// 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 {
return 0, err
}
if failed {
lo = mid
} else {
@ -590,17 +618,20 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
if err != nil {
return 0, err
}
if failed {
if result != nil && result.Err != vm.ErrOutOfGas {
if len(result.Revert()) > 0 {
return 0, newRevertError(result)
}
return 0, result.Err
}
// Otherwise, the specified gas cap is too low
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
}
}
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) {
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
}
head := b.blockchain.CurrentHeader()
if !b.blockchain.Config().IsLondon(head.Number) {
// If there's no basefee, then it must be a non-1559 execution
if call.GasPrice == nil {
call.GasPrice = new(big.Int)
}
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
} else {
// 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 {
call.GasFeeCap = new(big.Int)
}
if call.GasTipCap == nil {
call.GasTipCap = new(big.Int)
}
@ -642,6 +676,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
if call.Gas == 0 {
call.Gas = 50000000
}
if call.Value == nil {
call.Value = new(big.Int)
}
@ -687,10 +722,12 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
}
// Check transaction validity
signer := types.MakeSigner(b.blockchain.Config(), block.Number())
sender, err := types.Sender(signer, tx)
if err != nil {
return fmt.Errorf("invalid transaction: %v", err)
}
nonce := b.pendingState.GetNonce(sender)
if 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() {
block.AddTxWithChain(b.blockchain, tx)
}
block.AddTxWithChain(b.blockchain, tx)
})
stateDB, _ := b.blockchain.State()
@ -708,6 +746,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
b.pendingReceipts = receipts[0]
return nil
}
@ -726,6 +765,7 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter
if query.FromBlock != nil {
from = query.FromBlock.Int64()
}
to := int64(-1)
if query.ToBlock != nil {
to = query.ToBlock.Int64()
@ -738,10 +778,12 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter
if err != nil {
return nil, err
}
res := make([]types.Log, len(logs))
for i, nLog := range logs {
res[i] = *nLog
}
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
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
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 {
defer sub.Unsubscribe()
for {
select {
case head := <-sink:
@ -856,6 +900,7 @@ func (fb *filterBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNum
if block := fb.backend.pendingBlock; block != nil {
return block.Header(), nil
}
return nil, nil
case rpc.LatestBlockNumber:
return fb.bc.CurrentHeader(), nil
@ -889,6 +934,7 @@ func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (typ
if number == nil {
return nil, 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()...)
var gasLimit uint64 = 8000029
key, _ := crypto.GenerateKey() // nolint: gosec
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
genAlloc := make(core.GenesisAlloc)
@ -59,6 +60,7 @@ func TestSimulatedBackend(t *testing.T) {
if isPending {
t.Fatal("transaction should not be pending")
}
if err != ethereum.NotFound {
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))
code := `6060604052600a8060106000396000f360606040526008565b00`
var gas uint64 = 3000000
tx := types.NewContractCreation(0, big.NewInt(0), gas, gasPrice, common.FromHex(code))
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, key)
@ -79,18 +82,22 @@ func TestSimulatedBackend(t *testing.T) {
txHash = tx.Hash()
_, isPending, err = sim.TransactionByHash(context.Background(), txHash)
if err != nil {
t.Fatalf("error getting transaction with hash: %v", txHash.String())
}
if !isPending {
t.Fatal("transaction should have pending status")
}
sim.Commit()
_, isPending, err = sim.TransactionByHash(context.Background(), txHash)
if err != nil {
t.Fatalf("error getting transaction with hash: %v", txHash.String())
}
if isPending {
t.Fatal("transaction should not have pending status")
}
@ -128,6 +135,7 @@ func simTestBackend(testAddr common.Address) *SimulatedBackend {
func TestNewSimulatedBackend(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
expectedBal := big.NewInt(10000000000000000)
sim := simTestBackend(testAddr)
defer sim.Close()
@ -140,6 +148,7 @@ func TestNewSimulatedBackend(t *testing.T) {
}
stateDB, _ := sim.blockchain.State()
bal := stateDB.GetBalance(testAddr)
if bal.Cmp(expectedBal) != 0 {
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()
prevTime := sim.pendingBlock.Time()
if err := sim.AdjustTime(time.Second); err != nil {
t.Error(err)
}
newTime := sim.pendingBlock.Time()
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))
tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
}
sim.SendTransaction(context.Background(), signedTx)
// AdjustTime should fail on non-empty block
if err := sim.AdjustTime(time.Second); err == nil {
t.Error("Expected adjust time to error on non-empty block")
}
sim.Commit()
prevTime := sim.pendingBlock.Time()
if err := sim.AdjustTime(time.Minute); err != nil {
t.Error(err)
}
newTime := sim.pendingBlock.Time()
if newTime-prevTime != uint64(time.Minute.Seconds()) {
t.Errorf("adjusted time not equal to a minute. prev: %v, new: %v", prevTime, newTime)
}
// Put a transaction after adjusting time
tx2 := types.NewTransaction(1, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx2, err := types.SignTx(tx2, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
}
sim.SendTransaction(context.Background(), signedTx2)
sim.Commit()
newTime = sim.pendingBlock.Time()
if newTime-prevTime >= uint64(time.Minute.Seconds()) {
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) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
expectedBal := big.NewInt(10000000000000000)
sim := simTestBackend(testAddr)
defer sim.Close()
bgCtx := context.Background()
bal, err := sim.BalanceAt(bgCtx, testAddr, nil)
@ -227,12 +248,14 @@ func TestBlockByHash(t *testing.T) {
core.GenesisAlloc{}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
block, err := sim.BlockByNumber(bgCtx, nil)
if err != nil {
t.Errorf("could not get recent block: %v", err)
}
blockByHash, err := sim.BlockByHash(bgCtx, block.Hash())
if err != nil {
t.Errorf("could not get recent block: %v", err)
@ -248,12 +271,14 @@ func TestBlockByNumber(t *testing.T) {
core.GenesisAlloc{}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
block, err := sim.BlockByNumber(bgCtx, nil)
if err != nil {
t.Errorf("could not get recent block: %v", err)
}
if block.NumberU64() != 0 {
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 {
t.Errorf("could not get recent block: %v", err)
}
if block.NumberU64() != 1 {
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 {
t.Errorf("could not get block by number: %v", err)
}
if blockByNumber.Hash() != block.Hash() {
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)
defer sim.Close()
bgCtx := context.Background()
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))
tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
@ -309,6 +338,7 @@ func TestNonceAt(t *testing.T) {
if err != nil {
t.Errorf("could not add tx to pending block: %v", err)
}
sim.Commit()
newNonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(1))
@ -326,6 +356,7 @@ func TestNonceAt(t *testing.T) {
if err != nil {
t.Fatalf("could not get nonce for test addr: %v", err)
}
if newNonce != nonce+uint64(1) {
t.Fatalf("received incorrect nonce. expected 1, got %v", nonce)
}
@ -336,6 +367,7 @@ func TestSendTransaction(t *testing.T) {
sim := simTestBackend(testAddr)
defer sim.Close()
bgCtx := context.Background()
// 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))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
@ -353,6 +386,7 @@ func TestSendTransaction(t *testing.T) {
if err != nil {
t.Errorf("could not add tx to pending block: %v", err)
}
sim.Commit()
block, err := sim.BlockByNumber(bgCtx, big.NewInt(1))
@ -374,6 +408,7 @@ func TestTransactionByHash(t *testing.T) {
}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
// 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))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
@ -397,9 +433,11 @@ func TestTransactionByHash(t *testing.T) {
if err != nil {
t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
}
if !pending {
t.Errorf("expected transaction to be in pending state")
}
if receivedTx.Hash() != signedTx.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 {
t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
}
if pending {
t.Errorf("expected transaction to not be in pending state")
}
if receivedTx.Hash() != signedTx.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 contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033"
key, _ := crypto.GenerateKey()
@ -514,15 +555,18 @@ func TestEstimateGas(t *testing.T) {
Data: common.Hex2Bytes("e09fface"),
}, 21275, nil, nil},
}
for _, c := range cases {
got, err := sim.EstimateGas(context.Background(), c.message)
if c.expectError != nil {
if err == nil {
t.Fatalf("Expect error, got nil")
}
if c.expectError.Error() != err.Error() {
t.Fatalf("Expect error, want %v, got %v", c.expectError, err)
}
if c.expectData != nil {
if err, ok := err.(*revertError); !ok {
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())
}
}
continue
}
if got != c.expect {
t.Fatalf("Gas estimation mismatch, want %d, got %d", c.expect, got)
}
@ -546,6 +592,7 @@ func TestEstimateGasWithPrice(t *testing.T) {
defer sim.Close()
recipient := common.HexToAddress("deadbeef")
var cases = []struct {
name string
message ethereum.CallMsg
@ -608,20 +655,25 @@ func TestEstimateGasWithPrice(t *testing.T) {
Data: nil,
}, params.TxGas, errors.New("gas required exceeds allowance (20999)")}, // 20999=(2.2ether-0.1ether-1wei)/(1e14)
}
for i, c := range cases {
got, err := sim.EstimateGas(context.Background(), c.message)
if c.expectError != nil {
if err == nil {
t.Fatalf("test %d: expect error, got nil", i)
}
if c.expectError.Error() != err.Error() {
t.Fatalf("test %d: expect error, want %v, got %v", i, c.expectError, err)
}
continue
}
if c.expectError == nil && err != nil {
t.Fatalf("test %d: didn't expect error, got %v", i, err)
}
if got != c.expect {
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)
defer sim.Close()
bgCtx := context.Background()
header, err := sim.HeaderByNumber(bgCtx, nil)
if err != nil {
t.Errorf("could not get recent block: %v", err)
}
headerByHash, err := sim.HeaderByHash(bgCtx, header.Hash())
if err != nil {
t.Errorf("could not get recent block: %v", err)
@ -654,12 +708,14 @@ func TestHeaderByNumber(t *testing.T) {
sim := simTestBackend(testAddr)
defer sim.Close()
bgCtx := context.Background()
latestBlockHeader, err := sim.HeaderByNumber(bgCtx, nil)
if err != nil {
t.Errorf("could not get header for tip of chain: %v", err)
}
if latestBlockHeader == nil {
t.Errorf("received a nil block header")
} else if latestBlockHeader.Number.Uint64() != uint64(0) {
@ -681,6 +737,7 @@ func TestHeaderByNumber(t *testing.T) {
if blockHeader.Hash() != latestBlockHeader.Hash() {
t.Errorf("block header and latest block header are not the same")
}
if blockHeader.Number.Int64() != int64(1) {
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)
defer sim.Close()
bgCtx := context.Background()
currentBlock, err := sim.BlockByNumber(bgCtx, nil)
if err != nil || currentBlock == nil {
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))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
@ -752,12 +812,14 @@ func TestTransactionInBlock(t *testing.T) {
sim := simTestBackend(testAddr)
defer sim.Close()
bgCtx := context.Background()
transaction, err := sim.TransactionInBlock(bgCtx, sim.pendingBlock.Hash(), uint(0))
if err == nil && err != errTransactionDoesNotExist {
t.Errorf("expected a transaction does not exist error to be received but received %v", err)
}
if transaction != nil {
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))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
@ -798,6 +861,7 @@ func TestTransactionInBlock(t *testing.T) {
if err == nil && err != errTransactionDoesNotExist {
t.Errorf("expected a transaction does not exist error to be received but received %v", err)
}
if transaction != nil {
t.Errorf("expected transaction to be nil but received %v", transaction)
}
@ -817,6 +881,7 @@ func TestPendingNonceAt(t *testing.T) {
sim := simTestBackend(testAddr)
defer sim.Close()
bgCtx := context.Background()
// 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))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
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
tx = types.NewTransaction(uint64(1), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err = types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
}
err = sim.SendTransaction(bgCtx, signedTx)
if err != nil {
t.Errorf("could not send tx: %v", err)
@ -882,6 +950,7 @@ func TestTransactionReceipt(t *testing.T) {
sim := simTestBackend(testAddr)
defer sim.Close()
bgCtx := context.Background()
// 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))
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
@ -899,6 +969,7 @@ func TestTransactionReceipt(t *testing.T) {
if err != nil {
t.Errorf("could not add tx to pending block: %v", err)
}
sim.Commit()
receipt, err := sim.TransactionReceipt(bgCtx, signedTx.Hash())
@ -917,11 +988,14 @@ func TestSuggestGasPrice(t *testing.T) {
10000000,
)
defer sim.Close()
bgCtx := context.Background()
gasPrice, err := sim.SuggestGasPrice(bgCtx)
if err != nil {
t.Errorf("could not get gas price: %v", err)
}
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())
}
@ -929,13 +1003,17 @@ func TestSuggestGasPrice(t *testing.T) {
func TestPendingCodeAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
bgCtx := context.Background()
code, err := sim.CodeAt(bgCtx, testAddr, nil)
if err != nil {
t.Errorf("could not get code at test addr: %v", err)
}
if len(code) != 0 {
t.Errorf("got code for account that does not have contract code")
}
@ -944,7 +1022,9 @@ func TestPendingCodeAt(t *testing.T) {
if err != nil {
t.Errorf("could not get code at test addr: %v", err)
}
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
if err != nil {
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 {
t.Errorf("could not get code at test addr: %v", err)
}
if len(code) == 0 {
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) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
bgCtx := context.Background()
code, err := sim.CodeAt(bgCtx, testAddr, nil)
if err != nil {
t.Errorf("could not get code at test addr: %v", err)
}
if len(code) != 0 {
t.Errorf("got code for account that does not have contract code")
}
@ -980,17 +1065,21 @@ func TestCodeAt(t *testing.T) {
if err != nil {
t.Errorf("could not get code at test addr: %v", err)
}
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
if err != nil {
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
}
sim.Commit()
code, err = sim.CodeAt(bgCtx, contractAddr, nil)
if err != nil {
t.Errorf("could not get code at test addr: %v", err)
}
if len(code) == 0 {
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]}
func TestPendingAndCallContract(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
bgCtx := context.Background()
parsed, err := abi.JSON(strings.NewReader(abiJSON))
if err != nil {
t.Errorf("could not get code at test addr: %v", err)
}
contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(abiBin), sim)
if err != nil {
t.Errorf("could not deploy contract: %v", err)
@ -1033,6 +1126,7 @@ func TestPendingAndCallContract(t *testing.T) {
if err != nil {
t.Errorf("could not call receive method on contract: %v", err)
}
if len(res) == 0 {
t.Errorf("result of contract call was empty: %v", res)
}
@ -1053,6 +1147,7 @@ func TestPendingAndCallContract(t *testing.T) {
if err != nil {
t.Errorf("could not call receive method on contract: %v", err)
}
if len(res) == 0 {
t.Errorf("result of contract call was empty: %v", res)
}
@ -1089,8 +1184,10 @@ contract Reverter {
}*/
func TestCallContractRevert(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
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"}]`
@ -1100,7 +1197,9 @@ func TestCallContractRevert(t *testing.T) {
if err != nil {
t.Errorf("could not get code at test addr: %v", err)
}
contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(reverterBin), sim)
if err != nil {
t.Errorf("could not deploy contract: %v", err)
@ -1139,14 +1238,17 @@ func TestCallContractRevert(t *testing.T) {
if err == nil {
t.Errorf("call to %v was not reverted", key)
}
if res != nil {
t.Errorf("result from %v was not nil: %v", key, res)
}
if val != nil {
rerr, ok := err.(*revertError)
if !ok {
t.Errorf("expect revert error")
}
if rerr.Error() != "execution reverted: "+val.(string) {
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")
if err != nil {
t.Errorf("could not pack noRevert function on contract: %v", err)
}
res, err := cl(input)
if err != nil {
t.Error("call to noRevert was reverted")
}
if res == nil {
t.Errorf("result from noRevert was nil")
}
sim.Commit()
}
}
@ -1184,6 +1290,7 @@ func TestCallContractRevert(t *testing.T) {
// having a chain length of just n+1 means that a reorg occurred.
func TestFork(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
// 1.
@ -1237,15 +1344,18 @@ const callableBin = "6080604052348015600f57600080fd5b5060998061001e6000396000f3f
// 10. Check that the event was reborn.
func TestForkLogsReborn(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
// 1.
parsed, _ := abi.JSON(strings.NewReader(callableAbi))
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
_, _, contract, err := bind.DeployContract(auth, parsed, common.FromHex(callableBin), sim)
if err != nil {
t.Errorf("deploying contract: %v", err)
}
sim.Commit()
// 2.
logs, sub, err := contract.WatchLogs(nil, "Called")
@ -1260,12 +1370,14 @@ func TestForkLogsReborn(t *testing.T) {
if err != nil {
t.Errorf("transacting: %v", err)
}
sim.Commit()
// 5.
log := <-logs
if log.TxHash != tx.Hash() {
t.Error("wrong event tx hash")
}
if log.Removed {
t.Error("Event should be included")
}
@ -1281,6 +1393,7 @@ func TestForkLogsReborn(t *testing.T) {
if log.TxHash != tx.Hash() {
t.Error("wrong event tx hash")
}
if !log.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 {
t.Errorf("sending transaction: %v", err)
}
sim.Commit()
// 10.
log = <-logs
if log.TxHash != tx.Hash() {
t.Error("wrong event tx hash")
}
if log.Removed {
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.
func TestForkResendTx(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
defer sim.Close()
// 1.
@ -1333,9 +1449,11 @@ func TestForkResendTx(t *testing.T) {
}
// 5.
sim.Commit()
if err := sim.SendTransaction(context.Background(), tx); err != nil {
t.Errorf("sending transaction: %v", err)
}
sim.Commit()
// 6.
receipt, _ = sim.TransactionReceipt(context.Background(), tx.Hash())

View file

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

View file

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

View file

@ -2071,6 +2071,7 @@ func TestGolangBindings(t *testing.T) {
if err != nil {
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 {
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
moder := exec.Command(gocmd, "mod", "init", "bindtest")
moder.Dir = pkg
if out, err := moder.CombinedOutput(); err != nil {
t.Fatalf("failed to convert binding test to modules: %v\n%s", err, out)
}
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.Dir = pkg
if out, err := replacer.CombinedOutput(); err != nil {
t.Fatalf("failed to replace binding test dependency to current source tree: %v\n%s", err, out)
}
tidier := exec.Command(gocmd, "mod", "tidy")
tidier.Dir = pkg
if out, err := tidier.CombinedOutput(); err != nil {
t.Fatalf("failed to tidy Go module file: %v\n%s", err, out)
}
// Test the entire package and report any failures
cmd := exec.Command(gocmd, "test", "-v", "-count", "1")
cmd.Dir = pkg
if out, err := cmd.CombinedOutput(); err != nil {
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()
logger := log.New("hash", tx.Hash())
for {
receipt, err := b.TransactionReceipt(ctx, tx.Hash())
if err == nil {
@ -61,10 +62,12 @@ func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (
if tx.To() != nil {
return common.Address{}, errors.New("tx is not contract creation")
}
receipt, err := WaitMined(ctx, b, tx)
if err != nil {
return common.Address{}, err
}
if receipt.ContractAddress == (common.Address{}) {
return common.Address{}, errors.New("zero address")
}
@ -75,5 +78,6 @@ func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (
if err == nil && len(code) == 0 {
err = ErrNoCodeAfterDeploy
}
return receipt.ContractAddress, err
}

View file

@ -76,8 +76,10 @@ func TestWaitDeployed(t *testing.T) {
mined = make(chan struct{})
ctx = context.Background()
)
go func() {
address, err = bind.WaitDeployed(ctx, backend, tx)
close(mined)
}()
@ -90,6 +92,7 @@ func TestWaitDeployed(t *testing.T) {
if err != test.wantErr {
t.Errorf("test %q: error mismatch: want %q, got %q", name, test.wantErr, err)
}
if address != test.wantAddress {
t.Errorf("test %q: unexpected contract address %s", name, address.Hex())
}
@ -115,10 +118,12 @@ func TestWaitDeployedCornerCases(t *testing.T) {
code := "6060604052600a8060106000396000f360606040526008565b00"
tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, gasPrice, common.FromHex(code))
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
backend.SendTransaction(ctx, tx)
backend.Commit()
notContentCreation := errors.New("tx is not contract creation")
if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContentCreation.Error() {
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.
names := make([]string, len(inputs))
types := make([]string, len(inputs))
for i, input := range inputs {
if input.Name == "" {
inputs[i] = Argument{
@ -86,8 +87,10 @@ func (e *Error) Unpack(data []byte) (interface{}, error) {
if len(data) < 4 {
return "", errors.New("invalid data for unpacking")
}
if !bytes.Equal(data[:4], e.ID[:4]) {
return "", errors.New("invalid data for unpacking")
}
return e.Inputs.Unpack(data[4:])
}

View file

@ -40,6 +40,7 @@ func formatSliceString(kind reflect.Kind, sliceSize int) string {
if sliceSize == -1 {
return fmt.Sprintf("[]%v", 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() {
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
}
return nil
}

View file

@ -64,6 +64,7 @@ func NewEvent(name, rawName string, anonymous bool, inputs Arguments) Event {
// and precompute string and sig representation.
names := make([]string, len(inputs))
types := make([]string, len(inputs))
for i, input := range inputs {
if input.Name == "" {
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"}]}]`
abi, err := JSON(strings.NewReader(definition))
require.NoError(t, err)
var b bytes.Buffer
var i uint8 = 1
for ; i <= 3; i++ {
b.Write(packNum(reflect.ValueOf(i)))
}
unpacked, err := abi.Unpack("test", b.Bytes())
require.NoError(t, err)
require.Equal(t, [2]uint8{1, 2}, unpacked[0])
@ -209,6 +212,7 @@ func TestEventTupleUnpack(t *testing.T) {
bigintExpected2 := big.NewInt(2218516807680)
bigintExpected3 := big.NewInt(1000001)
addr := common.HexToAddress("0x00Ce0d46d924CC8437c806721496599FC3FFA268")
var testCases = []struct {
data string
dest interface{}
@ -343,24 +347,33 @@ func TestEventTupleUnpack(t *testing.T) {
func unpackTestEventData(dest interface{}, hexData string, jsonEvent []byte, assert *assert.Assertions) error {
data, err := hex.DecodeString(hexData)
assert.NoError(err, "Hex data should be a correct hex-string")
var e Event
assert.NoError(json.Unmarshal(jsonEvent, &e), "Should be able to unmarshal event ABI")
a := ABI{Events: map[string]Event{"e": e}}
return a.UnpackIntoInterface(dest, "e", data)
}
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
func TestEventUnpackIndexed(t *testing.T) {
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
type testStruct struct {
Value1 uint8 // indexed
Value2 uint8
}
abi, err := JSON(strings.NewReader(definition))
require.NoError(t, err)
var b bytes.Buffer
b.Write(packNum(reflect.ValueOf(uint8(8))))
var rst testStruct
require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes()))
require.Equal(t, uint8(0), rst.Value1)
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.
func TestEventIndexedWithArrayUnpack(t *testing.T) {
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"string"}]}]`
type testStruct struct {
Value1 [2]uint8 // indexed
Value2 string
}
abi, err := JSON(strings.NewReader(definition))
require.NoError(t, err)
var b bytes.Buffer
stringOut := "abc"
// number of fields that will be encoded * 32
b.Write(packNum(reflect.ValueOf(32)))
@ -383,6 +400,7 @@ func TestEventIndexedWithArrayUnpack(t *testing.T) {
b.Write(common.RightPadBytes([]byte(stringOut), 32))
var rst testStruct
require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes()))
require.Equal(t, [2]uint8{0, 0}, rst.Value1)
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))
outputNames = make([]string, len(outputs))
)
for i, input := range inputs {
inputNames[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
types[i] = input.Type.String()
}
for i, output := range outputs {
outputNames[i] = output.Type.String()
if len(output.Name) > 0 {
@ -113,6 +115,7 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
sig string
id []byte
)
if funType == Function {
sig = fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
id = crypto.Keccak256([]byte(sig))[:4]
@ -123,9 +126,11 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
if state == "nonpayable" {
state = ""
}
if state != "" {
state = state + " "
}
identity := fmt.Sprintf("function %v", rawName)
if funType == Fallback {
identity = "fallback"
@ -134,6 +139,7 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
} else if funType == Constructor {
identity = "constructor"
}
str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", "))
return Method{

View file

@ -91,6 +91,7 @@ func TestMethodString(t *testing.T) {
} else {
got = abi.Methods[test.method].String()
}
if got != test.expectation {
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][])",
},
}
abi, err := JSON(strings.NewReader(methoddata))
if err != nil {
t.Fatal(err)

View file

@ -51,19 +51,23 @@ func packElement(t Type, reflectValue reflect.Value) ([]byte, error) {
if reflectValue.Bool() {
return math.PaddedBigBytes(common.Big1, 32), nil
}
return math.PaddedBigBytes(common.Big0, 32), nil
case BytesTy:
if reflectValue.Kind() == reflect.Array {
reflectValue = mustArrayToByteSlice(reflectValue)
}
if reflectValue.Type() != reflect.TypeOf([]byte{}) {
return []byte{}, errors.New("Bytes type is neither slice nor array")
}
return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()), nil
case FixedBytesTy, FunctionTy:
if reflectValue.Kind() == reflect.Array {
reflectValue = mustArrayToByteSlice(reflectValue)
}
return common.RightPadBytes(reflectValue.Bytes(), 32), nil
default:
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 {
t.Fatalf("invalid hex %s: %v", test.packed, err)
}
inDef := fmt.Sprintf(`[{ "name" : "method", "type": "function", "inputs": %s}]`, test.def)
inAbi, err := JSON(strings.NewReader(inDef))
if err != nil {
t.Fatalf("invalid ABI definition %s, %v", inDef, err)
}
var packed []byte
packed, err = inAbi.Pack("method", test.unpacked)
if err != nil {
t.Fatalf("test %d (%v) failed: %v", i, test.def, err)
}
if !reflect.DeepEqual(packed[4:], encb) {
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}
sig = abi.Methods["sliceAddress"].ID
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
@ -86,11 +91,13 @@ func TestMethodPack(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
var addrC, addrD = common.Address{3}, common.Address{4}
sig = abi.Methods["sliceMultiAddress"].ID
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
@ -105,6 +112,7 @@ func TestMethodPack(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
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(addrC[:], 32)...)
sig = append(sig, common.LeftPadBytes(addrD[:], 32)...)
packed, err = abi.Pack("nestedArray", a, []common.Address{addrC, addrD})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
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)...)
packed, err = abi.Pack("nestedArray2", [2][]uint8{{1}, {1}})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
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{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
packed, err = abi.Pack("nestedSlice", [][]uint8{{1, 2}, {1, 2}})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
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 {
panic(err)
}
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{}) {
return indirect(v.Elem())
}
return v
}
@ -74,6 +76,7 @@ func reflectIntType(unsigned bool, size int) reflect.Type {
return reflect.TypeOf(uint64(0))
}
}
switch size {
case 8:
return reflect.TypeOf(int8(0))
@ -84,6 +87,7 @@ func reflectIntType(unsigned bool, size int) reflect.Type {
case 64:
return reflect.TypeOf(int64(0))
}
return reflect.TypeOf(&big.Int{})
}
@ -92,6 +96,7 @@ func reflectIntType(unsigned bool, size int) reflect.Type {
func mustArrayToByteSlice(value reflect.Value) reflect.Value {
slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len())
reflect.Copy(slice, value)
return slice
}
@ -101,6 +106,7 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value {
// strict ruleset as bare `reflect` does.
func set(dst, src reflect.Value) error {
dstType, srcType := dst.Type(), src.Type()
switch {
case dstType.Kind() == reflect.Interface && dst.Elem().IsValid() && (dst.Elem().Type().Kind() == reflect.Ptr || dst.Elem().CanSet()):
return set(dst.Elem(), src)
@ -117,6 +123,7 @@ func set(dst, src reflect.Value) error {
default:
return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
}
return nil
}
@ -130,10 +137,12 @@ func setSlice(dst, src reflect.Value) error {
return err
}
}
if dst.CanSet() {
dst.Set(slice)
return nil
}
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 {
return set(dst, indirect(src))
}
array := reflect.New(dst.Type()).Elem()
min := src.Len()
if src.Len() > dst.Len() {
min = dst.Len()
}
for i := 0; i < min; i++ {
if err := set(array.Index(i), src.Index(i)); err != nil {
return err
}
}
if dst.CanSet() {
dst.Set(array)
return nil
}
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++ {
srcField := src.Field(i)
dstField := dst.Field(i)
if !dstField.IsValid() || !srcField.IsValid() {
return fmt.Errorf("Could not find src field: %v value: %v in destination", srcField.Type().Name(), srcField)
}
if err := set(dstField, srcField); err != nil {
return err
}
}
return nil
}
@ -206,6 +223,7 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
}
// check which argument field matches with the abi tag.
found := false
for _, arg := range argNames {
if arg == tagName {
if abi2struct[arg] != "" {
@ -241,6 +259,7 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
value.FieldByName(structFieldName).IsValid() {
return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", argName)
}
continue
}
@ -260,5 +279,6 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
struct2abi[structFieldName] = argName
}
}
return abi2struct, nil
}

View file

@ -181,6 +181,7 @@ func TestReflectNameToStruct(t *testing.T) {
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
for fname := range test.want {
if m[fname] != test.want[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 {
t.Errorf("ConvertType failed, got %v want %v", out.X, big.NewInt(1))
}
if out.Y.Cmp(big.NewInt(2)) != 0 {
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(1).Field(0).Set(reflect.ValueOf(big.NewInt(3)))
val2.Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4)))
out2 := *ConvertType(val2.Interface(), new([]T)).(*[]T)
if out2[0].X.Cmp(big.NewInt(1)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1))
}
if out2[0].Y.Cmp(big.NewInt(2)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2))
}
if out2[1].X.Cmp(big.NewInt(3)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1))
}
if out2[1].Y.Cmp(big.NewInt(4)) != 0 {
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(1).Field(0).Set(reflect.ValueOf(big.NewInt(3)))
val3.Elem().Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4)))
out3 := *ConvertType(val3.Interface(), new([2]T)).(*[2]T)
if out3[0].X.Cmp(big.NewInt(1)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1))
}
if out3[0].Y.Cmp(big.NewInt(2)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2))
}
if out3[1].X.Cmp(big.NewInt(3)) != 0 {
t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1))
}
if out3[1].Y.Cmp(big.NewInt(4)) != 0 {
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 {
return "", "", fmt.Errorf("empty token")
}
firstChar := unescapedSelector[0]
position := 1
if !(isAlpha(firstChar) || (isIdent && isIdentifierSymbol(firstChar))) {
return "", "", fmt.Errorf("invalid token start: %c", firstChar)
}
for position < len(unescapedSelector) {
char := unescapedSelector[position]
if !(isAlpha(char) || isDigit(char) || (isIdent && isIdentifierSymbol(char))) {
break
}
position++
}
return unescapedSelector[:position], unescapedSelector[position:], nil
}
@ -70,16 +75,20 @@ func parseElementaryType(unescapedSelector string) (string, string, error) {
for len(rest) > 0 && rest[0] == '[' {
parsedType = parsedType + string(rest[0])
rest = rest[1:]
for len(rest) > 0 && isDigit(rest[0]) {
parsedType = parsedType + string(rest[0])
rest = rest[1:]
}
if len(rest) == 0 || rest[0] != ']' {
return "", "", fmt.Errorf("failed to parse array: expected ']', got %c", unescapedSelector[0])
}
parsedType = parsedType + string(rest[0])
rest = rest[1:]
}
return parsedType, rest, nil
}
@ -87,18 +96,23 @@ func parseCompositeType(unescapedSelector string) ([]interface{}, string, error)
if len(unescapedSelector) == 0 || unescapedSelector[0] != '(' {
return nil, "", fmt.Errorf("expected '(', got %c", unescapedSelector[0])
}
parsedType, rest, err := parseType(unescapedSelector[1:])
if err != nil {
return nil, "", fmt.Errorf("failed to parse type: %v", err)
}
result := []interface{}{parsedType}
for len(rest) > 0 && rest[0] != ')' {
parsedType, rest, err = parseType(rest[1:])
if err != nil {
return nil, "", fmt.Errorf("failed to parse type: %v", err)
}
result = append(result, parsedType)
}
if len(rest) == 0 || rest[0] != ')' {
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] == ']' {
return append(result, "[]"), rest[3:], nil
}
return result, rest[1:], nil
}
@ -113,6 +128,7 @@ func parseType(unescapedSelector string) (interface{}, string, error) {
if len(unescapedSelector) == 0 {
return nil, "", fmt.Errorf("empty type")
}
if unescapedSelector[0] == '(' {
return parseCompositeType(unescapedSelector)
} else {
@ -122,6 +138,7 @@ func parseType(unescapedSelector string) (interface{}, string, error) {
func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) {
arguments := make([]ArgumentMarshaling, 0)
for i, arg := range args {
// generate dummy name to avoid unmarshal issues
name := fmt.Sprintf("name%d", i)
@ -134,15 +151,18 @@ func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) {
}
// nolint:goconst
tupleType := "tuple"
if len(subArgs) != 0 && subArgs[len(subArgs)-1].Type == "[]" {
subArgs = subArgs[:len(subArgs)-1]
tupleType = "tuple[]"
}
arguments = append(arguments, ArgumentMarshaling{name, tupleType, tupleType, subArgs, false})
} else {
return nil, fmt.Errorf("failed to assemble args: unexpected type %T", arg)
}
}
return arguments, nil
}
@ -155,7 +175,9 @@ func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) {
if err != nil {
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
}
args := []interface{}{}
if len(rest) >= 2 && rest[0] == '(' && rest[1] == ')' {
rest = rest[2:]
} else {
@ -164,6 +186,7 @@ func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) {
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
}
}
if len(rest) > 0 {
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) {
mkType := func(types ...interface{}) []ArgumentMarshaling {
var result []ArgumentMarshaling
for i, typeOrComponents := range types {
name := fmt.Sprintf("name%d", i)
if typeName, ok := typeOrComponents.(string); ok {
@ -38,8 +39,10 @@ func TestParseSelector(t *testing.T) {
log.Fatalf("unexpected type %T", typeOrComponents)
}
}
return result
}
tests := []struct {
input string
name string
@ -65,6 +68,7 @@ func TestParseSelector(t *testing.T) {
if err != nil {
t.Errorf("test %d: failed to parse selector '%v': %v", i, tt.input, err)
}
if 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" {
t.Errorf("test %d: unexpected type: '%s' != '%s'", i, selector.Type, "function")
}
if !reflect.DeepEqual(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.
func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
topics := make([][]common.Hash, len(query))
for i, filter := range query {
for _, rule := range filter {
var topic common.Hash
@ -81,9 +82,9 @@ func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
//
// We only convert stringS and bytes to hash, still need to deal with
// array(both fixed-size and dynamic-size) and struct.
// Attempt to generate the topic from funky types
val := reflect.ValueOf(rule)
switch {
// static byte array
case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
@ -92,9 +93,11 @@ func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
return nil, fmt.Errorf("unsupported indexed type: %T", rule)
}
}
topics[i] = append(topics[i], topic)
}
}
return topics, nil
}
@ -105,9 +108,11 @@ func genIntType(rule int64, size uint) []byte {
// 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}
}
for i := uint(0); i < size; i++ {
topic[common.HashLength-i-1] = byte(rule >> (i * 8))
}
return topic[:]
}
@ -143,7 +148,9 @@ func parseTopicWithSetter(fields Arguments, topics []common.Hash, setter func(Ar
if !arg.Indexed {
return errors.New("non-indexed field in topic reconstruction")
}
var reconstr interface{}
switch arg.Type.T {
case TupleTy:
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 {
return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[i].Bytes())
}
var tmp [24]byte
copy(tmp[:], topics[i][8:32])
reconstr = tmp
default:
var err error
reconstr, err = toGoType(0, arg.Type, topics[i].Bytes())
if err != nil {
return err

View file

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

View file

@ -110,6 +110,7 @@ func TestTypeRegexp(t *testing.T) {
if err != nil {
t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
}
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)))
}
@ -288,6 +289,7 @@ func TestTypeCheck(t *testing.T) {
if err.Error() != test.err {
t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
}
continue
}
@ -296,6 +298,7 @@ func TestTypeCheck(t *testing.T) {
t.Errorf("%d failed. Expected no err but got: %v", i, err)
continue
}
if err == nil && len(test.err) != 0 {
t.Errorf("%d failed. Expected err: %v but got none", i, test.err)
continue
@ -323,9 +326,11 @@ func TestInternalType(t *testing.T) {
blob := "tuple"
typ, err := NewType(blob, internalType, components)
if err != nil {
t.Errorf("type %q: failed to parse type string: %v", blob, err)
}
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)))
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -62,6 +62,7 @@ func waitWatcherStart(ks *KeyStore) bool {
return true
}
}
return false
}
@ -76,9 +77,11 @@ func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
default:
return fmt.Errorf("wasn't notified of new accounts")
}
return nil
}
}
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.
ks.Accounts()
if !waitWatcherStart(ks) {
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.
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int()))
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
list := ks.Accounts()
if len(list) > 0 {
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.
os.MkdirAll(dir, 0700)
defer os.RemoveAll(dir)
file := filepath.Join(dir, "aaa")
if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
t.Fatal(err)
@ -134,6 +140,7 @@ func TestWatchNoDir(t *testing.T) {
// ks should see the account.
wantAccounts := []accounts.Account{cachetestAccounts[0]}
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
list = ks.Accounts()
if reflect.DeepEqual(list, wantAccounts) {
@ -143,8 +150,10 @@ func TestWatchNoDir(t *testing.T) {
default:
t.Fatalf("wasn't notified of new accounts")
}
return
}
time.Sleep(d)
}
t.Errorf("\ngot %v\nwant %v", list, wantAccounts)
@ -152,6 +161,7 @@ func TestWatchNoDir(t *testing.T) {
func TestCacheInitialReload(t *testing.T) {
cache, _ := newAccountCache(cachetestDir)
accounts := cache.accounts()
if !reflect.DeepEqual(accounts, 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))
copy(wantAccounts, accs)
sort.Sort(accountsByURL(wantAccounts))
list := cache.accounts()
if !reflect.DeepEqual(list, wantAccounts) {
t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts))
}
for _, a := range accs {
if !cache.hasAddress(a.Address) {
t.Errorf("expected hasAccount(%x) to return true", a.Address)
}
}
if cache.hasAddress(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],
}
list = cache.accounts()
if !reflect.DeepEqual(list, wantAccountsAfterDelete) {
t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete))
}
for _, a := range wantAccountsAfterDelete {
if !cache.hasAddress(a.Address) {
t.Errorf("expected hasAccount(%x) to return true", a.Address)
}
}
if cache.hasAddress(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"),
URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "something")},
}
tests := []struct {
Query 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)
continue
}
if a != test.WantResult {
t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult)
continue
@ -328,6 +346,7 @@ func TestUpdatedKeyfileContents(t *testing.T) {
if len(list) > 0 {
t.Error("initial account list not empty:", list)
}
if !waitWatcherStart(ks) {
t.Fatal("keystore watcher didn't start in time")
}
@ -344,6 +363,7 @@ func TestUpdatedKeyfileContents(t *testing.T) {
// ks should see the account.
wantAccounts := []accounts.Account{cachetestAccounts[0]}
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
if err := waitForAccounts(wantAccounts, ks); err != nil {
t.Error(err)
return
@ -356,11 +376,14 @@ func TestUpdatedKeyfileContents(t *testing.T) {
t.Fatal(err)
return
}
wantAccounts = []accounts.Account{cachetestAccounts[1]}
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
if err := waitForAccounts(wantAccounts, ks); err != nil {
t.Errorf("First replacement failed")
t.Error(err)
return
}
@ -372,11 +395,14 @@ func TestUpdatedKeyfileContents(t *testing.T) {
t.Fatal(err)
return
}
wantAccounts = []accounts.Account{cachetestAccounts[2]}
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
if err := waitForAccounts(wantAccounts, ks); err != nil {
t.Errorf("Second replacement failed")
t.Error(err)
return
}
@ -388,9 +414,11 @@ func TestUpdatedKeyfileContents(t *testing.T) {
t.Fatal(err)
return
}
if err := waitForAccounts([]accounts.Account{}, ks); err != nil {
t.Errorf("Emptying account file failed")
t.Error(err)
return
}
}
@ -401,5 +429,6 @@ func forceCopyFile(dst, src string) error {
if err != nil {
return err
}
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 {
return nil, nil, nil, err
}
t1 := time.Now()
fc.mu.Lock()
@ -55,6 +56,7 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string]
mods := mapset.NewThreadUnsafeSet[string]()
var newLastMod time.Time
for _, fi := range files {
path := filepath.Join(keyDir, fi.Name())
// 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 {
return nil, nil, nil, err
}
modified := info.ModTime()
if modified.After(fc.lastMod) {
mods.Add(path)
}
if modified.After(newLastMod) {
newLastMod = modified
}
}
t2 := time.Now()
// Update the tracked files and return the three sets
@ -89,6 +94,7 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string]
// Report on the scanning stats and return
log.Debug("FS scan times", "list", t1.Sub(t0), "set", t2.Sub(t1), "diff", t3.Sub(t2))
return creates, deletes, updates, nil
}
@ -102,5 +108,6 @@ func nonKeyFile(fi os.DirEntry) bool {
if fi.IsDir() || !fi.Type().IsRegular() {
return true
}
return false
}

View file

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

View file

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

View file

@ -42,28 +42,36 @@ func TestKeyStore(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if !strings.HasPrefix(a.URL.Path, dir) {
t.Errorf("account file %s doesn't have dir prefix", a.URL)
}
stat, err := os.Stat(a.URL.Path)
if err != nil {
t.Fatalf("account file %s doesn't exist (%v)", a.URL, err)
}
if runtime.GOOS != "windows" && stat.Mode() != 0600 {
t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600)
}
if !ks.HasAddress(a.Address) {
t.Errorf("HasAccount(%x) should've returned true", a.Address)
}
if err := ks.Update(a, "foo", "bar"); err != nil {
t.Errorf("Update error: %v", err)
}
if err := ks.Delete(a, "bar"); err != nil {
t.Errorf("Delete error: %v", err)
}
if common.FileExist(a.URL.Path) {
t.Errorf("account file %s should be gone after Delete", a.URL)
}
if ks.HasAddress(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)
pass := "" // not used but required by API
a1, err := ks.NewAccount(pass)
if err != nil {
t.Fatal(err)
@ -80,6 +89,7 @@ func TestSign(t *testing.T) {
if err := ks.Unlock(a1, ""); err != nil {
t.Fatal(err)
}
if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil {
t.Fatal(err)
}
@ -89,6 +99,7 @@ func TestSignWithPassphrase(t *testing.T) {
_, ks := tmpKeyStore(t, true)
pass := "passwd"
acc, err := ks.NewAccount(pass)
if err != nil {
t.Fatal(err)
@ -117,6 +128,7 @@ func TestTimedUnlock(t *testing.T) {
_, ks := tmpKeyStore(t, true)
pass := "foo"
a1, err := ks.NewAccount(pass)
if err != nil {
t.Fatal(err)
@ -141,6 +153,7 @@ func TestTimedUnlock(t *testing.T) {
// Signing fails again after automatic locking
time.Sleep(250 * time.Millisecond)
_, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
if err != ErrLocked {
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)
pass := "foo"
a1, err := ks.NewAccount(pass)
if err != nil {
t.Fatal(err)
@ -181,6 +195,7 @@ func TestOverrideUnlock(t *testing.T) {
// Signing fails again after automatic locking
time.Sleep(250 * time.Millisecond)
_, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
if err != ErrLocked {
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 {
t.Fatal("could not unlock the test account", err)
}
end := time.Now().Add(500 * time.Millisecond)
for time.Now().Before(end) {
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)
return
}
time.Sleep(1 * time.Millisecond)
}
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 {
return true
}
time.Sleep(25 * time.Millisecond)
}
return false
}
@ -250,6 +269,7 @@ func TestWalletNotifierLifecycle(t *testing.T) {
for i := 0; i < len(subs); i++ {
// Create a new subscription
subs[i] = ks.Subscribe(updates)
if !waitForKsUpdating(t, ks, true, 250*time.Millisecond) {
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.
subs[len(subs)-1].Unsubscribe()
if !waitForKsUpdating(t, ks, false, 4*time.Second) {
t.Errorf("wallet notifier didn't terminate after unsubscribe")
}
@ -288,7 +309,9 @@ func TestWalletNotifications(t *testing.T) {
updates = make(chan accounts.WalletEvent)
sub = ks.Subscribe(updates)
)
defer sub.Unsubscribe()
go func() {
for {
select {
@ -306,6 +329,7 @@ func TestWalletNotifications(t *testing.T) {
live = make(map[common.Address]accounts.Account)
wantEvents []walletEvent
)
for i := 0; i < 1024; i++ {
if create := len(live) == 0 || rand.Int()%4 > 0; create {
// Add a new account and ensure wallet notifications arrives
@ -313,6 +337,7 @@ func TestWalletNotifications(t *testing.T) {
if err != nil {
t.Fatalf("failed to create test account: %v", err)
}
live[account.Address] = account
wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletArrived}, account})
} else {
@ -322,9 +347,11 @@ func TestWalletNotifications(t *testing.T) {
account = a
break
}
if err := ks.Delete(account, ""); err != nil {
t.Fatalf("failed to delete test account: %v", err)
}
delete(live, account.Address)
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.
sub.Unsubscribe()
for ev := range updates {
events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]})
}
checkAccounts(t, live, ks.Wallets())
checkEvents(t, wantEvents, events)
}
@ -342,16 +371,20 @@ func TestWalletNotifications(t *testing.T) {
// TestImportExport tests the import functionality of a keystore.
func TestImportECDSA(t *testing.T) {
_, ks := tmpKeyStore(t, true)
key, err := crypto.GenerateKey()
if err != nil {
t.Fatalf("failed to generate key: %v", key)
}
if _, err = ks.ImportECDSA(key, "old"); err != nil {
t.Errorf("importing failed: %v", err)
}
if _, err = ks.ImportECDSA(key, "old"); err == nil {
t.Errorf("importing same key twice succeeded")
}
if _, err = ks.ImportECDSA(key, "new"); err == nil {
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.
func TestImportExport(t *testing.T) {
_, ks := tmpKeyStore(t, true)
acc, err := ks.NewAccount("old")
if err != nil {
t.Fatalf("failed to create account: %v", acc)
}
json, err := ks.Export(acc, "old", "new")
if err != nil {
t.Fatalf("failed to export account: %v", acc)
}
_, ks2 := tmpKeyStore(t, true)
if _, err = ks2.Import(json, "old", "old"); err == nil {
t.Errorf("importing with invalid password succeeded")
}
acc2, err := ks2.Import(json, "new", "new")
if err != nil {
t.Errorf("importing failed: %v", err)
}
if acc.Address != acc2.Address {
t.Error("imported account does not match exported account")
}
if _, err = ks2.Import(json, "new", "new"); err == nil {
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.
func TestImportRace(t *testing.T) {
_, ks := tmpKeyStore(t, true)
acc, err := ks.NewAccount("old")
if err != nil {
t.Fatalf("failed to create account: %v", acc)
}
json, err := ks.Export(acc, "old", "new")
if err != nil {
t.Fatalf("failed to export account: %v", acc)
}
_, ks2 := tmpKeyStore(t, true)
var atom uint32
var wg sync.WaitGroup
wg.Add(2)
for i := 0; i < 2; i++ {
go func() {
defer wg.Done()
if _, err := ks2.Import(json, "new", "new"); err != nil {
atomic.AddUint32(&atom, 1)
}
}()
}
wg.Wait()
if atom != 1 {
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))
return
}
liveList := make([]accounts.Account, 0, len(live))
for _, account := range live {
liveList = append(liveList, account)
}
sort.Sort(accountsByURL(liveList))
for j, wallet := range wallets {
if accs := wallet.Accounts(); len(accs) != 1 {
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) {
for _, wantEv := range want {
nmatch := 0
for ; len(have) > 0; nmatch++ {
if have[0].Kind != wantEv.Kind || have[0].a != wantEv.a {
break
}
have = have[1:]
}
if nmatch == 0 {
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) {
d := t.TempDir()
newKs := NewPlaintextKeyStore
if encrypted {
newKs = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) }
}
return d, newKs(d)
}

View file

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

View file

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

View file

@ -34,14 +34,18 @@ func (ks keyStorePlain) GetKey(addr common.Address, filename, auth string) (*Key
if err != nil {
return nil, err
}
defer fd.Close()
key := new(Key)
if err := json.NewDecoder(fd).Decode(key); err != nil {
return nil, err
}
if key.Address != addr {
return nil, fmt.Errorf("key content mismatch: have address %x, want %x", key.Address, addr)
}
return key, nil
}
@ -50,6 +54,7 @@ func (ks keyStorePlain) StoreKey(filename string, key *Key, auth string) error {
if err != nil {
return err
}
return writeKeyFile(filename, content)
}
@ -57,5 +62,6 @@ func (ks keyStorePlain) JoinPath(filename string) string {
if filepath.IsAbs(filename) {
return 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 {
ks = &keyStorePlain{d}
}
return d, ks
}
@ -43,17 +44,21 @@ func TestKeyStorePlain(t *testing.T) {
_, ks := tmpKeyStoreIface(t, false)
pass := "" // not used but required by API
k1, account, err := storeNewKey(ks, rand.Reader, pass)
if err != nil {
t.Fatal(err)
}
k2, err := ks.GetKey(k1.Address, account.URL.Path, pass)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(k1.Address, k2.Address) {
t.Fatal(err)
}
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
t.Fatal(err)
}
@ -63,17 +68,21 @@ func TestKeyStorePassphrase(t *testing.T) {
_, ks := tmpKeyStoreIface(t, true)
pass := "foo"
k1, account, err := storeNewKey(ks, rand.Reader, pass)
if err != nil {
t.Fatal(err)
}
k2, err := ks.GetKey(k1.Address, account.URL.Path, pass)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(k1.Address, k2.Address) {
t.Fatal(err)
}
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
t.Fatal(err)
}
@ -83,10 +92,12 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
_, ks := tmpKeyStoreIface(t, true)
pass := "foo"
k1, account, err := storeNewKey(ks, rand.Reader, pass)
if err != nil {
t.Fatal(err)
}
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)
}
@ -100,13 +111,16 @@ func TestImportPreSaleKey(t *testing.T) {
// with password "foo"
fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}"
pass := "foo"
account, _, err := importPreSaleKey(ks, []byte(fileContent), pass)
if err != nil {
t.Fatal(err)
}
if account.Address != common.HexToAddress("d4584b5f6229b7be90727b0fc8c6b91bb427821f") {
t.Errorf("imported account has wrong address %x", account.Address)
}
if !strings.HasPrefix(account.URL.Path, dir) {
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) {
t.Parallel()
ks := &keyStorePassphrase{"testdata/v1", LightScryptN, LightScryptP, true}
addr := common.HexToAddress("cb61d5a9c4896fb9658090b597ef0e7be6f7b67e")
file := "testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e"
k, err := ks.GetKey(addr, file, "g")
if err != nil {
t.Fatal(err)
}
privHex := hex.EncodeToString(crypto.FromECDSA(k.PrivateKey))
expectedHex := "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d"
if 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 {
t.Fatal(err)
}
privHex := hex.EncodeToString(privBytes)
if 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 {
t.Fatal(err)
}
privHex := hex.EncodeToString(privBytes)
if 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 {
tests := make(map[string]KeyStoreTestV3)
err := common.LoadJSON(file, &tests)
if err != nil {
t.Fatal(err)
}
return tests
}
func loadKeyStoreTestV1(file string, t *testing.T) map[string]KeyStoreTestV1 {
tests := make(map[string]KeyStoreTestV1)
err := common.LoadJSON(file, &tests)
if err != nil {
t.Fatal(err)
}
return tests
}
func TestKeyForDirectICAP(t *testing.T) {
t.Parallel()
key := NewKeyForDirectICAP(rand.Reader)
if !strings.HasPrefix(key.Address.Hex(), "0x00") {
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 {
return accounts.Account{}, nil, err
}
key.Id, err = uuid.NewRandom()
if err != nil {
return accounts.Account{}, nil, err
}
a := accounts.Account{
Address: key.Address,
URL: accounts.URL{
@ -49,6 +51,7 @@ func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accou
},
}
err = keyStore.StoreKey(a.URL.Path, key, password)
return a, key, err
}
@ -59,17 +62,21 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
Email string
BtcAddr string
}{}
err = json.Unmarshal(fileContent, &preSaleKeyStruct)
if err != nil {
return nil, err
}
encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
if err != nil {
return nil, errors.New("invalid hex in encSeed")
}
if len(encSeedBytes) < 16 {
return nil, errors.New("invalid encSeed, too short")
}
iv := encSeedBytes[:16]
cipherText := encSeedBytes[16:]
/*
@ -81,10 +88,12 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
*/
passBytes := []byte(password)
derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
if err != nil {
return nil, err
}
ethPriv := crypto.Keccak256(plainText)
ecKey := crypto.ToECDSAUnsafe(ethPriv)
@ -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"
expectedAddr := preSaleKeyStruct.EthAddr
if derivedAddr != expectedAddr {
err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr)
}
return key, err
}
@ -107,9 +118,11 @@ func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
if err != nil {
return nil, err
}
stream := cipher.NewCTR(aesBlock, iv)
outText := make([]byte, len(inText))
stream.XORKeyStream(outText, inText)
return outText, err
}
@ -118,13 +131,16 @@ func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
if err != nil {
return nil, err
}
decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
paddedPlaintext := make([]byte, len(cipherText))
decrypter.CryptBlocks(paddedPlaintext, cipherText)
plaintext := pkcs7Unpad(paddedPlaintext)
if plaintext == nil {
return nil, ErrDecrypt
}
return plaintext, err
}
@ -146,5 +162,6 @@ func pkcs7Unpad(in []byte) []byte {
return nil
}
}
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 {
return "Unlocked", nil
}
return "Locked", nil
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -41,8 +41,10 @@ func parse(data []byte) {
if err != nil {
die(err)
}
messages := apitypes.ValidationMessages{}
db.ValidateCallData(nil, data, &messages)
for _, m := range messages.Messages {
fmt.Printf("%v: %v\n", m.Typ, m.Message)
}
@ -56,10 +58,12 @@ func main() {
switch {
case flag.NArg() == 1:
hexdata := flag.Arg(0)
data, err := hex.DecodeString(strings.TrimPrefix(hexdata, "0x"))
if err != nil {
die(err)
}
parse(data)
default:
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) == "" {
utils.Fatalf("No destination package specified (--pkg)")
}
var lang bind.Lang
switch c.String(langFlag.Name) {
@ -116,6 +117,7 @@ func abigen(c *cli.Context) error {
libs = make(map[string]string)
aliases = make(map[string]string)
)
if c.String(abiFlag.Name) != "" {
// Load up the ABI, optional bytecode and type name from the parameters
var (
@ -129,9 +131,11 @@ func abigen(c *cli.Context) error {
} else {
abi, err = os.ReadFile(input)
}
if err != nil {
utils.Fatalf("Failed to read input ABI: %v", err)
}
abis = append(abis, string(abi))
var bin []byte
@ -140,26 +144,31 @@ func abigen(c *cli.Context) error {
if bin, err = os.ReadFile(binFile); err != nil {
utils.Fatalf("Failed to read input bytecode: %v", err)
}
if strings.Contains(string(bin), "//") {
utils.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos")
}
}
bins = append(bins, string(bin))
kind := c.String(typeFlag.Name)
if kind == "" {
kind = c.String(pkgFlag.Name)
}
types = append(types, kind)
} else {
// Generate the list of types to exclude from binding
var exclude *nameFilter
if c.IsSet(excFlag.Name) {
var err error
if exclude, err = newNameFilter(strings.Split(c.String(excFlag.Name), ",")...); err != nil {
utils.Fatalf("Failed to parse excludes: %v", err)
}
}
var contracts map[string]*compiler.Contract
if c.IsSet(jsonFlag.Name) {
@ -168,14 +177,17 @@ func abigen(c *cli.Context) error {
jsonOutput []byte
err error
)
if input == "-" {
jsonOutput, err = io.ReadAll(os.Stdin)
} else {
jsonOutput, err = os.ReadFile(input)
}
if err != nil {
utils.Fatalf("Failed to read combined-json: %v", err)
}
contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "")
if err != nil {
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>
nameParts := strings.Split(name, ":")
typeName := nameParts[len(nameParts)-1]
if exclude != nil && exclude.Matches(name) {
fmt.Fprintf(os.Stderr, "excluding: %v\n", name)
continue
}
abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse
if err != nil {
utils.Fatalf("Failed to parse ABIs from compiler output: %v", err)
}
abis = append(abis, string(abi))
bins = append(bins, contract.Code)
sigs = append(sigs, contract.Hashes)
@ -214,6 +229,7 @@ func abigen(c *cli.Context) error {
// foo=bar,foo2=bar2
// foo:bar,foo2:bar2
re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`)
submatches := re.FindAllStringSubmatch(c.String(aliasFlag.Name), -1)
for _, match := range submatches {
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 {
utils.Fatalf("Failed to write ABI binding: %v", err)
}
return nil
}

View file

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

View file

@ -17,6 +17,7 @@
package main
import (
"github.com/urfave/cli/v2"
"strconv"
"github.com/ethereum/go-ethereum/accounts"
@ -36,6 +37,7 @@ func newClient(ctx *cli.Context) *ethclient.Client {
if err != nil {
utils.Fatalf("Failed to connect to Ethereum node: %v", err)
}
return client
}
@ -45,6 +47,7 @@ func newRPCClient(url string) *rpc.Client {
if err != nil {
utils.Fatalf("Failed to connect to Ethereum node: %v", err)
}
return client
}
@ -55,6 +58,7 @@ func getContractAddr(client *rpc.Client) common.Address {
if err := client.Call(&addr, "les_getCheckpointContractAddress"); err != nil {
utils.Fatalf("Failed to fetch checkpoint oracle address: %v", err)
}
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 {
utils.Fatalf("Failed to get local checkpoint %v, please ensure the les API is exposed", err)
}
checkpoint = &params.TrustedCheckpoint{
SectionIndex: index,
SectionHead: common.HexToHash(result[0]),
@ -78,14 +83,17 @@ func getCheckpoint(ctx *cli.Context, client *rpc.Client) *params.TrustedCheckpoi
}
} else {
var result [4]string
err := client.Call(&result, "les_latestCheckpoint")
if err != nil {
utils.Fatalf("Failed to get local checkpoint %v, please ensure the les API is exposed", err)
}
index, err := strconv.ParseUint(result[0], 0, 64)
if err != nil {
utils.Fatalf("Failed to parse checkpoint index %v", err)
}
checkpoint = &params.TrustedCheckpoint{
SectionIndex: index,
SectionHead: common.HexToHash(result[1]),
@ -93,6 +101,7 @@ func getCheckpoint(ctx *cli.Context, client *rpc.Client) *params.TrustedCheckpoi
BloomRoot: common.HexToHash(result[3]),
}
}
return checkpoint
}
@ -103,10 +112,12 @@ func newContract(client *rpc.Client) (common.Address, *checkpointoracle.Checkpoi
if addr == (common.Address{}) {
utils.Fatalf("No specified registrar contract address")
}
contract, err := checkpointoracle.NewCheckpointOracle(addr, ethclient.NewClient(client))
if err != nil {
utils.Fatalf("Failed to setup registrar contract %s: %v", addr, err)
}
return addr, contract
}
@ -116,5 +127,6 @@ func newClefSigner(ctx *cli.Context) *bind.TransactOpts {
if err != nil {
utils.Fatalf("Failed to create clef signer %v", err)
}
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 {
// Gather all the addresses that should be permitted to sign
var addrs []common.Address
for _, account := range strings.Split(ctx.String(signersFlag.Name), ",") {
if trimmed := strings.TrimSpace(account); !common.IsHexAddress(trimmed) {
utils.Fatalf("Invalid account in --signers: '%s'", trimmed)
}
addrs = append(addrs, common.HexToAddress(account))
}
// 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
fmt.Printf("Deploying new checkpoint oracle:\n\n")
for i, addr := range addrs {
fmt.Printf("Admin %d => %s\n", i+1, addr.Hex())
}
fmt.Printf("\nSignatures needed to publish: %d\n", needed)
// 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
fmt.Println("Sending deploy request to Clef...")
oracle, tx, _, err := contract.DeployCheckpointOracle(transactor, client, addrs, big.NewInt(int64(params.CheckpointFrequency)),
big.NewInt(int64(params.CheckpointProcessConfirmations)), big.NewInt(int64(needed)))
if err != nil {
utils.Fatalf("Failed to deploy checkpoint oracle %v", err)
}
log.Info("Deployed checkpoint oracle", "address", oracle, "tx", tx.Hash().Hex())
return nil
@ -133,22 +139,27 @@ func sign(ctx *cli.Context) error {
node *rpc.Client
oracle *checkpointoracle.CheckpointOracle
)
if !ctx.IsSet(nodeURLFlag.Name) {
// Offline mode signing
offline = true
if !ctx.IsSet(hashFlag.Name) {
utils.Fatalf("Please specify the checkpoint hash (--hash) to sign in offline mode")
}
chash = common.HexToHash(ctx.String(hashFlag.Name))
if !ctx.IsSet(indexFlag.Name) {
utils.Fatalf("Please specify checkpoint index (--index) to sign in offline mode")
}
cindex = ctx.Uint64(indexFlag.Name)
if !ctx.IsSet(oracleFlag.Name) {
utils.Fatalf("Please specify oracle address (--oracle) to sign in offline mode")
}
address = common.HexToAddress(ctx.String(oracleFlag.Name))
} else {
// Interactive mode signing, retrieve the data from the remote node
@ -165,22 +176,28 @@ func sign(ctx *cli.Context) error {
if err != nil {
return err
}
num := head.Number.Uint64()
if num < ((cindex+1)*params.CheckpointFrequency + params.CheckpointProcessConfirmations) {
utils.Fatalf("Invalid future checkpoint")
}
_, oracle = newContract(node)
latest, _, h, err := oracle.Contract().GetLatestCheckpoint(nil)
if err != nil {
return err
}
if cindex < latest {
utils.Fatalf("Checkpoint is too old")
}
if cindex == latest && (latest != 0 || h.Uint64() != 0) {
utils.Fatalf("Stale checkpoint, latest registered %d, given %d", latest, cindex)
}
}
var (
signature string
signer string
@ -191,11 +208,13 @@ func sign(ctx *cli.Context) error {
if err != nil {
return err
}
for _, s := range signers {
if s == addr {
return nil
}
}
return fmt.Errorf("signer %v is not the admin", addr.Hex())
}
// Print to the user the data thy are about to sign
@ -210,19 +229,24 @@ func sign(ctx *cli.Context) error {
return err
}
}
clef := newRPCClient(ctx.String(clefURLFlag.Name))
p := make(map[string]string)
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, cindex)
p["address"] = address.Hex()
p["message"] = hexutil.Encode(append(buf, chash.Bytes()...))
fmt.Println("Sending signing request to Clef...")
if err := clef.Call(&signature, "account_signData", accounts.MimetypeDataWithValidator, signer, p); err != nil {
utils.Fatalf("Failed to sign checkpoint, err %v", err)
}
fmt.Printf("Signer => %s\n", signer)
fmt.Printf("Signature => %s\n", signature)
return nil
}
@ -232,6 +256,7 @@ func sighash(index uint64, oracle common.Address, hash common.Hash) []byte {
binary.BigEndian.PutUint64(buf, index)
data := append([]byte{0x19, 0x00}, append(oracle[:], append(buf, hash[:]...)...)...)
return crypto.Keccak256(data)
}
@ -244,6 +269,7 @@ func ecrecover(sighash []byte, sig []byte) common.Address {
if err != nil {
utils.Fatalf("Failed to recover sender from signature %x: %v", sig, err)
}
return crypto.PubkeyToAddress(*signer)
}
@ -256,6 +282,7 @@ func publish(ctx *cli.Context) error {
// Gather the signatures from the CLI
var sigs [][]byte
for _, sig := range strings.Split(ctx.String(signaturesFlag.Name), ",") {
trimmed := strings.TrimPrefix(strings.TrimSpace(sig), "0x")
if len(trimmed) != 130 {
@ -271,10 +298,12 @@ func publish(ctx *cli.Context) error {
checkpoint = getCheckpoint(ctx, client)
sighash = sighash(checkpoint.SectionIndex, addr, checkpoint.Hash())
)
for i := 0; i < len(sigs); i++ {
for j := i + 1; j < len(sigs); j++ {
signerA := ecrecover(sighash, sigs[i])
signerB := ecrecover(sighash, sigs[j])
if bytes.Compare(signerA.Bytes(), signerB.Bytes()) > 0 {
sigs[i], sigs[j] = sigs[j], sigs[i]
}
@ -288,25 +317,32 @@ func publish(ctx *cli.Context) error {
if err != nil {
return err
}
num := head.Number.Uint64()
recent, err := ethclient.NewClient(client).HeaderByNumber(reqCtx, big.NewInt(int64(num-128)))
if err != nil {
return err
}
// Print a summary of the operation that's going to be performed
fmt.Printf("Publishing %d => %s:\n\n", checkpoint.SectionIndex, checkpoint.Hash().Hex())
for i, sig := range sigs {
fmt.Printf("Signer %d => %s\n", i+1, ecrecover(sighash, sig).Hex())
}
fmt.Println()
fmt.Printf("Sentry number => %d\nSentry hash => %s\n", recent.Number, recent.Hash().Hex())
// Publish the checkpoint into the oracle
fmt.Println("Sending publish request to Clef...")
tx, err := oracle.RegisterCheckpoint(newClefSigner(ctx), checkpoint.SectionIndex, checkpoint.Hash().Bytes(), recent.Number, recent.Hash(), sigs)
if err != nil {
utils.Fatalf("Register contract failed %v", err)
}
log.Info("Successfully registered checkpoint", "tx", tx.Hash().Hex())
return nil
}

View file

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

View file

@ -37,6 +37,7 @@ func TestImportRaw(t *testing.T) {
// Run clef importraw
clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "importraw", keyPath)
clef.input("myverylongpassword").input("myverylongpassword")
if out := string(clef.Output()); !strings.Contains(out,
"Key imported:\n Address 0x9160DC9105f7De5dC5E7f3d97ef11DA47269BdA6") {
t.Logf("Output\n%v", out)
@ -49,6 +50,7 @@ func TestImportRaw(t *testing.T) {
// Run clef importraw
clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "importraw", keyPath)
clef.input("myverylongpassword1").input("myverylongpassword2").WaitExit()
if have, want := clef.StderrText(), "Passwords do not match\n"; have != want {
t.Errorf("have %q, want %q", have, want)
}
@ -59,6 +61,7 @@ func TestImportRaw(t *testing.T) {
// Run clef importraw
clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "importraw", keyPath)
clef.input("shorty").input("shorty").WaitExit()
if have, want := clef.StderrText(),
"password requirements not met: password too short (<10 characters)\n"; 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.Parallel()
clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "list-accounts")
if out := string(clef.Output()); !strings.Contains(out, "The keystore is empty.") {
t.Logf("Output\n%v", out)
@ -106,6 +110,7 @@ func TestListWallets(t *testing.T) {
t.Run("no-accounts", func(t *testing.T) {
t.Parallel()
clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "list-wallets")
if out := string(clef.Output()); !strings.Contains(out, "There are no wallets.") {
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) {
return err
}
location := filepath.Join(configDir, "masterseed.json")
if _, err := os.Stat(location); err == nil {
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
masterSeed := make([]byte, 256)
num, err := io.ReadFull(rand.Reader, masterSeed)
if err != nil {
return err
}
if num != len(masterSeed) {
return fmt.Errorf("failed to read enough random")
}
n, p := keystore.StandardScryptN, keystore.StandardScryptP
if c.Bool(utils.LightKDFFlag.Name) {
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!"
var password string
for {
password = utils.GetPassPhrase(text, true)
if err := core.ValidatePasswordFormat(password); err != nil {
@ -344,6 +351,7 @@ func initializeSecrets(c *cli.Context) error {
break
}
}
cipherSeed, err := encryptSeed(masterSeed, []byte(password), n, p)
if err != nil {
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) {
return err
}
if _, err := os.Stat(location); err == nil {
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 {
return err
}
fmt.Printf("A master seed has been generated into %s\n", location)
fmt.Printf(`
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!
`)
return nil
}
@ -378,6 +389,7 @@ func attestFile(ctx *cli.Context) error {
if ctx.NArg() < 1 {
utils.Fatalf("This command requires an argument.")
}
if err := initialize(ctx); err != nil {
return err
}
@ -396,6 +408,7 @@ func attestFile(ctx *cli.Context) error {
val := ctx.Args().First()
configStorage.Put("ruleset_sha256", val)
log.Info("Ruleset attestation updated", "sha256", val)
return nil
}
@ -422,15 +435,19 @@ func setCredential(ctx *cli.Context) error {
if ctx.NArg() < 1 {
utils.Fatalf("This command requires an address to be passed as an argument")
}
if err := initialize(ctx); err != nil {
return err
}
addr := ctx.Args().First()
if !common.IsHexAddress(addr) {
utils.Fatalf("Invalid address specified: %s", addr)
}
address := common.HexToAddress(addr)
password := utils.GetPassPhrase("Please enter a password to store for this address:", true)
fmt.Println()
stretchedKey, err := readMasterKey(ctx, nil)
@ -446,6 +463,7 @@ func setCredential(ctx *cli.Context) error {
pwStorage.Put(address.Hex(), password)
log.Info("Credential store updated", "set", address)
return nil
}
@ -453,13 +471,16 @@ func removeCredential(ctx *cli.Context) error {
if ctx.NArg() < 1 {
utils.Fatalf("This command requires an address to be passed as an argument")
}
if err := initialize(ctx); err != nil {
return err
}
addr := ctx.Args().First()
if !common.IsHexAddress(addr) {
utils.Fatalf("Invalid address specified: %s", addr)
}
address := common.HexToAddress(addr)
stretchedKey, err := readMasterKey(ctx, nil)
@ -475,6 +496,7 @@ func removeCredential(ctx *cli.Context) error {
pwStorage.Del(address.Hex())
log.Info("Credential store updated", "unset", address)
return nil
}
@ -491,13 +513,17 @@ func initialize(c *cli.Context) error {
if !confirm(legalWarning) {
return fmt.Errorf("aborted by user")
}
fmt.Println()
}
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
output := io.Writer(logOutput)
if usecolor {
output = colorable.NewColorable(logOutput)
}
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(output, log.TerminalFormat(usecolor))))
return nil
@ -644,6 +670,7 @@ func ipcEndpoint(ipcPath, datadir string) string {
if strings.HasPrefix(ipcPath, `\\.\pipe\`) {
return ipcPath
}
return `\\.\pipe\` + ipcPath
}
// Resolve names into the data directory full paths otherwise
@ -651,8 +678,10 @@ func ipcEndpoint(ipcPath, datadir string) string {
if datadir == "" {
return filepath.Join(os.TempDir(), ipcPath)
}
return filepath.Join(datadir, ipcPath)
}
return ipcPath
}
@ -661,26 +690,32 @@ func signer(c *cli.Context) error {
if c.NArg() > 0 {
return fmt.Errorf("invalid command: %q", c.Args().First())
}
if err := initialize(c); err != nil {
return err
}
var (
ui core.UIClientAPI
)
if c.Bool(stdiouiFlag.Name) {
log.Info("Using stdin/stdout as UI-channel")
ui = core.NewStdIOUI()
} else {
log.Info("Using CLI as UI-channel")
ui = core.NewCommandlineUI()
}
// 4bytedb data
fourByteLocal := c.String(customDBFlag.Name)
db, err := fourbyte.NewWithFile(fourByteLocal)
if err != nil {
utils.Fatalf(err.Error())
}
embeds, locals := db.Size()
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)
if stretchedKey, err := readMasterKey(c, ui); err != nil {
log.Warn("Failed to open master, rules disabled", "err", err)
} else {
@ -714,6 +750,7 @@ func signer(c *cli.Context) error {
shasum := sha256.Sum256(ruleJS)
foundShaSum := hex.EncodeToString(shasum[:])
storedShasum, _ := configStorage.Get("ruleset_sha256")
if storedShasum != foundShaSum {
log.Warn("Rule hash not attested, disabling", "hash", foundShaSum, "attested", storedShasum)
} else {
@ -722,13 +759,16 @@ func signer(c *cli.Context) error {
if err != nil {
utils.Fatalf(err.Error())
}
ruleEngine.Init(string(ruleJS))
ui = ruleEngine
log.Info("Rule engine configured", "file", c.String(ruleFlag.Name))
}
}
}
}
var (
chainId = c.Int64(chainIdFlag.Name)
ksLoc = c.String(keystoreFlag.Name)
@ -737,8 +777,10 @@ func signer(c *cli.Context) error {
nousb = c.Bool(utils.NoUSBFlag.Name)
scpath = c.String(utils.SmartCardDaemonPathFlag.Name)
)
log.Info("Starting signer", "chainid", chainId, "keystore", ksLoc,
"light-kdf", lightKdf, "advanced", advanced)
am := core.StartClefAccountManager(ksLoc, nousb, lightKdf, scpath)
apiImpl := core.NewSignerAPI(am, chainId, nousb, ui, db, advanced, pwStorage)
@ -753,6 +795,7 @@ func signer(c *cli.Context) error {
if err != nil {
utils.Fatalf(err.Error())
}
log.Info("Audit logs configured", "file", logfile)
}
// register signer API with server
@ -760,6 +803,7 @@ func signer(c *cli.Context) error {
extapiURL = "n/a"
ipcapiURL = "n/a"
)
rpcAPI := []rpc.API{
{
Namespace: "account",
@ -772,10 +816,12 @@ func signer(c *cli.Context) error {
cors := utils.SplitAndTrim(c.String(utils.HTTPCORSDomainFlag.Name))
srv := rpc.NewServer(0, 0)
err := node.RegisterApis(rpcAPI, []string{"account"}, srv)
if err != nil {
utils.Fatalf("Could not register API: %w", err)
}
handler := node.NewHTTPHandlerStack(srv, cors, vhosts, nil)
// set port
@ -783,10 +829,12 @@ func signer(c *cli.Context) error {
// start http server
httpEndpoint := fmt.Sprintf("%s:%d", c.String(utils.HTTPListenAddrFlag.Name), port)
httpServer, addr, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler)
if err != nil {
utils.Fatalf("Could not start RPC api: %v", err)
}
extapiURL = fmt.Sprintf("http://%v/", addr)
log.Info("HTTP endpoint opened", "url", extapiURL)
@ -800,11 +848,14 @@ func signer(c *cli.Context) error {
if !c.Bool(utils.IPCDisabledFlag.Name) {
givenPath := c.String(utils.IPCPathFlag.Name)
ipcapiURL = ipcEndpoint(filepath.Join(givenPath, "clef.ipc"), configDir)
listener, _, err := rpc.StartIPCEndpoint(ipcapiURL, rpcAPI)
if err != nil {
utils.Fatalf("Could not start IPC api: %v", err)
}
log.Info("IPC endpoint opened", "url", ipcapiURL)
defer func() {
listener.Close()
log.Info("IPC endpoint closed", "url", ipcapiURL)
@ -813,8 +864,10 @@ func signer(c *cli.Context) error {
if c.Bool(testFlag.Name) {
log.Info("Performing UI test")
go testExternalUI(apiImpl)
}
ui.OnSignerStartup(core.StartupInfo{
Info: map[string]interface{}{
"intapi_version": core.InternalAPIVersion,
@ -845,8 +898,10 @@ func DefaultConfigDir() string {
if appdata != "" {
return filepath.Join(appdata, "Signer")
}
return filepath.Join(home, "AppData", "Roaming", "Signer")
}
return filepath.Join(home, ".clef")
}
// 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 {
file = filepath.Join(configDir, "masterseed.json")
}
if err := checkFile(file); err != nil {
return nil, err
}
@ -872,6 +928,7 @@ func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) {
if err != nil {
return nil, err
}
var password string
// If ui is not nil, get the password from ui.
if ui != nil {
@ -882,23 +939,28 @@ func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) {
if err != nil {
return nil, err
}
password = resp.Text
} else {
password = utils.GetPassPhrase("Decrypt master seed of clef", false)
}
masterSeed, err := decryptSeed(cipherKey, password)
if err != nil {
return nil, fmt.Errorf("failed to decrypt the master seed of clef")
}
if len(masterSeed) < 256 {
return nil, fmt.Errorf("master seed of insufficient length, expected >255 bytes, got %d", len(masterSeed))
}
// Create vault location
vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), masterSeed)[:10]))
err = os.Mkdir(vaultLocation, 0700)
if err != nil && !os.IsExist(err) {
return nil, err
}
return masterSeed, nil
}
@ -916,6 +978,7 @@ func checkFile(filename string) error {
if runtime.GOOS != "windows" && info.Mode().Perm()&0377 != 0 {
return fmt.Errorf("file (%v) has insecure file permissions (%v)", filename, info.Mode().String())
}
return nil
}
@ -928,9 +991,11 @@ func confirm(text string) bool {
if err != nil {
log.Crit("Failed to read user input", "err", err)
}
if text := strings.TrimSpace(text); text == "ok" {
return true
}
return false
}
@ -954,6 +1019,7 @@ func testExternalUI(api *core.SignerAPI) {
if err != nil {
addErr(err.Error())
}
return resp.Text
}
expectResponse := func(testcase, question, expect string) {
@ -965,6 +1031,7 @@ func testExternalUI(api *core.SignerAPI) {
if err == nil || err == accounts.ErrUnknownAccount {
return
}
addErr(fmt.Sprintf("%v: expected no error, got %v", testcase, 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))
}
}
var delay = 1 * time.Second
// Test display of info and error
{
@ -985,6 +1053,7 @@ func testExternalUI(api *core.SignerAPI) {
{ // Sign data test - clique header
api.UI.ShowInfo("Please approve the next request for signing a clique header")
time.Sleep(delay)
cliqueHeader := types.Header{
ParentHash: 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"),
MixDigest: common.HexToHash("0x0000H45H"),
}
cliqueRlp, err := rlp.EncodeToBytes(cliqueHeader)
if err != nil {
utils.Fatalf("Should not error: %v", err)
}
addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
_, err = api.SignData(ctx, accounts.MimetypeClique, *addr, hexutil.Encode(cliqueRlp))
expectApprove("signdata - clique header", err)
@ -1011,10 +1082,12 @@ func testExternalUI(api *core.SignerAPI) {
{ // Sign data test - typed data
api.UI.ShowInfo("Please approve the next request for signing EIP-712 typed data")
time.Sleep(delay)
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!"}}`
//_, err := api.SignData(ctx, accounts.MimetypeTypedData, *addr, hexutil.Encode([]byte(data)))
var typedData apitypes.TypedData
json.Unmarshal([]byte(data), &typedData)
_, err := api.SignTypedData(ctx, *addr, typedData)
expectApprove("sign 712 typed data", err)
@ -1022,6 +1095,7 @@ func testExternalUI(api *core.SignerAPI) {
{ // Sign data test - plain text
api.UI.ShowInfo("Please approve the next request for signing text")
time.Sleep(delay)
addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
_, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world")))
expectApprove("signdata - text", err)
@ -1029,6 +1103,7 @@ func testExternalUI(api *core.SignerAPI) {
{ // Sign data test - plain text reject
api.UI.ShowInfo("Please deny the next request for signing text")
time.Sleep(delay)
addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
_, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world")))
expectDeny("signdata - text", err)
@ -1036,6 +1111,7 @@ func testExternalUI(api *core.SignerAPI) {
{ // Sign transaction
api.UI.ShowInfo("Please reject next transaction")
time.Sleep(delay)
data := hexutil.Bytes([]byte{})
to := common.NewMixedcaseAddress(a)
tx := apitypes.SendTxArgs{
@ -1055,12 +1131,14 @@ func testExternalUI(api *core.SignerAPI) {
{ // Listing
api.UI.ShowInfo("Please reject listing-request")
time.Sleep(delay)
_, err := api.List(ctx)
expectDeny("list", err)
}
{ // Import
api.UI.ShowInfo("Please reject new account-request")
time.Sleep(delay)
_, err := api.New(ctx)
expectDeny("newaccount", err)
}
@ -1074,6 +1152,7 @@ func testExternalUI(api *core.SignerAPI) {
for _, e := range errs {
log.Error(e)
}
result := fmt.Sprintf("Tests completed. %d errors:\n%s\n", len(errs), strings.Join(errs, "\n"))
api.UI.ShowInfo(result)
}
@ -1091,6 +1170,7 @@ func encryptSeed(seed []byte, auth []byte, scryptN, scryptP int) ([]byte, error)
if err != nil {
return nil, err
}
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 {
return nil, err
}
if encSeed.Version != 1 {
log.Warn(fmt.Sprintf("unsupported encryption format of seed: %d, operation will likely fail", encSeed.Version))
}
seed, err := keystore.DecryptDataV3(encSeed.Params, auth)
if err != nil {
return nil, 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"
rlpdata := common.FromHex("0xf85d640101948a8eafb1cf62bfbeb1741769dae1a9dd47996192018026a0716bd90515acb1e68e5ac5867aa11a1e65399c3349d479f5fb698554ebc6f293a04e8a4ebfff434e971e0ef12c5bf3a881b06fd04fc3f8b8a7291fb67a26a1d4ed")
var tx types.Transaction
tx.UnmarshalBinary(rlpdata)
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
These data types are defined in the channel between clef and the UI`)
for _, elem := range output {
fmt.Println(elem)
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -47,20 +47,25 @@ var enrdumpCommand = &cli.Command{
func enrdump(ctx *cli.Context) error {
var source string
if file := ctx.String(fileFlag.Name); file != "" {
if ctx.NArg() != 0 {
return fmt.Errorf("can't dump record from command-line argument in -file mode")
}
var b []byte
var err error
if file == "-" {
b, err = io.ReadAll(os.Stdin)
} else {
b, err = os.ReadFile(file)
}
if err != nil {
return err
}
source = string(b)
} else if ctx.NArg() == 1 {
source = ctx.Args().First()
@ -72,7 +77,9 @@ func enrdump(ctx *cli.Context) error {
if err != nil {
return fmt.Errorf("INVALID: %v", err)
}
dumpRecord(os.Stdout, r)
return nil
}
@ -85,6 +92,7 @@ func dumpRecord(out io.Writer, r *enr.Record) {
fmt.Fprintf(out, "Node ID: %v\n", n.ID())
dumpNodeURL(out, n)
}
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.Fprint(out, dumpRecordKV(kv, 2))
@ -95,13 +103,16 @@ func dumpNodeURL(out io.Writer, n *enode.Node) {
if n.Load(&key) != nil {
return // no secp256k1 public key
}
fmt.Fprintf(out, "URLv4: %s\n", n.URLv4())
}
func dumpRecordKV(kv []interface{}, indent int) string {
// Determine the longest key name for alignment.
var out string
var longestKey = 0
for i := 0; i < len(kv); i += 2 {
key := kv[i].(string)
if len(key) > longestKey {
@ -114,10 +125,12 @@ func dumpRecordKV(kv []interface{}, indent int) string {
val := kv[i+1].(rlp.RawValue)
pad := longestKey - len(key)
out += strings.Repeat(" ", indent) + strconv.Quote(key) + strings.Repeat(" ", pad+1)
formatter := attrFormatters[key]
if formatter == nil {
formatter = formatAttrRaw
}
fmtval, ok := formatter(val)
if ok {
out += fmtval + "\n"
@ -125,6 +138,7 @@ func dumpRecordKV(kv []interface{}, indent int) string {
out += hex.EncodeToString(val) + " (!)\n"
}
}
return out
}
@ -133,10 +147,12 @@ func parseNode(source string) (*enode.Node, error) {
if strings.HasPrefix(source, "enode://") {
return enode.ParseV4(source)
}
r, err := parseRecord(source)
if err != nil {
return nil, err
}
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 {
bin = d
}
var r enr.Record
err := rlp.DecodeBytes(bin, &r)
return &r, err
}
@ -157,8 +175,10 @@ func decodeRecordHex(b []byte) ([]byte, bool) {
if bytes.HasPrefix(b, []byte("0x")) {
b = b[2:]
}
dec := make([]byte, hex.DecodedLen(len(b)))
_, err := hex.Decode(dec, b)
return dec, err == nil
}
@ -166,8 +186,10 @@ func decodeRecordBase64(b []byte) ([]byte, bool) {
if bytes.HasPrefix(b, []byte("enr:")) {
b = b[4:]
}
dec := make([]byte, base64.RawURLEncoding.DecodedLen(len(b)))
n, err := base64.RawURLEncoding.Decode(dec, b)
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 {
return "", false
}
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 {
return "", false
}
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()] {
sum.Add(sum, block.Difficulty())
}
return sum
}
@ -61,9 +62,11 @@ func (c *Chain) TotalDifficultyAt(height int) *big.Int {
if height >= c.Len() {
return sum
}
for _, block := range c.blocks[:height+1] {
sum.Add(sum, block.Difficulty())
}
return sum
}
@ -71,6 +74,7 @@ func (c *Chain) RootAt(height int) common.Hash {
if height < c.Len() {
return c.blocks[height].Root()
}
return common.Hash{}
}
@ -85,6 +89,7 @@ func (c *Chain) Shorten(height int) *Chain {
copy(blocks, c.blocks[:height])
config := *c.chainConfig
return &Chain{
blocks: blocks,
chainConfig: &config,
@ -103,6 +108,7 @@ func (c *Chain) GetHeaders(req *GetBlockHeaders) ([]*types.Header, error) {
}
headers := make([]*types.Header, req.Amount)
var blockNumber uint64
// 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()
}
}
if headers[0] == nil {
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}
return c, nil
}
@ -157,10 +165,12 @@ func loadGenesis(genesisFile string) (core.Genesis, error) {
if err != nil {
return core.Genesis{}, err
}
var gen core.Genesis
if err := json.Unmarshal(chainConfig, &gen); err != nil {
return core.Genesis{}, err
}
return gen, nil
}
@ -170,16 +180,21 @@ func blocksFromFile(chainfile string, gblock *types.Block) ([]*types.Block, erro
if err != nil {
return nil, err
}
defer fh.Close()
var reader io.Reader = fh
if strings.HasSuffix(chainfile, ".gz") {
if reader, err = gzip.NewReader(reader); err != nil {
return nil, err
}
}
stream := rlp.NewStream(reader, 0)
var blocks = make([]*types.Block, 1)
blocks[0] = gblock
for i := 0; ; i++ {
var b types.Block
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 {
return nil, fmt.Errorf("at block index %d: %v", i, err)
}
if b.NumberU64() != uint64(i+1) {
return nil, fmt.Errorf("block at index %d has wrong number %d", i, b.NumberU64())
}
blocks = append(blocks, &b)
}
return blocks, nil
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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