123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- // Copyright 2016 The go-ethereum Authors
- // This file is part of the go-ethereum library.
- //
- // The go-ethereum library is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Lesser General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // The go-ethereum library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
- package abi
- import (
- "bytes"
- "encoding/hex"
- "encoding/json"
- "math/big"
- "reflect"
- "strings"
- "testing"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- )
- var jsonEventTransfer = []byte(`{
- "anonymous": false,
- "inputs": [
- {
- "indexed": true, "name": "from", "type": "address"
- }, {
- "indexed": true, "name": "to", "type": "address"
- }, {
- "indexed": false, "name": "value", "type": "uint256"
- }],
- "name": "Transfer",
- "type": "event"
- }`)
- var jsonEventPledge = []byte(`{
- "anonymous": false,
- "inputs": [{
- "indexed": false, "name": "who", "type": "address"
- }, {
- "indexed": false, "name": "wad", "type": "uint128"
- }, {
- "indexed": false, "name": "currency", "type": "bytes3"
- }],
- "name": "Pledge",
- "type": "event"
- }`)
- var jsonEventMixedCase = []byte(`{
- "anonymous": false,
- "inputs": [{
- "indexed": false, "name": "value", "type": "uint256"
- }, {
- "indexed": false, "name": "_value", "type": "uint256"
- }, {
- "indexed": false, "name": "Value", "type": "uint256"
- }],
- "name": "MixedCase",
- "type": "event"
- }`)
- // 1000000
- var transferData1 = "00000000000000000000000000000000000000000000000000000000000f4240"
- // "0x00Ce0d46d924CC8437c806721496599FC3FFA268", 2218516807680, "usd"
- var pledgeData1 = "00000000000000000000000000ce0d46d924cc8437c806721496599fc3ffa2680000000000000000000000000000000000000000000000000000020489e800007573640000000000000000000000000000000000000000000000000000000000"
- // 1000000,2218516807680,1000001
- var mixedCaseData1 = "00000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000020489e8000000000000000000000000000000000000000000000000000000000000000f4241"
- func TestEventId(t *testing.T) {
- var table = []struct {
- definition string
- expectations map[string]common.Hash
- }{
- {
- definition: `[
- { "type" : "event", "name" : "balance", "inputs": [{ "name" : "in", "type": "uint256" }] },
- { "type" : "event", "name" : "check", "inputs": [{ "name" : "t", "type": "address" }, { "name": "b", "type": "uint256" }] }
- ]`,
- expectations: map[string]common.Hash{
- "balance": crypto.Keccak256Hash([]byte("balance(uint256)")),
- "check": crypto.Keccak256Hash([]byte("check(address,uint256)")),
- },
- },
- }
- for _, test := range table {
- abi, err := JSON(strings.NewReader(test.definition))
- if err != nil {
- t.Fatal(err)
- }
- for name, event := range abi.Events {
- if event.Id() != test.expectations[name] {
- t.Errorf("expected id to be %x, got %x", test.expectations[name], event.Id())
- }
- }
- }
- }
- // TestEventMultiValueWithArrayUnpack verifies that array fields will be counted after parsing array.
- func TestEventMultiValueWithArrayUnpack(t *testing.T) {
- definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
- type testStruct struct {
- Value1 [2]uint8
- Value2 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)))
- }
- var rst testStruct
- require.NoError(t, abi.Unpack(&rst, "test", b.Bytes()))
- require.Equal(t, [2]uint8{1, 2}, rst.Value1)
- require.Equal(t, uint8(3), rst.Value2)
- }
- func TestEventTupleUnpack(t *testing.T) {
- type EventTransfer struct {
- Value *big.Int
- }
- type EventTransferWithTag struct {
- // this is valid because `value` is not exportable,
- // so value is only unmarshalled into `Value1`.
- value *big.Int
- Value1 *big.Int `abi:"value"`
- }
- type BadEventTransferWithSameFieldAndTag struct {
- Value *big.Int
- Value1 *big.Int `abi:"value"`
- }
- type BadEventTransferWithDuplicatedTag struct {
- Value1 *big.Int `abi:"value"`
- Value2 *big.Int `abi:"value"`
- }
- type BadEventTransferWithEmptyTag struct {
- Value *big.Int `abi:""`
- }
- type EventPledge struct {
- Who common.Address
- Wad *big.Int
- Currency [3]byte
- }
- type BadEventPledge struct {
- Who string
- Wad int
- Currency [3]byte
- }
- type EventMixedCase struct {
- Value1 *big.Int `abi:"value"`
- Value2 *big.Int `abi:"_value"`
- Value3 *big.Int `abi:"Value"`
- }
- bigint := new(big.Int)
- bigintExpected := big.NewInt(1000000)
- bigintExpected2 := big.NewInt(2218516807680)
- bigintExpected3 := big.NewInt(1000001)
- addr := common.HexToAddress("0x00Ce0d46d924CC8437c806721496599FC3FFA268")
- var testCases = []struct {
- data string
- dest interface{}
- expected interface{}
- jsonLog []byte
- error string
- name string
- }{{
- transferData1,
- &EventTransfer{},
- &EventTransfer{Value: bigintExpected},
- jsonEventTransfer,
- "",
- "Can unpack ERC20 Transfer event into structure",
- }, {
- transferData1,
- &[]interface{}{&bigint},
- &[]interface{}{&bigintExpected},
- jsonEventTransfer,
- "",
- "Can unpack ERC20 Transfer event into slice",
- }, {
- transferData1,
- &EventTransferWithTag{},
- &EventTransferWithTag{Value1: bigintExpected},
- jsonEventTransfer,
- "",
- "Can unpack ERC20 Transfer event into structure with abi: tag",
- }, {
- transferData1,
- &BadEventTransferWithDuplicatedTag{},
- &BadEventTransferWithDuplicatedTag{},
- jsonEventTransfer,
- "struct: abi tag in 'Value2' already mapped",
- "Can not unpack ERC20 Transfer event with duplicated abi tag",
- }, {
- transferData1,
- &BadEventTransferWithSameFieldAndTag{},
- &BadEventTransferWithSameFieldAndTag{},
- jsonEventTransfer,
- "abi: multiple variables maps to the same abi field 'value'",
- "Can not unpack ERC20 Transfer event with a field and a tag mapping to the same abi variable",
- }, {
- transferData1,
- &BadEventTransferWithEmptyTag{},
- &BadEventTransferWithEmptyTag{},
- jsonEventTransfer,
- "struct: abi tag in 'Value' is empty",
- "Can not unpack ERC20 Transfer event with an empty tag",
- }, {
- pledgeData1,
- &EventPledge{},
- &EventPledge{
- addr,
- bigintExpected2,
- [3]byte{'u', 's', 'd'}},
- jsonEventPledge,
- "",
- "Can unpack Pledge event into structure",
- }, {
- pledgeData1,
- &[]interface{}{&common.Address{}, &bigint, &[3]byte{}},
- &[]interface{}{
- &addr,
- &bigintExpected2,
- &[3]byte{'u', 's', 'd'}},
- jsonEventPledge,
- "",
- "Can unpack Pledge event into slice",
- }, {
- pledgeData1,
- &[3]interface{}{&common.Address{}, &bigint, &[3]byte{}},
- &[3]interface{}{
- &addr,
- &bigintExpected2,
- &[3]byte{'u', 's', 'd'}},
- jsonEventPledge,
- "",
- "Can unpack Pledge event into an array",
- }, {
- pledgeData1,
- &[]interface{}{new(int), 0, 0},
- &[]interface{}{},
- jsonEventPledge,
- "abi: cannot unmarshal common.Address in to int",
- "Can not unpack Pledge event into slice with wrong types",
- }, {
- pledgeData1,
- &BadEventPledge{},
- &BadEventPledge{},
- jsonEventPledge,
- "abi: cannot unmarshal common.Address in to string",
- "Can not unpack Pledge event into struct with wrong filed types",
- }, {
- pledgeData1,
- &[]interface{}{common.Address{}, new(big.Int)},
- &[]interface{}{},
- jsonEventPledge,
- "abi: insufficient number of elements in the list/array for unpack, want 3, got 2",
- "Can not unpack Pledge event into too short slice",
- }, {
- pledgeData1,
- new(map[string]interface{}),
- &[]interface{}{},
- jsonEventPledge,
- "abi: cannot unmarshal tuple into map[string]interface {}",
- "Can not unpack Pledge event into map",
- }, {
- mixedCaseData1,
- &EventMixedCase{},
- &EventMixedCase{Value1: bigintExpected, Value2: bigintExpected2, Value3: bigintExpected3},
- jsonEventMixedCase,
- "",
- "Can unpack abi variables with mixed case",
- }}
- for _, tc := range testCases {
- assert := assert.New(t)
- tc := tc
- t.Run(tc.name, func(t *testing.T) {
- err := unpackTestEventData(tc.dest, tc.data, tc.jsonLog, assert)
- if tc.error == "" {
- assert.Nil(err, "Should be able to unpack event data.")
- assert.Equal(tc.expected, tc.dest, tc.name)
- } else {
- assert.EqualError(err, tc.error, tc.name)
- }
- })
- }
- }
- 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.Unpack(dest, "e", data)
- }
- /*
- Taken from
- https://github.com/ethereum/go-ethereum/pull/15568
- */
- type testResult struct {
- Values [2]*big.Int
- Value1 *big.Int
- Value2 *big.Int
- }
- type testCase struct {
- definition string
- want testResult
- }
- func (tc testCase) encoded(intType, arrayType Type) []byte {
- var b bytes.Buffer
- if tc.want.Value1 != nil {
- val, _ := intType.pack(reflect.ValueOf(tc.want.Value1))
- b.Write(val)
- }
- if !reflect.DeepEqual(tc.want.Values, [2]*big.Int{nil, nil}) {
- val, _ := arrayType.pack(reflect.ValueOf(tc.want.Values))
- b.Write(val)
- }
- if tc.want.Value2 != nil {
- val, _ := intType.pack(reflect.ValueOf(tc.want.Value2))
- b.Write(val)
- }
- return b.Bytes()
- }
- // 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
- 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.Unpack(&rst, "test", b.Bytes()))
- require.Equal(t, uint8(0), rst.Value1)
- require.Equal(t, uint8(8), rst.Value2)
- }
- // TestEventIndexedWithArrayUnpack verifies that decoder will not overlow 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
- 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)))
- b.Write(packNum(reflect.ValueOf(len(stringOut))))
- b.Write(common.RightPadBytes([]byte(stringOut), 32))
- var rst testStruct
- require.NoError(t, abi.Unpack(&rst, "test", b.Bytes()))
- require.Equal(t, [2]uint8{0, 0}, rst.Value1)
- require.Equal(t, stringOut, rst.Value2)
- }
|