mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 09:53:48 +00:00
cmd/signer: methods to validate calldata against abi
This commit is contained in:
parent
b8b84fec75
commit
08301bbcc0
2 changed files with 186 additions and 0 deletions
107
cmd/signer/abihelper.go
Normal file
107
cmd/signer/abihelper.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright 2017 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"strings"
|
||||
"bytes"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
type decodedArgument struct {
|
||||
soltype string
|
||||
value interface{}
|
||||
}
|
||||
type decodedCallData struct {
|
||||
signature string
|
||||
name string
|
||||
inputs []decodedArgument
|
||||
}
|
||||
|
||||
func (arg decodedArgument) String() string {
|
||||
return fmt.Sprintf("%v: %v", arg.soltype, arg.value)
|
||||
}
|
||||
func (cd decodedCallData) String() string {
|
||||
args := make([]string, len(cd.inputs))
|
||||
for i, arg := range cd.inputs {
|
||||
args[i] = arg.String()
|
||||
}
|
||||
return fmt.Sprintf("%s(%s)", cd.name, strings.Join(args, ","))
|
||||
}
|
||||
|
||||
// parseCallData matches the provided call data against the abi definition,
|
||||
// and returns a struct containing the actual go-typed values
|
||||
func parseCallData(calldata []byte, abidata string) (*decodedCallData, error) {
|
||||
|
||||
if len(calldata) < 4 {
|
||||
return nil, fmt.Errorf("Invalid ABI-data, incomplete method signature of (%d bytes)", len(calldata))
|
||||
}
|
||||
|
||||
abispec, err := abi.JSON(strings.NewReader(abidata))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed parsing JSON ABI: %v", err)
|
||||
}
|
||||
|
||||
sigdata, argdata := calldata[:4], calldata[4:]
|
||||
if len(argdata)%32 != 0 {
|
||||
return nil, fmt.Errorf("Not ABI-encoded data; length should be a multiple of 32 (was %d)", len(argdata))
|
||||
}
|
||||
|
||||
method := abispec.MethodById(sigdata)
|
||||
if method == nil {
|
||||
return nil, fmt.Errorf("Supplied ABI spec does not contain method signature in data: 0x%x", sigdata)
|
||||
}
|
||||
|
||||
decoded := decodedCallData{signature: method.Sig(), name: method.Name}
|
||||
|
||||
for n, argument := range method.Inputs {
|
||||
value, err := abi.ToGoType(n*32, argument.Type, argdata)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to decode argument %d (signature %v): %v", n, method.Sig(), err)
|
||||
} else {
|
||||
decodedArg := decodedArgument{
|
||||
soltype: argument.Type.String(),
|
||||
value: value,
|
||||
}
|
||||
decoded.inputs = append(decoded.inputs, decodedArg)
|
||||
}
|
||||
}
|
||||
|
||||
// We're finished decoding the data. At this point, we encode the decoded data to see if it matches with the
|
||||
// original data. If we didn't do that, it would e.g. be possible to stuff extra data into the arguments, which
|
||||
// is not detected by merely decoding the data.
|
||||
|
||||
var (
|
||||
gotypedArguments = make([]interface{}, len(decoded.inputs))
|
||||
encoded []byte
|
||||
)
|
||||
for i, arg := range decoded.inputs {
|
||||
gotypedArguments[i] = arg.value
|
||||
}
|
||||
encoded, err = abispec.Pack(method.Name, gotypedArguments...)
|
||||
|
||||
if !bytes.Equal(encoded, calldata){
|
||||
exp := common.Bytes2Hex(encoded)
|
||||
was := common.Bytes2Hex(calldata)
|
||||
return nil, fmt.Errorf("WARNING: Supplied data is stuffed with extra data. %v \nWant %s\nHave %s", decoded,was, exp)
|
||||
}
|
||||
|
||||
return &decoded, nil
|
||||
}
|
||||
79
cmd/signer/abihelper_test.go
Normal file
79
cmd/signer/abihelper_test.go
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2017 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMartin(t *testing.T) {
|
||||
|
||||
// send(uint256) : a52c101e
|
||||
// compareAndApprove(address,uint256,uint256) : 751e1079
|
||||
// issue(address[],uint256) : 42958b54
|
||||
jsondata := `
|
||||
[
|
||||
{"type":"function","name":"send","inputs":[{"name":"a","type":"uint256"}]},
|
||||
{"type":"function","name":"compareAndApprove","inputs":[{"name":"a","type":"address"},{"name":"a","type":"uint256"},{"name":"a","type":"uint256"}]},
|
||||
{"type":"function","name":"issue","inputs":[{"name":"a","type":"address[]"},{"name":"a","type":"uint256"}]},
|
||||
{"type":"function","name":"sam","inputs":[{"name":"a","type":"bytes"},{"name":"a","type":"bool"},{"name":"a","type":"uint256[]"}]}
|
||||
]`
|
||||
//Expected failures
|
||||
for _, hexdata := range []string{
|
||||
"a52c101e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000042",
|
||||
"a52c101e000000000000000000000000000000000000000000000000000000000000001200",
|
||||
"a52c101e00000000000000000000000000000000000000000000000000000000000000",
|
||||
"a52c101e",
|
||||
"a52c10",
|
||||
"",
|
||||
"751e10790000000000000000000000000000000000000000000000000000000000000012",
|
||||
"751e1079FFffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"deadbeef00000000000000000000000000000000000000000000000000000000000000",
|
||||
"42958b5400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000042",
|
||||
"a52c101e00ff0000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000042",
|
||||
} {
|
||||
_, err := parseCallData(common.Hex2Bytes(hexdata), jsondata)
|
||||
if err == nil {
|
||||
t.Errorf("Expected decoding to fail: %s", hexdata)
|
||||
}
|
||||
}
|
||||
|
||||
//Expected success
|
||||
for _, hexdata := range []string{
|
||||
// From https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
|
||||
"a5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
|
||||
"a52c101e0000000000000000000000000000000000000000000000000000000000000012",
|
||||
"a52c101eFFffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"751e1079000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"42958b54" +
|
||||
// start of dynamic type
|
||||
"0000000000000000000000000000000000000000000000000000000000000040" +
|
||||
//uint256
|
||||
"0000000000000000000000000000000000000000000000000000000000000001" +
|
||||
// length of array
|
||||
"0000000000000000000000000000000000000000000000000000000000000002" +
|
||||
// array values
|
||||
"000000000000000000000000000000000000000000000000000000000000dead" +
|
||||
"000000000000000000000000000000000000000000000000000000000000beef",
|
||||
} {
|
||||
_, err := parseCallData(common.Hex2Bytes(hexdata), jsondata)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected failure on input %s:\n %v (%d bytes) ", hexdata, err, len(common.Hex2Bytes(hexdata)))
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue