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') 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 b26f5e0bb7e8922c80bc3513d1ebce2c99a081f5 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 31 May 2015 15:53:17 +0200 Subject: types: block json unmarshal method added --- core/genesis.go | 4 ++-- core/types/block.go | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/genesis.go b/core/genesis.go index e72834822..a9b7339f3 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -36,7 +36,7 @@ func GenesisBlock(db common.Database) *types.Block { Balance string Code string } - err := json.Unmarshal(GenesisData, &accounts) + err := json.Unmarshal(GenesisAccounts, &accounts) if err != nil { fmt.Println("enable to decode genesis json data:", err) os.Exit(1) @@ -57,7 +57,7 @@ func GenesisBlock(db common.Database) *types.Block { return genesis } -var GenesisData = []byte(`{ +var GenesisAccounts = []byte(`{ "0000000000000000000000000000000000000001": {"balance": "1"}, "0000000000000000000000000000000000000002": {"balance": "1"}, "0000000000000000000000000000000000000003": {"balance": "1"}, diff --git a/core/types/block.go b/core/types/block.go index c93452fa7..d7963981e 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -1,7 +1,9 @@ package types import ( + "bytes" "encoding/binary" + "encoding/json" "fmt" "io" "math/big" @@ -80,6 +82,28 @@ func (self *Header) RlpData() interface{} { return self.rlpData(true) } +func (h *Header) UnmarshalJSON(data []byte) error { + var ext struct { + ParentHash string + Coinbase string + Difficulty string + GasLimit string + Time uint64 + Extra string + } + dec := json.NewDecoder(bytes.NewReader(data)) + if err := dec.Decode(&ext); err != nil { + return err + } + + h.ParentHash = common.HexToHash(ext.ParentHash) + h.Coinbase = common.HexToAddress(ext.Coinbase) + h.Difficulty = common.String2Big(ext.Difficulty) + h.Time = ext.Time + h.Extra = []byte(ext.Extra) + return nil +} + func rlpHash(x interface{}) (h common.Hash) { hw := sha3.NewKeccak256() rlp.Encode(hw, x) -- cgit v1.2.3 From 770a0e78396d66dc0b15a267891ed69be1414f52 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 3 Jun 2015 13:14:06 +0200 Subject: wip --- core/block_processor.go | 6 ------ core/chain_manager.go | 5 +++-- 2 files changed, 3 insertions(+), 8 deletions(-) (limited to 'core') diff --git a/core/block_processor.go b/core/block_processor.go index a3ad383d0..9a013b280 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -178,7 +178,6 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs state.Logs, err erro return nil, ParentError(header.ParentHash) } parent := sm.bc.GetBlock(header.ParentHash) - return sm.processWithParent(block, parent) } @@ -254,14 +253,9 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st return nil, err } - // Calculate the td for this block - //td = CalculateTD(block, parent) // Sync the current block's state to the database state.Sync() - // Remove transactions from the pool - sm.txpool.RemoveTransactions(block.Transactions()) - // This puts transactions in a extra db for rpc for i, tx := range block.Transactions() { putTx(sm.extraDb, tx, block, uint64(i)) diff --git a/core/chain_manager.go b/core/chain_manager.go index 927055103..d58c0d504 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -560,6 +560,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { defer close(nonceQuit) for i, block := range chain { + bstart := time.Now() // Wait for block i's nonce to be verified before processing // its state transition. for nonceChecked[i] { @@ -642,11 +643,11 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { queueEvent.canonicalCount++ if glog.V(logger.Debug) { - glog.Infof("[%v] inserted block #%d (%d TXs %d UNCs) (%x...)\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4]) + glog.Infof("[%v] inserted block #%d (%d TXs %d UNCs) (%x...). Took %v\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart)) } } else { if glog.V(logger.Detail) { - glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...)\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4]) + glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...). Took %v\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart)) } queue[i] = ChainSideEvent{block, logs} -- cgit v1.2.3 From 08befff8f168f0fd59a3ff36a7205ba44fb82540 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 3 Jun 2015 14:06:20 +0200 Subject: core: compute less transaction hashes in TxPool --- core/transaction_pool.go | 144 +++++++++++++++++++++--------------------- core/transaction_pool_test.go | 20 +++--- 2 files changed, 82 insertions(+), 82 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 4296c79f6..d8debe1c0 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -61,7 +61,7 @@ type TxPool struct { txs map[common.Hash]*types.Transaction invalidHashes *set.Set - queue map[common.Address]types.Transactions + queue map[common.Address]map[common.Hash]*types.Transaction subscribers []chan TxMsg @@ -71,7 +71,7 @@ type TxPool struct { func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { txPool := &TxPool{ txs: make(map[common.Hash]*types.Transaction), - queue: make(map[common.Address]types.Transactions), + queue: make(map[common.Address]map[common.Hash]*types.Transaction), queueChan: make(chan *types.Transaction, txPoolQueueSize), quit: make(chan bool), eventMux: eventMux, @@ -157,22 +157,20 @@ func (self *TxPool) add(tx *types.Transaction) error { if err != nil { return err } - - self.queueTx(tx) - - var toname string - if to := tx.To(); to != nil { - toname = common.Bytes2Hex(to[:4]) - } else { - toname = "[NEW_CONTRACT]" - } - // we can ignore the error here because From is - // verified in ValidateTransaction. - f, _ := tx.From() - from := common.Bytes2Hex(f[:4]) + self.queueTx(hash, tx) if glog.V(logger.Debug) { - glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, tx.Hash()) + var toname string + if to := tx.To(); to != nil { + toname = common.Bytes2Hex(to[:4]) + } else { + toname = "[NEW_CONTRACT]" + } + // we can ignore the error here because From is + // verified in ValidateTransaction. + f, _ := tx.From() + from := common.Bytes2Hex(f[:4]) + glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, hash) } return nil @@ -211,16 +209,12 @@ func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { if tx, ok := tp.txs[hash]; ok { return tx } - // check queue for _, txs := range tp.queue { - for _, tx := range txs { - if tx.Hash() == hash { - return tx - } + if tx, ok := txs[hash]; ok { + return tx } } - return nil } @@ -234,26 +228,26 @@ func (self *TxPool) GetTransactions() (txs types.Transactions) { txs[i] = tx i++ } - - return + return txs } func (self *TxPool) GetQueuedTransactions() types.Transactions { self.mu.RLock() defer self.mu.RUnlock() - var txs types.Transactions - for _, ts := range self.queue { - txs = append(txs, ts...) + var ret types.Transactions + for _, txs := range self.queue { + for _, tx := range txs { + ret = append(ret, tx) + } } - - return txs + sort.Sort(types.TxByNonce{ret}) + return ret } func (self *TxPool) RemoveTransactions(txs types.Transactions) { self.mu.Lock() defer self.mu.Unlock() - for _, tx := range txs { self.removeTx(tx.Hash()) } @@ -270,14 +264,17 @@ func (pool *TxPool) Stop() { glog.V(logger.Info).Infoln("TX Pool stopped") } -func (self *TxPool) queueTx(tx *types.Transaction) { +func (self *TxPool) queueTx(hash common.Hash, tx *types.Transaction) { from, _ := tx.From() // already validated - self.queue[from] = append(self.queue[from], tx) + if self.queue[from] == nil { + self.queue[from] = make(map[common.Hash]*types.Transaction) + } + self.queue[from][hash] = tx } -func (pool *TxPool) addTx(tx *types.Transaction) { - if _, ok := pool.txs[tx.Hash()]; !ok { - pool.txs[tx.Hash()] = tx +func (pool *TxPool) addTx(hash common.Hash, tx *types.Transaction) { + if _, ok := pool.txs[hash]; !ok { + pool.txs[hash] = tx // Notify the subscribers. This event is posted in a goroutine // because it's possible that somewhere during the post "Remove transaction" // gets called which will then wait for the global tx pool lock and deadlock. @@ -291,36 +288,33 @@ func (pool *TxPool) checkQueue() { defer pool.mu.Unlock() statedb := pool.currentState() + var addq txQueue for address, txs := range pool.queue { - sort.Sort(types.TxByNonce{txs}) - - var ( - nonce = statedb.GetNonce(address) - start int - ) - // Clean up the transactions first and determine the start of the nonces - for _, tx := range txs { - if tx.Nonce() >= nonce { - break + curnonce := statedb.GetNonce(address) + addq := addq[:0] + for hash, tx := range txs { + if tx.AccountNonce < curnonce { + // Drop queued transactions whose nonce is lower than + // the account nonce because they have been processed. + delete(txs, hash) + } else { + // Collect the remaining transactions for the next pass. + addq = append(addq, txQueueEntry{hash, tx}) } - start++ } - pool.queue[address] = txs[start:] - - // expected nonce - enonce := nonce - for _, tx := range pool.queue[address] { - // If the expected nonce does not match up with the next one - // (i.e. a nonce gap), we stop the loop - if enonce != tx.Nonce() { + // Find the next consecutive nonce range starting at the + // current account nonce. + sort.Sort(addq) + for _, e := range addq { + if e.AccountNonce != curnonce { break } - enonce++ - - pool.addTx(tx) + curnonce++ + delete(txs, e.hash) + pool.addTx(e.hash, e.Transaction) } - // delete the entire queue entry if it's empty. There's no need to keep it - if len(pool.queue[address]) == 0 { + // Delete the entire queue entry if it became empty. + if len(txs) == 0 { delete(pool.queue, address) } } @@ -329,20 +323,16 @@ func (pool *TxPool) checkQueue() { func (pool *TxPool) removeTx(hash common.Hash) { // delete from pending pool delete(pool.txs, hash) - // delete from queue -out: for address, txs := range pool.queue { - for i, tx := range txs { - if tx.Hash() == hash { - if len(txs) == 1 { - // if only one tx, remove entire address entry - delete(pool.queue, address) - } else { - pool.queue[address][len(txs)-1], pool.queue[address] = nil, append(txs[:i], txs[i+1:]...) - } - break out + if _, ok := txs[hash]; ok { + if len(txs) == 1 { + // if only one tx, remove entire address entry. + delete(pool.queue, address) + } else { + delete(txs, hash) } + break } } } @@ -356,8 +346,18 @@ func (pool *TxPool) validatePool() { if glog.V(logger.Info) { glog.Infof("removed tx (%x) from pool: %v\n", hash[:4], err) } - - pool.removeTx(hash) + delete(pool.txs, hash) } } } + +type txQueue []txQueueEntry + +type txQueueEntry struct { + hash common.Hash + *types.Transaction +} + +func (q txQueue) Len() int { return len(q) } +func (q txQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] } +func (q txQueue) Less(i, j int) bool { return q[i].AccountNonce < q[j].AccountNonce } diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go index d6ea4a2a9..600fd9b4f 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -68,7 +68,7 @@ func TestTransactionQueue(t *testing.T) { tx.SignECDSA(key) from, _ := tx.From() pool.currentState().AddBalance(from, big.NewInt(1)) - pool.queueTx(tx) + pool.queueTx(tx.Hash(), tx) pool.checkQueue() if len(pool.txs) != 1 { @@ -80,7 +80,7 @@ func TestTransactionQueue(t *testing.T) { from, _ = tx.From() pool.currentState().SetNonce(from, 10) tx.SetNonce(1) - pool.queueTx(tx) + pool.queueTx(tx.Hash(), tx) pool.checkQueue() if _, ok := pool.txs[tx.Hash()]; ok { t.Error("expected transaction to be in tx pool") @@ -97,18 +97,18 @@ func TestTransactionQueue(t *testing.T) { tx1.SignECDSA(key) tx2.SignECDSA(key) tx3.SignECDSA(key) - pool.queueTx(tx1) - pool.queueTx(tx2) - pool.queueTx(tx3) + pool.queueTx(tx1.Hash(), tx1) + pool.queueTx(tx2.Hash(), tx2) + pool.queueTx(tx3.Hash(), tx3) from, _ = tx1.From() + pool.checkQueue() if len(pool.txs) != 1 { t.Error("expected tx pool to be 1 =") } - - if len(pool.queue[from]) != 3 { - t.Error("expected transaction queue to be empty. is", len(pool.queue[from])) + if len(pool.queue[from]) != 2 { + t.Error("expected len(queue) == 2, got", len(pool.queue[from])) } } @@ -118,8 +118,8 @@ func TestRemoveTx(t *testing.T) { tx.SignECDSA(key) from, _ := tx.From() pool.currentState().AddBalance(from, big.NewInt(1)) - pool.queueTx(tx) - pool.addTx(tx) + pool.queueTx(tx.Hash(), tx) + pool.addTx(tx.Hash(), tx) if len(pool.queue) != 1 { t.Error("expected queue to be 1, got", len(pool.queue)) } -- cgit v1.2.3 From ca31d71107127a1b4f950e334ee5a99955d97e3c Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 3 Jun 2015 14:20:44 +0200 Subject: core: remove unused code from TxPool --- core/transaction_pool.go | 54 ++++++++++++------------------------------------ 1 file changed, 13 insertions(+), 41 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index d8debe1c0..fa2c4fed6 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -14,7 +14,6 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" - "gopkg.in/fatih/set.v0" ) var ( @@ -28,58 +27,31 @@ var ( ErrNegativeValue = errors.New("Negative value") ) -const txPoolQueueSize = 50 - -type TxPoolHook chan *types.Transaction -type TxMsg struct{ Tx *types.Transaction } - type stateFn func() *state.StateDB -const ( - minGasPrice = 1000000 -) - -type TxProcessor interface { - ProcessTransaction(tx *types.Transaction) -} - // The tx pool a thread safe transaction pool handler. In order to // guarantee a non blocking pool we use a queue channel which can be // independently read without needing access to the actual pool. type TxPool struct { - mu sync.RWMutex - // Queueing channel for reading and writing incoming - // transactions to - queueChan chan *types.Transaction - // Quiting channel - quit chan bool - // The state function which will allow us to do some pre checkes - currentState stateFn - // The current gas limit function callback - gasLimit func() *big.Int - // The actual pool - txs map[common.Hash]*types.Transaction - invalidHashes *set.Set + quit chan bool // Quiting channel + currentState stateFn // The state function which will allow us to do some pre checkes + gasLimit func() *big.Int // The current gas limit function callback + eventMux *event.TypeMux + mu sync.RWMutex + txs map[common.Hash]*types.Transaction // The actual pool queue map[common.Address]map[common.Hash]*types.Transaction - - subscribers []chan TxMsg - - eventMux *event.TypeMux } func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { - txPool := &TxPool{ - txs: make(map[common.Hash]*types.Transaction), - queue: make(map[common.Address]map[common.Hash]*types.Transaction), - queueChan: make(chan *types.Transaction, txPoolQueueSize), - quit: make(chan bool), - eventMux: eventMux, - invalidHashes: set.New(), - currentState: currentStateFn, - gasLimit: gasLimitFn, + return &TxPool{ + txs: make(map[common.Hash]*types.Transaction), + queue: make(map[common.Address]map[common.Hash]*types.Transaction), + quit: make(chan bool), + eventMux: eventMux, + currentState: currentStateFn, + gasLimit: gasLimitFn, } - return txPool } func (pool *TxPool) Start() { -- cgit v1.2.3 From 5721c43585100aad82a4c18341e9518c6e91393b Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 3 Jun 2015 15:23:31 +0200 Subject: core: update documentation comments for TxPool --- core/transaction_pool.go | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index fa2c4fed6..7d58ffbd9 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -17,6 +17,7 @@ import ( ) var ( + // Transaction Pool Errors ErrInvalidSender = errors.New("Invalid sender") ErrNonce = errors.New("Nonce too low") ErrBalance = errors.New("Insufficient balance") @@ -29,9 +30,13 @@ var ( type stateFn func() *state.StateDB -// The tx pool a thread safe transaction pool handler. In order to -// guarantee a non blocking pool we use a queue channel which can be -// independently read without needing access to the actual pool. +// TxPool contains all currently known transactions. Transactions +// enter the pool when they are received from the network or submitted +// locally. They exit the pool when they are included in the blockchain. +// +// The pool separates processable transactions (which can be applied to the +// current state) and future transactions. Transactions move between those +// two states over time as they are received and processed. type TxPool struct { quit chan bool // Quiting channel currentState stateFn // The state function which will allow us to do some pre checkes @@ -39,7 +44,7 @@ type TxPool struct { eventMux *event.TypeMux mu sync.RWMutex - txs map[common.Hash]*types.Transaction // The actual pool + txs map[common.Hash]*types.Transaction // processable transactions queue map[common.Address]map[common.Hash]*types.Transaction } @@ -73,7 +78,9 @@ done: } } -func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error { +// validateTx checks whether a transaction is valid according +// to the consensus rules. +func (pool *TxPool) validateTx(tx *types.Transaction) error { // Validate sender var ( from common.Address @@ -125,7 +132,7 @@ func (self *TxPool) add(tx *types.Transaction) error { if self.txs[hash] != nil { return fmt.Errorf("Known transaction (%x)", hash[:4]) } - err := self.ValidateTransaction(tx) + err := self.validateTx(tx) if err != nil { return err } @@ -148,10 +155,7 @@ func (self *TxPool) add(tx *types.Transaction) error { return nil } -func (self *TxPool) Size() int { - return len(self.txs) -} - +// Add queues a single transaction in the pool if it is valid. func (self *TxPool) Add(tx *types.Transaction) error { self.mu.Lock() defer self.mu.Unlock() @@ -159,6 +163,7 @@ func (self *TxPool) Add(tx *types.Transaction) error { return self.add(tx) } +// AddTransactions attempts to queue all valid transactions in txs. func (self *TxPool) AddTransactions(txs []*types.Transaction) { self.mu.Lock() defer self.mu.Unlock() @@ -173,9 +178,8 @@ func (self *TxPool) AddTransactions(txs []*types.Transaction) { } } -// GetTransaction allows you to check the pending and queued transaction in the -// transaction pool. -// It has two stategies, first check the pool (map) then check the queue +// GetTransaction returns a transaction if it is contained in the pool +// and nil otherwise. func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { // check the txs first if tx, ok := tp.txs[hash]; ok { @@ -190,11 +194,12 @@ func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { return nil } +// GetTransactions returns all currently processable transactions. func (self *TxPool) GetTransactions() (txs types.Transactions) { self.mu.RLock() defer self.mu.RUnlock() - txs = make(types.Transactions, self.Size()) + txs = make(types.Transactions, len(self.txs)) i := 0 for _, tx := range self.txs { txs[i] = tx @@ -203,6 +208,7 @@ func (self *TxPool) GetTransactions() (txs types.Transactions) { return txs } +// GetQueuedTransactions returns all non-processable transactions. func (self *TxPool) GetQueuedTransactions() types.Transactions { self.mu.RLock() defer self.mu.RUnlock() @@ -217,6 +223,7 @@ func (self *TxPool) GetQueuedTransactions() types.Transactions { return ret } +// RemoveTransactions removes all given transactions from the pool. func (self *TxPool) RemoveTransactions(txs types.Transactions) { self.mu.Lock() defer self.mu.Unlock() @@ -225,14 +232,9 @@ func (self *TxPool) RemoveTransactions(txs types.Transactions) { } } -func (pool *TxPool) Flush() { - pool.txs = make(map[common.Hash]*types.Transaction) -} - func (pool *TxPool) Stop() { - pool.Flush() + pool.txs = make(map[common.Hash]*types.Transaction) close(pool.quit) - glog.V(logger.Info).Infoln("TX Pool stopped") } @@ -254,7 +256,7 @@ func (pool *TxPool) addTx(hash common.Hash, tx *types.Transaction) { } } -// check queue will attempt to insert +// checkQueue moves transactions that have become processable to main pool. func (pool *TxPool) checkQueue() { pool.mu.Lock() defer pool.mu.Unlock() @@ -309,12 +311,13 @@ func (pool *TxPool) removeTx(hash common.Hash) { } } +// validatePool removes invalid and processed transactions from the main pool. func (pool *TxPool) validatePool() { pool.mu.Lock() defer pool.mu.Unlock() for hash, tx := range pool.txs { - if err := pool.ValidateTransaction(tx); err != nil { + if err := pool.validateTx(tx); err != nil { if glog.V(logger.Info) { glog.Infof("removed tx (%x) from pool: %v\n", hash[:4], err) } -- cgit v1.2.3 From ec7a2c34423f9337c238ff07c39a15eb18eed79a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 3 Jun 2015 15:52:25 +0200 Subject: core: don't remove transactions after block processing The transaction pool drops processed transactions on its own during pool maintenance. --- core/block_processor.go | 5 +---- core/block_processor_test.go | 2 +- core/chain_makers.go | 3 +-- core/chain_manager_test.go | 6 ++---- 4 files changed, 5 insertions(+), 11 deletions(-) (limited to 'core') diff --git a/core/block_processor.go b/core/block_processor.go index 9a013b280..190e72694 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -38,14 +38,12 @@ type BlockProcessor struct { // Proof of work used for validating Pow pow.PoW - txpool *TxPool - events event.Subscription eventMux *event.TypeMux } -func NewBlockProcessor(db, extra common.Database, pow pow.PoW, txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor { +func NewBlockProcessor(db, extra common.Database, pow pow.PoW, chainManager *ChainManager, eventMux *event.TypeMux) *BlockProcessor { sm := &BlockProcessor{ db: db, extraDb: extra, @@ -53,7 +51,6 @@ func NewBlockProcessor(db, extra common.Database, pow pow.PoW, txpool *TxPool, c Pow: pow, bc: chainManager, eventMux: eventMux, - txpool: txpool, } return sm diff --git a/core/block_processor_test.go b/core/block_processor_test.go index 72b173a71..b52c3d3f8 100644 --- a/core/block_processor_test.go +++ b/core/block_processor_test.go @@ -17,7 +17,7 @@ func proc() (*BlockProcessor, *ChainManager) { var mux event.TypeMux chainMan := NewChainManager(db, db, thePow(), &mux) - return NewBlockProcessor(db, db, ezp.New(), nil, chainMan, &mux), chainMan + return NewBlockProcessor(db, db, ezp.New(), chainMan, &mux), chainMan } func TestNumber(t *testing.T) { diff --git a/core/chain_makers.go b/core/chain_makers.go index 44f17cc33..3039e52da 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -124,8 +124,7 @@ func newChainManager(block *types.Block, eventMux *event.TypeMux, db common.Data // block processor with fake pow func newBlockProcessor(db common.Database, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor { chainMan := newChainManager(nil, eventMux, db) - txpool := NewTxPool(eventMux, chainMan.State, chainMan.GasLimit) - bman := NewBlockProcessor(db, db, FakePow{}, txpool, chainMan, eventMux) + bman := NewBlockProcessor(db, db, FakePow{}, chainMan, eventMux) return bman } diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go index 7dc7358c0..560e85f77 100644 --- a/core/chain_manager_test.go +++ b/core/chain_manager_test.go @@ -267,8 +267,7 @@ func TestChainInsertions(t *testing.T) { var eventMux event.TypeMux chainMan := NewChainManager(db, db, thePow(), &eventMux) - txPool := NewTxPool(&eventMux, chainMan.State, func() *big.Int { return big.NewInt(100000000) }) - blockMan := NewBlockProcessor(db, db, nil, txPool, chainMan, &eventMux) + blockMan := NewBlockProcessor(db, db, nil, chainMan, &eventMux) chainMan.SetProcessor(blockMan) const max = 2 @@ -313,8 +312,7 @@ func TestChainMultipleInsertions(t *testing.T) { } var eventMux event.TypeMux chainMan := NewChainManager(db, db, thePow(), &eventMux) - txPool := NewTxPool(&eventMux, chainMan.State, func() *big.Int { return big.NewInt(100000000) }) - blockMan := NewBlockProcessor(db, db, nil, txPool, chainMan, &eventMux) + blockMan := NewBlockProcessor(db, db, nil, chainMan, &eventMux) chainMan.SetProcessor(blockMan) done := make(chan bool, max) for i, chain := range chains { -- cgit v1.2.3 From d09a6e54215bef8b1ac16a99f0b1d75a8a92a6a8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 3 Jun 2015 22:22:20 +0200 Subject: core, eth, miner: moved nonce management to tx pool. Removed the managed tx state from the chain manager to the transaction pool where it's much easier to keep track of nonces (and manage them). The transaction pool now also uses the queue and pending txs differently where queued txs are now moved over to the pending queue (i.e. txs ready for processing and propagation). --- core/chain_manager.go | 21 +---------- core/transaction_pool.go | 88 +++++++++++++++++++++++++------------------ core/transaction_pool_test.go | 20 +++++----- 3 files changed, 62 insertions(+), 67 deletions(-) (limited to 'core') diff --git a/core/chain_manager.go b/core/chain_manager.go index d58c0d504..d14a19fea 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -214,19 +214,6 @@ func (self *ChainManager) TransState() *state.StateDB { return self.transState } -func (self *ChainManager) TxState() *state.ManagedState { - self.tsmu.RLock() - defer self.tsmu.RUnlock() - - return self.txState -} - -func (self *ChainManager) setTxState(statedb *state.StateDB) { - self.tsmu.Lock() - defer self.tsmu.Unlock() - self.txState = state.ManageState(statedb) -} - func (self *ChainManager) setTransState(statedb *state.StateDB) { self.transState = statedb } @@ -751,7 +738,7 @@ out: case ev := <-events.Chan(): switch ev := ev.(type) { case queueEvent: - for i, event := range ev.queue { + for _, event := range ev.queue { switch event := event.(type) { case ChainEvent: // We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long @@ -760,12 +747,6 @@ out: self.currentGasLimit = CalcGasLimit(event.Block) self.eventMux.Post(ChainHeadEvent{event.Block}) } - case ChainSplitEvent: - // On chain splits we need to reset the transaction state. We can't be sure whether the actual - // state of the accounts are still valid. - if i == ev.splitCount { - self.setTxState(state.New(event.Block.Root(), self.stateDb)) - } } self.eventMux.Post(event) diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 7d58ffbd9..5e6f2c6a4 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -6,7 +6,6 @@ import ( "math/big" "sort" "sync" - "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" @@ -38,10 +37,12 @@ type stateFn func() *state.StateDB // current state) and future transactions. Transactions move between those // two states over time as they are received and processed. type TxPool struct { - quit chan bool // Quiting channel - currentState stateFn // The state function which will allow us to do some pre checkes + quit chan bool // Quiting channel + currentState stateFn // The state function which will allow us to do some pre checkes + state *state.ManagedState gasLimit func() *big.Int // The current gas limit function callback eventMux *event.TypeMux + events event.Subscription mu sync.RWMutex txs map[common.Hash]*types.Transaction // processable transactions @@ -56,28 +57,41 @@ func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func( eventMux: eventMux, currentState: currentStateFn, gasLimit: gasLimitFn, + state: state.ManageState(currentStateFn()), } } func (pool *TxPool) Start() { - // Queue timer will tick so we can attempt to move items from the queue to the - // main transaction pool. - queueTimer := time.NewTicker(300 * time.Millisecond) - // Removal timer will tick and attempt to remove bad transactions (account.nonce>tx.nonce) - removalTimer := time.NewTicker(1 * time.Second) -done: - for { - select { - case <-queueTimer.C: - pool.checkQueue() - case <-removalTimer.C: - pool.validatePool() - case <-pool.quit: - break done + pool.events = pool.eventMux.Subscribe(ChainEvent{}) + for _ = range pool.events.Chan() { + pool.mu.Lock() + pool.state = state.ManageState(pool.currentState()) + + for _, tx := range pool.txs { + if addr, err := tx.From(); err == nil { + pool.state.SetNonce(addr, tx.Nonce()) + } } + + pool.checkQueue() + pool.mu.Unlock() } } +func (pool *TxPool) Stop() { + pool.txs = make(map[common.Hash]*types.Transaction) + close(pool.quit) + pool.events.Unsubscribe() + glog.V(logger.Info).Infoln("TX Pool stopped") +} + +func (pool *TxPool) State() *state.ManagedState { + pool.mu.RLock() + defer pool.mu.RUnlock() + + return pool.state +} + // validateTx checks whether a transaction is valid according // to the consensus rules. func (pool *TxPool) validateTx(tx *types.Transaction) error { @@ -152,6 +166,9 @@ func (self *TxPool) add(tx *types.Transaction) error { glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, hash) } + // check and validate the queueue + self.checkQueue() + return nil } @@ -196,8 +213,13 @@ func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { // GetTransactions returns all currently processable transactions. func (self *TxPool) GetTransactions() (txs types.Transactions) { - self.mu.RLock() - defer self.mu.RUnlock() + self.mu.Lock() + defer self.mu.Unlock() + + // check queue first + self.checkQueue() + // invalidate any txs + self.validatePool() txs = make(types.Transactions, len(self.txs)) i := 0 @@ -232,12 +254,6 @@ func (self *TxPool) RemoveTransactions(txs types.Transactions) { } } -func (pool *TxPool) Stop() { - pool.txs = make(map[common.Hash]*types.Transaction) - close(pool.quit) - glog.V(logger.Info).Infoln("TX Pool stopped") -} - func (self *TxPool) queueTx(hash common.Hash, tx *types.Transaction) { from, _ := tx.From() // already validated if self.queue[from] == nil { @@ -246,9 +262,11 @@ func (self *TxPool) queueTx(hash common.Hash, tx *types.Transaction) { self.queue[from][hash] = tx } -func (pool *TxPool) addTx(hash common.Hash, tx *types.Transaction) { +func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Transaction) { if _, ok := pool.txs[hash]; !ok { pool.txs[hash] = tx + + pool.state.SetNonce(addr, tx.AccountNonce) // Notify the subscribers. This event is posted in a goroutine // because it's possible that somewhere during the post "Remove transaction" // gets called which will then wait for the global tx pool lock and deadlock. @@ -258,34 +276,32 @@ func (pool *TxPool) addTx(hash common.Hash, tx *types.Transaction) { // checkQueue moves transactions that have become processable to main pool. func (pool *TxPool) checkQueue() { - pool.mu.Lock() - defer pool.mu.Unlock() + state := pool.state - statedb := pool.currentState() var addq txQueue for address, txs := range pool.queue { - curnonce := statedb.GetNonce(address) + curnonce := state.GetNonce(address) addq := addq[:0] for hash, tx := range txs { if tx.AccountNonce < curnonce { + fmt.Println("delete the tx", tx.AccountNonce, curnonce) // Drop queued transactions whose nonce is lower than // the account nonce because they have been processed. delete(txs, hash) } else { // Collect the remaining transactions for the next pass. - addq = append(addq, txQueueEntry{hash, tx}) + addq = append(addq, txQueueEntry{hash, address, tx}) } } // Find the next consecutive nonce range starting at the // current account nonce. sort.Sort(addq) for _, e := range addq { - if e.AccountNonce != curnonce { + if e.AccountNonce > curnonce+1 { break } - curnonce++ delete(txs, e.hash) - pool.addTx(e.hash, e.Transaction) + pool.addTx(e.hash, address, e.Transaction) } // Delete the entire queue entry if it became empty. if len(txs) == 0 { @@ -313,9 +329,6 @@ func (pool *TxPool) removeTx(hash common.Hash) { // validatePool removes invalid and processed transactions from the main pool. func (pool *TxPool) validatePool() { - pool.mu.Lock() - defer pool.mu.Unlock() - for hash, tx := range pool.txs { if err := pool.validateTx(tx); err != nil { if glog.V(logger.Info) { @@ -330,6 +343,7 @@ type txQueue []txQueueEntry type txQueueEntry struct { hash common.Hash + addr common.Address *types.Transaction } diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go index 600fd9b4f..170bdfa72 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -37,21 +37,21 @@ func TestInvalidTransactions(t *testing.T) { } from, _ := tx.From() - pool.currentState().AddBalance(from, big.NewInt(1)) + pool.state.AddBalance(from, big.NewInt(1)) err = pool.Add(tx) if err != ErrInsufficientFunds { t.Error("expected", ErrInsufficientFunds) } balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(tx.Gas(), tx.GasPrice())) - pool.currentState().AddBalance(from, balance) + pool.state.AddBalance(from, balance) err = pool.Add(tx) if err != ErrIntrinsicGas { t.Error("expected", ErrIntrinsicGas, "got", err) } - pool.currentState().SetNonce(from, 1) - pool.currentState().AddBalance(from, big.NewInt(0xffffffffffffff)) + pool.state.SetNonce(from, 1) + pool.state.AddBalance(from, big.NewInt(0xffffffffffffff)) tx.GasLimit = big.NewInt(100000) tx.Price = big.NewInt(1) tx.SignECDSA(key) @@ -67,7 +67,7 @@ func TestTransactionQueue(t *testing.T) { tx := transaction() tx.SignECDSA(key) from, _ := tx.From() - pool.currentState().AddBalance(from, big.NewInt(1)) + pool.state.AddBalance(from, big.NewInt(1)) pool.queueTx(tx.Hash(), tx) pool.checkQueue() @@ -76,17 +76,17 @@ func TestTransactionQueue(t *testing.T) { } tx = transaction() + tx.SetNonce(1) tx.SignECDSA(key) from, _ = tx.From() - pool.currentState().SetNonce(from, 10) - tx.SetNonce(1) + pool.state.SetNonce(from, 2) pool.queueTx(tx.Hash(), tx) pool.checkQueue() if _, ok := pool.txs[tx.Hash()]; ok { t.Error("expected transaction to be in tx pool") } - if len(pool.queue[from]) != 0 { + if len(pool.queue[from]) > 0 { t.Error("expected transaction queue to be empty. is", len(pool.queue[from])) } @@ -117,7 +117,7 @@ func TestRemoveTx(t *testing.T) { tx := transaction() tx.SignECDSA(key) from, _ := tx.From() - pool.currentState().AddBalance(from, big.NewInt(1)) + pool.state.AddBalance(from, big.NewInt(1)) pool.queueTx(tx.Hash(), tx) pool.addTx(tx.Hash(), tx) if len(pool.queue) != 1 { @@ -146,7 +146,7 @@ func TestNegativeValue(t *testing.T) { tx.Value().Set(big.NewInt(-1)) tx.SignECDSA(key) from, _ := tx.From() - pool.currentState().AddBalance(from, big.NewInt(1)) + pool.state.AddBalance(from, big.NewInt(1)) err := pool.Add(tx) if err != ErrNegativeValue { t.Error("expected", ErrNegativeValue, "got", err) -- cgit v1.2.3 From 140d8839018527fe64a0c1a6b79af4ccae66ec3a Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 3 Jun 2015 22:53:33 +0200 Subject: core: test updates --- core/transaction_pool.go | 1 - core/transaction_pool_test.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 5e6f2c6a4..ba0fba91f 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -284,7 +284,6 @@ func (pool *TxPool) checkQueue() { addq := addq[:0] for hash, tx := range txs { if tx.AccountNonce < curnonce { - fmt.Println("delete the tx", tx.AccountNonce, curnonce) // Drop queued transactions whose nonce is lower than // the account nonce because they have been processed. delete(txs, hash) diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go index 170bdfa72..df5af9429 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -119,7 +119,7 @@ func TestRemoveTx(t *testing.T) { from, _ := tx.From() pool.state.AddBalance(from, big.NewInt(1)) pool.queueTx(tx.Hash(), tx) - pool.addTx(tx.Hash(), tx) + pool.addTx(tx.Hash(), from, tx) if len(pool.queue) != 1 { t.Error("expected queue to be 1, got", len(pool.queue)) } -- cgit v1.2.3 From 9dd12a64a7dc998e32ee1bcf6b23d9d55cf6e6c0 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 4 Jun 2015 12:47:46 +0200 Subject: core: renamed txs to pending --- core/transaction_pool.go | 32 ++++++++++++++++---------------- core/transaction_pool_test.go | 16 ++++++++-------- 2 files changed, 24 insertions(+), 24 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index ba0fba91f..c59eaa061 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -44,14 +44,14 @@ type TxPool struct { eventMux *event.TypeMux events event.Subscription - mu sync.RWMutex - txs map[common.Hash]*types.Transaction // processable transactions - queue map[common.Address]map[common.Hash]*types.Transaction + mu sync.RWMutex + pending map[common.Hash]*types.Transaction // processable transactions + queue map[common.Address]map[common.Hash]*types.Transaction } func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { return &TxPool{ - txs: make(map[common.Hash]*types.Transaction), + pending: make(map[common.Hash]*types.Transaction), queue: make(map[common.Address]map[common.Hash]*types.Transaction), quit: make(chan bool), eventMux: eventMux, @@ -67,7 +67,7 @@ func (pool *TxPool) Start() { pool.mu.Lock() pool.state = state.ManageState(pool.currentState()) - for _, tx := range pool.txs { + for _, tx := range pool.pending { if addr, err := tx.From(); err == nil { pool.state.SetNonce(addr, tx.Nonce()) } @@ -79,7 +79,7 @@ func (pool *TxPool) Start() { } func (pool *TxPool) Stop() { - pool.txs = make(map[common.Hash]*types.Transaction) + pool.pending = make(map[common.Hash]*types.Transaction) close(pool.quit) pool.events.Unsubscribe() glog.V(logger.Info).Infoln("TX Pool stopped") @@ -143,7 +143,7 @@ func (self *TxPool) add(tx *types.Transaction) error { return fmt.Errorf("Invalid transaction (%x)", hash[:4]) } */ - if self.txs[hash] != nil { + if self.pending[hash] != nil { return fmt.Errorf("Known transaction (%x)", hash[:4]) } err := self.validateTx(tx) @@ -199,7 +199,7 @@ func (self *TxPool) AddTransactions(txs []*types.Transaction) { // and nil otherwise. func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { // check the txs first - if tx, ok := tp.txs[hash]; ok { + if tx, ok := tp.pending[hash]; ok { return tx } // check queue @@ -221,9 +221,9 @@ func (self *TxPool) GetTransactions() (txs types.Transactions) { // invalidate any txs self.validatePool() - txs = make(types.Transactions, len(self.txs)) + txs = make(types.Transactions, len(self.pending)) i := 0 - for _, tx := range self.txs { + for _, tx := range self.pending { txs[i] = tx i++ } @@ -263,8 +263,8 @@ func (self *TxPool) queueTx(hash common.Hash, tx *types.Transaction) { } func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Transaction) { - if _, ok := pool.txs[hash]; !ok { - pool.txs[hash] = tx + if _, ok := pool.pending[hash]; !ok { + pool.pending[hash] = tx pool.state.SetNonce(addr, tx.AccountNonce) // Notify the subscribers. This event is posted in a goroutine @@ -311,7 +311,7 @@ func (pool *TxPool) checkQueue() { func (pool *TxPool) removeTx(hash common.Hash) { // delete from pending pool - delete(pool.txs, hash) + delete(pool.pending, hash) // delete from queue for address, txs := range pool.queue { if _, ok := txs[hash]; ok { @@ -328,12 +328,12 @@ func (pool *TxPool) removeTx(hash common.Hash) { // validatePool removes invalid and processed transactions from the main pool. func (pool *TxPool) validatePool() { - for hash, tx := range pool.txs { + for hash, tx := range pool.pending { if err := pool.validateTx(tx); err != nil { - if glog.V(logger.Info) { + if glog.V(logger.Core) { glog.Infof("removed tx (%x) from pool: %v\n", hash[:4], err) } - delete(pool.txs, hash) + delete(pool.pending, hash) } } } diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go index df5af9429..bbd5ddad4 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -71,8 +71,8 @@ func TestTransactionQueue(t *testing.T) { pool.queueTx(tx.Hash(), tx) pool.checkQueue() - if len(pool.txs) != 1 { - t.Error("expected valid txs to be 1 is", len(pool.txs)) + if len(pool.pending) != 1 { + t.Error("expected valid txs to be 1 is", len(pool.pending)) } tx = transaction() @@ -82,7 +82,7 @@ func TestTransactionQueue(t *testing.T) { pool.state.SetNonce(from, 2) pool.queueTx(tx.Hash(), tx) pool.checkQueue() - if _, ok := pool.txs[tx.Hash()]; ok { + if _, ok := pool.pending[tx.Hash()]; ok { t.Error("expected transaction to be in tx pool") } @@ -104,7 +104,7 @@ func TestTransactionQueue(t *testing.T) { pool.checkQueue() - if len(pool.txs) != 1 { + if len(pool.pending) != 1 { t.Error("expected tx pool to be 1 =") } if len(pool.queue[from]) != 2 { @@ -124,8 +124,8 @@ func TestRemoveTx(t *testing.T) { t.Error("expected queue to be 1, got", len(pool.queue)) } - if len(pool.txs) != 1 { - t.Error("expected txs to be 1, got", len(pool.txs)) + if len(pool.pending) != 1 { + t.Error("expected txs to be 1, got", len(pool.pending)) } pool.removeTx(tx.Hash()) @@ -134,8 +134,8 @@ func TestRemoveTx(t *testing.T) { t.Error("expected queue to be 0, got", len(pool.queue)) } - if len(pool.txs) > 0 { - t.Error("expected txs to be 0, got", len(pool.txs)) + if len(pool.pending) > 0 { + t.Error("expected txs to be 0, got", len(pool.pending)) } } -- cgit v1.2.3 From cf5ad266f6b8b7dd4800b63404c0efe680d47673 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 4 Jun 2015 15:44:42 +0200 Subject: core: only change the nonce if the account nonce is lower --- core/transaction_pool.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index c59eaa061..a0f3c326a 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -69,7 +69,9 @@ func (pool *TxPool) Start() { for _, tx := range pool.pending { if addr, err := tx.From(); err == nil { - pool.state.SetNonce(addr, tx.Nonce()) + if pool.state.GetNonce(addr) < tx.Nonce() { + pool.state.SetNonce(addr, tx.Nonce()) + } } } -- cgit v1.2.3 From dcdb4554d7f7ea3e96fec57805f0ea1042d0abc7 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 4 Jun 2015 16:19:22 +0200 Subject: core: documented changes in tx pool --- core/transaction_pool.go | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index a0f3c326a..462159fa7 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -62,19 +62,32 @@ func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func( } func (pool *TxPool) Start() { + // Track chain events. When a chain events occurs (new chain canon block) + // we need to know the new state. The new state will help us determine + // the nonces in the managed state pool.events = pool.eventMux.Subscribe(ChainEvent{}) for _ = range pool.events.Chan() { pool.mu.Lock() pool.state = state.ManageState(pool.currentState()) + // validate the pool of pending transactions, this will remove + // any transactions that have been included in the block or + // have been invalidated because of another transaction (e.g. + // higher gas price) + pool.validatePool() + + // Loop over the pending transactions and base the nonce of the new + // pending transaction set. for _, tx := range pool.pending { if addr, err := tx.From(); err == nil { - if pool.state.GetNonce(addr) < tx.Nonce() { - pool.state.SetNonce(addr, tx.Nonce()) - } + // Set the nonce. Transaction nonce can never be lower + // than the state nonce; validatePool took care of that. + pool.state.SetNonce(addr, tx.Nonce()) } } + // Check the queue and move transactions over to the pending if possible + // or remove those that have become invalid pool.checkQueue() pool.mu.Unlock() } @@ -103,32 +116,46 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { err error ) + // Validate the transaction sender and it's sig. Throw + // if the from fields is invalid. if from, err = tx.From(); err != nil { return ErrInvalidSender } + // Make sure the account exist. Non existant accounts + // haven't got funds and well therefor never pass. if !pool.currentState().HasAccount(from) { return ErrNonExistentAccount } + // Check the transaction doesn't exceed the current + // block limit gas. if pool.gasLimit().Cmp(tx.GasLimit) < 0 { return ErrGasLimit } + // Transactions can't be negative. This may never happen + // using RLP decoded transactions but may occur if you create + // a transaction using the RPC for example. if tx.Amount.Cmp(common.Big0) < 0 { return ErrNegativeValue } + // Transactor should have enough funds to cover the costs + // cost == V + GP * GL total := new(big.Int).Mul(tx.Price, tx.GasLimit) total.Add(total, tx.Value()) if pool.currentState().GetBalance(from).Cmp(total) < 0 { return ErrInsufficientFunds } + // Should supply enough intrinsic gas if tx.GasLimit.Cmp(IntrinsicGas(tx)) < 0 { return ErrIntrinsicGas } + // Last but not least check for nonce errors (intensive + // operation, saved for last) if pool.currentState().GetNonce(from) > tx.Nonce() { return ErrNonce } -- cgit v1.2.3 From 0f51ee6c88f0697cec368d6e2c88b35cc173e37a Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 4 Jun 2015 16:52:23 +0200 Subject: crypto: return common.Address rather than raw bytes --- core/types/transaction_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'core') diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index dada424e9..492059c28 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -64,7 +64,7 @@ func decodeTx(data []byte) (*Transaction, error) { return &tx, rlp.Decode(bytes.NewReader(data), &tx) } -func defaultTestKey() (*ecdsa.PrivateKey, []byte) { +func defaultTestKey() (*ecdsa.PrivateKey, common.Address) { key := crypto.ToECDSA(common.Hex2Bytes("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")) addr := crypto.PubkeyToAddress(key.PublicKey) return key, addr @@ -85,7 +85,7 @@ func TestRecipientEmpty(t *testing.T) { t.FailNow() } - if !bytes.Equal(addr, from.Bytes()) { + if addr != from { t.Error("derived address doesn't match") } } @@ -105,7 +105,7 @@ func TestRecipientNormal(t *testing.T) { t.FailNow() } - if !bytes.Equal(addr, from.Bytes()) { + if addr != from { t.Error("derived address doesn't match") } } -- cgit v1.2.3 From 912cf7ba049e4bcd5e497c62bb7cb96e7502f1b9 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 4 Jun 2015 17:28:09 +0200 Subject: core: added fork test & double nonce test --- core/transaction_pool.go | 46 ++++++++++++++++++++++------------------ core/transaction_pool_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 20 deletions(-) (limited to 'core') diff --git a/core/transaction_pool.go b/core/transaction_pool.go index 462159fa7..27dc1b0d1 100644 --- a/core/transaction_pool.go +++ b/core/transaction_pool.go @@ -68,31 +68,37 @@ func (pool *TxPool) Start() { pool.events = pool.eventMux.Subscribe(ChainEvent{}) for _ = range pool.events.Chan() { pool.mu.Lock() - pool.state = state.ManageState(pool.currentState()) - - // validate the pool of pending transactions, this will remove - // any transactions that have been included in the block or - // have been invalidated because of another transaction (e.g. - // higher gas price) - pool.validatePool() - - // Loop over the pending transactions and base the nonce of the new - // pending transaction set. - for _, tx := range pool.pending { - if addr, err := tx.From(); err == nil { - // Set the nonce. Transaction nonce can never be lower - // than the state nonce; validatePool took care of that. - pool.state.SetNonce(addr, tx.Nonce()) - } - } - // Check the queue and move transactions over to the pending if possible - // or remove those that have become invalid - pool.checkQueue() + pool.resetState() + pool.mu.Unlock() } } +func (pool *TxPool) resetState() { + pool.state = state.ManageState(pool.currentState()) + + // validate the pool of pending transactions, this will remove + // any transactions that have been included in the block or + // have been invalidated because of another transaction (e.g. + // higher gas price) + pool.validatePool() + + // Loop over the pending transactions and base the nonce of the new + // pending transaction set. + for _, tx := range pool.pending { + if addr, err := tx.From(); err == nil { + // Set the nonce. Transaction nonce can never be lower + // than the state nonce; validatePool took care of that. + pool.state.SetNonce(addr, tx.Nonce()) + } + } + + // Check the queue and move transactions over to the pending if possible + // or remove those that have become invalid + pool.checkQueue() +} + func (pool *TxPool) Stop() { pool.pending = make(map[common.Hash]*types.Transaction) close(pool.quit) diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go index bbd5ddad4..ac297d266 100644 --- a/core/transaction_pool_test.go +++ b/core/transaction_pool_test.go @@ -152,3 +152,52 @@ func TestNegativeValue(t *testing.T) { t.Error("expected", ErrNegativeValue, "got", err) } } + +func TestTransactionChainFork(t *testing.T) { + pool, key := setupTxPool() + addr := crypto.PubkeyToAddress(key.PublicKey) + pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) + tx := transaction() + tx.GasLimit = big.NewInt(100000) + tx.SignECDSA(key) + + err := pool.add(tx) + if err != nil { + t.Error("didn't expect error", err) + } + pool.RemoveTransactions([]*types.Transaction{tx}) + + // reset the pool's internal state + pool.resetState() + err = pool.add(tx) + if err != nil { + t.Error("didn't expect error", err) + } +} + +func TestTransactionDoubleNonce(t *testing.T) { + pool, key := setupTxPool() + addr := crypto.PubkeyToAddress(key.PublicKey) + pool.currentState().AddBalance(addr, big.NewInt(100000000000000)) + tx := transaction() + tx.GasLimit = big.NewInt(100000) + tx.SignECDSA(key) + + err := pool.add(tx) + if err != nil { + t.Error("didn't expect error", err) + } + + tx2 := transaction() + tx2.GasLimit = big.NewInt(1000000) + tx2.SignECDSA(key) + + err = pool.add(tx2) + if err != nil { + t.Error("didn't expect error", err) + } + + if len(pool.pending) != 2 { + t.Error("expected 2 pending txs. Got", len(pool.pending)) + } +} -- cgit v1.2.3