diff --git a/rpc/api.go b/rpc/api.go index 76fa9b9df6..534b6fc5d4 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -471,42 +471,29 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err func toFilterOptions(options *BlockFilterArgs) *core.FilterOptions { var opts core.FilterOptions - // Convert optional address slice/string to byte slice - if str, ok := options.Address.(string); ok { - opts.Address = []common.Address{common.HexToAddress(str)} - } else if slice, ok := options.Address.([]interface{}); ok { - bslice := make([]common.Address, len(slice)) - for i, addr := range slice { - if saddr, ok := addr.(string); ok { - bslice[i] = common.HexToAddress(saddr) - } - } - opts.Address = bslice - } + opts.Address = cAddress(options.Address) + opts.Topics = cTopics(options.Topics) opts.Earliest = options.Earliest opts.Latest = options.Latest - topics := make([][]common.Hash, len(options.Topics)) - for i, topicDat := range options.Topics { - if slice, ok := topicDat.([]interface{}); ok { - topics[i] = make([]common.Hash, len(slice)) - for j, topic := range slice { - topics[i][j] = common.HexToHash(topic.(string)) - } - } else if str, ok := topicDat.(string); ok { - topics[i] = []common.Hash{common.HexToHash(str)} - } - } - opts.Topics = topics - return &opts } -/* - Work() chan<- *types.Block - SetWorkCh(chan<- Work) - Stop() - Start() - Rate() uint64 -*/ +func cAddress(a []string) []common.Address { + bslice := make([]common.Address, len(a)) + for i, addr := range a { + bslice[i] = common.HexToAddress(addr) + } + return bslice +} + +func cTopics(t [][]string) [][]common.Hash { + topics := make([][]common.Hash, len(t)) + for i, iv := range t { + for j, jv := range iv { + topics[i][j] = common.HexToHash(jv) + } + } + return topics +} diff --git a/rpc/args.go b/rpc/args.go index 78cbca5a98..a075f1a596 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -4,12 +4,17 @@ import ( "bytes" "encoding/json" // "errors" - // "fmt" + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" ) +const ( + defaultLogLimit = 100 + defaultLogOffset = 0 +) + func blockHeightFromJson(msg json.RawMessage, number *int64) error { var raw interface{} if err := json.Unmarshal(msg, &raw); err != nil { @@ -483,20 +488,20 @@ func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) { type BlockFilterArgs struct { Earliest int64 Latest int64 - Address interface{} - Topics []interface{} + Address []string + Topics [][]string Skip int Max int } func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) { var obj []struct { - FromBlock interface{} `json:"fromBlock"` - ToBlock interface{} `json:"toBlock"` - Limit interface{} `json:"limit"` - Offset interface{} `json:"offset"` - Address string `json:"address"` - Topics []interface{} `json:"topics"` + FromBlock interface{} `json:"fromBlock"` + ToBlock interface{} `json:"toBlock"` + Limit interface{} `json:"limit"` + Offset interface{} `json:"offset"` + Address interface{} `json:"address"` + Topics interface{} `json:"topics"` } if err = json.Unmarshal(b, &obj); err != nil { @@ -516,33 +521,104 @@ func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) { // return NewDecodeParamError(fmt.Sprintf("ToBlock %v", err)) var num int64 - if err := blockHeight(obj[0].FromBlock, &num); err != nil { - return err + + // if blank then latest + if obj[0].FromBlock == nil { + num = -1 + } else { + if err := blockHeight(obj[0].FromBlock, &num); err != nil { + return err + } } + // if -2 or other "silly" number, use latest if num < 0 { args.Earliest = -1 //latest block } else { args.Earliest = num } - if err := blockHeight(obj[0].ToBlock, &num); err != nil { - return err + // if blank than latest + if obj[0].ToBlock == nil { + num = -1 + } else { + if err := blockHeight(obj[0].ToBlock, &num); err != nil { + return err + } } args.Latest = num - if err := numString(obj[0].Limit, &num); err != nil { - return err + if obj[0].Limit == nil { + num = defaultLogLimit + } else { + if err := numString(obj[0].Limit, &num); err != nil { + return err + } } args.Max = int(num) - if err := numString(obj[0].Offset, &num); err != nil { - return err - + if obj[0].Offset == nil { + num = defaultLogOffset + } else { + if err := numString(obj[0].Offset, &num); err != nil { + return err + } } args.Skip = int(num) - args.Address = obj[0].Address - args.Topics = obj[0].Topics + if obj[0].Address != nil { + marg, ok := obj[0].Address.([]interface{}) + if ok { + v := make([]string, len(marg)) + for i, arg := range marg { + argstr, ok := arg.(string) + if !ok { + return NewInvalidTypeError(fmt.Sprintf("address[%d]", i), "is not a string") + } + v[i] = argstr + } + args.Address = v + } else { + argstr, ok := obj[0].Address.(string) + if ok { + v := make([]string, 1) + v[0] = argstr + args.Address = v + } else { + return NewInvalidTypeError("address", "is not a string or array") + } + } + } + + if obj[0].Topics != nil { + other, ok := obj[0].Topics.([]interface{}) + if ok { + topicdbl := make([][]string, len(other)) + for i, iv := range other { + if argstr, ok := iv.(string); ok { + // Found a string, push into first element of array + topicsgl := make([]string, 1) + topicsgl[0] = argstr + topicdbl[i] = topicsgl + } else if argarray, ok := iv.([]interface{}); ok { + // Found an array of other + topicdbl[i] = make([]string, len(argarray)) + for j, jv := range argarray { + if v, ok := jv.(string); ok { + topicdbl[i][j] = v + } else { + return NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", i, j), "is not a string") + } + } + } else { + return NewInvalidTypeError(fmt.Sprintf("topic[%d]", i), "not a string or array") + } + } + args.Topics = topicdbl + return nil + } else { + return NewInvalidTypeError("topic", "is not a string or array") + } + } return nil } diff --git a/rpc/args_test.go b/rpc/args_test.go index cb1d1904b3..602631b677 100644 --- a/rpc/args_test.go +++ b/rpc/args_test.go @@ -804,14 +804,23 @@ func TestBlockFilterArgs(t *testing.T) { "limit": "0x3", "offset": "0x0", "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", - "topics": ["0x12341234"]}]` + "topics": + [ + ["0xAA", "0xBB"], + ["0xCC", "0xDD"] + ] + }]` + expected := new(BlockFilterArgs) expected.Earliest = 1 expected.Latest = 2 expected.Max = 3 expected.Skip = 0 - expected.Address = "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8" - // expected.Topics = []string{"0x12341234"} + expected.Address = []string{"0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"} + expected.Topics = [][]string{ + []string{"0xAA", "0xBB"}, + []string{"0xCC", "0xDD"}, + } args := new(BlockFilterArgs) if err := json.Unmarshal([]byte(input), &args); err != nil { @@ -834,17 +843,73 @@ func TestBlockFilterArgs(t *testing.T) { t.Errorf("Skip shoud be %#v but is %#v", expected.Skip, args.Skip) } - if expected.Address != args.Address { + if expected.Address[0] != args.Address[0] { t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) } - // if expected.Topics != args.Topics { - // t.Errorf("Topic shoud be %#v but is %#v", expected.Topic, args.Topic) - // } + if expected.Topics[0][0] != args.Topics[0][0] { + t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + } + if expected.Topics[0][1] != args.Topics[0][1] { + t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + } + if expected.Topics[1][0] != args.Topics[1][0] { + t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + } + if expected.Topics[1][1] != args.Topics[1][1] { + t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + } + +} + +func TestBlockFilterArgsDefaults(t *testing.T) { + input := `[{ + "address": ["0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"], + "topics": ["0xAA","0xBB"] + }]` + expected := new(BlockFilterArgs) + expected.Earliest = -1 + expected.Latest = -1 + expected.Max = 100 + expected.Skip = 0 + expected.Address = []string{"0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"} + expected.Topics = [][]string{[]string{"0xAA"}, []string{"0xBB"}} + + args := new(BlockFilterArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + } + + if expected.Earliest != args.Earliest { + t.Errorf("Earliest shoud be %#v but is %#v", expected.Earliest, args.Earliest) + } + + if expected.Latest != args.Latest { + t.Errorf("Latest shoud be %#v but is %#v", expected.Latest, args.Latest) + } + + if expected.Max != args.Max { + t.Errorf("Max shoud be %#v but is %#v", expected.Max, args.Max) + } + + if expected.Skip != args.Skip { + t.Errorf("Skip shoud be %#v but is %#v", expected.Skip, args.Skip) + } + + if expected.Address[0] != args.Address[0] { + t.Errorf("Address shoud be %#v but is %#v", expected.Address, args.Address) + } + + if expected.Topics[0][0] != args.Topics[0][0] { + t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + } + + if expected.Topics[1][0] != args.Topics[1][0] { + t.Errorf("Topics shoud be %#v but is %#v", expected.Topics, args.Topics) + } } func TestBlockFilterArgsWords(t *testing.T) { - t.Skip() input := `[{ "fromBlock": "latest", "toBlock": "pending" @@ -867,10 +932,33 @@ func TestBlockFilterArgsWords(t *testing.T) { } } -func TestBlockFilterArgsBool(t *testing.T) { +func TestBlockFilterArgsInvalid(t *testing.T) { + input := `{}` + + args := new(BlockFilterArgs) + str := ExpectDecodeParamError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsFromBool(t *testing.T) { input := `[{ "fromBlock": true, - "toBlock": false + "toBlock": "pending" + }]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsToBool(t *testing.T) { + input := `[{ + "fromBlock": "pending", + "toBlock": true }]` args := new(BlockFilterArgs) @@ -890,6 +978,112 @@ func TestBlockFilterArgsEmptyArgs(t *testing.T) { } } +func TestBlockFilterArgsLimitInvalid(t *testing.T) { + input := `[{"limit": false}]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsOffsetInvalid(t *testing.T) { + input := `[{"offset": true}]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsAddressInt(t *testing.T) { + input := `[{ + "address": 1, + "topics": "0x12341234"}]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsAddressSliceInt(t *testing.T) { + input := `[{ + "address": [1], + "topics": "0x12341234"}]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsTopicInt(t *testing.T) { + input := `[{ + "address": ["0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8"], + "topics": 1}]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsTopicSliceInt(t *testing.T) { + input := `[{ + "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", + "topics": [1]}]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsTopicSliceInt2(t *testing.T) { + input := `[{ + "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", + "topics": ["0xAA", [1]]}]` + + args := new(BlockFilterArgs) + str := ExpectInvalidTypeError(json.Unmarshal([]byte(input), &args)) + if len(str) > 0 { + t.Error(str) + } +} + +func TestBlockFilterArgsTopicComplex(t *testing.T) { + input := `[{ + "address": "0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8", + "topics": ["0xAA", ["0xBB", "0xCC"]] + }]` + + args := new(BlockFilterArgs) + if err := json.Unmarshal([]byte(input), &args); err != nil { + t.Error(err) + fmt.Printf("%v\n", args) + return + } + + if args.Topics[0][0] != "0xAA" { + t.Errorf("Topic should be %s but is %s", "0xAA", args.Topics[0][0]) + } + + if args.Topics[1][0] != "0xBB" { + t.Errorf("Topic should be %s but is %s", "0xBB", args.Topics[0][0]) + } + + if args.Topics[1][1] != "0xCC" { + t.Errorf("Topic should be %s but is %s", "0xCC", args.Topics[0][0]) + } +} + func TestDbArgs(t *testing.T) { input := `["testDB","myKey","0xbeef"]` expected := new(DbArgs)