From ea2718c9462ae351baab5eaa05a7e1ef9dc916fa Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 29 May 2015 14:40:45 +0200 Subject: core/vm: improve JUMPDEST analysis * JUMPDEST analysis is faster because less type conversions are performed. * The map of JUMPDEST locations is now created lazily at the first JUMP. * The result of the analysis is kept around for recursive invocations through CALL/CALLCODE. Fixes #1147 --- core/vm/analysis.go | 41 ++++++++++++++++++++++++++--------------- core/vm/context.go | 11 ++++++++++- core/vm/vm.go | 17 ++++++++--------- 3 files changed, 44 insertions(+), 25 deletions(-) (limited to 'core/vm') diff --git a/core/vm/analysis.go b/core/vm/analysis.go index 264d55cb9..a7aa8da39 100644 --- a/core/vm/analysis.go +++ b/core/vm/analysis.go @@ -3,34 +3,45 @@ package vm import ( "math/big" - "gopkg.in/fatih/set.v0" + "github.com/ethereum/go-ethereum/common" ) -type destinations struct { - set *set.Set -} +var bigMaxUint64 = new(big.Int).SetUint64(^uint64(0)) -func (d *destinations) Has(dest *big.Int) bool { - return d.set.Has(string(dest.Bytes())) -} +// destinations stores one map per contract (keyed by hash of code). +// The maps contain an entry for each location of a JUMPDEST +// instruction. +type destinations map[common.Hash]map[uint64]struct{} -func (d *destinations) Add(dest *big.Int) { - d.set.Add(string(dest.Bytes())) +// has checks whether code has a JUMPDEST at dest. +func (d destinations) has(codehash common.Hash, code []byte, dest *big.Int) bool { + // PC cannot go beyond len(code) and certainly can't be bigger than 64bits. + // Don't bother checking for JUMPDEST in that case. + if dest.Cmp(bigMaxUint64) > 0 { + return false + } + m, analysed := d[codehash] + if !analysed { + m = jumpdests(code) + d[codehash] = m + } + _, ok := m[dest.Uint64()] + return ok } -func analyseJumpDests(code []byte) (dests *destinations) { - dests = &destinations{set.New()} - +// jumpdests creates a map that contains an entry for each +// PC location that is a JUMPDEST instruction. +func jumpdests(code []byte) map[uint64]struct{} { + m := make(map[uint64]struct{}) for pc := uint64(0); pc < uint64(len(code)); pc++ { var op OpCode = OpCode(code[pc]) switch op { case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: a := uint64(op) - uint64(PUSH1) + 1 - pc += a case JUMPDEST: - dests.Add(big.NewInt(int64(pc))) + m[pc] = struct{}{} } } - return + return m } diff --git a/core/vm/context.go b/core/vm/context.go index 29bb9f74e..de03f84f0 100644 --- a/core/vm/context.go +++ b/core/vm/context.go @@ -16,6 +16,8 @@ type Context struct { caller ContextRef self ContextRef + jumpdests destinations // result of JUMPDEST analysis. + Code []byte CodeAddr *common.Address @@ -24,10 +26,17 @@ type Context struct { Args []byte } -// Create a new context for the given data items +// Create a new context for the given data items. func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int) *Context { c := &Context{caller: caller, self: object, Args: nil} + if parent, ok := caller.(*Context); ok { + // Reuse JUMPDEST analysis from parent context if available. + c.jumpdests = parent.jumpdests + } else { + c.jumpdests = make(destinations) + } + // Gas should be a pointer so it can safely be reduced through the run // This pointer will be off the state transition c.Gas = gas //new(big.Int).Set(gas) diff --git a/core/vm/vm.go b/core/vm/vm.go index 6db99bdcc..0d8facbb6 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -72,17 +72,16 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { } var ( - op OpCode - - destinations = analyseJumpDests(context.Code) - mem = NewMemory() - stack = newStack() - pc = new(big.Int) - statedb = self.env.State() + op OpCode + codehash = crypto.Sha3Hash(code) + mem = NewMemory() + stack = newStack() + pc = new(big.Int) + statedb = self.env.State() jump = func(from *big.Int, to *big.Int) error { - nop := context.GetOp(to) - if !destinations.Has(to) { + if !context.jumpdests.has(codehash, code, to) { + nop := context.GetOp(to) return fmt.Errorf("invalid jump destination (%v) %v", nop, to) } -- cgit v1.2.3 From 48fb0c3213a1634a266dd661d30c9ecd3c352f49 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 29 May 2015 14:58:57 +0200 Subject: core/vm: check for 'no code' before doing any work --- core/vm/vm.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'core/vm') diff --git a/core/vm/vm.go b/core/vm/vm.go index 0d8facbb6..2bd950385 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -71,6 +71,11 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { } } + // Don't bother with the execution if there's no code. + if len(code) == 0 { + return context.Return(nil), nil + } + var ( op OpCode codehash = crypto.Sha3Hash(code) @@ -94,11 +99,6 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { } ) - // Don't bother with the execution if there's no code. - if len(code) == 0 { - return context.Return(nil), nil - } - for { // The base for all big integer arithmetic base := new(big.Int) -- cgit v1.2.3 From fa4aefee44c5dd32fb7e0d02960c1550e9c8a330 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 31 May 2015 15:05:00 +0200 Subject: core/vm: cleanup and renames --- core/vm/address.go | 98 --------------- core/vm/contracts.go | 98 +++++++++++++++ core/vm/environment.go | 40 ------ core/vm/main_test.go | 9 -- core/vm/opcodes.go | 334 +++++++++++++++++++++++++++++++++++++++++++++++++ core/vm/types.go | 334 ------------------------------------------------- core/vm/vm_test.go | 3 - 7 files changed, 432 insertions(+), 484 deletions(-) delete mode 100644 core/vm/address.go create mode 100644 core/vm/contracts.go delete mode 100644 core/vm/main_test.go create mode 100644 core/vm/opcodes.go delete mode 100644 core/vm/types.go delete mode 100644 core/vm/vm_test.go (limited to 'core/vm') diff --git a/core/vm/address.go b/core/vm/address.go deleted file mode 100644 index 742017dd2..000000000 --- a/core/vm/address.go +++ /dev/null @@ -1,98 +0,0 @@ -package vm - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/params" -) - -type Address interface { - Call(in []byte) []byte -} - -type PrecompiledAccount struct { - Gas func(l int) *big.Int - fn func(in []byte) []byte -} - -func (self PrecompiledAccount) Call(in []byte) []byte { - return self.fn(in) -} - -var Precompiled = PrecompiledContracts() - -// XXX Could set directly. Testing requires resetting and setting of pre compiled contracts. -func PrecompiledContracts() map[string]*PrecompiledAccount { - return map[string]*PrecompiledAccount{ - // ECRECOVER - string(common.LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int { - return params.EcrecoverGas - }, ecrecoverFunc}, - - // SHA256 - string(common.LeftPadBytes([]byte{2}, 20)): &PrecompiledAccount{func(l int) *big.Int { - n := big.NewInt(int64(l+31) / 32) - n.Mul(n, params.Sha256WordGas) - return n.Add(n, params.Sha256Gas) - }, sha256Func}, - - // RIPEMD160 - string(common.LeftPadBytes([]byte{3}, 20)): &PrecompiledAccount{func(l int) *big.Int { - n := big.NewInt(int64(l+31) / 32) - n.Mul(n, params.Ripemd160WordGas) - return n.Add(n, params.Ripemd160Gas) - }, ripemd160Func}, - - string(common.LeftPadBytes([]byte{4}, 20)): &PrecompiledAccount{func(l int) *big.Int { - n := big.NewInt(int64(l+31) / 32) - n.Mul(n, params.IdentityWordGas) - - return n.Add(n, params.IdentityGas) - }, memCpy}, - } -} - -func sha256Func(in []byte) []byte { - return crypto.Sha256(in) -} - -func ripemd160Func(in []byte) []byte { - return common.LeftPadBytes(crypto.Ripemd160(in), 32) -} - -const ecRecoverInputLength = 128 - -func ecrecoverFunc(in []byte) []byte { - // "in" is (hash, v, r, s), each 32 bytes - // but for ecrecover we want (r, s, v) - if len(in) < ecRecoverInputLength { - return nil - } - - // Treat V as a 256bit integer - v := new(big.Int).Sub(common.Bytes2Big(in[32:64]), big.NewInt(27)) - // Ethereum requires V to be either 0 or 1 => (27 || 28) - if !(v.Cmp(Zero) == 0 || v.Cmp(One) == 0) { - return nil - } - - // v needs to be moved to the end - rsv := append(in[64:128], byte(v.Uint64())) - pubKey, err := crypto.Ecrecover(in[:32], rsv) - // make sure the public key is a valid one - if err != nil { - glog.V(logger.Error).Infof("EC RECOVER FAIL: ", err) - return nil - } - - // the first byte of pubkey is bitcoin heritage - return common.LeftPadBytes(crypto.Sha3(pubKey[1:])[12:], 32) -} - -func memCpy(in []byte) []byte { - return in -} diff --git a/core/vm/contracts.go b/core/vm/contracts.go new file mode 100644 index 000000000..742017dd2 --- /dev/null +++ b/core/vm/contracts.go @@ -0,0 +1,98 @@ +package vm + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" +) + +type Address interface { + Call(in []byte) []byte +} + +type PrecompiledAccount struct { + Gas func(l int) *big.Int + fn func(in []byte) []byte +} + +func (self PrecompiledAccount) Call(in []byte) []byte { + return self.fn(in) +} + +var Precompiled = PrecompiledContracts() + +// XXX Could set directly. Testing requires resetting and setting of pre compiled contracts. +func PrecompiledContracts() map[string]*PrecompiledAccount { + return map[string]*PrecompiledAccount{ + // ECRECOVER + string(common.LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int { + return params.EcrecoverGas + }, ecrecoverFunc}, + + // SHA256 + string(common.LeftPadBytes([]byte{2}, 20)): &PrecompiledAccount{func(l int) *big.Int { + n := big.NewInt(int64(l+31) / 32) + n.Mul(n, params.Sha256WordGas) + return n.Add(n, params.Sha256Gas) + }, sha256Func}, + + // RIPEMD160 + string(common.LeftPadBytes([]byte{3}, 20)): &PrecompiledAccount{func(l int) *big.Int { + n := big.NewInt(int64(l+31) / 32) + n.Mul(n, params.Ripemd160WordGas) + return n.Add(n, params.Ripemd160Gas) + }, ripemd160Func}, + + string(common.LeftPadBytes([]byte{4}, 20)): &PrecompiledAccount{func(l int) *big.Int { + n := big.NewInt(int64(l+31) / 32) + n.Mul(n, params.IdentityWordGas) + + return n.Add(n, params.IdentityGas) + }, memCpy}, + } +} + +func sha256Func(in []byte) []byte { + return crypto.Sha256(in) +} + +func ripemd160Func(in []byte) []byte { + return common.LeftPadBytes(crypto.Ripemd160(in), 32) +} + +const ecRecoverInputLength = 128 + +func ecrecoverFunc(in []byte) []byte { + // "in" is (hash, v, r, s), each 32 bytes + // but for ecrecover we want (r, s, v) + if len(in) < ecRecoverInputLength { + return nil + } + + // Treat V as a 256bit integer + v := new(big.Int).Sub(common.Bytes2Big(in[32:64]), big.NewInt(27)) + // Ethereum requires V to be either 0 or 1 => (27 || 28) + if !(v.Cmp(Zero) == 0 || v.Cmp(One) == 0) { + return nil + } + + // v needs to be moved to the end + rsv := append(in[64:128], byte(v.Uint64())) + pubKey, err := crypto.Ecrecover(in[:32], rsv) + // make sure the public key is a valid one + if err != nil { + glog.V(logger.Error).Infof("EC RECOVER FAIL: ", err) + return nil + } + + // the first byte of pubkey is bitcoin heritage + return common.LeftPadBytes(crypto.Sha3(pubKey[1:])[12:], 32) +} + +func memCpy(in []byte) []byte { + return in +} diff --git a/core/vm/environment.go b/core/vm/environment.go index cc9570fc8..282d19578 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -2,13 +2,10 @@ package vm import ( "errors" - "fmt" - "io" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/rlp" ) type Environment interface { @@ -52,40 +49,3 @@ func Transfer(from, to Account, amount *big.Int) error { return nil } - -type Log struct { - address common.Address - topics []common.Hash - data []byte - log uint64 -} - -func (self *Log) Address() common.Address { - return self.address -} - -func (self *Log) Topics() []common.Hash { - return self.topics -} - -func (self *Log) Data() []byte { - return self.data -} - -func (self *Log) Number() uint64 { - return self.log -} - -func (self *Log) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, []interface{}{self.address, self.topics, self.data}) -} - -/* -func (self *Log) RlpData() interface{} { - return []interface{}{self.address, common.ByteSliceToInterface(self.topics), self.data} -} -*/ - -func (self *Log) String() string { - return fmt.Sprintf("{%x %x %x}", self.address, self.data, self.topics) -} diff --git a/core/vm/main_test.go b/core/vm/main_test.go deleted file mode 100644 index 0ae03bf6a..000000000 --- a/core/vm/main_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package vm - -import ( - "testing" - - checker "gopkg.in/check.v1" -) - -func Test(t *testing.T) { checker.TestingT(t) } diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go new file mode 100644 index 000000000..1ea80a212 --- /dev/null +++ b/core/vm/opcodes.go @@ -0,0 +1,334 @@ +package vm + +import ( + "fmt" +) + +type OpCode byte + +// Op codes +const ( + // 0x0 range - arithmetic ops + STOP OpCode = iota + ADD + MUL + SUB + DIV + SDIV + MOD + SMOD + ADDMOD + MULMOD + EXP + SIGNEXTEND +) + +const ( + LT OpCode = iota + 0x10 + GT + SLT + SGT + EQ + ISZERO + AND + OR + XOR + NOT + BYTE + + SHA3 = 0x20 +) + +const ( + // 0x30 range - closure state + ADDRESS OpCode = 0x30 + iota + BALANCE + ORIGIN + CALLER + CALLVALUE + CALLDATALOAD + CALLDATASIZE + CALLDATACOPY + CODESIZE + CODECOPY + GASPRICE + EXTCODESIZE + EXTCODECOPY +) + +const ( + + // 0x40 range - block operations + BLOCKHASH OpCode = 0x40 + iota + COINBASE + TIMESTAMP + NUMBER + DIFFICULTY + GASLIMIT +) + +const ( + // 0x50 range - 'storage' and execution + POP OpCode = 0x50 + iota + MLOAD + MSTORE + MSTORE8 + SLOAD + SSTORE + JUMP + JUMPI + PC + MSIZE + GAS + JUMPDEST +) + +const ( + // 0x60 range + PUSH1 OpCode = 0x60 + iota + PUSH2 + PUSH3 + PUSH4 + PUSH5 + PUSH6 + PUSH7 + PUSH8 + PUSH9 + PUSH10 + PUSH11 + PUSH12 + PUSH13 + PUSH14 + PUSH15 + PUSH16 + PUSH17 + PUSH18 + PUSH19 + PUSH20 + PUSH21 + PUSH22 + PUSH23 + PUSH24 + PUSH25 + PUSH26 + PUSH27 + PUSH28 + PUSH29 + PUSH30 + PUSH31 + PUSH32 + DUP1 + DUP2 + DUP3 + DUP4 + DUP5 + DUP6 + DUP7 + DUP8 + DUP9 + DUP10 + DUP11 + DUP12 + DUP13 + DUP14 + DUP15 + DUP16 + SWAP1 + SWAP2 + SWAP3 + SWAP4 + SWAP5 + SWAP6 + SWAP7 + SWAP8 + SWAP9 + SWAP10 + SWAP11 + SWAP12 + SWAP13 + SWAP14 + SWAP15 + SWAP16 +) + +const ( + LOG0 OpCode = 0xa0 + iota + LOG1 + LOG2 + LOG3 + LOG4 +) + +const ( + // 0xf0 range - closures + CREATE OpCode = 0xf0 + iota + CALL + CALLCODE + RETURN + + // 0x70 range - other + SUICIDE = 0xff +) + +// Since the opcodes aren't all in order we can't use a regular slice +var opCodeToString = map[OpCode]string{ + // 0x0 range - arithmetic ops + STOP: "STOP", + ADD: "ADD", + MUL: "MUL", + SUB: "SUB", + DIV: "DIV", + SDIV: "SDIV", + MOD: "MOD", + SMOD: "SMOD", + EXP: "EXP", + NOT: "NOT", + LT: "LT", + GT: "GT", + SLT: "SLT", + SGT: "SGT", + EQ: "EQ", + ISZERO: "ISZERO", + SIGNEXTEND: "SIGNEXTEND", + + // 0x10 range - bit ops + AND: "AND", + OR: "OR", + XOR: "XOR", + BYTE: "BYTE", + ADDMOD: "ADDMOD", + MULMOD: "MULMOD", + + // 0x20 range - crypto + SHA3: "SHA3", + + // 0x30 range - closure state + ADDRESS: "ADDRESS", + BALANCE: "BALANCE", + ORIGIN: "ORIGIN", + CALLER: "CALLER", + CALLVALUE: "CALLVALUE", + CALLDATALOAD: "CALLDATALOAD", + CALLDATASIZE: "CALLDATASIZE", + CALLDATACOPY: "CALLDATACOPY", + CODESIZE: "CODESIZE", + CODECOPY: "CODECOPY", + GASPRICE: "TXGASPRICE", + + // 0x40 range - block operations + BLOCKHASH: "BLOCKHASH", + COINBASE: "COINBASE", + TIMESTAMP: "TIMESTAMP", + NUMBER: "NUMBER", + DIFFICULTY: "DIFFICULTY", + GASLIMIT: "GASLIMIT", + EXTCODESIZE: "EXTCODESIZE", + EXTCODECOPY: "EXTCODECOPY", + + // 0x50 range - 'storage' and execution + POP: "POP", + //DUP: "DUP", + //SWAP: "SWAP", + MLOAD: "MLOAD", + MSTORE: "MSTORE", + MSTORE8: "MSTORE8", + SLOAD: "SLOAD", + SSTORE: "SSTORE", + JUMP: "JUMP", + JUMPI: "JUMPI", + PC: "PC", + MSIZE: "MSIZE", + GAS: "GAS", + JUMPDEST: "JUMPDEST", + + // 0x60 range - push + PUSH1: "PUSH1", + PUSH2: "PUSH2", + PUSH3: "PUSH3", + PUSH4: "PUSH4", + PUSH5: "PUSH5", + PUSH6: "PUSH6", + PUSH7: "PUSH7", + PUSH8: "PUSH8", + PUSH9: "PUSH9", + PUSH10: "PUSH10", + PUSH11: "PUSH11", + PUSH12: "PUSH12", + PUSH13: "PUSH13", + PUSH14: "PUSH14", + PUSH15: "PUSH15", + PUSH16: "PUSH16", + PUSH17: "PUSH17", + PUSH18: "PUSH18", + PUSH19: "PUSH19", + PUSH20: "PUSH20", + PUSH21: "PUSH21", + PUSH22: "PUSH22", + PUSH23: "PUSH23", + PUSH24: "PUSH24", + PUSH25: "PUSH25", + PUSH26: "PUSH26", + PUSH27: "PUSH27", + PUSH28: "PUSH28", + PUSH29: "PUSH29", + PUSH30: "PUSH30", + PUSH31: "PUSH31", + PUSH32: "PUSH32", + + DUP1: "DUP1", + DUP2: "DUP2", + DUP3: "DUP3", + DUP4: "DUP4", + DUP5: "DUP5", + DUP6: "DUP6", + DUP7: "DUP7", + DUP8: "DUP8", + DUP9: "DUP9", + DUP10: "DUP10", + DUP11: "DUP11", + DUP12: "DUP12", + DUP13: "DUP13", + DUP14: "DUP14", + DUP15: "DUP15", + DUP16: "DUP16", + + SWAP1: "SWAP1", + SWAP2: "SWAP2", + SWAP3: "SWAP3", + SWAP4: "SWAP4", + SWAP5: "SWAP5", + SWAP6: "SWAP6", + SWAP7: "SWAP7", + SWAP8: "SWAP8", + SWAP9: "SWAP9", + SWAP10: "SWAP10", + SWAP11: "SWAP11", + SWAP12: "SWAP12", + SWAP13: "SWAP13", + SWAP14: "SWAP14", + SWAP15: "SWAP15", + SWAP16: "SWAP16", + LOG0: "LOG0", + LOG1: "LOG1", + LOG2: "LOG2", + LOG3: "LOG3", + LOG4: "LOG4", + + // 0xf0 range + CREATE: "CREATE", + CALL: "CALL", + RETURN: "RETURN", + CALLCODE: "CALLCODE", + + // 0x70 range - other + SUICIDE: "SUICIDE", +} + +func (o OpCode) String() string { + str := opCodeToString[o] + if len(str) == 0 { + return fmt.Sprintf("Missing opcode 0x%x", int(o)) + } + + return str +} diff --git a/core/vm/types.go b/core/vm/types.go deleted file mode 100644 index 1ea80a212..000000000 --- a/core/vm/types.go +++ /dev/null @@ -1,334 +0,0 @@ -package vm - -import ( - "fmt" -) - -type OpCode byte - -// Op codes -const ( - // 0x0 range - arithmetic ops - STOP OpCode = iota - ADD - MUL - SUB - DIV - SDIV - MOD - SMOD - ADDMOD - MULMOD - EXP - SIGNEXTEND -) - -const ( - LT OpCode = iota + 0x10 - GT - SLT - SGT - EQ - ISZERO - AND - OR - XOR - NOT - BYTE - - SHA3 = 0x20 -) - -const ( - // 0x30 range - closure state - ADDRESS OpCode = 0x30 + iota - BALANCE - ORIGIN - CALLER - CALLVALUE - CALLDATALOAD - CALLDATASIZE - CALLDATACOPY - CODESIZE - CODECOPY - GASPRICE - EXTCODESIZE - EXTCODECOPY -) - -const ( - - // 0x40 range - block operations - BLOCKHASH OpCode = 0x40 + iota - COINBASE - TIMESTAMP - NUMBER - DIFFICULTY - GASLIMIT -) - -const ( - // 0x50 range - 'storage' and execution - POP OpCode = 0x50 + iota - MLOAD - MSTORE - MSTORE8 - SLOAD - SSTORE - JUMP - JUMPI - PC - MSIZE - GAS - JUMPDEST -) - -const ( - // 0x60 range - PUSH1 OpCode = 0x60 + iota - PUSH2 - PUSH3 - PUSH4 - PUSH5 - PUSH6 - PUSH7 - PUSH8 - PUSH9 - PUSH10 - PUSH11 - PUSH12 - PUSH13 - PUSH14 - PUSH15 - PUSH16 - PUSH17 - PUSH18 - PUSH19 - PUSH20 - PUSH21 - PUSH22 - PUSH23 - PUSH24 - PUSH25 - PUSH26 - PUSH27 - PUSH28 - PUSH29 - PUSH30 - PUSH31 - PUSH32 - DUP1 - DUP2 - DUP3 - DUP4 - DUP5 - DUP6 - DUP7 - DUP8 - DUP9 - DUP10 - DUP11 - DUP12 - DUP13 - DUP14 - DUP15 - DUP16 - SWAP1 - SWAP2 - SWAP3 - SWAP4 - SWAP5 - SWAP6 - SWAP7 - SWAP8 - SWAP9 - SWAP10 - SWAP11 - SWAP12 - SWAP13 - SWAP14 - SWAP15 - SWAP16 -) - -const ( - LOG0 OpCode = 0xa0 + iota - LOG1 - LOG2 - LOG3 - LOG4 -) - -const ( - // 0xf0 range - closures - CREATE OpCode = 0xf0 + iota - CALL - CALLCODE - RETURN - - // 0x70 range - other - SUICIDE = 0xff -) - -// Since the opcodes aren't all in order we can't use a regular slice -var opCodeToString = map[OpCode]string{ - // 0x0 range - arithmetic ops - STOP: "STOP", - ADD: "ADD", - MUL: "MUL", - SUB: "SUB", - DIV: "DIV", - SDIV: "SDIV", - MOD: "MOD", - SMOD: "SMOD", - EXP: "EXP", - NOT: "NOT", - LT: "LT", - GT: "GT", - SLT: "SLT", - SGT: "SGT", - EQ: "EQ", - ISZERO: "ISZERO", - SIGNEXTEND: "SIGNEXTEND", - - // 0x10 range - bit ops - AND: "AND", - OR: "OR", - XOR: "XOR", - BYTE: "BYTE", - ADDMOD: "ADDMOD", - MULMOD: "MULMOD", - - // 0x20 range - crypto - SHA3: "SHA3", - - // 0x30 range - closure state - ADDRESS: "ADDRESS", - BALANCE: "BALANCE", - ORIGIN: "ORIGIN", - CALLER: "CALLER", - CALLVALUE: "CALLVALUE", - CALLDATALOAD: "CALLDATALOAD", - CALLDATASIZE: "CALLDATASIZE", - CALLDATACOPY: "CALLDATACOPY", - CODESIZE: "CODESIZE", - CODECOPY: "CODECOPY", - GASPRICE: "TXGASPRICE", - - // 0x40 range - block operations - BLOCKHASH: "BLOCKHASH", - COINBASE: "COINBASE", - TIMESTAMP: "TIMESTAMP", - NUMBER: "NUMBER", - DIFFICULTY: "DIFFICULTY", - GASLIMIT: "GASLIMIT", - EXTCODESIZE: "EXTCODESIZE", - EXTCODECOPY: "EXTCODECOPY", - - // 0x50 range - 'storage' and execution - POP: "POP", - //DUP: "DUP", - //SWAP: "SWAP", - MLOAD: "MLOAD", - MSTORE: "MSTORE", - MSTORE8: "MSTORE8", - SLOAD: "SLOAD", - SSTORE: "SSTORE", - JUMP: "JUMP", - JUMPI: "JUMPI", - PC: "PC", - MSIZE: "MSIZE", - GAS: "GAS", - JUMPDEST: "JUMPDEST", - - // 0x60 range - push - PUSH1: "PUSH1", - PUSH2: "PUSH2", - PUSH3: "PUSH3", - PUSH4: "PUSH4", - PUSH5: "PUSH5", - PUSH6: "PUSH6", - PUSH7: "PUSH7", - PUSH8: "PUSH8", - PUSH9: "PUSH9", - PUSH10: "PUSH10", - PUSH11: "PUSH11", - PUSH12: "PUSH12", - PUSH13: "PUSH13", - PUSH14: "PUSH14", - PUSH15: "PUSH15", - PUSH16: "PUSH16", - PUSH17: "PUSH17", - PUSH18: "PUSH18", - PUSH19: "PUSH19", - PUSH20: "PUSH20", - PUSH21: "PUSH21", - PUSH22: "PUSH22", - PUSH23: "PUSH23", - PUSH24: "PUSH24", - PUSH25: "PUSH25", - PUSH26: "PUSH26", - PUSH27: "PUSH27", - PUSH28: "PUSH28", - PUSH29: "PUSH29", - PUSH30: "PUSH30", - PUSH31: "PUSH31", - PUSH32: "PUSH32", - - DUP1: "DUP1", - DUP2: "DUP2", - DUP3: "DUP3", - DUP4: "DUP4", - DUP5: "DUP5", - DUP6: "DUP6", - DUP7: "DUP7", - DUP8: "DUP8", - DUP9: "DUP9", - DUP10: "DUP10", - DUP11: "DUP11", - DUP12: "DUP12", - DUP13: "DUP13", - DUP14: "DUP14", - DUP15: "DUP15", - DUP16: "DUP16", - - SWAP1: "SWAP1", - SWAP2: "SWAP2", - SWAP3: "SWAP3", - SWAP4: "SWAP4", - SWAP5: "SWAP5", - SWAP6: "SWAP6", - SWAP7: "SWAP7", - SWAP8: "SWAP8", - SWAP9: "SWAP9", - SWAP10: "SWAP10", - SWAP11: "SWAP11", - SWAP12: "SWAP12", - SWAP13: "SWAP13", - SWAP14: "SWAP14", - SWAP15: "SWAP15", - SWAP16: "SWAP16", - LOG0: "LOG0", - LOG1: "LOG1", - LOG2: "LOG2", - LOG3: "LOG3", - LOG4: "LOG4", - - // 0xf0 range - CREATE: "CREATE", - CALL: "CALL", - RETURN: "RETURN", - CALLCODE: "CALLCODE", - - // 0x70 range - other - SUICIDE: "SUICIDE", -} - -func (o OpCode) String() string { - str := opCodeToString[o] - if len(str) == 0 { - return fmt.Sprintf("Missing opcode 0x%x", int(o)) - } - - return str -} diff --git a/core/vm/vm_test.go b/core/vm/vm_test.go deleted file mode 100644 index 9bd147a72..000000000 --- a/core/vm/vm_test.go +++ /dev/null @@ -1,3 +0,0 @@ -package vm - -// Tests have been removed in favour of general tests. If anything implementation specific needs testing, put it here -- cgit v1.2.3 From ad5b5a4895f39180f329f2532e2484430c1ac480 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Tue, 9 Jun 2015 15:41:15 +0200 Subject: Pad precompiled EC recover input and add validations --- core/vm/contracts.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'core/vm') diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 742017dd2..b5cb9ccd2 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -67,21 +67,27 @@ func ripemd160Func(in []byte) []byte { const ecRecoverInputLength = 128 func ecrecoverFunc(in []byte) []byte { - // "in" is (hash, v, r, s), each 32 bytes - // but for ecrecover we want (r, s, v) if len(in) < ecRecoverInputLength { - return nil + in = common.RightPadBytes(in, 128) } + // "in" is (hash, v, r, s), each 32 bytes + // but for ecrecover we want (r, s, v) + r := common.BytesToBig(in[64:96]) + s := common.BytesToBig(in[96:128]) // Treat V as a 256bit integer - v := new(big.Int).Sub(common.Bytes2Big(in[32:64]), big.NewInt(27)) - // Ethereum requires V to be either 0 or 1 => (27 || 28) - if !(v.Cmp(Zero) == 0 || v.Cmp(One) == 0) { + vbig := common.Bytes2Big(in[32:64]) + v := byte(vbig.Uint64()) + + if !crypto.ValidateSignatureValues(v, r, s) { + glog.V(logger.Error).Infof("EC RECOVER FAIL: v, r or s value invalid") return nil } - // v needs to be moved to the end - rsv := append(in[64:128], byte(v.Uint64())) + // v needs to be at the end and normalized for libsecp256k1 + vbignormal := new(big.Int).Sub(vbig, big.NewInt(27)) + vnormal := byte(vbignormal.Uint64()) + rsv := append(in[64:128], vnormal) pubKey, err := crypto.Ecrecover(in[:32], rsv) // make sure the public key is a valid one if err != nil { -- cgit v1.2.3 From 6e3b58e491c822cc6e4aa822c31c6dee034e3df9 Mon Sep 17 00:00:00 2001 From: Gustav Simonsson Date: Tue, 9 Jun 2015 16:03:05 +0200 Subject: Remove unneeded if check on EC recover padding --- core/vm/contracts.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'core/vm') diff --git a/core/vm/contracts.go b/core/vm/contracts.go index b5cb9ccd2..90e356b1d 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -67,9 +67,7 @@ func ripemd160Func(in []byte) []byte { const ecRecoverInputLength = 128 func ecrecoverFunc(in []byte) []byte { - if len(in) < ecRecoverInputLength { - in = common.RightPadBytes(in, 128) - } + in = common.RightPadBytes(in, 128) // "in" is (hash, v, r, s), each 32 bytes // but for ecrecover we want (r, s, v) -- cgit v1.2.3