This commit is contained in:
Yohan Graterol 2018-02-21 22:15:00 -05:00
commit 00c021e89d
No known key found for this signature in database
GPG key ID: D8C4A70CC26A1F90
12 changed files with 260 additions and 77 deletions

View file

@ -136,11 +136,11 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
// MethodById looks up a method by the 4-byte id // MethodById looks up a method by the 4-byte id
// returns nil if none found // returns nil if none found
func (abi *ABI) MethodById(sigdata []byte) *Method { func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
for _, method := range abi.Methods { for _, method := range abi.Methods {
if bytes.Equal(method.Id(), sigdata[:4]) { if bytes.Equal(method.Id(), sigdata[:4]) {
return &method return &method, nil
} }
} }
return nil return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
} }

View file

@ -689,7 +689,11 @@ func TestABI_MethodById(t *testing.T) {
} }
for name, m := range abi.Methods { for name, m := range abi.Methods {
a := fmt.Sprintf("%v", m) a := fmt.Sprintf("%v", m)
b := fmt.Sprintf("%v", abi.MethodById(m.Id())) 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 { if a != b {
t.Errorf("Method %v (id %v) not 'findable' by id in ABI", name, common.ToHex(m.Id())) t.Errorf("Method %v (id %v) not 'findable' by id in ABI", name, common.ToHex(m.Id()))
} }

View file

@ -67,6 +67,17 @@ func (arguments Arguments) LengthNonIndexed() int {
return out return out
} }
// 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
}
// isTuple returns true for non-atomic constructs, like (uint,uint) or uint[] // isTuple returns true for non-atomic constructs, like (uint,uint) or uint[]
func (arguments Arguments) isTuple() bool { func (arguments Arguments) isTuple() bool {
return len(arguments) > 1 return len(arguments) > 1
@ -74,21 +85,25 @@ func (arguments Arguments) isTuple() bool {
// Unpack performs the operation hexdata -> Go format // Unpack performs the operation hexdata -> Go format
func (arguments Arguments) Unpack(v interface{}, data []byte) error { func (arguments Arguments) Unpack(v interface{}, data []byte) error {
if arguments.isTuple() {
return arguments.unpackTuple(v, data)
}
return arguments.unpackAtomic(v, data)
}
func (arguments Arguments) unpackTuple(v interface{}, output []byte) error {
// make sure the passed value is arguments pointer // make sure the passed value is arguments pointer
valueOf := reflect.ValueOf(v) if reflect.Ptr != reflect.ValueOf(v).Kind() {
if reflect.Ptr != valueOf.Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v) return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
} }
marshalledValues, err := arguments.UnpackValues(data)
if err != nil {
return err
}
if arguments.isTuple() {
return arguments.unpackTuple(v, marshalledValues)
}
return arguments.unpackAtomic(v, marshalledValues)
}
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
var ( var (
value = valueOf.Elem() value = reflect.ValueOf(v).Elem()
typ = value.Type() typ = value.Type()
kind = value.Kind() kind = value.Kind()
) )
@ -110,30 +125,9 @@ func (arguments Arguments) unpackTuple(v interface{}, output []byte) error {
exists[field] = true exists[field] = true
} }
} }
// `i` counts the nonindexed arguments. for i, arg := range arguments.NonIndexed() {
// `j` counts the number of complex types.
// both `i` and `j` are used to to correctly compute `data` offset.
i, j := -1, 0 reflectValue := reflect.ValueOf(marshalledValues[i])
for _, arg := range arguments {
if arg.Indexed {
// can't read, continue
continue
}
i++
marshalledValue, err := toGoType((i+j)*32, arg.Type, output)
if err != nil {
return err
}
if arg.Type.T == ArrayTy {
// combined index ('i' + 'j') need to be adjusted only by size of array, thus
// we need to decrement 'j' because 'i' was incremented
j += arg.Type.Size - 1
}
reflectValue := reflect.ValueOf(marshalledValue)
switch kind { switch kind {
case reflect.Struct: case reflect.Struct:
@ -166,34 +160,52 @@ func (arguments Arguments) unpackTuple(v interface{}, output []byte) error {
} }
// unpackAtomic unpacks ( hexdata -> go ) a single value // unpackAtomic unpacks ( hexdata -> go ) a single value
func (arguments Arguments) unpackAtomic(v interface{}, output []byte) error { func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues []interface{}) error {
// make sure the passed value is arguments pointer if len(marshalledValues) != 1 {
valueOf := reflect.ValueOf(v) return fmt.Errorf("abi: wrong length, expected single value, got %d", len(marshalledValues))
if reflect.Ptr != valueOf.Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
} }
arg := arguments[0] elem := reflect.ValueOf(v).Elem()
if arg.Indexed { reflectValue := reflect.ValueOf(marshalledValues[0])
return fmt.Errorf("abi: attempting to unpack indexed variable into element.") return set(elem, reflectValue, arguments.NonIndexed()[0])
}
value := valueOf.Elem()
marshalledValue, err := toGoType(0, arg.Type, output)
if err != nil {
return err
}
return set(value, reflect.ValueOf(marshalledValue), arg)
} }
// Unpack performs the operation Go format -> Hexdata // UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
// without supplying a struct to unpack into. Instead, this method returns a list containing the
// values. An atomic argument will be a list with one element.
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
retval := make([]interface{}, 0, arguments.LengthNonIndexed())
virtualArgs := 0
for index, arg := range arguments.NonIndexed() {
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
if arg.Type.T == ArrayTy {
// If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256.
// This means that we need to add two 'virtual' arguments when
// we count the index from now on
virtualArgs += arg.Type.Size - 1
}
if err != nil {
return nil, err
}
retval = append(retval, marshalledValue)
}
return retval, nil
}
// PackValues performs the operation Go format -> Hexdata
// It is the semantic opposite of UnpackValues
func (arguments Arguments) PackValues(args []interface{}) ([]byte, error) {
return arguments.Pack(args...)
}
// Pack performs the operation Go format -> Hexdata
func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) { func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
// Make sure arguments match up and pack them // Make sure arguments match up and pack them
abiArgs := arguments abiArgs := arguments
if len(args) != len(abiArgs) { if len(args) != len(abiArgs) {
return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(abiArgs)) return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(abiArgs))
} }
// variable input is the output appended at the end of packed // variable input is the output appended at the end of packed
// output. This is used for strings and bytes types input. // output. This is used for strings and bytes types input.
var variableInput []byte var variableInput []byte
@ -207,7 +219,6 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
inputOffset += 32 inputOffset += 32
} }
} }
var ret []byte var ret []byte
for i, a := range args { for i, a := range args {
input := abiArgs[i] input := abiArgs[i]
@ -216,7 +227,6 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// check for a slice type (string, bytes, slice) // check for a slice type (string, bytes, slice)
if input.Type.requiresLengthPrefix() { if input.Type.requiresLengthPrefix() {
// calculate the offset // calculate the offset

View file

@ -95,6 +95,9 @@ func readFixedBytes(t Type, word []byte) (interface{}, error) {
// iteratively unpack elements // iteratively unpack elements
func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) { 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) { if start+32*size > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size) return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
} }
@ -181,16 +184,32 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
// interprets a 32 byte slice as an offset and then determines which indice to look to decode the type. // interprets a 32 byte slice as an offset and then determines which indice to look to decode the type.
func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) { func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
offset := int(binary.BigEndian.Uint64(output[index+24 : index+32])) bigOffsetEnd := big.NewInt(0).SetBytes(output[index : index+32])
if offset+32 > len(output) { bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
return 0, 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32) outputLength := big.NewInt(int64(len(output)))
}
length = int(binary.BigEndian.Uint64(output[offset+24 : offset+32]))
if offset+32+length > len(output) {
return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+length)
}
start = offset + 32
//fmt.Printf("LENGTH PREFIX INFO: \nsize: %v\noffset: %v\nstart: %v\n", length, offset, start) if bigOffsetEnd.Cmp(outputLength) > 0 {
return 0, 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", bigOffsetEnd, outputLength)
}
if bigOffsetEnd.BitLen() > 63 {
return 0, 0, fmt.Errorf("abi offset larger than int64: %v", bigOffsetEnd)
}
offsetEnd := int(bigOffsetEnd.Uint64())
lengthBig := big.NewInt(0).SetBytes(output[offsetEnd-32 : offsetEnd])
totalSize := big.NewInt(0)
totalSize.Add(totalSize, bigOffsetEnd)
totalSize.Add(totalSize, lengthBig)
if totalSize.BitLen() > 63 {
return 0, 0, fmt.Errorf("abi length larger than int64: %v", totalSize)
}
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 return
} }

View file

@ -130,7 +130,7 @@ var unpackTests = []unpackTest{
{ {
def: `[{"type": "bytes32"}]`, def: `[{"type": "bytes32"}]`,
enc: "0100000000000000000000000000000000000000000000000000000000000000", enc: "0100000000000000000000000000000000000000000000000000000000000000",
want: common.HexToHash("0100000000000000000000000000000000000000000000000000000000000000"), want: [32]byte{1, 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},
}, },
{ {
def: `[{"type": "function"}]`, def: `[{"type": "function"}]`,
@ -683,3 +683,73 @@ func TestUnmarshal(t *testing.T) {
t.Fatal("expected error:", err) t.Fatal("expected error:", err)
} }
} }
func TestOOMMaliciousInput(t *testing.T) {
oomTests := []unpackTest{
{
def: `[{"type": "uint8[]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
"0000000000000000000000000000000000000000000000000000000000000003" + // num elems
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
},
{ // Length larger than 64 bits
def: `[{"type": "uint8[]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
"00ffffffffffffffffffffffffffffffffffffffffffffff0000000000000002" + // num elems
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
},
{ // Offset very large (over 64 bits)
def: `[{"type": "uint8[]"}]`,
enc: "00ffffffffffffffffffffffffffffffffffffffffffffff0000000000000020" + // offset
"0000000000000000000000000000000000000000000000000000000000000002" + // num elems
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
},
{ // Offset very large (below 64 bits)
def: `[{"type": "uint8[]"}]`,
enc: "0000000000000000000000000000000000000000000000007ffffffffff00020" + // offset
"0000000000000000000000000000000000000000000000000000000000000002" + // num elems
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
},
{ // Offset negative (as 64 bit)
def: `[{"type": "uint8[]"}]`,
enc: "000000000000000000000000000000000000000000000000f000000000000020" + // offset
"0000000000000000000000000000000000000000000000000000000000000002" + // num elems
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
},
{ // Negative length
def: `[{"type": "uint8[]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
"000000000000000000000000000000000000000000000000f000000000000002" + // num elems
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
},
{ // Very large length
def: `[{"type": "uint8[]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
"0000000000000000000000000000000000000000000000007fffffffff000002" + // num elems
"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
},
}
for i, test := range oomTests {
def := fmt.Sprintf(`[{ "name" : "method", "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

@ -22,6 +22,7 @@ import (
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"strings" "strings"
"syscall"
"github.com/EthereumCommonwealth/go-callisto/cmd/utils" "github.com/EthereumCommonwealth/go-callisto/cmd/utils"
"github.com/EthereumCommonwealth/go-callisto/console" "github.com/EthereumCommonwealth/go-callisto/console"
@ -207,7 +208,7 @@ func ephemeralConsole(ctx *cli.Context) error {
} }
// Wait for pending callbacks, but stop for Ctrl-C. // Wait for pending callbacks, but stop for Ctrl-C.
abort := make(chan os.Signal, 1) abort := make(chan os.Signal, 1)
signal.Notify(abort, os.Interrupt) signal.Notify(abort, syscall.SIGINT, syscall.SIGTERM)
go func() { go func() {
<-abort <-abort

View file

@ -25,6 +25,7 @@ import (
"os/signal" "os/signal"
"runtime" "runtime"
"strings" "strings"
"syscall"
"github.com/EthereumCommonwealth/go-callisto/core" "github.com/EthereumCommonwealth/go-callisto/core"
"github.com/EthereumCommonwealth/go-callisto/core/types" "github.com/EthereumCommonwealth/go-callisto/core/types"
@ -64,7 +65,7 @@ func StartNode(stack *node.Node) {
} }
go func() { go func() {
sigc := make(chan os.Signal, 1) sigc := make(chan os.Signal, 1)
signal.Notify(sigc, os.Interrupt) signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(sigc) defer signal.Stop(sigc)
<-sigc <-sigc
log.Info("Got interrupt, shutting down...") log.Info("Got interrupt, shutting down...")
@ -85,7 +86,7 @@ func ImportChain(chain *core.BlockChain, fn string) error {
// If a signal is received, the import will stop at the next batch. // If a signal is received, the import will stop at the next batch.
interrupt := make(chan os.Signal, 1) interrupt := make(chan os.Signal, 1)
stop := make(chan struct{}) stop := make(chan struct{})
signal.Notify(interrupt, os.Interrupt) signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(interrupt) defer signal.Stop(interrupt)
defer close(interrupt) defer close(interrupt)
go func() { go func() {

View file

@ -26,6 +26,7 @@ import (
"regexp" "regexp"
"sort" "sort"
"strings" "strings"
"syscall"
"github.com/EthereumCommonwealth/go-callisto/internal/jsre" "github.com/EthereumCommonwealth/go-callisto/internal/jsre"
"github.com/EthereumCommonwealth/go-callisto/internal/web3ext" "github.com/EthereumCommonwealth/go-callisto/internal/web3ext"
@ -332,7 +333,7 @@ func (c *Console) Interactive() {
}() }()
// Monitor Ctrl-C too in case the input is empty and we need to bail // Monitor Ctrl-C too in case the input is empty and we need to bail
abort := make(chan os.Signal, 1) abort := make(chan os.Signal, 1)
signal.Notify(abort, os.Interrupt) signal.Notify(abort, syscall.SIGINT, syscall.SIGTERM)
// Start sending prompts to the user and reading back inputs // Start sending prompts to the user and reading back inputs
for { for {

View file

@ -1137,6 +1137,18 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) { if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
return errors.New(`Both "data" and "input" are set and not equal. Please use "input" to pass transaction call data.`) return errors.New(`Both "data" and "input" are set and not equal. Please use "input" to pass transaction call data.`)
} }
if args.To == nil {
// Contract creation
var input []byte
if args.Data != nil {
input = *args.Data
} else if args.Input != nil {
input = *args.Input
}
if len(input) == 0 {
return errors.New(`contract creation without any data provided`)
}
}
return nil return nil
} }

View file

@ -154,12 +154,20 @@ func (c *BoundContract) GetDeployer() *Transaction {
// Call invokes the (constant) contract method with params as input values and // Call invokes the (constant) contract method with params as input values and
// sets the output to result. // sets the output to result.
func (c *BoundContract) Call(opts *CallOpts, out *Interfaces, method string, args *Interfaces) error { func (c *BoundContract) Call(opts *CallOpts, out *Interfaces, method string, args *Interfaces) error {
results := make([]interface{}, len(out.objects)) if len(out.objects) == 1 {
copy(results, out.objects) result := out.objects[0]
if err := c.contract.Call(&opts.opts, &results, method, args.objects...); err != nil { if err := c.contract.Call(&opts.opts, result, method, args.objects...); err != nil {
return err return err
}
out.objects[0] = result
} else {
results := make([]interface{}, len(out.objects))
copy(results, out.objects)
if err := c.contract.Call(&opts.opts, &results, method, args.objects...); err != nil {
return err
}
copy(out.objects, results)
} }
copy(out.objects, results)
return nil return nil
} }

View file

@ -154,6 +154,9 @@ func (s *dialstate) addStatic(n *discover.Node) {
func (s *dialstate) removeStatic(n *discover.Node) { func (s *dialstate) removeStatic(n *discover.Node) {
// This removes a task so future attempts to connect will not be made. // This removes a task so future attempts to connect will not be made.
delete(s.static, n.ID) delete(s.static, n.ID)
// This removes a previous dial timestamp so that application
// can force a server to reconnect with chosen peer immediately.
s.hist.remove(n.ID)
} }
func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task { func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task {
@ -390,6 +393,16 @@ func (h dialHistory) min() pastDial {
} }
func (h *dialHistory) add(id discover.NodeID, exp time.Time) { func (h *dialHistory) add(id discover.NodeID, exp time.Time) {
heap.Push(h, pastDial{id, exp}) heap.Push(h, pastDial{id, exp})
}
func (h *dialHistory) remove(id discover.NodeID) bool {
for i, v := range *h {
if v.id == id {
heap.Remove(h, i)
return true
}
}
return false
} }
func (h dialHistory) contains(id discover.NodeID) bool { func (h dialHistory) contains(id discover.NodeID) bool {
for _, v := range h { for _, v := range h {

View file

@ -515,6 +515,50 @@ func TestDialStateStaticDial(t *testing.T) {
}) })
} }
// This test checks that static peers will be redialed immediately if they were re-added to a static list.
func TestDialStaticAfterReset(t *testing.T) {
wantStatic := []*discover.Node{
{ID: uintID(1)},
{ID: uintID(2)},
}
rounds := []round{
// Static dials are launched for the nodes that aren't yet connected.
{
peers: nil,
new: []task{
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
},
},
// No new dial tasks, all peers are connected.
{
peers: []*Peer{
{rw: &conn{flags: staticDialedConn, id: uintID(1)}},
{rw: &conn{flags: staticDialedConn, id: uintID(2)}},
},
done: []task{
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
},
new: []task{
&waitExpireTask{Duration: 30 * time.Second},
},
},
}
dTest := dialtest{
init: newDialState(wantStatic, nil, fakeTable{}, 0, nil),
rounds: rounds,
}
runDialTest(t, dTest)
for _, n := range wantStatic {
dTest.init.removeStatic(n)
dTest.init.addStatic(n)
}
// without removing peers they will be considered recently dialed
runDialTest(t, dTest)
}
// This test checks that past dials are not retried for some time. // This test checks that past dials are not retried for some time.
func TestDialStateCache(t *testing.T) { func TestDialStateCache(t *testing.T) {
wantStatic := []*discover.Node{ wantStatic := []*discover.Node{