diff options
Diffstat (limited to 'core/vm/evm')
32 files changed, 11563 insertions, 0 deletions
diff --git a/core/vm/evm/analysis.go b/core/vm/evm/analysis.go new file mode 100644 index 000000000..5e430f965 --- /dev/null +++ b/core/vm/evm/analysis.go @@ -0,0 +1,62 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +// bitvec is a bit vector which maps bytes in a program. +// An unset bit means the byte is an opcode, a set bit means +// it's data (i.e. argument of PUSHxx). +type bitvec []byte + +func (bits *bitvec) set(pos uint64) { + (*bits)[pos/8] |= 0x80 >> (pos % 8) +} +func (bits *bitvec) set8(pos uint64) { + (*bits)[pos/8] |= 0xFF >> (pos % 8) + (*bits)[pos/8+1] |= ^(0xFF >> (pos % 8)) +} + +// codeSegment checks if the position is in a code segment. +func (bits *bitvec) codeSegment(pos uint64) bool { + return ((*bits)[pos/8] & (0x80 >> (pos % 8))) == 0 +} + +// codeBitmap collects data locations in code. +func codeBitmap(code []byte) bitvec { + // The bitmap is 4 bytes longer than necessary, in case the code + // ends with a PUSH32, the algorithm will push zeroes onto the + // bitvector outside the bounds of the actual code. + bits := make(bitvec, len(code)/8+1+4) + for pc := uint64(0); pc < uint64(len(code)); { + op := OpCode(code[pc]) + + if op >= PUSH1 && op <= PUSH32 { + numbits := op - PUSH1 + 1 + pc++ + for ; numbits >= 8; numbits -= 8 { + bits.set8(pc) // 8 + pc += 8 + } + for ; numbits > 0; numbits-- { + bits.set(pc) + pc++ + } + } else { + pc++ + } + } + return bits +} diff --git a/core/vm/evm/analysis_test.go b/core/vm/evm/analysis_test.go new file mode 100644 index 000000000..6805f6076 --- /dev/null +++ b/core/vm/evm/analysis_test.go @@ -0,0 +1,75 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "testing" + + "github.com/dexon-foundation/dexon/crypto" +) + +func TestJumpDestAnalysis(t *testing.T) { + tests := []struct { + code []byte + exp byte + which int + }{ + {[]byte{byte(PUSH1), 0x01, 0x01, 0x01}, 0x40, 0}, + {[]byte{byte(PUSH1), byte(PUSH1), byte(PUSH1), byte(PUSH1)}, 0x50, 0}, + {[]byte{byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), 0x01, 0x01, 0x01}, 0x7F, 0}, + {[]byte{byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x80, 1}, + {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), byte(PUSH2), byte(PUSH2), 0x01, 0x01, 0x01}, 0x03, 0}, + {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), 0x01, 0x01, 0x01, 0x01, 0x01}, 0x00, 1}, + {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x74, 0}, + {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x00, 1}, + {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x3F, 0}, + {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0xC0, 1}, + {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x7F, 0}, + {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0xFF, 1}, + {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x80, 2}, + {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0x7f, 0}, + {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0xA0, 1}, + {[]byte{byte(PUSH32)}, 0x7F, 0}, + {[]byte{byte(PUSH32)}, 0xFF, 1}, + {[]byte{byte(PUSH32)}, 0xFF, 2}, + } + for _, test := range tests { + ret := codeBitmap(test.code) + if ret[test.which] != test.exp { + t.Fatalf("expected %x, got %02x", test.exp, ret[test.which]) + } + } +} + +func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) { + // 1.4 ms + code := make([]byte, 1200000) + bench.ResetTimer() + for i := 0; i < bench.N; i++ { + codeBitmap(code) + } + bench.StopTimer() +} +func BenchmarkJumpdestHashing_1200k(bench *testing.B) { + // 4 ms + code := make([]byte, 1200000) + bench.ResetTimer() + for i := 0; i < bench.N; i++ { + crypto.Keccak256Hash(code) + } + bench.StopTimer() +} diff --git a/core/vm/evm/contract.go b/core/vm/evm/contract.go new file mode 100644 index 000000000..a7ce2ddfd --- /dev/null +++ b/core/vm/evm/contract.go @@ -0,0 +1,184 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "math/big" + + "github.com/dexon-foundation/dexon/common" +) + +// ContractRef is a reference to the contract's backing object +type ContractRef interface { + Address() common.Address +} + +// AccountRef implements ContractRef. +// +// Account references are used during EVM initialisation and +// it's primary use is to fetch addresses. Removing this object +// proves difficult because of the cached jump destinations which +// are fetched from the parent contract (i.e. the caller), which +// is a ContractRef. +type AccountRef common.Address + +// Address casts AccountRef to a Address +func (ar AccountRef) Address() common.Address { return (common.Address)(ar) } + +// Contract represents an ethereum contract in the state database. It contains +// the contract code, calling arguments. Contract implements ContractRef +type Contract struct { + // CallerAddress is the result of the caller which initialised this + // contract. However when the "call method" is delegated this value + // needs to be initialised to that of the caller's caller. + CallerAddress common.Address + caller ContractRef + self ContractRef + + jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis. + analysis bitvec // Locally cached result of JUMPDEST analysis + + Code []byte + CodeHash common.Hash + CodeAddr *common.Address + Input []byte + + Gas uint64 + value *big.Int +} + +// NewContract returns a new contract environment for the execution of EVM. +func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract { + c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object} + + if parent, ok := caller.(*Contract); ok { + // Reuse JUMPDEST analysis from parent context if available. + c.jumpdests = parent.jumpdests + } else { + c.jumpdests = make(map[common.Hash]bitvec) + } + + // 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 + // ensures a value is set + c.value = value + + return c +} + +func (c *Contract) validJumpdest(dest *big.Int) bool { + udest := dest.Uint64() + // PC cannot go beyond len(code) and certainly can't be bigger than 63bits. + // Don't bother checking for JUMPDEST in that case. + if dest.BitLen() >= 63 || udest >= uint64(len(c.Code)) { + return false + } + // Only JUMPDESTs allowed for destinations + if OpCode(c.Code[udest]) != JUMPDEST { + return false + } + // Do we have a contract hash already? + if c.CodeHash != (common.Hash{}) { + // Does parent context have the analysis? + analysis, exist := c.jumpdests[c.CodeHash] + if !exist { + // Do the analysis and save in parent context + // We do not need to store it in c.analysis + analysis = codeBitmap(c.Code) + c.jumpdests[c.CodeHash] = analysis + } + return analysis.codeSegment(udest) + } + // We don't have the code hash, most likely a piece of initcode not already + // in state trie. In that case, we do an analysis, and save it locally, so + // we don't have to recalculate it for every JUMP instruction in the execution + // However, we don't save it within the parent context + if c.analysis == nil { + c.analysis = codeBitmap(c.Code) + } + return c.analysis.codeSegment(udest) +} + +// AsDelegate sets the contract to be a delegate call and returns the current +// contract (for chaining calls) +func (c *Contract) AsDelegate() *Contract { + // NOTE: caller must, at all times be a contract. It should never happen + // that caller is something other than a Contract. + parent := c.caller.(*Contract) + c.CallerAddress = parent.CallerAddress + c.value = parent.value + + return c +} + +// GetOp returns the n'th element in the contract's byte array +func (c *Contract) GetOp(n uint64) OpCode { + return OpCode(c.GetByte(n)) +} + +// GetByte returns the n'th byte in the contract's byte array +func (c *Contract) GetByte(n uint64) byte { + if n < uint64(len(c.Code)) { + return c.Code[n] + } + + return 0 +} + +// Caller returns the caller of the contract. +// +// Caller will recursively call caller when the contract is a delegate +// call, including that of caller's caller. +func (c *Contract) Caller() common.Address { + return c.CallerAddress +} + +// UseGas attempts the use gas and subtracts it and returns true on success +func (c *Contract) UseGas(gas uint64) (ok bool) { + if c.Gas < gas { + return false + } + c.Gas -= gas + return true +} + +// Address returns the contracts address +func (c *Contract) Address() common.Address { + return c.self.Address() +} + +// Value returns the contracts value (sent to it from it's caller) +func (c *Contract) Value() *big.Int { + return c.value +} + +// SetCallCode sets the code of the contract and address of the backing data +// object +func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) { + c.Code = code + c.CodeHash = hash + c.CodeAddr = addr +} + +// SetCodeOptionalHash can be used to provide code, but it's optional to provide hash. +// In case hash is not provided, the jumpdest analysis will not be saved to the parent context +func (c *Contract) SetCodeOptionalHash(addr *common.Address, codeAndHash *codeAndHash) { + c.Code = codeAndHash.code + c.CodeHash = codeAndHash.hash + c.CodeAddr = addr +} diff --git a/core/vm/evm/contracts.go b/core/vm/evm/contracts.go new file mode 100644 index 000000000..52bb0a83b --- /dev/null +++ b/core/vm/evm/contracts.go @@ -0,0 +1,361 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "crypto/sha256" + "errors" + "math/big" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/common/math" + "github.com/dexon-foundation/dexon/core/vm" + "github.com/dexon-foundation/dexon/crypto" + "github.com/dexon-foundation/dexon/crypto/bn256" + "github.com/dexon-foundation/dexon/params" + "golang.org/x/crypto/ripemd160" +) + +// PrecompiledContract is the basic interface for native Go contracts. The implementation +// requires a deterministic gas count based on the input size of the Run method of the +// contract. +type PrecompiledContract interface { + RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use + Run(input []byte) ([]byte, error) // Run runs the precompiled contract +} + +// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum +// contracts used in the Frontier and Homestead releases. +var PrecompiledContractsHomestead = map[common.Address]PrecompiledContract{ + common.BytesToAddress([]byte{1}): &ecrecover{}, + common.BytesToAddress([]byte{2}): &sha256hash{}, + common.BytesToAddress([]byte{3}): &ripemd160hash{}, + common.BytesToAddress([]byte{4}): &dataCopy{}, +} + +// PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum +// contracts used in the Byzantium release. +var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{ + common.BytesToAddress([]byte{1}): &ecrecover{}, + common.BytesToAddress([]byte{2}): &sha256hash{}, + common.BytesToAddress([]byte{3}): &ripemd160hash{}, + common.BytesToAddress([]byte{4}): &dataCopy{}, + common.BytesToAddress([]byte{5}): &bigModExp{}, + common.BytesToAddress([]byte{6}): &bn256Add{}, + common.BytesToAddress([]byte{7}): &bn256ScalarMul{}, + common.BytesToAddress([]byte{8}): &bn256Pairing{}, +} + +// RunPrecompiledContract runs and evaluates the output of a precompiled contract. +func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) { + gas := p.RequiredGas(input) + if contract.UseGas(gas) { + return p.Run(input) + } + return nil, vm.ErrOutOfGas +} + +// ECRECOVER implemented as a native contract. +type ecrecover struct{} + +func (c *ecrecover) RequiredGas(input []byte) uint64 { + return params.EcrecoverGas +} + +func (c *ecrecover) Run(input []byte) ([]byte, error) { + const ecRecoverInputLength = 128 + + input = common.RightPadBytes(input, ecRecoverInputLength) + // "input" is (hash, v, r, s), each 32 bytes + // but for ecrecover we want (r, s, v) + + r := new(big.Int).SetBytes(input[64:96]) + s := new(big.Int).SetBytes(input[96:128]) + v := input[63] - 27 + + // tighter sig s values input homestead only apply to tx sigs + if !vm.AllZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) { + return nil, nil + } + // v needs to be at the end for libsecp256k1 + pubKey, err := crypto.Ecrecover(input[:32], append(input[64:128], v)) + // make sure the public key is a valid one + if err != nil { + return nil, nil + } + + // the first byte of pubkey is bitcoin heritage + return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32), nil +} + +// SHA256 implemented as a native contract. +type sha256hash struct{} + +// RequiredGas returns the gas required to execute the pre-compiled contract. +// +// This method does not require any overflow checking as the input size gas costs +// required for anything significant is so high it's impossible to pay for. +func (c *sha256hash) RequiredGas(input []byte) uint64 { + return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas +} +func (c *sha256hash) Run(input []byte) ([]byte, error) { + h := sha256.Sum256(input) + return h[:], nil +} + +// RIPEMD160 implemented as a native contract. +type ripemd160hash struct{} + +// RequiredGas returns the gas required to execute the pre-compiled contract. +// +// This method does not require any overflow checking as the input size gas costs +// required for anything significant is so high it's impossible to pay for. +func (c *ripemd160hash) RequiredGas(input []byte) uint64 { + return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas +} +func (c *ripemd160hash) Run(input []byte) ([]byte, error) { + ripemd := ripemd160.New() + ripemd.Write(input) + return common.LeftPadBytes(ripemd.Sum(nil), 32), nil +} + +// data copy implemented as a native contract. +type dataCopy struct{} + +// RequiredGas returns the gas required to execute the pre-compiled contract. +// +// This method does not require any overflow checking as the input size gas costs +// required for anything significant is so high it's impossible to pay for. +func (c *dataCopy) RequiredGas(input []byte) uint64 { + return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas +} +func (c *dataCopy) Run(in []byte) ([]byte, error) { + return in, nil +} + +// bigModExp implements a native big integer exponential modular operation. +type bigModExp struct{} + +var ( + big1 = big.NewInt(1) + big4 = big.NewInt(4) + big8 = big.NewInt(8) + big16 = big.NewInt(16) + big32 = big.NewInt(32) + big64 = big.NewInt(64) + big96 = big.NewInt(96) + big480 = big.NewInt(480) + big1024 = big.NewInt(1024) + big3072 = big.NewInt(3072) + big199680 = big.NewInt(199680) +) + +// RequiredGas returns the gas required to execute the pre-compiled contract. +func (c *bigModExp) RequiredGas(input []byte) uint64 { + var ( + baseLen = new(big.Int).SetBytes(vm.GetData(input, 0, 32)) + expLen = new(big.Int).SetBytes(vm.GetData(input, 32, 32)) + modLen = new(big.Int).SetBytes(vm.GetData(input, 64, 32)) + ) + if len(input) > 96 { + input = input[96:] + } else { + input = input[:0] + } + // Retrieve the head 32 bytes of exp for the adjusted exponent length + var expHead *big.Int + if big.NewInt(int64(len(input))).Cmp(baseLen) <= 0 { + expHead = new(big.Int) + } else { + if expLen.Cmp(big32) > 0 { + expHead = new(big.Int).SetBytes(vm.GetData(input, baseLen.Uint64(), 32)) + } else { + expHead = new(big.Int).SetBytes(vm.GetData(input, baseLen.Uint64(), expLen.Uint64())) + } + } + // Calculate the adjusted exponent length + var msb int + if bitlen := expHead.BitLen(); bitlen > 0 { + msb = bitlen - 1 + } + adjExpLen := new(big.Int) + if expLen.Cmp(big32) > 0 { + adjExpLen.Sub(expLen, big32) + adjExpLen.Mul(big8, adjExpLen) + } + adjExpLen.Add(adjExpLen, big.NewInt(int64(msb))) + + // Calculate the gas cost of the operation + gas := new(big.Int).Set(math.BigMax(modLen, baseLen)) + switch { + case gas.Cmp(big64) <= 0: + gas.Mul(gas, gas) + case gas.Cmp(big1024) <= 0: + gas = new(big.Int).Add( + new(big.Int).Div(new(big.Int).Mul(gas, gas), big4), + new(big.Int).Sub(new(big.Int).Mul(big96, gas), big3072), + ) + default: + gas = new(big.Int).Add( + new(big.Int).Div(new(big.Int).Mul(gas, gas), big16), + new(big.Int).Sub(new(big.Int).Mul(big480, gas), big199680), + ) + } + gas.Mul(gas, math.BigMax(adjExpLen, big1)) + gas.Div(gas, new(big.Int).SetUint64(params.ModExpQuadCoeffDiv)) + + if gas.BitLen() > 64 { + return math.MaxUint64 + } + return gas.Uint64() +} + +func (c *bigModExp) Run(input []byte) ([]byte, error) { + var ( + baseLen = new(big.Int).SetBytes(vm.GetData(input, 0, 32)).Uint64() + expLen = new(big.Int).SetBytes(vm.GetData(input, 32, 32)).Uint64() + modLen = new(big.Int).SetBytes(vm.GetData(input, 64, 32)).Uint64() + ) + if len(input) > 96 { + input = input[96:] + } else { + input = input[:0] + } + // Handle a special case when both the base and mod length is zero + if baseLen == 0 && modLen == 0 { + return []byte{}, nil + } + // Retrieve the operands and execute the exponentiation + var ( + base = new(big.Int).SetBytes(vm.GetData(input, 0, baseLen)) + exp = new(big.Int).SetBytes(vm.GetData(input, baseLen, expLen)) + mod = new(big.Int).SetBytes(vm.GetData(input, baseLen+expLen, modLen)) + ) + if mod.BitLen() == 0 { + // Modulo 0 is undefined, return zero + return common.LeftPadBytes([]byte{}, int(modLen)), nil + } + return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), nil +} + +// newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point, +// returning it, or an error if the point is invalid. +func newCurvePoint(blob []byte) (*bn256.G1, error) { + p := new(bn256.G1) + if _, err := p.Unmarshal(blob); err != nil { + return nil, err + } + return p, nil +} + +// newTwistPoint unmarshals a binary blob into a bn256 elliptic curve point, +// returning it, or an error if the point is invalid. +func newTwistPoint(blob []byte) (*bn256.G2, error) { + p := new(bn256.G2) + if _, err := p.Unmarshal(blob); err != nil { + return nil, err + } + return p, nil +} + +// bn256Add implements a native elliptic curve point addition. +type bn256Add struct{} + +// RequiredGas returns the gas required to execute the pre-compiled contract. +func (c *bn256Add) RequiredGas(input []byte) uint64 { + return params.Bn256AddGas +} + +func (c *bn256Add) Run(input []byte) ([]byte, error) { + x, err := newCurvePoint(vm.GetData(input, 0, 64)) + if err != nil { + return nil, err + } + y, err := newCurvePoint(vm.GetData(input, 64, 64)) + if err != nil { + return nil, err + } + res := new(bn256.G1) + res.Add(x, y) + return res.Marshal(), nil +} + +// bn256ScalarMul implements a native elliptic curve scalar multiplication. +type bn256ScalarMul struct{} + +// RequiredGas returns the gas required to execute the pre-compiled contract. +func (c *bn256ScalarMul) RequiredGas(input []byte) uint64 { + return params.Bn256ScalarMulGas +} + +func (c *bn256ScalarMul) Run(input []byte) ([]byte, error) { + p, err := newCurvePoint(vm.GetData(input, 0, 64)) + if err != nil { + return nil, err + } + res := new(bn256.G1) + res.ScalarMult(p, new(big.Int).SetBytes(vm.GetData(input, 64, 32))) + return res.Marshal(), nil +} + +var ( + // true32Byte is returned if the bn256 pairing check succeeds. + true32Byte = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} + + // false32Byte is returned if the bn256 pairing check fails. + false32Byte = make([]byte, 32) + + // errBadPairingInput is returned if the bn256 pairing input is invalid. + errBadPairingInput = errors.New("bad elliptic curve pairing size") +) + +// bn256Pairing implements a pairing pre-compile for the bn256 curve +type bn256Pairing struct{} + +// RequiredGas returns the gas required to execute the pre-compiled contract. +func (c *bn256Pairing) RequiredGas(input []byte) uint64 { + return params.Bn256PairingBaseGas + uint64(len(input)/192)*params.Bn256PairingPerPointGas +} + +func (c *bn256Pairing) Run(input []byte) ([]byte, error) { + // Handle some corner cases cheaply + if len(input)%192 > 0 { + return nil, errBadPairingInput + } + // Convert the input into a set of coordinates + var ( + cs []*bn256.G1 + ts []*bn256.G2 + ) + for i := 0; i < len(input); i += 192 { + c, err := newCurvePoint(input[i : i+64]) + if err != nil { + return nil, err + } + t, err := newTwistPoint(input[i+64 : i+192]) + if err != nil { + return nil, err + } + cs = append(cs, c) + ts = append(ts, t) + } + // Execute the pairing checks and return the results + if bn256.PairingCheck(cs, ts) { + return true32Byte, nil + } + return false32Byte, nil +} diff --git a/core/vm/evm/contracts_test.go b/core/vm/evm/contracts_test.go new file mode 100644 index 000000000..4b43f777b --- /dev/null +++ b/core/vm/evm/contracts_test.go @@ -0,0 +1,483 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "fmt" + "math/big" + "testing" + + "github.com/dexon-foundation/dexon/common" +) + +// precompiledTest defines the input/output pairs for precompiled contract tests. +type precompiledTest struct { + input, expected string + gas uint64 + name string + noBenchmark bool // Benchmark primarily the worst-cases +} + +// modexpTests are the test and benchmark data for the modexp precompiled contract. +var modexpTests = []precompiledTest{ + { + input: "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "03" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + name: "eip_example1", + }, { + input: "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000020" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + expected: "0000000000000000000000000000000000000000000000000000000000000000", + name: "eip_example2", + }, { + input: "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb502fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + expected: "60008f1614cc01dcfb6bfb09c625cf90b47d4468db81b5f8b7a39d42f332eab9b2da8f2d95311648a8f243f4bb13cfb3d8f7f2a3c014122ebb3ed41b02783adc", + name: "nagydani-1-square", + }, { + input: "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb503fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + expected: "4834a46ba565db27903b1c720c9d593e84e4cbd6ad2e64b31885d944f68cd801f92225a8961c952ddf2797fa4701b330c85c4b363798100b921a1a22a46a7fec", + name: "nagydani-1-qube", + }, { + input: "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5010001fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", + expected: "c36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f736766d9dc27143e4da981dfa4d67f7dc474cbee6d2", + name: "nagydani-1-pow0x10001", + }, { + input: "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5102e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + expected: "981dd99c3b113fae3e3eaa9435c0dc96779a23c12a53d1084b4f67b0b053a27560f627b873e3f16ad78f28c94f14b6392def26e4d8896c5e3c984e50fa0b3aa44f1da78b913187c6128baa9340b1e9c9a0fd02cb78885e72576da4a8f7e5a113e173a7a2889fde9d407bd9f06eb05bc8fc7b4229377a32941a02bf4edcc06d70", + name: "nagydani-2-square", + }, { + input: "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5103e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + expected: "d89ceb68c32da4f6364978d62aaa40d7b09b59ec61eb3c0159c87ec3a91037f7dc6967594e530a69d049b64adfa39c8fa208ea970cfe4b7bcd359d345744405afe1cbf761647e32b3184c7fbe87cee8c6c7ff3b378faba6c68b83b6889cb40f1603ee68c56b4c03d48c595c826c041112dc941878f8c5be828154afd4a16311f", + name: "nagydani-2-qube", + }, { + input: "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51010001e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", + expected: "ad85e8ef13fd1dd46eae44af8b91ad1ccae5b7a1c92944f92a19f21b0b658139e0cabe9c1f679507c2de354bf2c91ebd965d1e633978a830d517d2f6f8dd5fd58065d58559de7e2334a878f8ec6992d9b9e77430d4764e863d77c0f87beede8f2f7f2ab2e7222f85cc9d98b8467f4bb72e87ef2882423ebdb6daf02dddac6db2", + name: "nagydani-2-pow0x10001", + }, { + input: "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb02d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + expected: "affc7507ea6d84751ec6b3f0d7b99dbcc263f33330e450d1b3ff0bc3d0874320bf4edd57debd587306988157958cb3cfd369cc0c9c198706f635c9e0f15d047df5cb44d03e2727f26b083c4ad8485080e1293f171c1ed52aef5993a5815c35108e848c951cf1e334490b4a539a139e57b68f44fee583306f5b85ffa57206b3ee5660458858534e5386b9584af3c7f67806e84c189d695e5eb96e1272d06ec2df5dc5fabc6e94b793718c60c36be0a4d031fc84cd658aa72294b2e16fc240aef70cb9e591248e38bd49c5a554d1afa01f38dab72733092f7555334bbef6c8c430119840492380aa95fa025dcf699f0a39669d812b0c6946b6091e6e235337b6f8", + name: "nagydani-3-square", + }, { + input: "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb03d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + expected: "1b280ecd6a6bf906b806d527c2a831e23b238f89da48449003a88ac3ac7150d6a5e9e6b3be4054c7da11dd1e470ec29a606f5115801b5bf53bc1900271d7c3ff3cd5ed790d1c219a9800437a689f2388ba1a11d68f6a8e5b74e9a3b1fac6ee85fc6afbac599f93c391f5dc82a759e3c6c0ab45ce3f5d25d9b0c1bf94cf701ea6466fc9a478dacc5754e593172b5111eeba88557048bceae401337cd4c1182ad9f700852bc8c99933a193f0b94cf1aedbefc48be3bc93ef5cb276d7c2d5462ac8bb0c8fe8923a1db2afe1c6b90d59c534994a6a633f0ead1d638fdc293486bb634ff2c8ec9e7297c04241a61c37e3ae95b11d53343d4ba2b4cc33d2cfa7eb705e", + name: "nagydani-3-qube", + }, { + input: "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb010001d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", + expected: "37843d7c67920b5f177372fa56e2a09117df585f81df8b300fba245b1175f488c99476019857198ed459ed8d9799c377330e49f4180c4bf8e8f66240c64f65ede93d601f957b95b83efdee1e1bfde74169ff77002eaf078c71815a9220c80b2e3b3ff22c2f358111d816ebf83c2999026b6de50bfc711ff68705d2f40b753424aefc9f70f08d908b5a20276ad613b4ab4309a3ea72f0c17ea9df6b3367d44fb3acab11c333909e02e81ea2ed404a712d3ea96bba87461720e2d98723e7acd0520ac1a5212dbedcd8dc0c1abf61d4719e319ff4758a774790b8d463cdfe131d1b2dcfee52d002694e98e720cb6ae7ccea353bc503269ba35f0f63bf8d7b672a76", + name: "nagydani-3-pow0x10001", + }, { + input: "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8102df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + expected: "8a5aea5f50dcc03dc7a7a272b5aeebc040554dbc1ffe36753c4fc75f7ed5f6c2cc0de3a922bf96c78bf0643a73025ad21f45a4a5cadd717612c511ab2bff1190fe5f1ae05ba9f8fe3624de1de2a817da6072ddcdb933b50216811dbe6a9ca79d3a3c6b3a476b079fd0d05f04fb154e2dd3e5cb83b148a006f2bcbf0042efb2ae7b916ea81b27aac25c3bf9a8b6d35440062ad8eae34a83f3ffa2cc7b40346b62174a4422584f72f95316f6b2bee9ff232ba9739301c97c99a9ded26c45d72676eb856ad6ecc81d36a6de36d7f9dafafee11baa43a4b0d5e4ecffa7b9b7dcefd58c397dd373e6db4acd2b2c02717712e6289bed7c813b670c4a0c6735aa7f3b0f1ce556eae9fcc94b501b2c8781ba50a8c6220e8246371c3c7359fe4ef9da786ca7d98256754ca4e496be0a9174bedbecb384bdf470779186d6a833f068d2838a88d90ef3ad48ff963b67c39cc5a3ee123baf7bf3125f64e77af7f30e105d72c4b9b5b237ed251e4c122c6d8c1405e736299c3afd6db16a28c6a9cfa68241e53de4cd388271fe534a6a9b0dbea6171d170db1b89858468885d08fecbd54c8e471c3e25d48e97ba450b96d0d87e00ac732aaa0d3ce4309c1064bd8a4c0808a97e0143e43a24cfa847635125cd41c13e0574487963e9d725c01375db99c31da67b4cf65eff555f0c0ac416c727ff8d438ad7c42030551d68c2e7adda0abb1ca7c10", + name: "nagydani-4-square", + }, { + input: "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8103df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + expected: "5a2664252aba2d6e19d9600da582cdd1f09d7a890ac48e6b8da15ae7c6ff1856fc67a841ac2314d283ffa3ca81a0ecf7c27d89ef91a5a893297928f5da0245c99645676b481b7e20a566ee6a4f2481942bee191deec5544600bb2441fd0fb19e2ee7d801ad8911c6b7750affec367a4b29a22942c0f5f4744a4e77a8b654da2a82571037099e9c6d930794efe5cdca73c7b6c0844e386bdca8ea01b3d7807146bb81365e2cdc6475f8c23e0ff84463126189dc9789f72bbce2e3d2d114d728a272f1345122de23df54c922ec7a16e5c2a8f84da8871482bd258c20a7c09bbcd64c7a96a51029bbfe848736a6ba7bf9d931a9b7de0bcaf3635034d4958b20ae9ab3a95a147b0421dd5f7ebff46c971010ebfc4adbbe0ad94d5498c853e7142c450d8c71de4b2f84edbf8acd2e16d00c8115b150b1c30e553dbb82635e781379fe2a56360420ff7e9f70cc64c00aba7e26ed13c7c19622865ae07248daced36416080f35f8cc157a857ed70ea4f347f17d1bee80fa038abd6e39b1ba06b97264388b21364f7c56e192d4b62d9b161405f32ab1e2594e86243e56fcf2cb30d21adef15b9940f91af681da24328c883d892670c6aa47940867a81830a82b82716895db810df1b834640abefb7db2092dd92912cb9a735175bc447be40a503cf22dfe565b4ed7a3293ca0dfd63a507430b323ee248ec82e843b673c97ad730728cebc", + name: "nagydani-4-qube", + }, { + input: "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81010001df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", + expected: "bed8b970c4a34849fc6926b08e40e20b21c15ed68d18f228904878d4370b56322d0da5789da0318768a374758e6375bfe4641fca5285ec7171828922160f48f5ca7efbfee4d5148612c38ad683ae4e3c3a053d2b7c098cf2b34f2cb19146eadd53c86b2d7ccf3d83b2c370bfb840913ee3879b1057a6b4e07e110b6bcd5e958bc71a14798c91d518cc70abee264b0d25a4110962a764b364ac0b0dd1ee8abc8426d775ec0f22b7e47b32576afaf1b5a48f64573ed1c5c29f50ab412188d9685307323d990802b81dacc06c6e05a1e901830ba9fcc67688dc29c5e27bde0a6e845ca925f5454b6fb3747edfaa2a5820838fb759eadf57f7cb5cec57fc213ddd8a4298fa079c3c0f472b07fb15aa6a7f0a3780bd296ff6a62e58ef443870b02260bd4fd2bbc98255674b8e1f1f9f8d33c7170b0ebbea4523b695911abbf26e41885344823bd0587115fdd83b721a4e8457a31c9a84b3d3520a07e0e35df7f48e5a9d534d0ec7feef1ff74de6a11e7f93eab95175b6ce22c68d78a642ad642837897ec11349205d8593ac19300207572c38d29ca5dfa03bc14cdbc32153c80e5cc3e739403d34c75915e49beb43094cc6dcafb3665b305ddec9286934ae66ec6b777ca528728c851318eb0f207b39f1caaf96db6eeead6b55ed08f451939314577d42bcc9f97c0b52d0234f88fd07e4c1d7780fdebc025cfffcb572cb27a8c33963", + name: "nagydani-4-pow0x10001", + }, { + input: "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf02e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + expected: "d61fe4e3f32ac260915b5b03b78a86d11bfc41d973fce5b0cc59035cf8289a8a2e3878ea15fa46565b0d806e2f85b53873ea20ed653869b688adf83f3ef444535bf91598ff7e80f334fb782539b92f39f55310cc4b35349ab7b278346eda9bc37c0d8acd3557fae38197f412f8d9e57ce6a76b7205c23564cab06e5615be7c6f05c3d05ec690cba91da5e89d55b152ff8dd2157dc5458190025cf94b1ad98f7cbe64e9482faba95e6b33844afc640892872b44a9932096508f4a782a4805323808f23e54b6ff9b841dbfa87db3505ae4f687972c18ea0f0d0af89d36c1c2a5b14560c153c3fee406f5cf15cfd1c0bb45d767426d465f2f14c158495069d0c5955a00150707862ecaae30624ebacdd8ac33e4e6aab3ff90b6ba445a84689386b9e945d01823a65874444316e83767290fcff630d2477f49d5d8ffdd200e08ee1274270f86ed14c687895f6caf5ce528bd970c20d2408a9ba66216324c6a011ac4999098362dbd98a038129a2d40c8da6ab88318aa3046cb660327cc44236d9e5d2163bd0959062195c51ed93d0088b6f92051fc99050ece2538749165976233697ab4b610385366e5ce0b02ad6b61c168ecfbedcdf74278a38de340fd7a5fead8e588e294795f9b011e2e60377a89e25c90e145397cdeabc60fd32444a6b7642a611a83c464d8b8976666351b4865c37b02e6dc21dbcdf5f930341707b618cc0f03c3122646b3385c9df9f2ec730eec9d49e7dfc9153b6e6289da8c4f0ebea9ccc1b751948e3bb7171c9e4d57423b0eeeb79095c030cb52677b3f7e0b45c30f645391f3f9c957afa549c4e0b2465b03c67993cd200b1af01035962edbc4c9e89b31c82ac121987d6529dafdeef67a132dc04b6dc68e77f22862040b75e2ceb9ff16da0fca534e6db7bd12fa7b7f51b6c08c1e23dfcdb7acbd2da0b51c87ffbced065a612e9b1c8bba9b7e2d8d7a2f04fcc4aaf355b60d764879a76b5e16762d5f2f55d585d0c8e82df6940960cddfb72c91dfa71f6b4e1c6ca25dfc39a878e998a663c04fe29d5e83b9586d047b4d7ff70a9f0d44f127e7d741685ca75f11629128d916a0ffef4be586a30c4b70389cc746e84ebf177c01ee8a4511cfbb9d1ecf7f7b33c7dd8177896e10bbc82f838dcd6db7ac67de62bf46b6a640fb580c5d1d2708f3862e3d2b645d0d18e49ef088053e3a220adc0e033c2afcfe61c90e32151152eb3caaf746c5e377d541cafc6cbb0cc0fa48b5caf1728f2e1957f5addfc234f1a9d89e40d49356c9172d0561a695fce6dab1d412321bbf407f63766ffd7b6b3d79bcfa07991c5a9709849c1008689e3b47c50d613980bec239fb64185249d055b30375ccb4354d71fe4d05648fbf6c80634dfc3575f2f24abb714c1e4c95e8896763bf4316e954c7ad19e5780ab7a040ca6fb9271f90a8b22ae738daf6cb", + name: "nagydani-5-square", + }, { + input: "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf03e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + expected: "5f9c70ec884926a89461056ad20ac4c30155e817f807e4d3f5bb743d789c83386762435c3627773fa77da5144451f2a8aad8adba88e0b669f5377c5e9bad70e45c86fe952b613f015a9953b8a5de5eaee4566acf98d41e327d93a35bd5cef4607d025e58951167957df4ff9b1627649d3943805472e5e293d3efb687cfd1e503faafeb2840a3e3b3f85d016051a58e1c9498aab72e63b748d834b31eb05d85dcde65e27834e266b85c75cc4ec0135135e0601cb93eeeb6e0010c8ceb65c4c319623c5e573a2c8c9fbbf7df68a930beb412d3f4dfd146175484f45d7afaa0d2e60684af9b34730f7c8438465ad3e1d0c3237336722f2aa51095bd5759f4b8ab4dda111b684aa3dac62a761722e7ae43495b7709933512c81c4e3c9133a51f7ce9f2b51fcec064f65779666960b4e45df3900f54311f5613e8012dd1b8efd359eda31a778264c72aa8bb419d862734d769076bce2810011989a45374e5c5d8729fec21427f0bf397eacbb4220f603cf463a4b0c94efd858ffd9768cd60d6ce68d755e0fbad007ce5c2223d70c7018345a102e4ab3c60a13a9e7794303156d4c2063e919f2153c13961fb324c80b240742f47773a7a8e25b3e3fb19b00ce839346c6eb3c732fbc6b888df0b1fe0a3d07b053a2e9402c267b2d62f794d8a2840526e3ade15ce2264496ccd7519571dfde47f7a4bb16292241c20b2be59f3f8fb4f6383f232d838c5a22d8c95b6834d9d2ca493f5a505ebe8899503b0e8f9b19e6e2dd81c1628b80016d02097e0134de51054c4e7674824d4d758760fc52377d2cad145e259aa2ffaf54139e1a66b1e0c1c191e32ac59474c6b526f5b3ba07d3e5ec286eddf531fcd5292869be58c9f22ef91026159f7cf9d05ef66b4299f4da48cc1635bf2243051d342d378a22c83390553e873713c0454ce5f3234397111ac3fe3207b86f0ed9fc025c81903e1748103692074f83824fda6341be4f95ff00b0a9a208c267e12fa01825054cc0513629bf3dbb56dc5b90d4316f87654a8be18227978ea0a8a522760cad620d0d14fd38920fb7321314062914275a5f99f677145a6979b156bd82ecd36f23f8e1273cc2759ecc0b2c69d94dad5211d1bed939dd87ed9e07b91d49713a6e16ade0a98aea789f04994e318e4ff2c8a188cd8d43aeb52c6daa3bc29b4af50ea82a247c5cd67b573b34cbadcc0a376d3bbd530d50367b42705d870f2e27a8197ef46070528bfe408360faa2ebb8bf76e9f388572842bcb119f4d84ee34ae31f5cc594f23705a49197b181fb78ed1ec99499c690f843a4d0cf2e226d118e9372271054fbabdcc5c92ae9fefaef0589cd0e722eaf30c1703ec4289c7fd81beaa8a455ccee5298e31e2080c10c366a6fcf56f7d13582ad0bcad037c612b710fc595b70fbefaaca23623b60c6c39b11beb8e5843b6b3dac60f", + name: "nagydani-5-qube", + }, { + input: "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf010001e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", + expected: "5a0eb2bdf0ac1cae8e586689fa16cd4b07dfdedaec8a110ea1fdb059dd5253231b6132987598dfc6e11f86780428982d50cf68f67ae452622c3b336b537ef3298ca645e8f89ee39a26758206a5a3f6409afc709582f95274b57b71fae5c6b74619ae6f089a5393c5b79235d9caf699d23d88fb873f78379690ad8405e34c19f5257d596580c7a6a7206a3712825afe630c76b31cdb4a23e7f0632e10f14f4e282c81a66451a26f8df2a352b5b9f607a7198449d1b926e27036810368e691a74b91c61afa73d9d3b99453e7c8b50fd4f09c039a2f2feb5c419206694c31b92df1d9586140cb3417b38d0c503c7b508cc2ed12e813a1c795e9829eb39ee78eeaf360a169b491a1d4e419574e712402de9d48d54c1ae5e03739b7156615e8267e1fb0a897f067afd11fb33f6e24182d7aaaaa18fe5bc1982f20d6b871e5a398f0f6f718181d31ec225cfa9a0a70124ed9a70031bdf0c1c7829f708b6e17d50419ef361cf77d99c85f44607186c8d683106b8bd38a49b5d0fb503b397a83388c5678dcfcc737499d84512690701ed621a6f0172aecf037184ddf0f2453e4053024018e5ab2e30d6d5363b56e8b41509317c99042f517247474ab3abc848e00a07f69c254f46f2a05cf6ed84e5cc906a518fdcfdf2c61ce731f24c5264f1a25fc04934dc28aec112134dd523f70115074ca34e3807aa4cb925147f3a0ce152d323bd8c675ace446d0fd1ae30c4b57f0eb2c23884bc18f0964c0114796c5b6d080c3d89175665fbf63a6381a6a9da39ad070b645c8bb1779506da14439a9f5b5d481954764ea114fac688930bc68534d403cff4210673b6a6ff7ae416b7cd41404c3d3f282fcd193b86d0f54d0006c2a503b40d5c3930da980565b8f9630e9493a79d1c03e74e5f93ac8e4dc1a901ec5e3b3e57049124c7b72ea345aa359e782285d9e6a5c144a378111dd02c40855ff9c2be9b48425cb0b2fd62dc8678fd151121cf26a65e917d65d8e0dacfae108eb5508b601fb8ffa370be1f9a8b749a2d12eeab81f41079de87e2d777994fa4d28188c579ad327f9957fb7bdecec5c680844dd43cb57cf87aeb763c003e65011f73f8c63442df39a92b946a6bd968a1c1e4d5fa7d88476a68bd8e20e5b70a99259c7d3f85fb1b65cd2e93972e6264e74ebf289b8b6979b9b68a85cd5b360c1987f87235c3c845d62489e33acf85d53fa3561fe3a3aee18924588d9c6eba4edb7a4d106b31173e42929f6f0c48c80ce6a72d54eca7c0fe870068b7a7c89c63cdda593f5b32d3cb4ea8a32c39f00ab449155757172d66763ed9527019d6de6c9f2416aa6203f4d11c9ebee1e1d3845099e55504446448027212616167eb36035726daa7698b075286f5379cd3e93cb3e0cf4f9cb8d017facbb5550ed32d5ec5400ae57e47e2bf78d1eaeff9480cc765ceff39db500", + name: "nagydani-5-pow0x10001", + }, +} + +// bn256AddTests are the test and benchmark data for the bn256 addition precompiled +// contract. +var bn256AddTests = []precompiledTest{ + { + input: "18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f3726607c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7", + expected: "2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915", + name: "chfast1", + }, { + input: "2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c91518b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266", + expected: "2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb721611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204", + name: "chfast2", + }, { + input: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + expected: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + name: "cdetrio1", + }, { + input: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + expected: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + name: "cdetrio2", + }, { + input: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + expected: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + name: "cdetrio3", + }, { + input: "", + expected: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + name: "cdetrio4", + }, { + input: "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + expected: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + name: "cdetrio5", + }, { + input: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + expected: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + name: "cdetrio6", + }, { + input: "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + expected: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + name: "cdetrio7", + }, { + input: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + expected: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + name: "cdetrio8", + }, { + input: "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + expected: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + name: "cdetrio9", + }, { + input: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + expected: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + name: "cdetrio10", + }, { + input: "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + expected: "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", + name: "cdetrio11", + }, { + input: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + expected: "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", + name: "cdetrio12", + }, { + input: "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98", + expected: "15bf2bb17880144b5d1cd2b1f46eff9d617bffd1ca57c37fb5a49bd84e53cf66049c797f9ce0d17083deb32b5e36f2ea2a212ee036598dd7624c168993d1355f", + name: "cdetrio13", + }, { + input: "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa92e83f8d734803fc370eba25ed1f6b8768bd6d83887b87165fc2434fe11a830cb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + expected: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + name: "cdetrio14", + }, +} + +// bn256ScalarMulTests are the test and benchmark data for the bn256 scalar +// multipication precompiled contract. +var bn256ScalarMulTests = []precompiledTest{ + { + input: "2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb721611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb20400000000000000000000000000000000000000000000000011138ce750fa15c2", + expected: "070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc", + name: "chfast1", + }, { + input: "070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46", + expected: "025a6f4181d2b4ea8b724290ffb40156eb0adb514c688556eb79cdea0752c2bb2eff3f31dea215f1eb86023a133a996eb6300b44da664d64251d05381bb8a02e", + name: "chfast2", + }, { + input: "025a6f4181d2b4ea8b724290ffb40156eb0adb514c688556eb79cdea0752c2bb2eff3f31dea215f1eb86023a133a996eb6300b44da664d64251d05381bb8a02e183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3", + expected: "14789d0d4a730b354403b5fac948113739e276c23e0258d8596ee72f9cd9d3230af18a63153e0ec25ff9f2951dd3fa90ed0197bfef6e2a1a62b5095b9d2b4a27", + name: "chfast3", + }, { + input: "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + expected: "2cde5879ba6f13c0b5aa4ef627f159a3347df9722efce88a9afbb20b763b4c411aa7e43076f6aee272755a7f9b84832e71559ba0d2e0b17d5f9f01755e5b0d11", + name: "cdetrio1", + }, { + input: "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f630644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000", + expected: "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe3163511ddc1c3f25d396745388200081287b3fd1472d8339d5fecb2eae0830451", + name: "cdetrio2", + noBenchmark: true, + }, { + input: "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000100000000000000000000000000000000", + expected: "1051acb0700ec6d42a88215852d582efbaef31529b6fcbc3277b5c1b300f5cf0135b2394bb45ab04b8bd7611bd2dfe1de6a4e6e2ccea1ea1955f577cd66af85b", + name: "cdetrio3", + noBenchmark: true, + }, { + input: "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000000000000000000000000000000000009", + expected: "1dbad7d39dbc56379f78fac1bca147dc8e66de1b9d183c7b167351bfe0aeab742cd757d51289cd8dbd0acf9e673ad67d0f0a89f912af47ed1be53664f5692575", + name: "cdetrio4", + noBenchmark: true, + }, { + input: "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f60000000000000000000000000000000000000000000000000000000000000001", + expected: "1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe31a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6", + name: "cdetrio5", + noBenchmark: true, + }, { + input: "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + expected: "29e587aadd7c06722aabba753017c093f70ba7eb1f1c0104ec0564e7e3e21f6022b1143f6a41008e7755c71c3d00b6b915d386de21783ef590486d8afa8453b1", + name: "cdetrio6", + }, { + input: "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000", + expected: "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa92e83f8d734803fc370eba25ed1f6b8768bd6d83887b87165fc2434fe11a830cb", + name: "cdetrio7", + noBenchmark: true, + }, { + input: "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c0000000000000000000000000000000100000000000000000000000000000000", + expected: "221a3577763877920d0d14a91cd59b9479f83b87a653bb41f82a3f6f120cea7c2752c7f64cdd7f0e494bff7b60419f242210f2026ed2ec70f89f78a4c56a1f15", + name: "cdetrio8", + noBenchmark: true, + }, { + input: "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c0000000000000000000000000000000000000000000000000000000000000009", + expected: "228e687a379ba154554040f8821f4e41ee2be287c201aa9c3bc02c9dd12f1e691e0fd6ee672d04cfd924ed8fdc7ba5f2d06c53c1edc30f65f2af5a5b97f0a76a", + name: "cdetrio9", + noBenchmark: true, + }, { + input: "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c0000000000000000000000000000000000000000000000000000000000000001", + expected: "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa901e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c", + name: "cdetrio10", + noBenchmark: true, + }, { + input: "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + expected: "00a1a234d08efaa2616607e31eca1980128b00b415c845ff25bba3afcb81dc00242077290ed33906aeb8e42fd98c41bcb9057ba03421af3f2d08cfc441186024", + name: "cdetrio11", + }, { + input: "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d9830644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000", + expected: "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b8692929ee761a352600f54921df9bf472e66217e7bb0cee9032e00acc86b3c8bfaf", + name: "cdetrio12", + noBenchmark: true, + }, { + input: "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000100000000000000000000000000000000", + expected: "1071b63011e8c222c5a771dfa03c2e11aac9666dd097f2c620852c3951a4376a2f46fe2f73e1cf310a168d56baa5575a8319389d7bfa6b29ee2d908305791434", + name: "cdetrio13", + noBenchmark: true, + }, { + input: "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000000000000000000000000000000000009", + expected: "19f75b9dd68c080a688774a6213f131e3052bd353a304a189d7a2ee367e3c2582612f545fb9fc89fde80fd81c68fc7dcb27fea5fc124eeda69433cf5c46d2d7f", + name: "cdetrio14", + noBenchmark: true, + }, { + input: "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d980000000000000000000000000000000000000000000000000000000000000001", + expected: "039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98", + name: "cdetrio15", + noBenchmark: true, + }, +} + +// bn256PairingTests are the test and benchmark data for the bn256 pairing check +// precompiled contract. +var bn256PairingTests = []precompiledTest{ + { + input: "1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + name: "jeff1", + }, { + input: "2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc0203d3260361bb8451de5ff5ecd17f010ff22f5c31cdf184e9020b06fa5997db841213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f06967a1237ebfeca9aaae0d6d0bab8e28c198c5a339ef8a2407e31cdac516db922160fa257a5fd5b280642ff47b65eca77e626cb685c84fa6d3b6882a283ddd1198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + name: "jeff2", + }, { + input: "0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba2e89718ad33c8bed92e210e81d1853435399a271913a6520736a4729cf0d51eb01a9e2ffa2e92599b68e44de5bcf354fa2642bd4f26b259daa6f7ce3ed57aeb314a9a87b789a58af499b314e13c3d65bede56c07ea2d418d6874857b70763713178fb49a2d6cd347dc58973ff49613a20757d0fcc22079f9abd10c3baee245901b9e027bd5cfc2cb5db82d4dc9677ac795ec500ecd47deee3b5da006d6d049b811d7511c78158de484232fc68daf8a45cf217d1c2fae693ff5871e8752d73b21198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + name: "jeff3", + }, { + input: "2f2ea0b3da1e8ef11914acf8b2e1b32d99df51f5f4f206fc6b947eae860eddb6068134ddb33dc888ef446b648d72338684d678d2eb2371c61a50734d78da4b7225f83c8b6ab9de74e7da488ef02645c5a16a6652c3c71a15dc37fe3a5dcb7cb122acdedd6308e3bb230d226d16a105295f523a8a02bfc5e8bd2da135ac4c245d065bbad92e7c4e31bf3757f1fe7362a63fbfee50e7dc68da116e67d600d9bf6806d302580dc0661002994e7cd3a7f224e7ddc27802777486bf80f40e4ca3cfdb186bac5188a98c45e6016873d107f5cd131f3a3e339d0375e58bd6219347b008122ae2b09e539e152ec5364e7e2204b03d11d3caa038bfc7cd499f8176aacbee1f39e4e4afc4bc74790a4a028aff2c3d2538731fb755edefd8cb48d6ea589b5e283f150794b6736f670d6a1033f9b46c6f5204f50813eb85c8dc4b59db1c5d39140d97ee4d2b36d99bc49974d18ecca3e7ad51011956051b464d9e27d46cc25e0764bb98575bd466d32db7b15f582b2d5c452b36aa394b789366e5e3ca5aabd415794ab061441e51d01e94640b7e3084a07e02c78cf3103c542bc5b298669f211b88da1679b0b64a63b7e0e7bfe52aae524f73a55be7fe70c7e9bfc94b4cf0da1213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + name: "jeff4", + }, { + input: "20a754d2071d4d53903e3b31a7e98ad6882d58aec240ef981fdf0a9d22c5926a29c853fcea789887315916bbeb89ca37edb355b4f980c9a12a94f30deeed30211213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f1abb4a25eb9379ae96c84fff9f0540abcfc0a0d11aeda02d4f37e4baf74cb0c11073b3ff2cdbb38755f8691ea59e9606696b3ff278acfc098fa8226470d03869217cee0a9ad79a4493b5253e2e4e3a39fc2df38419f230d341f60cb064a0ac290a3d76f140db8418ba512272381446eb73958670f00cf46f1d9e64cba057b53c26f64a8ec70387a13e41430ed3ee4a7db2059cc5fc13c067194bcc0cb49a98552fd72bd9edb657346127da132e5b82ab908f5816c826acb499e22f2412d1a2d70f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2198a1f162a73261f112401aa2db79c7dab1533c9935c77290a6ce3b191f2318d198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + name: "jeff5", + }, { + input: "1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c103188585e2364128fe25c70558f1560f4f9350baf3959e603cc91486e110936198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + expected: "0000000000000000000000000000000000000000000000000000000000000000", + name: "jeff6", + }, { // ecpairing_empty_data_insufficient_gas + input: "", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + name: "empty_data", + }, { // ecpairing_one_point_insufficient_gas + input: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + expected: "0000000000000000000000000000000000000000000000000000000000000000", + name: "one_point", + }, { // ecpairing_two_point_match_2 + input: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + name: "two_point_match_2", + }, { // ecpairing_two_point_match_3 + input: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + name: "two_point_match_3", + }, { // ecpairing_two_point_match_4 + input: "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + name: "two_point_match_4", + }, { + input: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + name: "ten_point_match_1", + }, { + input: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + name: "ten_point_match_2", + }, { // ecpairing_two_point_match_4 + input: "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75", + expected: "0000000000000000000000000000000000000000000000000000000000000001", + name: "ten_point_match_3", + }, +} + +func testPrecompiled(addr string, test precompiledTest, t *testing.T) { + p := PrecompiledContractsByzantium[common.HexToAddress(addr)] + in := common.Hex2Bytes(test.input) + contract := NewContract(AccountRef(common.HexToAddress("1337")), + nil, new(big.Int), p.RequiredGas(in)) + t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) { + if res, err := RunPrecompiledContract(p, in, contract); err != nil { + t.Error(err) + } else if common.Bytes2Hex(res) != test.expected { + t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res)) + } + }) +} + +func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { + if test.noBenchmark { + return + } + p := PrecompiledContractsByzantium[common.HexToAddress(addr)] + in := common.Hex2Bytes(test.input) + reqGas := p.RequiredGas(in) + contract := NewContract(AccountRef(common.HexToAddress("1337")), + nil, new(big.Int), reqGas) + + var ( + res []byte + err error + data = make([]byte, len(in)) + ) + + bench.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(bench *testing.B) { + bench.ResetTimer() + for i := 0; i < bench.N; i++ { + contract.Gas = reqGas + copy(data, in) + res, err = RunPrecompiledContract(p, data, contract) + } + bench.StopTimer() + //Check if it is correct + if err != nil { + bench.Error(err) + return + } + if common.Bytes2Hex(res) != test.expected { + bench.Error(fmt.Sprintf("Expected %v, got %v", test.expected, common.Bytes2Hex(res))) + return + } + }) +} + +// Benchmarks the sample inputs from the ECRECOVER precompile. +func BenchmarkPrecompiledEcrecover(bench *testing.B) { + t := precompiledTest{ + input: "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", + expected: "000000000000000000000000ceaccac640adf55b2028469bd36ba501f28b699d", + name: "", + } + benchmarkPrecompiled("01", t, bench) +} + +// Benchmarks the sample inputs from the SHA256 precompile. +func BenchmarkPrecompiledSha256(bench *testing.B) { + t := precompiledTest{ + input: "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", + expected: "811c7003375852fabd0d362e40e68607a12bdabae61a7d068fe5fdd1dbbf2a5d", + name: "128", + } + benchmarkPrecompiled("02", t, bench) +} + +// Benchmarks the sample inputs from the RIPEMD precompile. +func BenchmarkPrecompiledRipeMD(bench *testing.B) { + t := precompiledTest{ + input: "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", + expected: "0000000000000000000000009215b8d9882ff46f0dfde6684d78e831467f65e6", + name: "128", + } + benchmarkPrecompiled("03", t, bench) +} + +// Benchmarks the sample inputs from the identiy precompile. +func BenchmarkPrecompiledIdentity(bench *testing.B) { + t := precompiledTest{ + input: "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", + expected: "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02", + name: "128", + } + benchmarkPrecompiled("04", t, bench) +} + +// Tests the sample inputs from the ModExp EIP 198. +func TestPrecompiledModExp(t *testing.T) { + for _, test := range modexpTests { + testPrecompiled("05", test, t) + } +} + +// Benchmarks the sample inputs from the ModExp EIP 198. +func BenchmarkPrecompiledModExp(bench *testing.B) { + for _, test := range modexpTests { + benchmarkPrecompiled("05", test, bench) + } +} + +// Tests the sample inputs from the elliptic curve addition EIP 213. +func TestPrecompiledBn256Add(t *testing.T) { + for _, test := range bn256AddTests { + testPrecompiled("06", test, t) + } +} + +// Benchmarks the sample inputs from the elliptic curve addition EIP 213. +func BenchmarkPrecompiledBn256Add(bench *testing.B) { + for _, test := range bn256AddTests { + benchmarkPrecompiled("06", test, bench) + } +} + +// Tests the sample inputs from the elliptic curve scalar multiplication EIP 213. +func TestPrecompiledBn256ScalarMul(t *testing.T) { + for _, test := range bn256ScalarMulTests { + testPrecompiled("07", test, t) + } +} + +// Benchmarks the sample inputs from the elliptic curve scalar multiplication EIP 213. +func BenchmarkPrecompiledBn256ScalarMul(bench *testing.B) { + for _, test := range bn256ScalarMulTests { + benchmarkPrecompiled("07", test, bench) + } +} + +// Tests the sample inputs from the elliptic curve pairing check EIP 197. +func TestPrecompiledBn256Pairing(t *testing.T) { + for _, test := range bn256PairingTests { + testPrecompiled("08", test, t) + } +} + +// Behcnmarks the sample inputs from the elliptic curve pairing check EIP 197. +func BenchmarkPrecompiledBn256Pairing(bench *testing.B) { + for _, test := range bn256PairingTests { + benchmarkPrecompiled("08", test, bench) + } +} diff --git a/core/vm/evm/doc.go b/core/vm/evm/doc.go new file mode 100644 index 000000000..27986e0c4 --- /dev/null +++ b/core/vm/evm/doc.go @@ -0,0 +1,24 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +/* +Package vm implements the Ethereum Virtual Machine. + +The vm package implements one EVM, a byte code VM. The BC (Byte Code) VM loops +over a set of bytes and executes them according to the set of rules defined +in the Ethereum yellow paper. +*/ +package evm diff --git a/core/vm/evm/evm.go b/core/vm/evm/evm.go new file mode 100644 index 000000000..11d25792e --- /dev/null +++ b/core/vm/evm/evm.go @@ -0,0 +1,489 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "math/big" + "sync/atomic" + "time" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/core/state" + "github.com/dexon-foundation/dexon/core/vm" + "github.com/dexon-foundation/dexon/crypto" + "github.com/dexon-foundation/dexon/params" +) + +// emptyCodeHash is used by create to ensure deployment is disallowed to already +// deployed contract addresses (relevant after the account abstraction). +var emptyCodeHash = crypto.Keccak256Hash(nil) + +type ( + // CanTransferFunc is the signature of a transfer guard function + CanTransferFunc func(StateDB, common.Address, *big.Int) bool + // TransferFunc is the signature of a transfer function + TransferFunc func(StateDB, common.Address, common.Address, *big.Int) + // GetHashFunc returns the nth block hash in the blockchain + // and is used by the BLOCKHASH EVM op code. + GetHashFunc func(uint64) common.Hash + // StateAtFunc returns the statedb given a root hash. + StateAtNumberFunc func(uint64) (*state.StateDB, error) + // GetRoundHeightFunc returns the round height. + GetRoundHeightFunc func(uint64) (uint64, bool) +) + +// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. +func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) { + if contract.CodeAddr != nil { + if o := OracleContracts[*contract.CodeAddr]; o != nil { + return RunOracleContract(o(), evm, input, contract) + } + precompiles := PrecompiledContractsHomestead + if evm.ChainConfig().IsByzantium(evm.BlockNumber) { + precompiles = PrecompiledContractsByzantium + } + if p := precompiles[*contract.CodeAddr]; p != nil { + return RunPrecompiledContract(p, input, contract) + } + } + for _, interpreter := range evm.interpreters { + if interpreter.CanRun(contract.Code) { + if evm.interpreter != interpreter { + // Ensure that the interpreter pointer is set back + // to its current value upon return. + defer func(i Interpreter) { + evm.interpreter = i + }(evm.interpreter) + evm.interpreter = interpreter + } + return interpreter.Run(contract, input, readOnly) + } + } + return nil, vm.ErrNoCompatibleInterpreter +} + +// Context provides the EVM with auxiliary information. Once provided +// it shouldn't be modified. +type Context struct { + // CanTransfer returns whether the account contains + // sufficient ether to transfer the value + CanTransfer CanTransferFunc + // Transfer transfers ether from one account to the other + Transfer TransferFunc + // GetHash returns the hash corresponding to n + GetHash GetHashFunc + // StateAtNumber returns the statedb given a root hash. + StateAtNumber StateAtNumberFunc + // GetRoundHeight returns the round height. + GetRoundHeight GetRoundHeightFunc + + // Message information + Origin common.Address // Provides information for ORIGIN + GasPrice *big.Int // Provides information for GASPRICE + + // Block information + Coinbase common.Address // Provides information for COINBASE + GasLimit uint64 // Provides information for GASLIMIT + BlockNumber *big.Int // Provides information for NUMBER + Time *big.Int // Provides information for TIME + Randomness []byte // Provides information for RAND + Difficulty *big.Int // Provides information for DIFFICULTY + Round *big.Int // Current round number. + + RandCallIndex uint64 // Number of times opRand is called +} + +// EVM is the Ethereum Virtual Machine base object and provides +// the necessary tools to run a contract on the given state with +// the provided context. It should be noted that any error +// generated through any of the calls should be considered a +// revert-state-and-consume-all-gas operation, no checks on +// specific errors should ever be performed. The interpreter makes +// sure that any errors generated are to be considered faulty code. +// +// The EVM should never be reused and is not thread safe. +type EVM struct { + // Context provides auxiliary blockchain related information + Context + // StateDB gives access to the underlying state + StateDB StateDB + // Depth is the current call stack + depth int + + // chainConfig contains information about the current chain + chainConfig *params.ChainConfig + // chain rules contains the chain rules for the current epoch + chainRules params.Rules + // virtual machine configuration options used to initialise the + // evm. + vmConfig Config + // global (to this context) ethereum virtual machine + // used throughout the execution of the tx. + interpreters []Interpreter + interpreter Interpreter + // abort is used to abort the EVM calling operations + // NOTE: must be set atomically + abort int32 + // callGasTemp holds the gas available for the current call. This is needed because the + // available gas is calculated in gasCall* according to the 63/64 rule and later + // applied in opCall*. + callGasTemp uint64 +} + +// NewEVM returns a new EVM. The returned EVM is not thread safe and should +// only ever be used *once*. +func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM { + evm := &EVM{ + Context: ctx, + StateDB: statedb, + vmConfig: vmConfig, + chainConfig: chainConfig, + chainRules: chainConfig.Rules(ctx.BlockNumber), + interpreters: make([]Interpreter, 0, 1), + } + + if chainConfig.IsEWASM(ctx.BlockNumber) { + // to be implemented by EVM-C and Wagon PRs. + // if vmConfig.EWASMInterpreter != "" { + // extIntOpts := strings.Split(vmConfig.EWASMInterpreter, ":") + // path := extIntOpts[0] + // options := []string{} + // if len(extIntOpts) > 1 { + // options = extIntOpts[1..] + // } + // evm.interpreters = append(evm.interpreters, NewEVMVCInterpreter(evm, vmConfig, options)) + // } else { + // evm.interpreters = append(evm.interpreters, NewEWASMInterpreter(evm, vmConfig)) + // } + panic("No supported ewasm interpreter yet.") + } + + // vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here + // as we always want to have the built-in EVM as the failover option. + evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig)) + evm.interpreter = evm.interpreters[0] + + return evm +} + +// Cancel cancels any running EVM operation. This may be called concurrently and +// it's safe to be called multiple times. +func (evm *EVM) Cancel() { + atomic.StoreInt32(&evm.abort, 1) +} + +// Interpreter returns the current interpreter +func (evm *EVM) Interpreter() Interpreter { + return evm.interpreter +} + +// Call executes the contract associated with the addr with the given input as +// parameters. It also handles any necessary value transfer required and takes +// the necessary steps to create accounts and reverses the state in case of an +// execution error or failed value transfer. +func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { + if evm.vmConfig.NoRecursion && evm.depth > 0 { + return nil, gas, nil + } + + // Fail if we're trying to execute above the call depth limit + if evm.depth > int(params.CallCreateDepth) { + return nil, gas, vm.ErrDepth + } + // Fail if we're trying to transfer more than the available balance + if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { + return nil, gas, vm.ErrInsufficientBalance + } + + var ( + to = AccountRef(addr) + snapshot = evm.StateDB.Snapshot() + ) + if !evm.StateDB.Exist(addr) { + precompiles := PrecompiledContractsHomestead + if evm.ChainConfig().IsByzantium(evm.BlockNumber) { + precompiles = PrecompiledContractsByzantium + } + if precompiles[addr] == nil && OracleContracts[addr] == nil && + evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 { + // Calling a non existing account, don't do anything, but ping the tracer + if evm.vmConfig.Debug && evm.depth == 0 { + evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) + evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil) + } + return nil, gas, nil + } + evm.StateDB.CreateAccount(addr) + } + evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) + // Initialise a new contract and set the code that is to be used by the EVM. + // The contract is a scoped environment for this execution context only. + contract := NewContract(caller, to, value, gas) + contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + + // Even if the account has no code, we need to continue because it might be a precompile + start := time.Now() + + // Capture the tracer start/end events in debug mode + if evm.vmConfig.Debug && evm.depth == 0 { + evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) + + defer func() { // Lazy evaluation of the parameters + evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) + }() + } + ret, err = run(evm, contract, input, false) + + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in homestead this also counts for code storage gas errors. + if err != nil { + evm.StateDB.RevertToSnapshot(snapshot) + if err != errExecutionReverted { + contract.UseGas(contract.Gas) + } + } + return ret, contract.Gas, err +} + +// CallCode executes the contract associated with the addr with the given input +// as parameters. It also handles any necessary value transfer required and takes +// the necessary steps to create accounts and reverses the state in case of an +// execution error or failed value transfer. +// +// CallCode differs from Call in the sense that it executes the given address' +// code with the caller as context. +func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { + if evm.vmConfig.NoRecursion && evm.depth > 0 { + return nil, gas, nil + } + + // Fail if we're trying to execute above the call depth limit + if evm.depth > int(params.CallCreateDepth) { + return nil, gas, vm.ErrDepth + } + // Fail if we're trying to transfer more than the available balance + if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { + return nil, gas, vm.ErrInsufficientBalance + } + + var ( + snapshot = evm.StateDB.Snapshot() + to = AccountRef(caller.Address()) + ) + // initialise a new contract and set the code that is to be used by the + // EVM. The contract is a scoped environment for this execution context + // only. + contract := NewContract(caller, to, value, gas) + contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + + ret, err = run(evm, contract, input, false) + if err != nil { + evm.StateDB.RevertToSnapshot(snapshot) + if err != errExecutionReverted { + contract.UseGas(contract.Gas) + } + } + return ret, contract.Gas, err +} + +// DelegateCall executes the contract associated with the addr with the given input +// as parameters. It reverses the state in case of an execution error. +// +// DelegateCall differs from CallCode in the sense that it executes the given address' +// code with the caller as context and the caller is set to the caller of the caller. +func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { + if evm.vmConfig.NoRecursion && evm.depth > 0 { + return nil, gas, nil + } + // Fail if we're trying to execute above the call depth limit + if evm.depth > int(params.CallCreateDepth) { + return nil, gas, vm.ErrDepth + } + + var ( + snapshot = evm.StateDB.Snapshot() + to = AccountRef(caller.Address()) + ) + + // Initialise a new contract and make initialise the delegate values + contract := NewContract(caller, to, nil, gas).AsDelegate() + contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + + ret, err = run(evm, contract, input, false) + if err != nil { + evm.StateDB.RevertToSnapshot(snapshot) + if err != errExecutionReverted { + contract.UseGas(contract.Gas) + } + } + return ret, contract.Gas, err +} + +// StaticCall executes the contract associated with the addr with the given input +// as parameters while disallowing any modifications to the state during the call. +// Opcodes that attempt to perform such modifications will result in exceptions +// instead of performing the modifications. +func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { + if evm.vmConfig.NoRecursion && evm.depth > 0 { + return nil, gas, nil + } + // Fail if we're trying to execute above the call depth limit + if evm.depth > int(params.CallCreateDepth) { + return nil, gas, vm.ErrDepth + } + + var ( + to = AccountRef(addr) + snapshot = evm.StateDB.Snapshot() + ) + // Initialise a new contract and set the code that is to be used by the + // EVM. The contract is a scoped environment for this execution context + // only. + contract := NewContract(caller, to, new(big.Int), gas) + contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + + // We do an AddBalance of zero here, just in order to trigger a touch. + // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, + // but is the correct thing to do and matters on other networks, in tests, and potential + // future scenarios + evm.StateDB.AddBalance(addr, bigZero) + + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in Homestead this also counts for code storage gas errors. + ret, err = run(evm, contract, input, true) + if err != nil { + evm.StateDB.RevertToSnapshot(snapshot) + if err != errExecutionReverted { + contract.UseGas(contract.Gas) + } + } + return ret, contract.Gas, err +} + +type codeAndHash struct { + code []byte + hash common.Hash +} + +func (c *codeAndHash) Hash() common.Hash { + if c.hash == (common.Hash{}) { + c.hash = crypto.Keccak256Hash(c.code) + } + return c.hash +} + +// create creates a new contract using code as deployment code. +func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) { + // Depth check execution. Fail if we're trying to execute above the + // limit. + if evm.depth > int(params.CallCreateDepth) { + return nil, common.Address{}, gas, vm.ErrDepth + } + if !evm.CanTransfer(evm.StateDB, caller.Address(), value) { + return nil, common.Address{}, gas, vm.ErrInsufficientBalance + } + nonce := evm.StateDB.GetNonce(caller.Address()) + evm.StateDB.SetNonce(caller.Address(), nonce+1) + + // Ensure there's no existing contract already at the designated address + contractHash := evm.StateDB.GetCodeHash(address) + if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { + return nil, common.Address{}, 0, vm.ErrContractAddressCollision + } + // Create a new account on the state + snapshot := evm.StateDB.Snapshot() + evm.StateDB.CreateAccount(address) + if evm.ChainConfig().IsEIP158(evm.BlockNumber) { + evm.StateDB.SetNonce(address, 1) + } + evm.Transfer(evm.StateDB, caller.Address(), address, value) + + // initialise a new contract and set the code that is to be used by the + // EVM. The contract is a scoped environment for this execution context + // only. + contract := NewContract(caller, AccountRef(address), value, gas) + contract.SetCodeOptionalHash(&address, codeAndHash) + + if evm.vmConfig.NoRecursion && evm.depth > 0 { + return nil, address, gas, nil + } + + if evm.vmConfig.Debug && evm.depth == 0 { + evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value) + } + start := time.Now() + + ret, err := run(evm, contract, nil, false) + + // check whether the max code size has been exceeded + maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize + // if the contract creation ran successfully and no errors were returned + // calculate the gas required to store the code. If the code could not + // be stored due to not enough gas set an error and let it be handled + // by the error checking condition below. + if err == nil && !maxCodeSizeExceeded { + createDataGas := uint64(len(ret)) * params.CreateDataGas + if contract.UseGas(createDataGas) { + evm.StateDB.SetCode(address, ret) + } else { + err = vm.ErrCodeStoreOutOfGas + } + } + + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in homestead this also counts for code storage gas errors. + if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != vm.ErrCodeStoreOutOfGas)) { + evm.StateDB.RevertToSnapshot(snapshot) + if err != errExecutionReverted { + contract.UseGas(contract.Gas) + } + } + // Assign err if contract code size exceeds the max while the err is still empty. + if maxCodeSizeExceeded && err == nil { + err = errMaxCodeSizeExceeded + } + if evm.vmConfig.Debug && evm.depth == 0 { + evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) + } + return ret, address, contract.Gas, err + +} + +// Create creates a new contract using code as deployment code. +func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { + contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address())) + return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr) +} + +// Create2 creates a new contract using code as deployment code. +// +// The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))[12:] +// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. +func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { + codeAndHash := &codeAndHash{code: code} + contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes()) + return evm.create(caller, codeAndHash, gas, endowment, contractAddr) +} + +// ChainConfig returns the environment's chain configuration +func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } + +// IsBlockProposer returns whether or not we are a block proposer. +func (evm *EVM) IsBlockProposer() bool { return evm.vmConfig.IsBlockProposer } diff --git a/core/vm/evm/evm_test.go b/core/vm/evm/evm_test.go new file mode 100644 index 000000000..2e382c15f --- /dev/null +++ b/core/vm/evm/evm_test.go @@ -0,0 +1,244 @@ +// Copyright 2018 The dexon-consensus Authors +// This file is part of the dexon-consensus library. +// +// The dexon-consensus library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus library. If not, see +// <http://www.gnu.org/licenses/>. + +package evm + +import ( + "fmt" + "math" + "math/big" + "math/rand" + "strings" + "testing" + "time" + + "github.com/dexon-foundation/dexon/accounts/abi" + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/core/state" + "github.com/dexon-foundation/dexon/crypto" + "github.com/dexon-foundation/dexon/ethdb" + "github.com/dexon-foundation/dexon/params" +) + +var cobContract = string("608060405234801561001057600080fd5b5033600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506123ba806100616000396000f300608060405260043610610154576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde0314610160578063078fd9ea146101f0578063095ea7b31461021b5780630b97bc861461028057806318160ddd146102ab57806323b872dd146102d6578063313ce5671461035b5780634042b66f1461038c5780634bb278f3146103b7578063521eb273146103ce578063661884631461042557806368428a1b1461048a57806370a08231146104b9578063715018a6146105105780638da5cb5b1461052757806395d89b411461057e578063a77c61f21461060e578063a9059cbb1461066d578063b52e0dc8146106d2578063b753a98c14610713578063c24a0f8b14610760578063c3262dfd1461078b578063d73dd623146107bc578063dd62ed3e14610821578063f2fde38b14610898578063f92ad219146108db575b61015e3334610946565b005b34801561016c57600080fd5b50610175610b97565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101b557808201518184015260208101905061019a565b50505050905090810190601f1680156101e25780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156101fc57600080fd5b50610205610bd0565b6040518082815260200191505060405180910390f35b34801561022757600080fd5b50610266600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610bd6565b604051808215151515815260200191505060405180910390f35b34801561028c57600080fd5b50610295610cc8565b6040518082815260200191505060405180910390f35b3480156102b757600080fd5b506102c0610cce565b6040518082815260200191505060405180910390f35b3480156102e257600080fd5b50610341600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610cd8565b604051808215151515815260200191505060405180910390f35b34801561036757600080fd5b50610370611093565b604051808260ff1660ff16815260200191505060405180910390f35b34801561039857600080fd5b506103a1611098565b6040518082815260200191505060405180910390f35b3480156103c357600080fd5b506103cc61109e565b005b3480156103da57600080fd5b506103e361123f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561043157600080fd5b50610470600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611265565b604051808215151515815260200191505060405180910390f35b34801561049657600080fd5b5061049f6114f7565b604051808215151515815260200191505060405180910390f35b3480156104c557600080fd5b506104fa600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611532565b6040518082815260200191505060405180910390f35b34801561051c57600080fd5b5061052561157a565b005b34801561053357600080fd5b5061053c61167f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561058a57600080fd5b506105936116a5565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156105d35780820151818401526020810190506105b8565b50505050905090810190601f1680156106005780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561061a57600080fd5b5061064f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506116de565b60405180826000191660001916815260200191505060405180910390f35b34801561067957600080fd5b506106b8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506116f6565b604051808215151515815260200191505060405180910390f35b3480156106de57600080fd5b506106fd60048036038101908080359060200190929190505050611916565b6040518082815260200191505060405180910390f35b34801561071f57600080fd5b5061075e600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506119ae565b005b34801561076c57600080fd5b50610775611c35565b6040518082815260200191505060405180910390f35b34801561079757600080fd5b506107ba6004803603810190808035600019169060200190929190505050611c3b565b005b3480156107c857600080fd5b50610807600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611cf9565b604051808215151515815260200191505060405180910390f35b34801561082d57600080fd5b50610882600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611ef5565b6040518082815260200191505060405180910390f35b3480156108a457600080fd5b506108d9600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611f7c565b005b3480156108e757600080fd5b50610944600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050611fe4565b005b6000806000806109546114f7565b151561095f57600080fd5b67016345785d8a0000851015151561097657600080fd5b84935061098e846009546121ec90919063ffffffff16565b92506109a061099b612208565b611916565b91506109b5828561221090919063ffffffff16565b9050806109c0612248565b101515156109cd57600080fd5b610a098160008060b173ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461227990919063ffffffff16565b60008060b173ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610a87816000808973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546121ec90919063ffffffff16565b6000808873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508573ffffffffffffffffffffffffffffffffffffffff167fcd60aa75dea3072fbc07ae6d7d856b5dc5f4eee88854f5b4abf7b680ef8bc50f8583604051808381526020018281526020019250505060405180910390a282600981905550600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc349081150290604051600060405180830381858888f19350505050158015610b8e573d6000803e3d6000fd5b50505050505050565b6040805190810160405280600f81526020017f436f62696e686f6f6420546f6b656e000000000000000000000000000000000081525081565b60075481565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60055481565b6000600454905090565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548211151515610d2757600080fd5b600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548211151515610db257600080fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614151515610dee57600080fd5b610e3f826000808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461227990919063ffffffff16565b6000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610ed2826000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546121ec90919063ffffffff16565b6000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610fa382600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461227990919063ffffffff16565b600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b601281565b60095481565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156110fa57600080fd5b6111026114f7565b15151561110e57600080fd5b6111aa60008060b173ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054600080600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546121ec90919063ffffffff16565b600080600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600080600060b173ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550565b600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600080600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508083101515611377576000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061140b565b61138a838261227990919063ffffffff16565b600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b8373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546040518082815260200191505060405180910390a3600191505092915050565b6000600554611504612208565b1015801561151a5750600654611518612208565b105b801561152d5750600061152b612248565b115b905090565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156115d657600080fd5b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167ff8df31144d9c2f0f6b59d69b8b98abd5459d07f2742c4df920b25aae33c6482060405160405180910390a26000600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6040805190810160405280600381526020017f434f42000000000000000000000000000000000000000000000000000000000081525081565b600a6020528060005260406000206000915090505481565b60008060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054821115151561174557600080fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415151561178157600080fd5b6117d2826000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461227990919063ffffffff16565b6000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550611865826000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546121ec90919063ffffffff16565b6000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b600060055482101561192b57600090506119a9565b62093a8060055401821015611944576115e090506119a9565b621275006005540182101561195d5761145090506119a9565b621baf8060055401821015611976576112c090506119a9565b6224ea006005540182101561198f5761113090506119a9565b600654821115156119a457610fa090506119a9565b600090505b919050565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611a0a57600080fd5b80600080600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515611a7957600080fd5b611aec81600080600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461227990919063ffffffff16565b600080600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550611ba1816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546121ec90919063ffffffff16565b6000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff167fdb2d10a559cb6e14fee5a7a2d8c216314e11c22404e85a4f9af45f07c87192bb826040518082815260200191505060405180910390a25050565b60065481565b80600a60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081600019169055507fa1a3e4c7b21b6004c77b4fe18bdd0d1bd1be31dbb88112463524daa9abacb8363382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182600019166000191681526020019250505060405180910390a150565b6000611d8a82600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546121ec90919063ffffffff16565b600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611fd857600080fd5b611fe181612292565b50565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561204057600080fd5b6000600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561208757600080fd5b61208f612208565b841015151561209d57600080fd5b82841015156120ab57600080fd5b60008573ffffffffffffffffffffffffffffffffffffffff16141515156120d157600080fd5b81811115156120df57600080fd5b83600581905550826006819055508160078190555084600860006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550806004819055506121516007548261227990919063ffffffff16565b600080600860009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555060075460008060b173ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505050505050565b600081830190508281101515156121ff57fe5b80905092915050565b600042905090565b6000808314156122235760009050612242565b818302905081838281151561223457fe5b0414151561223e57fe5b8090505b92915050565b600080600060b173ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905090565b600082821115151561228757fe5b818303905092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141515156122ce57600080fd5b8073ffffffffffffffffffffffffffffffffffffffff16600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a380600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505600a165627a7a723058209accf1a69ff5978107f5013b9b8716cc08726d534484a57fc04c94d084be8cf80029") +var cobABIJSON = ` +[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"saleCap","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"startDate","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"weiRaised","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"finalize","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"wallet","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"saleActive","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"cobinhoodUserIDs","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"at","type":"uint256"}],"name":"getRateAt","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"buyer","type":"address"},{"name":"amount","type":"uint256"}],"name":"push","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"endDate","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"user_id","type":"bytes32"}],"name":"setUserID","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_wallet","type":"address"},{"name":"_start","type":"uint256"},{"name":"_end","type":"uint256"},{"name":"_saleCap","type":"uint256"},{"name":"_totalSupply","type":"uint256"}],"name":"initialize","outputs":[],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"purchaser","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"TokenPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"buyer","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"PreICOTokenPushed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"user_id","type":"bytes32"}],"name":"UserIDChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}] +` +var cobABI abi.ABI +var cobSaleCap *big.Int +var cobTotalSupply *big.Int +var betContract = string("60806040523480156200001157600080fd5b5060405160208062000ee083398101806040528101908080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a36200010b8162000112640100000000026401000000009004565b50620003f5565b60006200011e62000364565b6000620001396200029f640100000000026401000000009004565b15156200014557600080fd5b606484101515620001e4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260248152602001807f4578706563746174696f6e2073686f756c64206265206c657373207468616e2081526020017f3130302e0000000000000000000000000000000000000000000000000000000081525060400191505060405180910390fd5b836001819055506200021061271085620002f664010000000002620008c4179091906401000000009004565b925060008260006064811015156200022457fe5b602002018181525050600190505b606481101562000285576200025f8184620003386401000000000262000902179091906401000000009004565b82826064811015156200026e57fe5b602002018181525050808060010191505062000232565b8160029060646200029892919062000388565b5050505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b60008060008414156200030d576000915062000331565b82840290508284828115156200031f57fe5b041415156200032d57600080fd5b8091505b5092915050565b6000806000831115156200034b57600080fd5b82848115156200035757fe5b0490508091505092915050565b610c8060405190810160405280606490602082028038833980820191505090505090565b8260648101928215620003ba579160200282015b82811115620003b95782518255916020019190600101906200039c565b5b509050620003c99190620003cd565b5090565b620003f291905b80821115620003ee576000816000905550600101620003d4565b5090565b90565b610adb80620004056000396000f3006080604052600436106100a4576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630c60e0c3146100a9578063379607f5146100d6578063715018a6146101035780637365870b1461011a5780638da5cb5b1461013a5780638f32d59b14610191578063e1152343146101c0578063ed88c68e14610201578063f2fde38b1461020b578063fc1c39dc1461024e575b600080fd5b3480156100b557600080fd5b506100d460048036038101908080359060200190929190505050610279565b005b3480156100e257600080fd5b50610101600480360381019080803590602001909291905050506103cb565b005b34801561010f57600080fd5b506101186104be565b005b61013860048036038101908080359060200190929190505050610590565b005b34801561014657600080fd5b5061014f610803565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561019d57600080fd5b506101a661082c565b604051808215151515815260200191505060405180910390f35b3480156101cc57600080fd5b506101eb60048036038101908080359060200190929190505050610883565b6040518082815260200191505060405180910390f35b61020961089d565b005b34801561021757600080fd5b5061024c600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061089f565b005b34801561025a57600080fd5b506102636108be565b6040518082815260200191505060405180910390f35b6000610283610a26565b600061028d61082c565b151561029857600080fd5b606484101515610336576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260248152602001807f4578706563746174696f6e2073686f756c64206265206c657373207468616e2081526020017f3130302e0000000000000000000000000000000000000000000000000000000081525060400191505060405180910390fd5b83600181905550610352612710856108c490919063ffffffff16565b9250600082600060648110151561036557fe5b602002018181525050600190505b60648110156103b35761038f818461090290919063ffffffff16565b828260648110151561039d57fe5b6020020181815250508080600101915050610373565b8160029060646103c4929190610a4a565b5050505050565b6103d361082c565b15156103de57600080fd5b3073ffffffffffffffffffffffffffffffffffffffff1631811115151561046d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f4e6f20656e6f756768206f6620746f6b656e20746f20636c61696d2e0000000081525060200191505060405180910390fd5b610475610803565b73ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156104ba573d6000803e3d6000fd5b5050565b6104c661082c565b15156104d157600080fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60008060018311151561060b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f5461726765742073686f756c64206265206269676765722e000000000000000081525060200191505060405180910390fd5b6001548311151515610685576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f5461726765742073686f756c6420626520736d616c6c65722e0000000000000081525060200191505060405180910390fd5b612710341115156106fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4d696e696d756d206265742069732031303030302e000000000000000000000081525060200191505060405180910390fd5b600160642f81151561070c57fe5b0601915060009050828210156107a05761075661271061074860026001870360648110151561073757fe5b0154346108c490919063ffffffff16565b61090290919063ffffffff16565b90503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f1935050505015801561079e573d6000803e3d6000fd5b505b3373ffffffffffffffffffffffffffffffffffffffff167f97371a3349bea11f577edf6e64350a3dfb9de665d1154c7e6d08eb0805aa043084348460405180848152602001838152602001828152602001935050505060405180910390a2505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b60028160648110151561089257fe5b016000915090505481565b565b6108a761082c565b15156108b257600080fd5b6108bb8161092c565b50565b60015481565b60008060008414156108d957600091506108fb565b82840290508284828115156108ea57fe5b041415156108f757600080fd5b8091505b5092915050565b60008060008311151561091457600080fd5b828481151561091f57fe5b0490508091505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415151561096857600080fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610c8060405190810160405280606490602082028038833980820191505090505090565b8260648101928215610a79579160200282015b82811115610a78578251825591602001919060010190610a5d565b5b509050610a869190610a8a565b5090565b610aac91905b80821115610aa8576000816000905550600101610a90565b5090565b905600a165627a7a7230582087d19571ab3ae7207fb8f6182b6b891f2414f00e1904790341a82c957044b5330029") +var betConstructor = []string{"62"} +var betABIJSON = ` +[ { "constant": false, "inputs": [], "name": "renounceOwnership", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "owner", "outputs": [ { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "isOwner", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "", "type": "uint256" } ], "name": "payout", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "newOwner", "type": "address" } ], "name": "transferOwnership", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "expectation", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "inputs": [ { "name": "_expectation", "type": "uint256" } ], "payable": false, "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "_addr", "type": "address" }, { "indexed": false, "name": "_target", "type": "uint256" }, { "indexed": false, "name": "_value", "type": "uint256" }, { "indexed": false, "name": "_pay", "type": "uint256" } ], "name": "Bet", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "previousOwner", "type": "address" }, { "indexed": true, "name": "newOwner", "type": "address" } ], "name": "OwnershipTransferred", "type": "event" }, { "constant": false, "inputs": [ { "name": "_expectation", "type": "uint256" } ], "name": "updateExpectation", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [ { "name": "_target", "type": "uint256" } ], "name": "bet", "outputs": [], "payable": true, "stateMutability": "payable", "type": "function" }, { "constant": false, "inputs": [ { "name": "_amount", "type": "uint256" } ], "name": "claim", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [], "name": "donate", "outputs": [], "payable": true, "stateMutability": "payable", "type": "function" } ] +` +var betABI abi.ABI + +var oneDEX *big.Int + +func init() { + oneDEX = new(big.Int).Exp(big.NewInt(10), big.NewInt(19), nil) + var err error + cobABI, err = abi.JSON(strings.NewReader(cobABIJSON)) + if err != nil { + panic(err) + } + var ok bool + cobSaleCap, ok = new(big.Int).SetString("19d971e4fe8401e74000000", 16) + if !ok { + panic(fmt.Errorf("error setting cobSaleCap")) + } + cobTotalSupply, ok = new(big.Int).SetString("33b2e3c9fd0803ce8000000", 16) + if !ok { + panic(fmt.Errorf("error setting cobTotalSupply")) + } + betABI, err = abi.JSON(strings.NewReader(betABIJSON)) + if err != nil { + panic(err) + } +} + +type testVM struct { + evm *EVM + interpreter Interpreter +} + +func newTestVM() *testVM { + memDB := ethdb.NewMemDatabase() + stateDB, err := state.New(common.Hash{}, state.NewDatabase(memDB)) + if err != nil { + panic(err) + } + + context := Context{ + CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, + Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, + Time: big.NewInt(time.Now().UnixNano() / 1000000000), + BlockNumber: big.NewInt(0), + } + + env := NewEVM(context, stateDB, params.TestChainConfig, Config{}) + evmInterpreter := NewEVMInterpreter(env, env.vmConfig) + + env.interpreter = evmInterpreter + + return &testVM{ + evm: env, + interpreter: evmInterpreter, + } +} + +func (vm *testVM) create(caller string, code []byte, value *big.Int) ( + ret []byte, contractAddr common.Address, err error) { + callerAddr := common.HexToAddress(caller) + contractAddr = crypto.CreateAddress(callerAddr, uint64(0)) + contract := NewContract(AccountRef(callerAddr), + AccountRef(contractAddr), value, math.MaxUint64) + contract.SetCodeOptionalHash(&callerAddr, &codeAndHash{code: code}) + ret, err = vm.interpreter.Run(contract, nil, false) + if err != nil { + contractAddr = common.Address{} + return + } + vm.evm.StateDB.SetCode(contractAddr, ret) + return +} + +func (vm *testVM) call( + caller string, addr common.Address, input []byte, value *big.Int) ( + ret []byte, err error) { + contract := vm.createContract(caller, addr, value) + return vm.callContract(contract, input) +} + +func (vm *testVM) createContract( + caller string, addr common.Address, value *big.Int) *Contract { + callerAddr := common.HexToAddress(caller) + vm.evm.StateDB.CreateAccount(callerAddr) + contract := NewContract(AccountRef(callerAddr), + AccountRef(addr), value, math.MaxUint64) + contract.SetCallCode( + &addr, vm.evm.StateDB.GetCodeHash(addr), vm.evm.StateDB.GetCode(addr)) + return contract +} + +func (vm *testVM) callContract(contract *Contract, input []byte) ( + ret []byte, err error) { + if len(contract.Code) == 0 { + panic(fmt.Errorf("no code")) + } + + if !vm.interpreter.CanRun(contract.Code) { + panic(fmt.Errorf("cannot run")) + } + return vm.interpreter.Run(contract, input, false) +} + +func BenchmarkContractCOBTransfer(bench *testing.B) { + vm := newTestVM() + owner := "1" + wallet := "2" + addresses := []common.Address{} + for i := 0; i < 100; i++ { + addr := crypto.CreateAddress(common.HexToAddress("0x123"), uint64(i)) + addresses = append(addresses, addr) + } + _, contractAddr, err := vm.create(owner, common.Hex2Bytes(cobContract), new(big.Int)) + if err != nil { + bench.Error(err) + return + } + start := time.Now().Add(86400*time.Second).UnixNano() / 1000000000 + end := time.Now().Add(86400*2*time.Second).UnixNano() / 1000000000 + input, err := cobABI.Pack("initialize", common.HexToAddress(wallet), big.NewInt(start), big.NewInt(end), cobSaleCap, cobTotalSupply) + if err != nil { + bench.Error(err) + return + } + _, err = vm.call(owner, contractAddr, input, new(big.Int)) + if err != nil { + bench.Error(err) + return + } + + transferContract := vm.createContract(wallet, contractAddr, new(big.Int)) + transferCalls := make([][]byte, len(addresses)) + for i, addr := range addresses { + transferCalls[i], err = cobABI.Pack("transfer", addr, big.NewInt(1)) + if err != nil { + bench.Error(err) + return + } + } + + bench.ResetTimer() + for i := 0; i < bench.N; i++ { + transferCall := transferCalls[rand.Intn(len(transferCalls))] + _, err = vm.callContract(transferContract, transferCall) + } + bench.StopTimer() + //Check if it is correct + if err != nil { + bench.Error(err) + return + } +} + +func contractConstructor(ctor []string) (output string) { + for _, input := range ctor { + output += fmt.Sprintf("%064s", input) + } + return +} + +func BenchmarkContractDEXONBet(bench *testing.B) { + vm := newTestVM() + owner := "1" + gambler := "2" + _, contractAddr, err := vm.create(owner, common.Hex2Bytes(betContract+contractConstructor(betConstructor)), new(big.Int)) + if err != nil { + bench.Error(err) + return + } + input, err := betABI.Pack("donate") + if err != nil { + bench.Error(err) + return + } + _, err = vm.call(owner, contractAddr, input, new(big.Int).Mul(big.NewInt(100), oneDEX)) + if err != nil { + bench.Error(err) + return + } + + betContract := vm.createContract(gambler, contractAddr, new(big.Int).Set(oneDEX)) + betCall, err := betABI.Pack("bet", big.NewInt(50)) + if err != nil { + bench.Error(err) + return + } + + bench.ResetTimer() + for i := 0; i < bench.N; i++ { + _, err = vm.callContract(betContract, betCall) + if err != nil { + bench.Error(err) + return + } + } + bench.StopTimer() +} diff --git a/core/vm/evm/gas.go b/core/vm/evm/gas.go new file mode 100644 index 000000000..edd772423 --- /dev/null +++ b/core/vm/evm/gas.go @@ -0,0 +1,59 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "math/big" + + "github.com/dexon-foundation/dexon/params" +) + +// Gas costs +const ( + GasQuickStep uint64 = 2 + GasFastestStep uint64 = 3 + GasFastStep uint64 = 5 + GasMidStep uint64 = 8 + GasSlowStep uint64 = 10 + GasExtStep uint64 = 20 + + GasReturn uint64 = 0 + GasStop uint64 = 0 + GasContractByte uint64 = 200 +) + +// calcGas returns the actual gas cost of the call. +// +// The cost of gas was changed during the homestead price change HF. To allow for EIP150 +// to be implemented. The returned gas is gas - base * 63 / 64. +func callGas(gasTable params.GasTable, availableGas, base uint64, callCost *big.Int) (uint64, error) { + if gasTable.CreateBySuicide > 0 { + availableGas = availableGas - base + gas := availableGas - availableGas/64 + // If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150 + // is smaller than the requested amount. Therefor we return the new gas instead + // of returning an error. + if callCost.BitLen() > 64 || gas < callCost.Uint64() { + return gas, nil + } + } + if callCost.BitLen() > 64 { + return 0, errGasUintOverflow + } + + return callCost.Uint64(), nil +} diff --git a/core/vm/evm/gas_table.go b/core/vm/evm/gas_table.go new file mode 100644 index 000000000..0087004cf --- /dev/null +++ b/core/vm/evm/gas_table.go @@ -0,0 +1,536 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/common/math" + "github.com/dexon-foundation/dexon/core/vm" + "github.com/dexon-foundation/dexon/params" +) + +// memoryGasCosts calculates the quadratic gas for memory expansion. It does so +// only for the memory region that is expanded, not the total memory. +func memoryGasCost(mem *vm.Memory, newMemSize uint64) (uint64, error) { + + if newMemSize == 0 { + return 0, nil + } + // The maximum that will fit in a uint64 is max_word_count - 1 + // anything above that will result in an overflow. + // Additionally, a newMemSize which results in a + // newMemSizeWords larger than 0x7ffffffff will cause the square operation + // to overflow. + // The constant 0xffffffffe0 is the highest number that can be used without + // overflowing the gas calculation + if newMemSize > 0xffffffffe0 { + return 0, errGasUintOverflow + } + + newMemSizeWords := vm.ToWordSize(newMemSize) + newMemSize = newMemSizeWords * 32 + + if newMemSize > uint64(mem.Len()) { + square := newMemSizeWords * newMemSizeWords + linCoef := newMemSizeWords * params.MemoryGas + quadCoef := square / params.QuadCoeffDiv + newTotalFee := linCoef + quadCoef + + fee := newTotalFee - mem.LastGasCost + mem.LastGasCost = newTotalFee + + return fee, nil + } + return 0, nil +} + +func constGasFunc(gas uint64) gasFunc { + return func(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + return gas, nil + } +} + +func gasCallDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + var overflow bool + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } + + words, overflow := vm.BigUint64(stack.Back(2)) + if overflow { + return 0, errGasUintOverflow + } + + if words, overflow = math.SafeMul(vm.ToWordSize(words), params.CopyGas); overflow { + return 0, errGasUintOverflow + } + + if gas, overflow = math.SafeAdd(gas, words); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasReturnDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + var overflow bool + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } + + words, overflow := vm.BigUint64(stack.Back(2)) + if overflow { + return 0, errGasUintOverflow + } + + if words, overflow = math.SafeMul(vm.ToWordSize(words), params.CopyGas); overflow { + return 0, errGasUintOverflow + } + + if gas, overflow = math.SafeAdd(gas, words); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + var ( + y, x = stack.Back(1), stack.Back(0) + current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) + ) + // The legacy gas metering only takes into consideration the current state + // Legacy rules should be applied if we are in Petersburg (removal of EIP-1283) + // OR Constantinople is not active + if evm.chainRules.IsPetersburg || !evm.chainRules.IsConstantinople { + // This checks for 3 scenario's and calculates gas accordingly: + // + // 1. From a zero-value address to a non-zero value (NEW VALUE) + // 2. From a non-zero value address to a zero-value address (DELETE) + // 3. From a non-zero to a non-zero (CHANGE) + switch { + case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0 + return params.SstoreSetGas, nil + case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0 + evm.StateDB.AddRefund(params.SstoreRefundGas) + return params.SstoreClearGas, nil + default: // non 0 => non 0 (or 0 => 0) + return params.SstoreResetGas, nil + } + } + // The new gas metering is based on net gas costs (EIP-1283): + // + // 1. If current value equals new value (this is a no-op), 200 gas is deducted. + // 2. If current value does not equal new value + // 2.1. If original value equals current value (this storage slot has not been changed by the current execution context) + // 2.1.1. If original value is 0, 20000 gas is deducted. + // 2.1.2. Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter. + // 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses. + // 2.2.1. If original value is not 0 + // 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0. + // 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter. + // 2.2.2. If original value equals new value (this storage slot is reset) + // 2.2.2.1. If original value is 0, add 19800 gas to refund counter. + // 2.2.2.2. Otherwise, add 4800 gas to refund counter. + value := common.BigToHash(y) + if current == value { // noop (1) + return params.NetSstoreNoopGas, nil + } + original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x)) + if original == current { + if original == (common.Hash{}) { // create slot (2.1.1) + return params.NetSstoreInitGas, nil + } + if value == (common.Hash{}) { // delete slot (2.1.2b) + evm.StateDB.AddRefund(params.NetSstoreClearRefund) + } + return params.NetSstoreCleanGas, nil // write existing slot (2.1.2) + } + if original != (common.Hash{}) { + if current == (common.Hash{}) { // recreate slot (2.2.1.1) + evm.StateDB.SubRefund(params.NetSstoreClearRefund) + } else if value == (common.Hash{}) { // delete slot (2.2.1.2) + evm.StateDB.AddRefund(params.NetSstoreClearRefund) + } + } + if original == value { + if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) + evm.StateDB.AddRefund(params.NetSstoreResetClearRefund) + } else { // reset to original existing slot (2.2.2.2) + evm.StateDB.AddRefund(params.NetSstoreResetRefund) + } + } + return params.NetSstoreDirtyGas, nil +} + +func makeGasLog(n uint64) gasFunc { + return func(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + requestedSize, overflow := vm.BigUint64(stack.Back(1)) + if overflow { + return 0, errGasUintOverflow + } + + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow { + return 0, errGasUintOverflow + } + + var memorySizeGas uint64 + if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil + } +} + +func gasSha3(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + if gas, overflow = math.SafeAdd(gas, params.Sha3Gas); overflow { + return 0, errGasUintOverflow + } + + wordGas, overflow := vm.BigUint64(stack.Back(1)) + if overflow { + return 0, errGasUintOverflow + } + if wordGas, overflow = math.SafeMul(vm.ToWordSize(wordGas), params.Sha3WordGas); overflow { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + var overflow bool + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } + + wordGas, overflow := vm.BigUint64(stack.Back(2)) + if overflow { + return 0, errGasUintOverflow + } + if wordGas, overflow = math.SafeMul(vm.ToWordSize(wordGas), params.CopyGas); overflow { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasExtCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + + var overflow bool + if gas, overflow = math.SafeAdd(gas, gt.ExtcodeCopy); overflow { + return 0, errGasUintOverflow + } + + wordGas, overflow := vm.BigUint64(stack.Back(3)) + if overflow { + return 0, errGasUintOverflow + } + + if wordGas, overflow = math.SafeMul(vm.ToWordSize(wordGas), params.CopyGas); overflow { + return 0, errGasUintOverflow + } + + if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasExtCodeHash(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + return gt.ExtcodeHash, nil +} + +func gasMLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasMStore8(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasMStore(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasCreate(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + if gas, overflow = math.SafeAdd(gas, params.CreateGas); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasCreate2(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + var overflow bool + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + if gas, overflow = math.SafeAdd(gas, params.Create2Gas); overflow { + return 0, errGasUintOverflow + } + wordGas, overflow := vm.BigUint64(stack.Back(2)) + if overflow { + return 0, errGasUintOverflow + } + if wordGas, overflow = math.SafeMul(vm.ToWordSize(wordGas), params.Sha3WordGas); overflow { + return 0, errGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + return 0, errGasUintOverflow + } + + return gas, nil +} + +func gasBalance(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + return gt.Balance, nil +} + +func gasExtCodeSize(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + return gt.ExtcodeSize, nil +} + +func gasSLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + return gt.SLoad, nil +} + +func gasExp(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + expByteLen := uint64((stack.Data[stack.Len()-2].BitLen() + 7) / 8) + + var ( + gas = expByteLen * gt.ExpByte // no overflow check required. Max is 256 * ExpByte gas + overflow bool + ) + if gas, overflow = math.SafeAdd(gas, GasSlowStep); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + var ( + gas = gt.Calls + transfersValue = stack.Back(2).Sign() != 0 + address = common.BigToAddress(stack.Back(1)) + eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber) + ) + if eip158 { + if transfersValue && evm.StateDB.Empty(address) { + gas += params.CallNewAccountGas + } + } else if !evm.StateDB.Exist(address) { + gas += params.CallNewAccountGas + } + if transfersValue { + gas += params.CallValueTransferGas + } + memoryGas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + var overflow bool + if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { + return 0, errGasUintOverflow + } + + evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0)) + if err != nil { + return 0, err + } + if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasCallCode(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + gas := gt.Calls + if stack.Back(2).Sign() != 0 { + gas += params.CallValueTransferGas + } + memoryGas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + var overflow bool + if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { + return 0, errGasUintOverflow + } + + evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0)) + if err != nil { + return 0, err + } + if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasReturn(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + return memoryGasCost(mem, memorySize) +} + +func gasRevert(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + return memoryGasCost(mem, memorySize) +} + +func gasSuicide(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + var gas uint64 + // EIP150 homestead gas reprice fork: + if evm.ChainConfig().IsEIP150(evm.BlockNumber) { + gas = gt.Suicide + var ( + address = common.BigToAddress(stack.Back(0)) + eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber) + ) + + if eip158 { + // if empty and transfers value + if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 { + gas += gt.CreateBySuicide + } + } else if !evm.StateDB.Exist(address) { + gas += gt.CreateBySuicide + } + } + + if !evm.StateDB.HasSuicided(contract.Address()) { + evm.StateDB.AddRefund(params.SuicideRefundGas) + } + return gas, nil +} + +func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + var overflow bool + if gas, overflow = math.SafeAdd(gas, gt.Calls); overflow { + return 0, errGasUintOverflow + } + + evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0)) + if err != nil { + return 0, err + } + if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + var overflow bool + if gas, overflow = math.SafeAdd(gas, gt.Calls); overflow { + return 0, errGasUintOverflow + } + + evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0)) + if err != nil { + return 0, err + } + if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { + return 0, errGasUintOverflow + } + return gas, nil +} + +func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + return GasFastestStep, nil +} + +func gasSwap(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + return GasFastestStep, nil +} + +func gasDup(gt params.GasTable, evm *EVM, contract *Contract, stack *vm.Stack, mem *vm.Memory, memorySize uint64) (uint64, error) { + return GasFastestStep, nil +} diff --git a/core/vm/evm/gas_table_test.go b/core/vm/evm/gas_table_test.go new file mode 100644 index 000000000..35a902c67 --- /dev/null +++ b/core/vm/evm/gas_table_test.go @@ -0,0 +1,40 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "testing" + + "github.com/dexon-foundation/dexon/core/vm" +) + +func TestMemoryGasCost(t *testing.T) { + //size := uint64(math.MaxUint64 - 64) + size := uint64(0xffffffffe0) + v, err := memoryGasCost(&vm.Memory{}, size) + if err != nil { + t.Error("didn't expect error:", err) + } + if v != 36028899963961341 { + t.Errorf("Expected: 36028899963961341, got %d", v) + } + + _, err = memoryGasCost(&vm.Memory{}, size+1) + if err == nil { + t.Error("expected error") + } +} diff --git a/core/vm/evm/gen_structlog.go b/core/vm/evm/gen_structlog.go new file mode 100644 index 000000000..600af4b52 --- /dev/null +++ b/core/vm/evm/gen_structlog.go @@ -0,0 +1,111 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package evm + +import ( + "encoding/json" + "math/big" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/common/hexutil" + "github.com/dexon-foundation/dexon/common/math" +) + +var _ = (*structLogMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (s StructLog) MarshalJSON() ([]byte, error) { + type StructLog struct { + Pc uint64 `json:"pc"` + Op OpCode `json:"op"` + Gas math.HexOrDecimal64 `json:"gas"` + GasCost math.HexOrDecimal64 `json:"gasCost"` + Memory hexutil.Bytes `json:"memory"` + MemorySize int `json:"memSize"` + Stack []*math.HexOrDecimal256 `json:"stack"` + Storage map[common.Hash]common.Hash `json:"-"` + Depth int `json:"depth"` + RefundCounter uint64 `json:"refund"` + Err error `json:"-"` + OpName string `json:"opName"` + ErrorString string `json:"error"` + } + var enc StructLog + enc.Pc = s.Pc + enc.Op = s.Op + enc.Gas = math.HexOrDecimal64(s.Gas) + enc.GasCost = math.HexOrDecimal64(s.GasCost) + enc.Memory = s.Memory + enc.MemorySize = s.MemorySize + if s.Stack != nil { + enc.Stack = make([]*math.HexOrDecimal256, len(s.Stack)) + for k, v := range s.Stack { + enc.Stack[k] = (*math.HexOrDecimal256)(v) + } + } + enc.Storage = s.Storage + enc.Depth = s.Depth + enc.RefundCounter = s.RefundCounter + enc.Err = s.Err + enc.OpName = s.OpName() + enc.ErrorString = s.ErrorString() + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (s *StructLog) UnmarshalJSON(input []byte) error { + type StructLog struct { + Pc *uint64 `json:"pc"` + Op *OpCode `json:"op"` + Gas *math.HexOrDecimal64 `json:"gas"` + GasCost *math.HexOrDecimal64 `json:"gasCost"` + Memory *hexutil.Bytes `json:"memory"` + MemorySize *int `json:"memSize"` + Stack []*math.HexOrDecimal256 `json:"stack"` + Storage map[common.Hash]common.Hash `json:"-"` + Depth *int `json:"depth"` + RefundCounter *uint64 `json:"refund"` + Err error `json:"-"` + } + var dec StructLog + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Pc != nil { + s.Pc = *dec.Pc + } + if dec.Op != nil { + s.Op = *dec.Op + } + if dec.Gas != nil { + s.Gas = uint64(*dec.Gas) + } + if dec.GasCost != nil { + s.GasCost = uint64(*dec.GasCost) + } + if dec.Memory != nil { + s.Memory = *dec.Memory + } + if dec.MemorySize != nil { + s.MemorySize = *dec.MemorySize + } + if dec.Stack != nil { + s.Stack = make([]*big.Int, len(dec.Stack)) + for k, v := range dec.Stack { + s.Stack[k] = (*big.Int)(v) + } + } + if dec.Storage != nil { + s.Storage = dec.Storage + } + if dec.Depth != nil { + s.Depth = *dec.Depth + } + if dec.RefundCounter != nil { + s.RefundCounter = *dec.RefundCounter + } + if dec.Err != nil { + s.Err = dec.Err + } + return nil +} diff --git a/core/vm/evm/governance.go b/core/vm/evm/governance.go new file mode 100644 index 000000000..8b31d3a01 --- /dev/null +++ b/core/vm/evm/governance.go @@ -0,0 +1,2198 @@ +// Copyright 2018 The dexon-consensus Authors +// This file is part of the dexon-consensus library. +// +// The dexon-consensus library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus library. If not, see +// <http://www.gnu.org/licenses/>. + +package evm + +import ( + "bytes" + "errors" + "math/big" + "sort" + "strings" + + "github.com/dexon-foundation/dexon/accounts/abi" + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/core/types" + "github.com/dexon-foundation/dexon/crypto" + "github.com/dexon-foundation/dexon/params" + "github.com/dexon-foundation/dexon/rlp" + + coreCommon "github.com/dexon-foundation/dexon-consensus/common" + "github.com/dexon-foundation/dexon-consensus/core" + coreCrypto "github.com/dexon-foundation/dexon-consensus/core/crypto" + coreUtils "github.com/dexon-foundation/dexon-consensus/core/utils" + "github.com/dexon-foundation/dexon/core/vm" + + "github.com/dexon-foundation/dexon-consensus/core/crypto/ecdsa" + coreTypes "github.com/dexon-foundation/dexon-consensus/core/types" + dkgTypes "github.com/dexon-foundation/dexon-consensus/core/types/dkg" +) + +var GovernanceContractAddress = common.HexToAddress("63751838d6485578b23e8b051d40861ecc416794") + +var abiObject abi.ABI +var GovernanceContractName2Method map[string]abi.Method +var sig2Method map[string]abi.Method +var events map[string]abi.Event + +type Bytes32 [32]byte + +type ReportType uint64 + +const ( + ReportTypeInvalidDKG = iota + ReportTypeForkVote + ReportTypeForkBlock +) + +func init() { + var err error + + // Parse governance contract ABI. + abiObject, err = abi.JSON(strings.NewReader(GovernanceABIJSON)) + if err != nil { + panic(err) + } + + sig2Method = make(map[string]abi.Method) + GovernanceContractName2Method = make(map[string]abi.Method) + + // Construct dispatch table. + for _, method := range abiObject.Methods { + sig2Method[string(method.Id())] = method + GovernanceContractName2Method[method.Name] = method + } + + events = make(map[string]abi.Event) + + // Event cache. + for _, event := range abiObject.Events { + events[event.Name] = event + } +} + +// RunGovernanceContract executes governance contract. +func RunGovernanceContract(evm *EVM, input []byte, contract *Contract) (ret []byte, err error) { + if len(input) < 4 { + return nil, nil + } + + // Parse input. + method, exists := sig2Method[string(input[:4])] + if !exists { + return nil, errExecutionReverted + } + + // Dispatch method call. + g := newGovernanceContract(evm, contract) + arguments := input[4:] + + switch method.Name { + case "addDKGComplaint": + args := struct { + Round *big.Int + Complaint []byte + }{} + if err := method.Inputs.Unpack(&args, arguments); err != nil { + return nil, errExecutionReverted + } + return g.addDKGComplaint(args.Round, args.Complaint) + case "addDKGMasterPublicKey": + args := struct { + Round *big.Int + PublicKey []byte + }{} + if err := method.Inputs.Unpack(&args, arguments); err != nil { + return nil, errExecutionReverted + } + return g.addDKGMasterPublicKey(args.Round, args.PublicKey) + case "addDKGMPKReady": + args := struct { + Round *big.Int + MPKReady []byte + }{} + if err := method.Inputs.Unpack(&args, arguments); err != nil { + return nil, errExecutionReverted + } + return g.addDKGMPKReady(args.Round, args.MPKReady) + case "addDKGFinalize": + args := struct { + Round *big.Int + Finalize []byte + }{} + if err := method.Inputs.Unpack(&args, arguments); err != nil { + return nil, errExecutionReverted + } + return g.addDKGFinalize(args.Round, args.Finalize) + case "delegate": + address := common.Address{} + if err := method.Inputs.Unpack(&address, arguments); err != nil { + return nil, errExecutionReverted + } + return g.delegate(address) + case "delegatorsLength": + address := common.Address{} + if err := method.Inputs.Unpack(&address, arguments); err != nil { + return nil, errExecutionReverted + } + res, err := method.Outputs.Pack(g.state.LenDelegators(address)) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "nodesLength": + res, err := method.Outputs.Pack(g.state.LenNodes()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "payFine": + address := common.Address{} + if err := method.Inputs.Unpack(&address, arguments); err != nil { + return nil, errExecutionReverted + } + return g.payFine(address) + case "proposeCRS": + args := struct { + Round *big.Int + SignedCRS []byte + }{} + if err := method.Inputs.Unpack(&args, arguments); err != nil { + return nil, errExecutionReverted + } + return g.proposeCRS(args.Round, args.SignedCRS) + case "report": + args := struct { + Type *big.Int + Arg1 []byte + Arg2 []byte + }{} + if err := method.Inputs.Unpack(&args, arguments); err != nil { + return nil, errExecutionReverted + } + return g.report(args.Type, args.Arg1, args.Arg2) + case "stake": + args := struct { + PublicKey []byte + Name string + Email string + Location string + Url string + }{} + if err := method.Inputs.Unpack(&args, arguments); err != nil { + return nil, errExecutionReverted + } + return g.stake(args.PublicKey, args.Name, args.Email, args.Location, args.Url) + case "snapshotRound": + args := struct { + Round *big.Int + Height *big.Int + }{} + if err := method.Inputs.Unpack(&args, arguments); err != nil { + return nil, errExecutionReverted + } + return g.snapshotRound(args.Round, args.Height) + case "transferOwnership": + var newOwner common.Address + if err := method.Inputs.Unpack(&newOwner, arguments); err != nil { + return nil, errExecutionReverted + } + return g.transferOwnership(newOwner) + case "undelegate": + address := common.Address{} + if err := method.Inputs.Unpack(&address, arguments); err != nil { + return nil, errExecutionReverted + } + return g.undelegate(address) + case "unstake": + return g.unstake() + case "updateConfiguration": + var cfg rawConfigStruct + if err := method.Inputs.Unpack(&cfg, arguments); err != nil { + return nil, errExecutionReverted + } + return g.updateConfiguration(&cfg) + case "withdraw": + address := common.Address{} + if err := method.Inputs.Unpack(&address, arguments); err != nil { + return nil, errExecutionReverted + } + return g.withdraw(address) + + // -------------------------------- + // Solidity auto generated methods. + // -------------------------------- + + case "blockGasLimit": + res, err := method.Outputs.Pack(g.state.BlockGasLimit()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "crs": + round := new(big.Int) + if err := method.Inputs.Unpack(&round, arguments); err != nil { + return nil, errExecutionReverted + } + res, err := method.Outputs.Pack(g.state.CRS(round)) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "delegators": + nodeAddr, index := common.Address{}, new(big.Int) + args := []interface{}{&nodeAddr, &index} + if err := method.Inputs.Unpack(&args, arguments); err != nil { + return nil, errExecutionReverted + } + delegator := g.state.Delegator(nodeAddr, index) + res, err := method.Outputs.Pack(delegator.Owner, delegator.Value, delegator.UndelegatedAt) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "delegatorsOffset": + nodeAddr, delegatorAddr := common.Address{}, common.Address{} + args := []interface{}{&nodeAddr, &delegatorAddr} + if err := method.Inputs.Unpack(&args, arguments); err != nil { + return nil, errExecutionReverted + } + res, err := method.Outputs.Pack(g.state.DelegatorsOffset(nodeAddr, delegatorAddr)) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "dkgComplaints": + round, index := new(big.Int), new(big.Int) + args := []interface{}{&round, &index} + if err := method.Inputs.Unpack(&args, arguments); err != nil { + return nil, errExecutionReverted + } + complaints := g.state.DKGComplaints(round) + if int(index.Uint64()) >= len(complaints) { + return nil, errExecutionReverted + } + complaint := complaints[index.Uint64()] + res, err := method.Outputs.Pack(complaint) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "dkgReadys": + round, addr := new(big.Int), common.Address{} + args := []interface{}{&round, &addr} + if err := method.Inputs.Unpack(&args, arguments); err != nil { + return nil, errExecutionReverted + } + ready := g.state.DKGMPKReady(round, addr) + res, err := method.Outputs.Pack(ready) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "dkgReadysCount": + round := new(big.Int) + if err := method.Inputs.Unpack(&round, arguments); err != nil { + return nil, errExecutionReverted + } + count := g.state.DKGMPKReadysCount(round) + res, err := method.Outputs.Pack(count) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + + case "dkgFinalizeds": + round, addr := new(big.Int), common.Address{} + args := []interface{}{&round, &addr} + if err := method.Inputs.Unpack(&args, arguments); err != nil { + return nil, errExecutionReverted + } + finalized := g.state.DKGFinalized(round, addr) + res, err := method.Outputs.Pack(finalized) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "dkgFinalizedsCount": + round := new(big.Int) + if err := method.Inputs.Unpack(&round, arguments); err != nil { + return nil, errExecutionReverted + } + count := g.state.DKGFinalizedsCount(round) + res, err := method.Outputs.Pack(count) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "dkgMasterPublicKeys": + round, index := new(big.Int), new(big.Int) + args := []interface{}{&round, &index} + if err := method.Inputs.Unpack(&args, arguments); err != nil { + return nil, errExecutionReverted + } + mpks := g.state.DKGMasterPublicKeys(round) + if int(index.Uint64()) >= len(mpks) { + return nil, errExecutionReverted + } + mpk := mpks[index.Uint64()] + res, err := method.Outputs.Pack(mpk) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "dkgSetSize": + res, err := method.Outputs.Pack(g.state.DKGSetSize()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "finedRecords": + record := Bytes32{} + if err := method.Inputs.Unpack(&record, arguments); err != nil { + return nil, errExecutionReverted + } + value := g.state.FineRecords(record) + res, err := method.Outputs.Pack(value) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "fineValues": + index := new(big.Int) + if err := method.Inputs.Unpack(&index, arguments); err != nil { + return nil, errExecutionReverted + } + value := g.state.FineValue(index) + res, err := method.Outputs.Pack(value) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "k": + res, err := method.Outputs.Pack(g.state.K()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "lambdaBA": + res, err := method.Outputs.Pack(g.state.LambdaBA()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "lambdaDKG": + res, err := method.Outputs.Pack(g.state.LambdaDKG()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "lastHalvedAmount": + res, err := method.Outputs.Pack(g.state.LastHalvedAmount()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "lockupPeriod": + res, err := method.Outputs.Pack(g.state.LockupPeriod()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "minBlockInterval": + res, err := method.Outputs.Pack(g.state.MinBlockInterval()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "miningVelocity": + res, err := method.Outputs.Pack(g.state.MiningVelocity()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "minStake": + res, err := method.Outputs.Pack(g.state.MinStake()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "nextHalvingSupply": + res, err := method.Outputs.Pack(g.state.NextHalvingSupply()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "numChains": + res, err := method.Outputs.Pack(g.state.NumChains()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "nodes": + index := new(big.Int) + if err := method.Inputs.Unpack(&index, arguments); err != nil { + return nil, errExecutionReverted + } + info := g.state.Node(index) + res, err := method.Outputs.Pack( + info.Owner, info.PublicKey, info.Staked, info.Fined, + info.Name, info.Email, info.Location, info.Url) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "nodesOffsetByAddress": + address := common.Address{} + if err := method.Inputs.Unpack(&address, arguments); err != nil { + return nil, errExecutionReverted + } + res, err := method.Outputs.Pack(g.state.NodesOffsetByAddress(address)) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "nodesOffsetByID": + var id Bytes32 + if err := method.Inputs.Unpack(&id, arguments); err != nil { + return nil, errExecutionReverted + } + res, err := method.Outputs.Pack(g.state.NodesOffsetByID(id)) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "notarySetSize": + res, err := method.Outputs.Pack(g.state.NotarySetSize()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "owner": + res, err := method.Outputs.Pack(g.state.Owner()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "phiRatio": + res, err := method.Outputs.Pack(g.state.PhiRatio()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "roundHeight": + round := new(big.Int) + if err := method.Inputs.Unpack(&round, arguments); err != nil { + return nil, errExecutionReverted + } + res, err := method.Outputs.Pack(g.state.RoundHeight(round)) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "roundInterval": + res, err := method.Outputs.Pack(g.state.RoundInterval()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "totalStaked": + res, err := method.Outputs.Pack(g.state.TotalStaked()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + case "totalSupply": + res, err := method.Outputs.Pack(g.state.TotalSupply()) + if err != nil { + return nil, errExecutionReverted + } + return res, nil + } + return nil, errExecutionReverted +} + +// Storage position enums. +const ( + roundHeightLoc = iota + totalSupplyLoc + totalStakedLoc + nodesLoc + nodesOffsetByAddressLoc + nodesOffsetByIDLoc + delegatorsLoc + delegatorsOffsetLoc + crsLoc + dkgMasterPublicKeysLoc + dkgComplaintsLoc + dkgReadyLoc + dkgReadysCountLoc + dkgFinalizedLoc + dkgFinalizedsCountLoc + ownerLoc + minStakeLoc + lockupPeriodLoc + miningVelocityLoc + nextHalvingSupplyLoc + lastHalvedAmountLoc + blockGasLimitLoc + numChainsLoc + lambdaBALoc + lambdaDKGLoc + kLoc + phiRatioLoc + notarySetSizeLoc + dkgSetSizeLoc + roundIntervalLoc + minBlockIntervalLoc + fineValuesLoc + finedRecordsLoc +) + +func publicKeyToNodeID(pkBytes []byte) (Bytes32, error) { + pk, err := crypto.UnmarshalPubkey(pkBytes) + if err != nil { + return Bytes32{}, err + } + id := Bytes32(coreTypes.NewNodeID(ecdsa.NewPublicKeyFromECDSA(pk)).Hash) + return id, nil +} + +// State manipulation helper fro the governance contract. +type GovernanceStateHelper struct { + StateDB StateDB +} + +func (s *GovernanceStateHelper) getState(loc common.Hash) common.Hash { + return s.StateDB.GetState(GovernanceContractAddress, loc) +} + +func (s *GovernanceStateHelper) setState(loc common.Hash, val common.Hash) { + s.StateDB.SetState(GovernanceContractAddress, loc, val) +} + +func (s *GovernanceStateHelper) getStateBigInt(loc *big.Int) *big.Int { + res := s.StateDB.GetState(GovernanceContractAddress, common.BigToHash(loc)) + return new(big.Int).SetBytes(res.Bytes()) +} + +func (s *GovernanceStateHelper) setStateBigInt(loc *big.Int, val *big.Int) { + s.setState(common.BigToHash(loc), common.BigToHash(val)) +} + +func (s *GovernanceStateHelper) getSlotLoc(loc *big.Int) *big.Int { + return new(big.Int).SetBytes(crypto.Keccak256(common.BigToHash(loc).Bytes())) +} + +func (s *GovernanceStateHelper) getMapLoc(pos *big.Int, key []byte) *big.Int { + return new(big.Int).SetBytes(crypto.Keccak256(key, common.BigToHash(pos).Bytes())) +} + +func (s *GovernanceStateHelper) readBytes(loc *big.Int) []byte { + // Length of the dynamic array (bytes). + rawLength := s.getStateBigInt(loc) + lengthByte := new(big.Int).Mod(rawLength, big.NewInt(256)) + + // Bytes length <= 31, lengthByte % 2 == 0 + // return the high 31 bytes. + if new(big.Int).Mod(lengthByte, big.NewInt(2)).Cmp(big.NewInt(0)) == 0 { + length := new(big.Int).Div(lengthByte, big.NewInt(2)).Uint64() + return rawLength.Bytes()[:length] + } + + // Actual length = (rawLength - 1) / 2 + length := new(big.Int).Div(new(big.Int).Sub(rawLength, big.NewInt(1)), big.NewInt(2)).Uint64() + + // Data address. + dataLoc := s.getSlotLoc(loc) + + // Read continuously for length bytes. + carry := int64(0) + if length%32 > 0 { + carry = 1 + } + chunks := int64(length/32) + carry + var data []byte + for i := int64(0); i < chunks; i++ { + loc = new(big.Int).Add(dataLoc, big.NewInt(i)) + data = append(data, s.getState(common.BigToHash(loc)).Bytes()...) + } + data = data[:length] + return data +} + +func (s *GovernanceStateHelper) writeBytes(loc *big.Int, data []byte) { + length := int64(len(data)) + + if length == 0 { + s.setState(common.BigToHash(loc), common.Hash{}) + return + } + + // Short bytes (length <= 31). + if length < 32 { + data2 := append([]byte(nil), data...) + // Right pad with zeros + for len(data2) < 31 { + data2 = append(data2, byte(0)) + } + data2 = append(data2, byte(length*2)) + s.setState(common.BigToHash(loc), common.BytesToHash(data2)) + return + } + + // Write 2 * length + 1. + storedLength := new(big.Int).Add(new(big.Int).Mul( + big.NewInt(length), big.NewInt(2)), big.NewInt(1)) + s.setStateBigInt(loc, storedLength) + // Write data chunck. + dataLoc := s.getSlotLoc(loc) + carry := int64(0) + if length%32 > 0 { + carry = 1 + } + chunks := length/32 + carry + for i := int64(0); i < chunks; i++ { + loc = new(big.Int).Add(dataLoc, big.NewInt(i)) + maxLoc := (i + 1) * 32 + if maxLoc > length { + maxLoc = length + } + data2 := data[i*32 : maxLoc] + // Right pad with zeros. + for len(data2) < 32 { + data2 = append(data2, byte(0)) + } + s.setState(common.BigToHash(loc), common.BytesToHash(data2)) + } +} + +func (s *GovernanceStateHelper) read2DByteArray(pos, index *big.Int) [][]byte { + baseLoc := s.getSlotLoc(pos) + loc := new(big.Int).Add(baseLoc, index) + + arrayLength := s.getStateBigInt(loc) + dataLoc := s.getSlotLoc(loc) + + data := [][]byte{} + for i := int64(0); i < int64(arrayLength.Uint64()); i++ { + elementLoc := new(big.Int).Add(dataLoc, big.NewInt(i)) + data = append(data, s.readBytes(elementLoc)) + } + + return data +} +func (s *GovernanceStateHelper) appendTo2DByteArray(pos, index *big.Int, data []byte) { + // Find the loc of the last element. + baseLoc := s.getSlotLoc(pos) + loc := new(big.Int).Add(baseLoc, index) + + // Increase length by 1. + arrayLength := s.getStateBigInt(loc) + s.setStateBigInt(loc, new(big.Int).Add(arrayLength, big.NewInt(1))) + + // Write element. + dataLoc := s.getSlotLoc(loc) + elementLoc := new(big.Int).Add(dataLoc, arrayLength) + s.writeBytes(elementLoc, data) +} + +// uint256[] public roundHeight; +func (s *GovernanceStateHelper) LenRoundHeight() *big.Int { + return s.getStateBigInt(big.NewInt(roundHeightLoc)) +} +func (s *GovernanceStateHelper) RoundHeight(round *big.Int) *big.Int { + baseLoc := s.getSlotLoc(big.NewInt(roundHeightLoc)) + loc := new(big.Int).Add(baseLoc, round) + return s.getStateBigInt(loc) +} +func (s *GovernanceStateHelper) PushRoundHeight(height *big.Int) { + // Increase length by 1. + length := s.getStateBigInt(big.NewInt(roundHeightLoc)) + s.setStateBigInt(big.NewInt(roundHeightLoc), new(big.Int).Add(length, big.NewInt(1))) + + baseLoc := s.getSlotLoc(big.NewInt(roundHeightLoc)) + loc := new(big.Int).Add(baseLoc, length) + + s.setStateBigInt(loc, height) +} + +// uint256 public totalSupply; +func (s *GovernanceStateHelper) TotalSupply() *big.Int { + return s.getStateBigInt(big.NewInt(totalSupplyLoc)) +} +func (s *GovernanceStateHelper) IncTotalSupply(amount *big.Int) { + s.setStateBigInt(big.NewInt(totalSupplyLoc), new(big.Int).Add(s.TotalSupply(), amount)) +} +func (s *GovernanceStateHelper) DecTotalSupply(amount *big.Int) { + s.setStateBigInt(big.NewInt(totalSupplyLoc), new(big.Int).Sub(s.TotalSupply(), amount)) +} + +// uint256 public totalStaked; +func (s *GovernanceStateHelper) TotalStaked() *big.Int { + return s.getStateBigInt(big.NewInt(totalStakedLoc)) +} +func (s *GovernanceStateHelper) IncTotalStaked(amount *big.Int) { + s.setStateBigInt(big.NewInt(totalStakedLoc), new(big.Int).Add(s.TotalStaked(), amount)) +} +func (s *GovernanceStateHelper) DecTotalStaked(amount *big.Int) { + s.setStateBigInt(big.NewInt(totalStakedLoc), new(big.Int).Sub(s.TotalStaked(), amount)) +} + +// struct Node { +// address owner; +// bytes publicKey; +// uint256 staked; +// uint256 fined; +// string name; +// string email; +// string location; +// string url; +// } +// +// Node[] nodes; + +type nodeInfo struct { + Owner common.Address + PublicKey []byte + Staked *big.Int + Fined *big.Int + Name string + Email string + Location string + Url string +} + +const nodeStructSize = 8 + +func (s *GovernanceStateHelper) LenNodes() *big.Int { + return s.getStateBigInt(big.NewInt(nodesLoc)) +} +func (s *GovernanceStateHelper) Node(index *big.Int) *nodeInfo { + node := new(nodeInfo) + + arrayBaseLoc := s.getSlotLoc(big.NewInt(nodesLoc)) + elementBaseLoc := new(big.Int).Add(arrayBaseLoc, + new(big.Int).Mul(index, big.NewInt(nodeStructSize))) + + // Owner. + loc := elementBaseLoc + node.Owner = common.BytesToAddress(s.getState(common.BigToHash(elementBaseLoc)).Bytes()) + + // PublicKey. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(1)) + node.PublicKey = s.readBytes(loc) + + // Staked. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(2)) + node.Staked = s.getStateBigInt(loc) + + // Fined. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(3)) + node.Fined = s.getStateBigInt(loc) + + // Name. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(4)) + node.Name = string(s.readBytes(loc)) + + // Email. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(5)) + node.Email = string(s.readBytes(loc)) + + // Location. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(6)) + node.Location = string(s.readBytes(loc)) + + // Url. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(7)) + node.Url = string(s.readBytes(loc)) + + return node +} +func (s *GovernanceStateHelper) PushNode(n *nodeInfo) { + // Increase length by 1. + arrayLength := s.LenNodes() + s.setStateBigInt(big.NewInt(nodesLoc), new(big.Int).Add(arrayLength, big.NewInt(1))) + + s.UpdateNode(arrayLength, n) +} +func (s *GovernanceStateHelper) UpdateNode(index *big.Int, n *nodeInfo) { + arrayBaseLoc := s.getSlotLoc(big.NewInt(nodesLoc)) + elementBaseLoc := new(big.Int).Add(arrayBaseLoc, + new(big.Int).Mul(index, big.NewInt(nodeStructSize))) + + // Owner. + loc := elementBaseLoc + s.setState(common.BigToHash(loc), n.Owner.Hash()) + + // PublicKey. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(1)) + s.writeBytes(loc, n.PublicKey) + + // Staked. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(2)) + s.setStateBigInt(loc, n.Staked) + + // Fined. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(3)) + s.setStateBigInt(loc, n.Fined) + + // Name. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(4)) + s.writeBytes(loc, []byte(n.Name)) + + // Email. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(5)) + s.writeBytes(loc, []byte(n.Email)) + + // Location. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(6)) + s.writeBytes(loc, []byte(n.Location)) + + // Url. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(7)) + s.writeBytes(loc, []byte(n.Url)) +} +func (s *GovernanceStateHelper) PopLastNode() { + // Decrease length by 1. + arrayLength := s.LenNodes() + newArrayLength := new(big.Int).Sub(arrayLength, big.NewInt(1)) + s.setStateBigInt(big.NewInt(nodesLoc), newArrayLength) + + s.UpdateNode(newArrayLength, &nodeInfo{ + Staked: big.NewInt(0), + Fined: big.NewInt(0), + }) +} +func (s *GovernanceStateHelper) Nodes() []*nodeInfo { + var nodes []*nodeInfo + for i := int64(0); i < int64(s.LenNodes().Uint64()); i++ { + nodes = append(nodes, s.Node(big.NewInt(i))) + } + return nodes +} +func (s *GovernanceStateHelper) QualifiedNodes() []*nodeInfo { + var nodes []*nodeInfo + for i := int64(0); i < int64(s.LenNodes().Uint64()); i++ { + node := s.Node(big.NewInt(i)) + if new(big.Int).Sub(node.Staked, node.Fined).Cmp(s.MinStake()) >= 0 { + nodes = append(nodes, node) + } + } + return nodes +} + +// mapping(address => uint256) public nodeOffsetByAddress; +func (s *GovernanceStateHelper) NodesOffsetByAddress(addr common.Address) *big.Int { + loc := s.getMapLoc(big.NewInt(nodesOffsetByAddressLoc), addr.Bytes()) + return new(big.Int).Sub(s.getStateBigInt(loc), big.NewInt(1)) +} +func (s *GovernanceStateHelper) PutNodesOffsetByAddress(addr common.Address, offset *big.Int) { + loc := s.getMapLoc(big.NewInt(nodesOffsetByAddressLoc), addr.Bytes()) + s.setStateBigInt(loc, new(big.Int).Add(offset, big.NewInt(1))) +} +func (s *GovernanceStateHelper) DeleteNodesOffsetByAddress(addr common.Address) { + loc := s.getMapLoc(big.NewInt(nodesOffsetByAddressLoc), addr.Bytes()) + s.setStateBigInt(loc, big.NewInt(0)) +} + +// mapping(address => uint256) public nodeOffsetByID; +func (s *GovernanceStateHelper) NodesOffsetByID(id Bytes32) *big.Int { + loc := s.getMapLoc(big.NewInt(nodesOffsetByIDLoc), id[:]) + return new(big.Int).Sub(s.getStateBigInt(loc), big.NewInt(1)) +} +func (s *GovernanceStateHelper) PutNodesOffsetByID(id Bytes32, offset *big.Int) { + loc := s.getMapLoc(big.NewInt(nodesOffsetByIDLoc), id[:]) + s.setStateBigInt(loc, new(big.Int).Add(offset, big.NewInt(1))) +} +func (s *GovernanceStateHelper) DeleteNodesOffsetByID(id Bytes32) { + loc := s.getMapLoc(big.NewInt(nodesOffsetByIDLoc), id[:]) + s.setStateBigInt(loc, big.NewInt(0)) +} + +func (s *GovernanceStateHelper) PutNodeOffsets(n *nodeInfo, offset *big.Int) error { + id, err := publicKeyToNodeID(n.PublicKey) + if err != nil { + return err + } + s.PutNodesOffsetByID(id, offset) + s.PutNodesOffsetByAddress(n.Owner, offset) + return nil +} + +// struct Delegator { +// address node; +// address owner; +// uint256 value; +// uint256 undelegated_at; +// } + +type delegatorInfo struct { + Owner common.Address + Value *big.Int + UndelegatedAt *big.Int +} + +const delegatorStructSize = 3 + +// mapping(address => Delegator[]) public delegators; +func (s *GovernanceStateHelper) LenDelegators(nodeAddr common.Address) *big.Int { + loc := s.getMapLoc(big.NewInt(delegatorsLoc), nodeAddr.Bytes()) + return s.getStateBigInt(loc) +} +func (s *GovernanceStateHelper) Delegator(nodeAddr common.Address, offset *big.Int) *delegatorInfo { + delegator := new(delegatorInfo) + + loc := s.getMapLoc(big.NewInt(delegatorsLoc), nodeAddr.Bytes()) + arrayBaseLoc := s.getSlotLoc(loc) + elementBaseLoc := new(big.Int).Add(arrayBaseLoc, new(big.Int).Mul(big.NewInt(delegatorStructSize), offset)) + + // Owner. + loc = elementBaseLoc + delegator.Owner = common.BytesToAddress(s.getState(common.BigToHash(elementBaseLoc)).Bytes()) + + // Value. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(1)) + delegator.Value = s.getStateBigInt(loc) + + // UndelegatedAt. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(2)) + delegator.UndelegatedAt = s.getStateBigInt(loc) + + return delegator +} +func (s *GovernanceStateHelper) PushDelegator(nodeAddr common.Address, delegator *delegatorInfo) { + // Increase length by 1. + arrayLength := s.LenDelegators(nodeAddr) + loc := s.getMapLoc(big.NewInt(delegatorsLoc), nodeAddr.Bytes()) + s.setStateBigInt(loc, new(big.Int).Add(arrayLength, big.NewInt(1))) + + s.UpdateDelegator(nodeAddr, arrayLength, delegator) +} +func (s *GovernanceStateHelper) UpdateDelegator(nodeAddr common.Address, offset *big.Int, delegator *delegatorInfo) { + loc := s.getMapLoc(big.NewInt(delegatorsLoc), nodeAddr.Bytes()) + arrayBaseLoc := s.getSlotLoc(loc) + elementBaseLoc := new(big.Int).Add(arrayBaseLoc, new(big.Int).Mul(big.NewInt(delegatorStructSize), offset)) + + // Owner. + loc = elementBaseLoc + s.setState(common.BigToHash(loc), delegator.Owner.Hash()) + + // Value. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(1)) + s.setStateBigInt(loc, delegator.Value) + + // UndelegatedAt. + loc = new(big.Int).Add(elementBaseLoc, big.NewInt(2)) + s.setStateBigInt(loc, delegator.UndelegatedAt) +} +func (s *GovernanceStateHelper) PopLastDelegator(nodeAddr common.Address) { + // Decrease length by 1. + arrayLength := s.LenDelegators(nodeAddr) + newArrayLength := new(big.Int).Sub(arrayLength, big.NewInt(1)) + loc := s.getMapLoc(big.NewInt(delegatorsLoc), nodeAddr.Bytes()) + s.setStateBigInt(loc, newArrayLength) + + s.UpdateDelegator(nodeAddr, newArrayLength, &delegatorInfo{ + Value: big.NewInt(0), + UndelegatedAt: big.NewInt(0), + }) +} + +// mapping(address => mapping(address => uint256)) delegatorsOffset; +func (s *GovernanceStateHelper) DelegatorsOffset(nodeAddr, delegatorAddr common.Address) *big.Int { + loc := s.getMapLoc(s.getMapLoc(big.NewInt(delegatorsOffsetLoc), nodeAddr.Bytes()), delegatorAddr.Bytes()) + return new(big.Int).Sub(s.getStateBigInt(loc), big.NewInt(1)) +} +func (s *GovernanceStateHelper) PutDelegatorOffset(nodeAddr, delegatorAddr common.Address, offset *big.Int) { + loc := s.getMapLoc(s.getMapLoc(big.NewInt(delegatorsOffsetLoc), nodeAddr.Bytes()), delegatorAddr.Bytes()) + s.setStateBigInt(loc, new(big.Int).Add(offset, big.NewInt(1))) +} +func (s *GovernanceStateHelper) DeleteDelegatorsOffset(nodeAddr, delegatorAddr common.Address) { + loc := s.getMapLoc(s.getMapLoc(big.NewInt(delegatorsOffsetLoc), nodeAddr.Bytes()), delegatorAddr.Bytes()) + s.setStateBigInt(loc, big.NewInt(0)) +} + +// bytes32[] public crs; +func (s *GovernanceStateHelper) LenCRS() *big.Int { + return s.getStateBigInt(big.NewInt(crsLoc)) +} +func (s *GovernanceStateHelper) CRS(index *big.Int) common.Hash { + baseLoc := s.getSlotLoc(big.NewInt(crsLoc)) + loc := new(big.Int).Add(baseLoc, index) + return s.getState(common.BigToHash(loc)) +} +func (s *GovernanceStateHelper) CurrentCRS() common.Hash { + return s.CRS(new(big.Int).Sub(s.LenCRS(), big.NewInt(1))) +} +func (s *GovernanceStateHelper) PushCRS(crs common.Hash) { + // increase length by 1. + length := s.getStateBigInt(big.NewInt(crsLoc)) + s.setStateBigInt(big.NewInt(crsLoc), new(big.Int).Add(length, big.NewInt(1))) + + baseLoc := s.getSlotLoc(big.NewInt(crsLoc)) + loc := new(big.Int).Add(baseLoc, length) + + s.setState(common.BigToHash(loc), crs) +} +func (s *GovernanceStateHelper) Round() *big.Int { + return new(big.Int).Sub(s.getStateBigInt(big.NewInt(crsLoc)), big.NewInt(1)) +} + +// bytes[][] public dkgMasterPublicKeys; +func (s *GovernanceStateHelper) DKGMasterPublicKeys(round *big.Int) [][]byte { + return s.read2DByteArray(big.NewInt(dkgMasterPublicKeysLoc), round) +} +func (s *GovernanceStateHelper) PushDKGMasterPublicKey(round *big.Int, mpk []byte) { + s.appendTo2DByteArray(big.NewInt(dkgMasterPublicKeysLoc), round, mpk) +} +func (s *GovernanceStateHelper) UniqueDKGMasterPublicKeys(round *big.Int) []*dkgTypes.MasterPublicKey { + // Prepare DKGMasterPublicKeys. + var dkgMasterPKs []*dkgTypes.MasterPublicKey + existence := make(map[coreTypes.NodeID]struct{}) + for _, mpk := range s.DKGMasterPublicKeys(round) { + x := new(dkgTypes.MasterPublicKey) + if err := rlp.DecodeBytes(mpk, x); err != nil { + panic(err) + } + + // Only the first DKG MPK submission is valid. + if _, exists := existence[x.ProposerID]; exists { + continue + } + existence[x.ProposerID] = struct{}{} + dkgMasterPKs = append(dkgMasterPKs, x) + } + return dkgMasterPKs +} +func (s *GovernanceStateHelper) GetDKGMasterPublicKeyByProposerID( + round *big.Int, proposerID coreTypes.NodeID) (*dkgTypes.MasterPublicKey, error) { + + for _, mpk := range s.DKGMasterPublicKeys(round) { + x := new(dkgTypes.MasterPublicKey) + if err := rlp.DecodeBytes(mpk, x); err != nil { + panic(err) + } + if x.ProposerID.Equal(proposerID) { + return x, nil + } + } + return nil, errors.New("not found") +} + +// bytes[][] public dkgComplaints; +func (s *GovernanceStateHelper) DKGComplaints(round *big.Int) [][]byte { + return s.read2DByteArray(big.NewInt(dkgComplaintsLoc), round) +} +func (s *GovernanceStateHelper) PushDKGComplaint(round *big.Int, complaint []byte) { + s.appendTo2DByteArray(big.NewInt(dkgComplaintsLoc), round, complaint) +} + +// mapping(address => bool)[] public dkgReady; +func (s *GovernanceStateHelper) DKGMPKReady(round *big.Int, addr common.Address) bool { + baseLoc := new(big.Int).Add(s.getSlotLoc(big.NewInt(dkgReadyLoc)), round) + mapLoc := s.getMapLoc(baseLoc, addr.Bytes()) + return s.getStateBigInt(mapLoc).Cmp(big.NewInt(0)) != 0 +} +func (s *GovernanceStateHelper) PutDKGMPKReady(round *big.Int, addr common.Address, ready bool) { + baseLoc := new(big.Int).Add(s.getSlotLoc(big.NewInt(dkgReadyLoc)), round) + mapLoc := s.getMapLoc(baseLoc, addr.Bytes()) + res := big.NewInt(0) + if ready { + res = big.NewInt(1) + } + s.setStateBigInt(mapLoc, res) +} + +// uint256[] public dkgReadysCount; +func (s *GovernanceStateHelper) DKGMPKReadysCount(round *big.Int) *big.Int { + loc := new(big.Int).Add(s.getSlotLoc(big.NewInt(dkgReadysCountLoc)), round) + return s.getStateBigInt(loc) +} +func (s *GovernanceStateHelper) IncDKGMPKReadysCount(round *big.Int) { + loc := new(big.Int).Add(s.getSlotLoc(big.NewInt(dkgReadysCountLoc)), round) + count := s.getStateBigInt(loc) + s.setStateBigInt(loc, new(big.Int).Add(count, big.NewInt(1))) +} + +// mapping(address => bool)[] public dkgFinalized; +func (s *GovernanceStateHelper) DKGFinalized(round *big.Int, addr common.Address) bool { + baseLoc := new(big.Int).Add(s.getSlotLoc(big.NewInt(dkgFinalizedLoc)), round) + mapLoc := s.getMapLoc(baseLoc, addr.Bytes()) + return s.getStateBigInt(mapLoc).Cmp(big.NewInt(0)) != 0 +} +func (s *GovernanceStateHelper) PutDKGFinalized(round *big.Int, addr common.Address, finalized bool) { + baseLoc := new(big.Int).Add(s.getSlotLoc(big.NewInt(dkgFinalizedLoc)), round) + mapLoc := s.getMapLoc(baseLoc, addr.Bytes()) + res := big.NewInt(0) + if finalized { + res = big.NewInt(1) + } + s.setStateBigInt(mapLoc, res) +} + +// uint256[] public dkgFinalizedsCount; +func (s *GovernanceStateHelper) DKGFinalizedsCount(round *big.Int) *big.Int { + loc := new(big.Int).Add(s.getSlotLoc(big.NewInt(dkgFinalizedsCountLoc)), round) + return s.getStateBigInt(loc) +} +func (s *GovernanceStateHelper) IncDKGFinalizedsCount(round *big.Int) { + loc := new(big.Int).Add(s.getSlotLoc(big.NewInt(dkgFinalizedsCountLoc)), round) + count := s.getStateBigInt(loc) + s.setStateBigInt(loc, new(big.Int).Add(count, big.NewInt(1))) +} + +// address public owner; +func (s *GovernanceStateHelper) Owner() common.Address { + val := s.getState(common.BigToHash(big.NewInt(ownerLoc))) + return common.BytesToAddress(val.Bytes()) +} +func (s *GovernanceStateHelper) SetOwner(newOwner common.Address) { + s.setState(common.BigToHash(big.NewInt(ownerLoc)), newOwner.Hash()) +} + +// uint256 public minStake; +func (s *GovernanceStateHelper) MinStake() *big.Int { + return s.getStateBigInt(big.NewInt(minStakeLoc)) +} + +// uint256 public lockupPeriod; +func (s *GovernanceStateHelper) LockupPeriod() *big.Int { + return s.getStateBigInt(big.NewInt(lockupPeriodLoc)) +} + +// uint256 public miningVelocity; +func (s *GovernanceStateHelper) MiningVelocity() *big.Int { + return s.getStateBigInt(big.NewInt(miningVelocityLoc)) +} +func (s *GovernanceStateHelper) HalfMiningVelocity() { + s.setStateBigInt(big.NewInt(miningVelocityLoc), + new(big.Int).Div(s.MiningVelocity(), big.NewInt(2))) +} + +// uint256 public nextHalvingSupply; +func (s *GovernanceStateHelper) NextHalvingSupply() *big.Int { + return s.getStateBigInt(big.NewInt(nextHalvingSupplyLoc)) +} +func (s *GovernanceStateHelper) IncNextHalvingSupply(amount *big.Int) { + s.setStateBigInt(big.NewInt(nextHalvingSupplyLoc), + new(big.Int).Add(s.NextHalvingSupply(), amount)) +} + +// uint256 public lastHalvedAmount; +func (s *GovernanceStateHelper) LastHalvedAmount() *big.Int { + return s.getStateBigInt(big.NewInt(lastHalvedAmountLoc)) +} +func (s *GovernanceStateHelper) HalfLastHalvedAmount() { + s.setStateBigInt(big.NewInt(lastHalvedAmountLoc), + new(big.Int).Div(s.LastHalvedAmount(), big.NewInt(2))) +} + +func (s *GovernanceStateHelper) MiningHalved() { + s.HalfMiningVelocity() + s.HalfLastHalvedAmount() + s.IncNextHalvingSupply(s.LastHalvedAmount()) +} + +// uint256 public blockGasLimit; +func (s *GovernanceStateHelper) BlockGasLimit() *big.Int { + return s.getStateBigInt(big.NewInt(blockGasLimitLoc)) +} +func (s *GovernanceStateHelper) SetBlockGasLimit(reward *big.Int) { + s.setStateBigInt(big.NewInt(blockGasLimitLoc), reward) +} + +// uint256 public numChains; +func (s *GovernanceStateHelper) NumChains() *big.Int { + return s.getStateBigInt(big.NewInt(numChainsLoc)) +} + +// uint256 public lambdaBA; +func (s *GovernanceStateHelper) LambdaBA() *big.Int { + return s.getStateBigInt(big.NewInt(lambdaBALoc)) +} + +// uint256 public lambdaDKG; +func (s *GovernanceStateHelper) LambdaDKG() *big.Int { + return s.getStateBigInt(big.NewInt(lambdaDKGLoc)) +} + +// uint256 public k; +func (s *GovernanceStateHelper) K() *big.Int { + return s.getStateBigInt(big.NewInt(kLoc)) +} + +// uint256 public phiRatio; // stored as PhiRatio * 10^6 +func (s *GovernanceStateHelper) PhiRatio() *big.Int { + return s.getStateBigInt(big.NewInt(phiRatioLoc)) +} + +// uint256 public notarySetSize; +func (s *GovernanceStateHelper) NotarySetSize() *big.Int { + return s.getStateBigInt(big.NewInt(notarySetSizeLoc)) +} + +// uint256 public dkgSetSize; +func (s *GovernanceStateHelper) DKGSetSize() *big.Int { + return s.getStateBigInt(big.NewInt(dkgSetSizeLoc)) +} + +// uint256 public roundInterval; +func (s *GovernanceStateHelper) RoundInterval() *big.Int { + return s.getStateBigInt(big.NewInt(roundIntervalLoc)) +} + +// uint256 public minBlockInterval; +func (s *GovernanceStateHelper) MinBlockInterval() *big.Int { + return s.getStateBigInt(big.NewInt(minBlockIntervalLoc)) +} + +// uint256[] public fineValues; +func (s *GovernanceStateHelper) FineValue(index *big.Int) *big.Int { + arrayBaseLoc := s.getSlotLoc(big.NewInt(fineValuesLoc)) + return s.getStateBigInt(new(big.Int).Add(arrayBaseLoc, index)) +} +func (s *GovernanceStateHelper) FineValues() []*big.Int { + len := s.getStateBigInt(big.NewInt(fineValuesLoc)) + result := make([]*big.Int, len.Uint64()) + for i := 0; i < int(len.Uint64()); i++ { + result[i] = s.FineValue(big.NewInt(int64(i))) + } + return result +} +func (s *GovernanceStateHelper) SetFineValues(values []*big.Int) { + s.setStateBigInt(big.NewInt(fineValuesLoc), big.NewInt(int64(len(values)))) + + arrayBaseLoc := s.getSlotLoc(big.NewInt(fineValuesLoc)) + for i, v := range values { + s.setStateBigInt(new(big.Int).Add(arrayBaseLoc, big.NewInt(int64(i))), v) + } +} + +// uint256[] public fineRdecords; +func (s *GovernanceStateHelper) FineRecords(recordHash Bytes32) bool { + loc := s.getMapLoc(big.NewInt(finedRecordsLoc), recordHash[:]) + return s.getStateBigInt(loc).Cmp(big.NewInt(0)) > 0 +} +func (s *GovernanceStateHelper) SetFineRecords(recordHash Bytes32, status bool) { + loc := s.getMapLoc(big.NewInt(finedRecordsLoc), recordHash[:]) + value := int64(0) + if status { + value = int64(1) + } + s.setStateBigInt(loc, big.NewInt(value)) +} + +// Stake is a helper function for creating genesis state. +func (s *GovernanceStateHelper) Stake( + addr common.Address, publicKey []byte, staked *big.Int, + name, email, location, url string) { + offset := s.LenNodes() + node := &nodeInfo{ + Owner: addr, + PublicKey: publicKey, + Staked: staked, + Fined: big.NewInt(0), + Name: name, + Email: email, + Location: location, + Url: url, + } + s.PushNode(node) + if err := s.PutNodeOffsets(node, offset); err != nil { + panic(err) + } + + if staked.Cmp(big.NewInt(0)) == 0 { + return + } + + offset = s.LenDelegators(addr) + s.PushDelegator(addr, &delegatorInfo{ + Owner: addr, + Value: staked, + UndelegatedAt: big.NewInt(0), + }) + s.PutDelegatorOffset(addr, addr, offset) + + // Add to network total staked. + s.IncTotalStaked(staked) +} + +const decimalMultiplier = 100000000.0 + +// Configuration returns the current configuration. +func (s *GovernanceStateHelper) Configuration() *params.DexconConfig { + return ¶ms.DexconConfig{ + MinStake: s.getStateBigInt(big.NewInt(minStakeLoc)), + LockupPeriod: s.getStateBigInt(big.NewInt(lockupPeriodLoc)).Uint64(), + MiningVelocity: float32(s.getStateBigInt(big.NewInt(miningVelocityLoc)).Uint64()) / decimalMultiplier, + NextHalvingSupply: s.getStateBigInt(big.NewInt(nextHalvingSupplyLoc)), + LastHalvedAmount: s.getStateBigInt(big.NewInt(lastHalvedAmountLoc)), + BlockGasLimit: s.getStateBigInt(big.NewInt(blockGasLimitLoc)).Uint64(), + NumChains: uint32(s.getStateBigInt(big.NewInt(numChainsLoc)).Uint64()), + LambdaBA: s.getStateBigInt(big.NewInt(lambdaBALoc)).Uint64(), + LambdaDKG: s.getStateBigInt(big.NewInt(lambdaDKGLoc)).Uint64(), + K: uint32(s.getStateBigInt(big.NewInt(kLoc)).Uint64()), + PhiRatio: float32(s.getStateBigInt(big.NewInt(phiRatioLoc)).Uint64()) / decimalMultiplier, + NotarySetSize: uint32(s.getStateBigInt(big.NewInt(notarySetSizeLoc)).Uint64()), + DKGSetSize: uint32(s.getStateBigInt(big.NewInt(dkgSetSizeLoc)).Uint64()), + RoundInterval: s.getStateBigInt(big.NewInt(roundIntervalLoc)).Uint64(), + MinBlockInterval: s.getStateBigInt(big.NewInt(minBlockIntervalLoc)).Uint64(), + FineValues: s.FineValues(), + } +} + +// UpdateConfiguration updates system configuration. +func (s *GovernanceStateHelper) UpdateConfiguration(cfg *params.DexconConfig) { + s.setStateBigInt(big.NewInt(minStakeLoc), cfg.MinStake) + s.setStateBigInt(big.NewInt(lockupPeriodLoc), big.NewInt(int64(cfg.LockupPeriod))) + s.setStateBigInt(big.NewInt(miningVelocityLoc), big.NewInt(int64(cfg.MiningVelocity*decimalMultiplier))) + s.setStateBigInt(big.NewInt(nextHalvingSupplyLoc), cfg.NextHalvingSupply) + s.setStateBigInt(big.NewInt(lastHalvedAmountLoc), cfg.LastHalvedAmount) + s.setStateBigInt(big.NewInt(blockGasLimitLoc), big.NewInt(int64(cfg.BlockGasLimit))) + s.setStateBigInt(big.NewInt(numChainsLoc), big.NewInt(int64(cfg.NumChains))) + s.setStateBigInt(big.NewInt(lambdaBALoc), big.NewInt(int64(cfg.LambdaBA))) + s.setStateBigInt(big.NewInt(lambdaDKGLoc), big.NewInt(int64(cfg.LambdaDKG))) + s.setStateBigInt(big.NewInt(kLoc), big.NewInt(int64(cfg.K))) + s.setStateBigInt(big.NewInt(phiRatioLoc), big.NewInt(int64(cfg.PhiRatio*decimalMultiplier))) + s.setStateBigInt(big.NewInt(notarySetSizeLoc), big.NewInt(int64(cfg.NotarySetSize))) + s.setStateBigInt(big.NewInt(dkgSetSizeLoc), big.NewInt(int64(cfg.DKGSetSize))) + s.setStateBigInt(big.NewInt(roundIntervalLoc), big.NewInt(int64(cfg.RoundInterval))) + s.setStateBigInt(big.NewInt(minBlockIntervalLoc), big.NewInt(int64(cfg.MinBlockInterval))) + s.SetFineValues(cfg.FineValues) +} + +type rawConfigStruct struct { + MinStake *big.Int + LockupPeriod *big.Int + MiningVelocity *big.Int + BlockGasLimit *big.Int + NumChains *big.Int + LambdaBA *big.Int + LambdaDKG *big.Int + K *big.Int + PhiRatio *big.Int + NotarySetSize *big.Int + DKGSetSize *big.Int + RoundInterval *big.Int + MinBlockInterval *big.Int + FineValues []*big.Int +} + +// UpdateConfigurationRaw updates system configuration. +func (s *GovernanceStateHelper) UpdateConfigurationRaw(cfg *rawConfigStruct) { + s.setStateBigInt(big.NewInt(minStakeLoc), cfg.MinStake) + s.setStateBigInt(big.NewInt(lockupPeriodLoc), cfg.LockupPeriod) + s.setStateBigInt(big.NewInt(miningVelocityLoc), cfg.MiningVelocity) + s.setStateBigInt(big.NewInt(blockGasLimitLoc), cfg.BlockGasLimit) + s.setStateBigInt(big.NewInt(numChainsLoc), cfg.NumChains) + s.setStateBigInt(big.NewInt(lambdaBALoc), cfg.LambdaBA) + s.setStateBigInt(big.NewInt(lambdaDKGLoc), cfg.LambdaDKG) + s.setStateBigInt(big.NewInt(kLoc), cfg.K) + s.setStateBigInt(big.NewInt(phiRatioLoc), cfg.PhiRatio) + s.setStateBigInt(big.NewInt(notarySetSizeLoc), cfg.NotarySetSize) + s.setStateBigInt(big.NewInt(dkgSetSizeLoc), cfg.DKGSetSize) + s.setStateBigInt(big.NewInt(roundIntervalLoc), cfg.RoundInterval) + s.setStateBigInt(big.NewInt(minBlockIntervalLoc), cfg.MinBlockInterval) + s.SetFineValues(cfg.FineValues) +} + +// event ConfigurationChanged(); +func (s *GovernanceStateHelper) emitConfigurationChangedEvent() { + s.StateDB.AddLog(&types.Log{ + Address: GovernanceContractAddress, + Topics: []common.Hash{events["ConfigurationChanged"].Id()}, + Data: []byte{}, + }) +} + +// event CRSProposed(uint256 round, bytes32 crs); +func (s *GovernanceStateHelper) emitCRSProposed(round *big.Int, crs common.Hash) { + s.StateDB.AddLog(&types.Log{ + Address: GovernanceContractAddress, + Topics: []common.Hash{events["CRSProposed"].Id(), common.BigToHash(round)}, + Data: crs.Bytes(), + }) +} + +// event Staked(address indexed NodeAddress, uint256 Amount); +func (s *GovernanceStateHelper) emitStaked(nodeAddr common.Address) { + s.StateDB.AddLog(&types.Log{ + Address: GovernanceContractAddress, + Topics: []common.Hash{events["Staked"].Id(), nodeAddr.Hash()}, + Data: []byte{}, + }) +} + +// event Unstaked(address indexed NodeAddress); +func (s *GovernanceStateHelper) emitUnstaked(nodeAddr common.Address) { + s.StateDB.AddLog(&types.Log{ + Address: GovernanceContractAddress, + Topics: []common.Hash{events["Unstaked"].Id(), nodeAddr.Hash()}, + Data: []byte{}, + }) +} + +// event Delegated(address indexed NodeAddress, address indexed DelegatorAddress, uint256 Amount); +func (s *GovernanceStateHelper) emitDelegated(nodeAddr, delegatorAddr common.Address, amount *big.Int) { + s.StateDB.AddLog(&types.Log{ + Address: GovernanceContractAddress, + Topics: []common.Hash{events["Delegated"].Id(), nodeAddr.Hash(), delegatorAddr.Hash()}, + Data: common.BigToHash(amount).Bytes(), + }) +} + +// event Undelegated(address indexed NodeAddress, address indexed DelegatorAddress); +func (s *GovernanceStateHelper) emitUndelegated(nodeAddr, delegatorAddr common.Address) { + s.StateDB.AddLog(&types.Log{ + Address: GovernanceContractAddress, + Topics: []common.Hash{events["Undelegated"].Id(), nodeAddr.Hash(), delegatorAddr.Hash()}, + Data: []byte{}, + }) +} + +// event Withdrawn(address indexed NodeAddress, uint256 Amount); +func (s *GovernanceStateHelper) emitWithdrawn(nodeAddr common.Address, amount *big.Int) { + s.StateDB.AddLog(&types.Log{ + Address: GovernanceContractAddress, + Topics: []common.Hash{events["Withdrawn"].Id(), nodeAddr.Hash()}, + Data: common.BigToHash(amount).Bytes(), + }) +} + +// event ForkReported(address indexed NodeAddress, address indexed Type, bytes Arg1, bytes Arg2); +func (s *GovernanceStateHelper) emitForkReported(nodeAddr common.Address, reportType *big.Int, arg1, arg2 []byte) { + + t, err := abi.NewType("bytes") + if err != nil { + panic(err) + } + + arg := abi.Arguments{ + abi.Argument{ + Name: "Arg1", + Type: t, + Indexed: false, + }, + abi.Argument{ + Name: "Arg2", + Type: t, + Indexed: false, + }, + } + + data, err := arg.Pack(arg1, arg2) + if err != nil { + panic(err) + } + s.StateDB.AddLog(&types.Log{ + Address: GovernanceContractAddress, + Topics: []common.Hash{events["ForkReported"].Id(), nodeAddr.Hash()}, + Data: data, + }) +} + +// event Fined(address indexed NodeAddress, uint256 Amount); +func (s *GovernanceStateHelper) emitFined(nodeAddr common.Address, amount *big.Int) { + s.StateDB.AddLog(&types.Log{ + Address: GovernanceContractAddress, + Topics: []common.Hash{events["Fined"].Id(), nodeAddr.Hash()}, + Data: common.BigToHash(amount).Bytes(), + }) +} + +// event FinePaid(address indexed NodeAddress, uint256 Amount); +func (s *GovernanceStateHelper) emitFinePaid(nodeAddr common.Address, amount *big.Int) { + s.StateDB.AddLog(&types.Log{ + Address: GovernanceContractAddress, + Topics: []common.Hash{events["FinePaid"].Id(), nodeAddr.Hash()}, + Data: common.BigToHash(amount).Bytes(), + }) +} + +// GovernanceContract represents the governance contract of DEXCON. +type GovernanceContract struct { + evm *EVM + state GovernanceStateHelper + contract *Contract +} + +func newGovernanceContract(evm *EVM, contract *Contract) *GovernanceContract { + return &GovernanceContract{ + evm: evm, + state: GovernanceStateHelper{evm.StateDB}, + contract: contract, + } +} + +func (g *GovernanceContract) Address() common.Address { + return GovernanceContractAddress +} + +func (g *GovernanceContract) transfer(from, to common.Address, amount *big.Int) bool { + // TODO(w): add this to debug trace so it shows up as internal transaction. + if g.evm.CanTransfer(g.evm.StateDB, from, amount) { + g.evm.Transfer(g.evm.StateDB, from, to, amount) + return true + } + return false +} + +func (g *GovernanceContract) useGas(gas uint64) ([]byte, error) { + if !g.contract.UseGas(gas) { + return nil, vm.ErrOutOfGas + } + return nil, nil +} + +func (g *GovernanceContract) penalize() ([]byte, error) { + g.useGas(g.contract.Gas) + return nil, errExecutionReverted +} + +func (g *GovernanceContract) inDKGSet(round *big.Int, nodeID coreTypes.NodeID) bool { + target := coreTypes.NewDKGSetTarget(coreCommon.Hash(g.state.CurrentCRS())) + ns := coreTypes.NewNodeSet() + + configRound := big.NewInt(0) // If round < core.ConfigRoundShift, use 0. + if round.Uint64() >= core.ConfigRoundShift { + configRound = new(big.Int).Sub(round, big.NewInt(int64(core.ConfigRoundShift))) + } + + statedb, err := g.evm.StateAtNumber(g.state.RoundHeight(configRound).Uint64()) + if err != nil { + panic(err) + } + + state := GovernanceStateHelper{statedb} + for _, x := range state.QualifiedNodes() { + mpk, err := ecdsa.NewPublicKeyFromByteSlice(x.PublicKey) + if err != nil { + panic(err) + } + ns.Add(coreTypes.NewNodeID(mpk)) + } + + dkgSet := ns.GetSubSet(int(g.state.DKGSetSize().Uint64()), target) + _, ok := dkgSet[nodeID] + return ok +} + +func (g *GovernanceContract) addDKGComplaint(round *big.Int, comp []byte) ([]byte, error) { + if round.Cmp(g.state.Round()) != 0 { + return g.penalize() + } + + caller := g.contract.Caller() + + // Finalized caller is not allowed to propose complaint. + if g.state.DKGFinalized(round, caller) { + return g.penalize() + } + + // Calculate 2f + threshold := new(big.Int).Mul( + big.NewInt(2), + new(big.Int).Div(g.state.DKGSetSize(), big.NewInt(3))) + + // If 2f + 1 of DKG set is finalized, one can not propose complaint anymore. + if g.state.DKGFinalizedsCount(round).Cmp(threshold) > 0 { + return nil, errExecutionReverted + } + + var dkgComplaint dkgTypes.Complaint + if err := rlp.DecodeBytes(comp, &dkgComplaint); err != nil { + return g.penalize() + } + + // DKGComplaint must belongs to someone in DKG set. + if !g.inDKGSet(round, dkgComplaint.ProposerID) { + return g.penalize() + } + + verified, _ := coreUtils.VerifyDKGComplaintSignature(&dkgComplaint) + if !verified { + return g.penalize() + } + + mpk, err := g.state.GetDKGMasterPublicKeyByProposerID( + round, dkgComplaint.PrivateShare.ProposerID) + if err != nil { + return g.penalize() + } + + // Verify DKG complaint is correct. + ok, err := coreUtils.VerifyDKGComplaint(&dkgComplaint, mpk) + if !ok || err != nil { + return g.penalize() + } + + // Fine the attacker. + need, err := coreUtils.NeedPenaltyDKGPrivateShare(&dkgComplaint, mpk) + if err != nil { + return g.penalize() + } + if need { + fineValue := g.state.FineValue(big.NewInt(ReportTypeInvalidDKG)) + offset := g.state.NodesOffsetByID(Bytes32(dkgComplaint.PrivateShare.ProposerID.Hash)) + node := g.state.Node(offset) + if err := g.fine(node.Owner, fineValue, comp, nil); err != nil { + return g.penalize() + } + } + + g.state.PushDKGComplaint(round, comp) + + // Set this to relatively high to prevent spamming + return g.useGas(5000000) +} + +func (g *GovernanceContract) addDKGMasterPublicKey(round *big.Int, mpk []byte) ([]byte, error) { + // Can only add DKG master public key of current and next round. + if round.Cmp(new(big.Int).Add(g.state.Round(), big.NewInt(1))) > 0 { + return g.penalize() + } + + caller := g.contract.Caller() + offset := g.state.NodesOffsetByAddress(caller) + + // Can not add dkg mpk if not staked. + if offset.Cmp(big.NewInt(0)) < 0 { + return nil, errExecutionReverted + } + + // MPKReady caller is not allowed to propose mpk. + if g.state.DKGMPKReady(round, caller) { + return g.penalize() + } + + // Calculate 2f + threshold := new(big.Int).Mul( + big.NewInt(2), + new(big.Int).Div(g.state.DKGSetSize(), big.NewInt(3))) + + // If 2f + 1 of DKG set is mpk ready, one can not propose mpk anymore. + if g.state.DKGMPKReadysCount(round).Cmp(threshold) > 0 { + return nil, errExecutionReverted + } + + var dkgMasterPK dkgTypes.MasterPublicKey + if err := rlp.DecodeBytes(mpk, &dkgMasterPK); err != nil { + return g.penalize() + } + + // DKGMasterPublicKey must belongs to someone in DKG set. + if !g.inDKGSet(round, dkgMasterPK.ProposerID) { + return g.penalize() + } + + verified, _ := coreUtils.VerifyDKGMasterPublicKeySignature(&dkgMasterPK) + if !verified { + return g.penalize() + } + + g.state.PushDKGMasterPublicKey(round, mpk) + + return g.useGas(100000) +} + +func (g *GovernanceContract) addDKGMPKReady(round *big.Int, ready []byte) ([]byte, error) { + if round.Cmp(g.state.Round()) != 0 { + return g.penalize() + } + + caller := g.contract.Caller() + + var dkgReady dkgTypes.MPKReady + if err := rlp.DecodeBytes(ready, &dkgReady); err != nil { + return g.penalize() + } + + // DKGFInalize must belongs to someone in DKG set. + if !g.inDKGSet(round, dkgReady.ProposerID) { + return g.penalize() + } + + verified, _ := coreUtils.VerifyDKGMPKReadySignature(&dkgReady) + if !verified { + return g.penalize() + } + + if !g.state.DKGMPKReady(round, caller) { + g.state.PutDKGMPKReady(round, caller, true) + g.state.IncDKGMPKReadysCount(round) + } + + return g.useGas(100000) +} +func (g *GovernanceContract) addDKGFinalize(round *big.Int, finalize []byte) ([]byte, error) { + if round.Cmp(g.state.Round()) != 0 { + return g.penalize() + } + + caller := g.contract.Caller() + + var dkgFinalize dkgTypes.Finalize + if err := rlp.DecodeBytes(finalize, &dkgFinalize); err != nil { + return g.penalize() + } + + // DKGFInalize must belongs to someone in DKG set. + if !g.inDKGSet(round, dkgFinalize.ProposerID) { + return g.penalize() + } + + verified, _ := coreUtils.VerifyDKGFinalizeSignature(&dkgFinalize) + if !verified { + return g.penalize() + } + + if !g.state.DKGFinalized(round, caller) { + g.state.PutDKGFinalized(round, caller, true) + g.state.IncDKGFinalizedsCount(round) + } + + return g.useGas(100000) +} + +func (g *GovernanceContract) delegate(nodeAddr common.Address) ([]byte, error) { + offset := g.state.NodesOffsetByAddress(nodeAddr) + if offset.Cmp(big.NewInt(0)) < 0 { + return nil, errExecutionReverted + } + + caller := g.contract.Caller() + value := g.contract.Value() + + // Can not delegate if no fund was sent. + if value.Cmp(big.NewInt(0)) == 0 { + return nil, errExecutionReverted + } + + // Can not delegate if already delegated. + delegatorOffset := g.state.DelegatorsOffset(nodeAddr, caller) + if delegatorOffset.Cmp(big.NewInt(0)) >= 0 { + return nil, errExecutionReverted + } + + // Add to the total staked of node. + node := g.state.Node(offset) + node.Staked = new(big.Int).Add(node.Staked, g.contract.Value()) + g.state.UpdateNode(offset, node) + + // Add to network total staked. + g.state.IncTotalStaked(g.contract.Value()) + + // Push delegator record. + offset = g.state.LenDelegators(nodeAddr) + g.state.PushDelegator(nodeAddr, &delegatorInfo{ + Owner: caller, + Value: value, + UndelegatedAt: big.NewInt(0), + }) + g.state.PutDelegatorOffset(nodeAddr, caller, offset) + g.state.emitDelegated(nodeAddr, caller, value) + + return g.useGas(200000) +} + +func (g *GovernanceContract) updateConfiguration(cfg *rawConfigStruct) ([]byte, error) { + // Only owner can update configuration. + if g.contract.Caller() != g.state.Owner() { + return nil, errExecutionReverted + } + + g.state.UpdateConfigurationRaw(cfg) + g.state.emitConfigurationChangedEvent() + return nil, nil +} + +func (g *GovernanceContract) stake( + publicKey []byte, name, email, location, url string) ([]byte, error) { + + // Reject invalid inputs. + if len(name) >= 32 || len(email) >= 32 || len(location) >= 32 || len(url) >= 128 { + return g.penalize() + } + + caller := g.contract.Caller() + offset := g.state.NodesOffsetByAddress(caller) + + // Can not stake if already staked. + if offset.Cmp(big.NewInt(0)) >= 0 { + return nil, errExecutionReverted + } + + offset = g.state.LenNodes() + node := &nodeInfo{ + Owner: caller, + PublicKey: publicKey, + Staked: big.NewInt(0), + Fined: big.NewInt(0), + Name: name, + Email: email, + Location: location, + Url: url, + } + g.state.PushNode(node) + if err := g.state.PutNodeOffsets(node, offset); err != nil { + return g.penalize() + } + + // Delegate fund to itself. + if g.contract.Value().Cmp(big.NewInt(0)) > 0 { + if ret, err := g.delegate(caller); err != nil { + return ret, err + } + } + + g.state.emitStaked(caller) + return g.useGas(100000) +} + +func (g *GovernanceContract) undelegateHelper(nodeAddr, caller common.Address) ([]byte, error) { + nodeOffset := g.state.NodesOffsetByAddress(nodeAddr) + if nodeOffset.Cmp(big.NewInt(0)) < 0 { + return nil, errExecutionReverted + } + + offset := g.state.DelegatorsOffset(nodeAddr, caller) + if offset.Cmp(big.NewInt(0)) < 0 { + return nil, errExecutionReverted + } + + node := g.state.Node(nodeOffset) + if node.Fined.Cmp(big.NewInt(0)) > 0 { + return nil, errExecutionReverted + } + + delegator := g.state.Delegator(nodeAddr, offset) + + if delegator.UndelegatedAt.Cmp(big.NewInt(0)) != 0 { + return nil, errExecutionReverted + } + + // Set undelegate time. + delegator.UndelegatedAt = g.evm.Time + g.state.UpdateDelegator(nodeAddr, offset, delegator) + + // Subtract from the total staked of node. + node.Staked = new(big.Int).Sub(node.Staked, delegator.Value) + g.state.UpdateNode(nodeOffset, node) + + // Subtract to network total staked. + g.state.DecTotalStaked(delegator.Value) + + g.state.emitUndelegated(nodeAddr, caller) + + return g.useGas(100000) +} + +func (g *GovernanceContract) undelegate(nodeAddr common.Address) ([]byte, error) { + return g.undelegateHelper(nodeAddr, g.contract.Caller()) +} + +func (g *GovernanceContract) withdraw(nodeAddr common.Address) ([]byte, error) { + caller := g.contract.Caller() + + nodeOffset := g.state.NodesOffsetByAddress(nodeAddr) + if nodeOffset.Cmp(big.NewInt(0)) < 0 { + return nil, errExecutionReverted + } + + offset := g.state.DelegatorsOffset(nodeAddr, caller) + if offset.Cmp(big.NewInt(0)) < 0 { + return nil, errExecutionReverted + } + + delegator := g.state.Delegator(nodeAddr, offset) + + // Not yet undelegated. + if delegator.UndelegatedAt.Cmp(big.NewInt(0)) == 0 { + return g.penalize() + } + + unlockTime := new(big.Int).Add(delegator.UndelegatedAt, g.state.LockupPeriod()) + if g.evm.Time.Cmp(unlockTime) <= 0 { + return g.penalize() + } + + length := g.state.LenDelegators(nodeAddr) + lastIndex := new(big.Int).Sub(length, big.NewInt(1)) + + // Delete the delegator. + if offset.Cmp(lastIndex) != 0 { + lastNode := g.state.Delegator(nodeAddr, lastIndex) + g.state.UpdateDelegator(nodeAddr, offset, lastNode) + g.state.PutDelegatorOffset(nodeAddr, lastNode.Owner, offset) + } + g.state.DeleteDelegatorsOffset(nodeAddr, caller) + g.state.PopLastDelegator(nodeAddr) + + // Return the staked fund. + if !g.transfer(GovernanceContractAddress, delegator.Owner, delegator.Value) { + return nil, errExecutionReverted + } + + g.state.emitWithdrawn(nodeAddr, delegator.Value) + + // We are the last delegator to withdraw the fund, remove the node info. + if g.state.LenDelegators(nodeAddr).Cmp(big.NewInt(0)) == 0 { + length := g.state.LenNodes() + lastIndex := new(big.Int).Sub(length, big.NewInt(1)) + + // Delete the node. + if offset.Cmp(lastIndex) != 0 { + lastNode := g.state.Node(lastIndex) + g.state.UpdateNode(offset, lastNode) + if err := g.state.PutNodeOffsets(lastNode, offset); err != nil { + panic(err) + } + } + g.state.DeleteNodesOffsetByAddress(nodeAddr) + g.state.PopLastNode() + } + + return g.useGas(100000) +} + +func (g *GovernanceContract) unstake() ([]byte, error) { + caller := g.contract.Caller() + offset := g.state.NodesOffsetByAddress(caller) + if offset.Cmp(big.NewInt(0)) < 0 { + return nil, errExecutionReverted + } + + node := g.state.Node(offset) + if node.Fined.Cmp(big.NewInt(0)) > 0 { + return nil, errExecutionReverted + } + + // Undelegate all delegators. + lenDelegators := g.state.LenDelegators(caller) + i := new(big.Int).Sub(lenDelegators, big.NewInt(1)) + for i.Cmp(big.NewInt(0)) >= 0 { + delegator := g.state.Delegator(caller, i) + if ret, err := g.undelegateHelper(caller, delegator.Owner); err != nil { + return ret, err + } + i = i.Sub(i, big.NewInt(1)) + } + + g.state.emitUnstaked(caller) + + return g.useGas(100000) +} + +func (g *GovernanceContract) payFine(nodeAddr common.Address) ([]byte, error) { + caller := g.contract.Caller() + + nodeOffset := g.state.NodesOffsetByAddress(nodeAddr) + if nodeOffset.Cmp(big.NewInt(0)) < 0 { + return nil, errExecutionReverted + } + + offset := g.state.DelegatorsOffset(nodeAddr, caller) + if offset.Cmp(big.NewInt(0)) < 0 { + return nil, errExecutionReverted + } + + node := g.state.Node(nodeOffset) + if node.Fined.Cmp(big.NewInt(0)) <= 0 || node.Fined.Cmp(g.contract.Value()) < 0 { + return nil, errExecutionReverted + } + + node.Fined = new(big.Int).Sub(node.Fined, g.contract.Value()) + g.state.UpdateNode(nodeOffset, node) + + // TODO: paid fine should be added to award pool. + + g.state.emitFinePaid(nodeAddr, g.contract.Value()) + + return g.useGas(100000) +} + +func (g *GovernanceContract) proposeCRS(nextRound *big.Int, signedCRS []byte) ([]byte, error) { + round := g.state.Round() + + if nextRound.Cmp(round) <= 0 { + return nil, errExecutionReverted + } + + prevCRS := g.state.CRS(round) + + // Prepare DKGMasterPublicKeys. + dkgMasterPKs := g.state.UniqueDKGMasterPublicKeys(round) + + // Prepare DKGComplaints. + var dkgComplaints []*dkgTypes.Complaint + for _, comp := range g.state.DKGComplaints(round) { + x := new(dkgTypes.Complaint) + if err := rlp.DecodeBytes(comp, x); err != nil { + panic(err) + } + dkgComplaints = append(dkgComplaints, x) + } + + threshold := int(g.state.DKGSetSize().Uint64()/3 + 1) + + dkgGPK, err := core.NewDKGGroupPublicKey( + round.Uint64(), dkgMasterPKs, dkgComplaints, threshold) + if err != nil { + return nil, errExecutionReverted + } + signature := coreCrypto.Signature{ + Type: "bls", + Signature: signedCRS, + } + if !dkgGPK.VerifySignature(coreCommon.Hash(prevCRS), signature) { + return g.penalize() + } + + // Save new CRS into state and increase round. + newCRS := crypto.Keccak256(signedCRS) + crs := common.BytesToHash(newCRS) + + g.state.PushCRS(crs) + g.state.emitCRSProposed(nextRound, crs) + + // To encourage DKG set to propose the correct value, correctly submitting + // this should cause nothing. + return g.useGas(0) +} + +type sortBytes [][]byte + +func (s sortBytes) Less(i, j int) bool { + return bytes.Compare(s[i], s[j]) < 0 +} + +func (s sortBytes) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s sortBytes) Len() int { + return len(s) +} + +func (g *GovernanceContract) fine(nodeAddr common.Address, amount *big.Int, payloads ...[]byte) error { + sort.Sort(sortBytes(payloads)) + + hash := Bytes32(crypto.Keccak256Hash(payloads...)) + if g.state.FineRecords(hash) { + return errors.New("already fined") + } + g.state.SetFineRecords(hash, true) + + nodeOffset := g.state.NodesOffsetByAddress(nodeAddr) + if nodeOffset.Cmp(big.NewInt(0)) < 0 { + return errExecutionReverted + } + + // Set fined value. + node := g.state.Node(nodeOffset) + node.Fined = new(big.Int).Add(node.Fined, amount) + g.state.UpdateNode(nodeOffset, node) + + g.state.emitFined(nodeAddr, amount) + + return nil +} + +func (g *GovernanceContract) report(reportType *big.Int, arg1, arg2 []byte) ([]byte, error) { + typeEnum := ReportType(reportType.Uint64()) + var reportedNodeID coreTypes.NodeID + + switch typeEnum { + case ReportTypeForkVote: + vote1 := new(coreTypes.Vote) + if err := rlp.DecodeBytes(arg1, vote1); err != nil { + return g.penalize() + } + vote2 := new(coreTypes.Vote) + if err := rlp.DecodeBytes(arg2, vote2); err != nil { + return g.penalize() + } + need, err := coreUtils.NeedPenaltyForkVote(vote1, vote2) + if !need || err != nil { + return g.penalize() + } + reportedNodeID = vote1.ProposerID + case ReportTypeForkBlock: + block1 := new(coreTypes.Block) + if err := rlp.DecodeBytes(arg1, block1); err != nil { + return g.penalize() + } + block2 := new(coreTypes.Block) + if err := rlp.DecodeBytes(arg2, block2); err != nil { + return g.penalize() + } + need, err := coreUtils.NeedPenaltyForkBlock(block1, block2) + if !need || err != nil { + return g.penalize() + } + reportedNodeID = block1.ProposerID + default: + return g.penalize() + } + + offset := g.state.NodesOffsetByID(Bytes32(reportedNodeID.Hash)) + node := g.state.Node(offset) + + g.state.emitForkReported(node.Owner, reportType, arg1, arg2) + + fineValue := g.state.FineValue(reportType) + if err := g.fine(node.Owner, fineValue, arg1, arg2); err != nil { + return nil, errExecutionReverted + } + return nil, nil +} + +func (g *GovernanceContract) transferOwnership(newOwner common.Address) ([]byte, error) { + // Only owner can update configuration. + if g.contract.Caller() != g.state.Owner() { + return nil, errExecutionReverted + } + g.state.SetOwner(newOwner) + return nil, nil +} + +func (g *GovernanceContract) snapshotRound(round, height *big.Int) ([]byte, error) { + // Validate if this mapping is correct. Only block proposer need to verify this. + if g.evm.IsBlockProposer() { + realHeight, ok := g.evm.GetRoundHeight(round.Uint64()) + if !ok { + return g.penalize() + } + + if height.Cmp(new(big.Int).SetUint64(realHeight)) != 0 { + return g.penalize() + } + } + + // Only allow updating the next round. + nextRound := g.state.LenRoundHeight() + if round.Cmp(nextRound) != 0 { + // No need to penalize, since the only possibility at this point is the + // round height is already snapshoted. + return nil, errExecutionReverted + } + + g.state.PushRoundHeight(height) + return nil, nil +} diff --git a/core/vm/evm/governance_abi.go b/core/vm/evm/governance_abi.go new file mode 100644 index 000000000..966c4c4f3 --- /dev/null +++ b/core/vm/evm/governance_abi.go @@ -0,0 +1,1133 @@ +// Copyright 2019 The dexon-consensus Authors +// This file is part of the dexon-consensus library. +// +// The dexon-consensus library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus library. If not, see +// <http://www.gnu.org/licenses/>. + +package evm + +// The governance ABI is generated from: +// https://github.com/dexon-foundation/governance-abi + +const GovernanceABIJSON = ` +[ + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "address" + } + ], + "name": "delegatorsOffset", + "outputs": [ + { + "name": "", + "type": "int256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "name": "dkgComplaints", + "outputs": [ + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "notarySetSize", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "dkgSetSize", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "nodes", + "outputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "publicKey", + "type": "bytes" + }, + { + "name": "staked", + "type": "uint256" + }, + { + "name": "fined", + "type": "uint256" + }, + { + "name": "name", + "type": "string" + }, + { + "name": "email", + "type": "string" + }, + { + "name": "location", + "type": "string" + }, + { + "name": "url", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "miningVelocity", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "lambdaBA", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minStake", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "crs", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "phiRatio", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "dkgMPKReadysCount", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "address" + } + ], + "name": "dkgMPKReadys", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "uint256" + } + ], + "name": "delegators", + "outputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "undelegated_at", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "blockGasLimit", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "name": "nodesOffsetByID", + "outputs": [ + { + "name": "", + "type": "int256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalStaked", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "roundInterval", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "nodesOffsetByAddress", + "outputs": [ + { + "name": "", + "type": "int256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "nextHalvingSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "lastHalvedAmount", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "name": "finedRecords", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "lambdaDKG", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "fineValues", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "roundHeight", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "minBlockInterval", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "k", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "uint256" + } + ], + "name": "dkgMasterPublicKeys", + "outputs": [ + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "address" + } + ], + "name": "dkgFinalizeds", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "numChains", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "lockupPeriod", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "dkgFinalizedsCount", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [], + "name": "ConfigurationChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "Round", + "type": "uint256" + }, + { + "indexed": false, + "name": "CRS", + "type": "bytes32" + } + ], + "name": "CRSProposed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "NodeAddress", + "type": "address" + } + ], + "name": "Staked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "NodeAddress", + "type": "address" + } + ], + "name": "Unstaked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "NodeAddress", + "type": "address" + }, + { + "indexed": true, + "name": "DelegatorAddress", + "type": "address" + }, + { + "indexed": false, + "name": "Amount", + "type": "uint256" + } + ], + "name": "Delegated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "NodeAddress", + "type": "address" + }, + { + "indexed": true, + "name": "DelegatorAddress", + "type": "address" + } + ], + "name": "Undelegated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "NodeAddress", + "type": "address" + }, + { + "indexed": false, + "name": "Amount", + "type": "uint256" + } + ], + "name": "Withdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "NodeAddress", + "type": "address" + }, + { + "indexed": false, + "name": "Type", + "type": "uint256" + }, + { + "indexed": false, + "name": "Arg1", + "type": "bytes" + }, + { + "indexed": false, + "name": "Arg2", + "type": "bytes" + } + ], + "name": "ForkReported", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "NodeAddress", + "type": "address" + }, + { + "indexed": false, + "name": "Amount", + "type": "uint256" + } + ], + "name": "Fined", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "NodeAddress", + "type": "address" + }, + { + "indexed": false, + "name": "Amount", + "type": "uint256" + } + ], + "name": "FinePaid", + "type": "event" + }, + { + "constant": false, + "inputs": [ + { + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "MinStake", + "type": "uint256" + }, + { + "name": "LockupPeriod", + "type": "uint256" + }, + { + "name": "MiningVelocity", + "type": "uint256" + }, + { + "name": "NextHalvingSupply", + "type": "uint256" + }, + { + "name": "LastHalvingAmount", + "type": "uint256" + }, + { + "name": "BlockGasLimit", + "type": "uint256" + }, + { + "name": "NumChains", + "type": "uint256" + }, + { + "name": "LambdaBA", + "type": "uint256" + }, + { + "name": "LambdaDKG", + "type": "uint256" + }, + { + "name": "K", + "type": "uint256" + }, + { + "name": "PhiRatio", + "type": "uint256" + }, + { + "name": "NotarySetSize", + "type": "uint256" + }, + { + "name": "DKGSetSize", + "type": "uint256" + }, + { + "name": "RoundInterval", + "type": "uint256" + }, + { + "name": "MinBlockInterval", + "type": "uint256" + }, + { + "name": "FineValues", + "type": "uint256[]" + } + ], + "name": "updateConfiguration", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "nodesLength", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "NodeAddress", + "type": "address" + } + ], + "name": "delegatorsLength", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "Round", + "type": "uint256" + }, + { + "name": "Height", + "type": "uint256" + } + ], + "name": "snapshotRound", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "Round", + "type": "uint256" + }, + { + "name": "SignedCRS", + "type": "bytes" + } + ], + "name": "proposeCRS", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "Round", + "type": "uint256" + }, + { + "name": "Complaint", + "type": "bytes" + } + ], + "name": "addDKGComplaint", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "Round", + "type": "uint256" + }, + { + "name": "PublicKey", + "type": "bytes" + } + ], + "name": "addDKGMasterPublicKey", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "Round", + "type": "uint256" + }, + { + "name": "MPKReady", + "type": "bytes" + } + ], + "name": "addDKGMPKReady", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "Round", + "type": "uint256" + }, + { + "name": "Finalize", + "type": "bytes" + } + ], + "name": "addDKGFinalize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "PublicKey", + "type": "bytes" + }, + { + "name": "Name", + "type": "string" + }, + { + "name": "Email", + "type": "string" + }, + { + "name": "Location", + "type": "string" + }, + { + "name": "Url", + "type": "string" + } + ], + "name": "stake", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "unstake", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "NodeAddress", + "type": "address" + } + ], + "name": "delegate", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "NodeAddress", + "type": "address" + } + ], + "name": "undelegate", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "NodeAddress", + "type": "address" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "NodeAddress", + "type": "address" + } + ], + "name": "payFine", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "Type", + "type": "uint256" + }, + { + "name": "Arg1", + "type": "bytes" + }, + { + "name": "Arg2", + "type": "bytes" + } + ], + "name": "report", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] +` diff --git a/core/vm/evm/governance_test.go b/core/vm/evm/governance_test.go new file mode 100644 index 000000000..1a67516ec --- /dev/null +++ b/core/vm/evm/governance_test.go @@ -0,0 +1,1060 @@ +// Copyright 2018 The dexon-consensus Authors +// This file is part of the dexon-consensus library. +// +// The dexon-consensus library is free software: you can redistribute it +// and/or modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus library is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +// General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus library. If not, see +// <http://www.gnu.org/licenses/>. + +package evm + +import ( + "bytes" + "crypto/ecdsa" + "math/big" + "math/rand" + "sort" + "testing" + "time" + + coreCommon "github.com/dexon-foundation/dexon-consensus/common" + coreCrypto "github.com/dexon-foundation/dexon-consensus/core/crypto" + coreEcdsa "github.com/dexon-foundation/dexon-consensus/core/crypto/ecdsa" + coreTypes "github.com/dexon-foundation/dexon-consensus/core/types" + coreUtils "github.com/dexon-foundation/dexon-consensus/core/utils" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/core/state" + "github.com/dexon-foundation/dexon/crypto" + "github.com/dexon-foundation/dexon/ethdb" + "github.com/dexon-foundation/dexon/params" + "github.com/dexon-foundation/dexon/rlp" + "github.com/stretchr/testify/suite" +) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +func randomBytes(minLength, maxLength int32) []byte { + length := rand.Int31()%(maxLength-minLength) + minLength + b := make([]byte, length) + for i := range b { + b[i] = byte(65 + rand.Int31()%60) + } + return b +} + +type GovernanceStateHelperTestSuite struct { + suite.Suite + + s *GovernanceStateHelper +} + +func (g *GovernanceStateHelperTestSuite) SetupTest() { + db := state.NewDatabase(ethdb.NewMemDatabase()) + statedb, err := state.New(common.Hash{}, db) + if err != nil { + panic(err) + } + g.s = &GovernanceStateHelper{statedb} +} + +func (g *GovernanceStateHelperTestSuite) TestReadWriteBytes() { + for i := 0; i < 100; i++ { + // Short bytes. + loc := big.NewInt(rand.Int63()) + data := randomBytes(3, 32) + g.s.writeBytes(loc, data) + read := g.s.readBytes(loc) + g.Require().Equal(0, bytes.Compare(data, read)) + + // long bytes. + loc = big.NewInt(rand.Int63()) + data = randomBytes(33, 2560) + g.s.writeBytes(loc, data) + read = g.s.readBytes(loc) + g.Require().Equal(0, bytes.Compare(data, read)) + } +} + +func TestGovernanceStateHelper(t *testing.T) { + suite.Run(t, new(GovernanceStateHelperTestSuite)) +} + +type GovernanceContractTestSuite struct { + suite.Suite + + config *params.DexconConfig + memDB *ethdb.MemDatabase + stateDB *state.StateDB + s *GovernanceStateHelper +} + +func (g *GovernanceContractTestSuite) SetupTest() { + memDB := ethdb.NewMemDatabase() + stateDB, err := state.New(common.Hash{}, state.NewDatabase(memDB)) + if err != nil { + panic(err) + } + g.memDB = memDB + g.stateDB = stateDB + g.s = &GovernanceStateHelper{stateDB} + + config := params.TestnetChainConfig.Dexcon + config.LockupPeriod = 1000 + config.NextHalvingSupply = new(big.Int).Mul(big.NewInt(1e18), big.NewInt(2.5e9)) + config.LastHalvedAmount = new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1.5e9)) + config.MiningVelocity = 0.1875 + + g.config = config + + // Give governance contract balance so it will not be deleted because of being an empty state object. + stateDB.AddBalance(GovernanceContractAddress, big.NewInt(1)) + + // Genesis CRS. + crs := crypto.Keccak256Hash([]byte(config.GenesisCRSText)) + g.s.PushCRS(crs) + + // Round 0 height. + g.s.PushRoundHeight(big.NewInt(0)) + + // Owner. + g.s.SetOwner(g.config.Owner) + + // Governance configuration. + g.s.UpdateConfiguration(config) + + g.stateDB.Commit(true) +} + +func (g *GovernanceContractTestSuite) newPrefundAccount() (*ecdsa.PrivateKey, common.Address) { + privKey, err := crypto.GenerateKey() + if err != nil { + panic(err) + } + address := crypto.PubkeyToAddress(privKey.PublicKey) + + g.stateDB.AddBalance(address, new(big.Int).Mul(big.NewInt(1e18), big.NewInt(2e6))) + return privKey, address +} + +func (g *GovernanceContractTestSuite) call(caller common.Address, input []byte, value *big.Int) ([]byte, error) { + context := Context{ + CanTransfer: func(db StateDB, addr common.Address, amount *big.Int) bool { + return db.GetBalance(addr).Cmp(amount) >= 0 + }, + Transfer: func(db StateDB, sender common.Address, recipient common.Address, amount *big.Int) { + db.SubBalance(sender, amount) + db.AddBalance(recipient, amount) + }, + GetRoundHeight: func(round uint64) (uint64, bool) { + switch round { + case 0: + return 0, true + case 1: + return 1000, true + case 2: + return 2000, true + } + return 0, false + }, + Time: big.NewInt(time.Now().UnixNano() / 1000000), + BlockNumber: big.NewInt(0), + } + + evm := NewEVM(context, g.stateDB, params.TestChainConfig, Config{IsBlockProposer: true}) + ret, _, err := evm.Call(AccountRef(caller), GovernanceContractAddress, input, 10000000, value) + return ret, err +} + +func (g *GovernanceContractTestSuite) TestTransferOwnership() { + _, addr := g.newPrefundAccount() + + input, err := abiObject.Pack("transferOwnership", addr) + g.Require().NoError(err) + + // Call with non-owner. + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NotNil(err) + + // Call with owner. + _, err = g.call(g.config.Owner, input, big.NewInt(0)) + g.Require().NoError(err) + g.Require().Equal(addr, g.s.Owner()) +} + +func (g *GovernanceContractTestSuite) TestStakeUnstakeWithoutExtraDelegators() { + privKey, addr := g.newPrefundAccount() + pk := crypto.FromECDSAPub(&privKey.PublicKey) + + // Stake. + amount := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)) + balanceBeforeStake := g.stateDB.GetBalance(addr) + input, err := abiObject.Pack("stake", pk, "Test1", "test1@dexon.org", "Taipei, Taiwan", "https://dexon.org") + g.Require().NoError(err) + _, err = g.call(addr, input, amount) + g.Require().NoError(err) + + g.Require().Equal(1, int(g.s.LenNodes().Uint64())) + g.Require().Equal(1, len(g.s.QualifiedNodes())) + g.Require().Equal("Test1", g.s.Node(big.NewInt(0)).Name) + g.Require().Equal(amount.String(), g.s.TotalStaked().String()) + + // Check balance. + g.Require().Equal(new(big.Int).Sub(balanceBeforeStake, amount), g.stateDB.GetBalance(addr)) + g.Require().Equal(new(big.Int).Add(big.NewInt(1), amount), g.stateDB.GetBalance(GovernanceContractAddress)) + + // Staking again should fail. + _, err = g.call(addr, input, amount) + g.Require().NotNil(err) + + // Unstake. + input, err = abiObject.Pack("unstake") + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + g.Require().Equal(0, len(g.s.QualifiedNodes())) + g.Require().Equal(1, int(g.s.LenDelegators(addr).Uint64())) + g.Require().Equal(1, int(g.s.LenNodes().Uint64())) + + node := g.s.Node(big.NewInt(0)) + g.Require().Equal(big.NewInt(0).String(), node.Staked.String()) + g.Require().Equal(big.NewInt(0).String(), g.s.TotalStaked().String()) + + // Wait for lockup time than withdraw. + time.Sleep(time.Second * 2) + input, err = abiObject.Pack("withdraw", addr) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + + g.Require().Equal(0, len(g.s.QualifiedNodes())) + g.Require().Equal(0, int(g.s.LenDelegators(addr).Uint64())) + g.Require().Equal(0, int(g.s.LenNodes().Uint64())) + + // Stake 2 nodes, and unstake the first then the second. + + // 2nd node Stake. + privKey2, addr2 := g.newPrefundAccount() + pk2 := crypto.FromECDSAPub(&privKey2.PublicKey) + input, err = abiObject.Pack("stake", pk2, "Test2", "test2@dexon.org", "Taipei, Taiwan", "https://dexon.org") + g.Require().NoError(err) + _, err = g.call(addr2, input, amount) + g.Require().NoError(err) + g.Require().Equal("Test2", g.s.Node(big.NewInt(0)).Name) + g.Require().Equal(0, int(g.s.NodesOffsetByAddress(addr2).Int64())) + + // 1st node Stake. + input, err = abiObject.Pack("stake", pk, "Test1", "test1@dexon.org", "Taipei, Taiwan", "https://dexon.org") + g.Require().NoError(err) + _, err = g.call(addr, input, amount) + g.Require().NoError(err) + + g.Require().Equal(2, len(g.s.QualifiedNodes())) + g.Require().Equal(new(big.Int).Mul(amount, big.NewInt(2)).String(), g.s.TotalStaked().String()) + + // 2nd node Unstake. + input, err = abiObject.Pack("unstake") + g.Require().NoError(err) + _, err = g.call(addr2, input, big.NewInt(0)) + g.Require().NoError(err) + node = g.s.Node(big.NewInt(0)) + g.Require().Equal("Test2", node.Name) + g.Require().Equal(big.NewInt(0).String(), node.Staked.String()) + g.Require().Equal(1, len(g.s.QualifiedNodes())) + g.Require().Equal(amount.String(), g.s.TotalStaked().String()) + + time.Sleep(time.Second * 2) + input, err = abiObject.Pack("withdraw", addr2) + g.Require().NoError(err) + _, err = g.call(addr2, input, big.NewInt(0)) + g.Require().NoError(err) + g.Require().Equal(1, len(g.s.QualifiedNodes())) + + g.Require().Equal(1, int(g.s.LenNodes().Uint64())) + g.Require().Equal("Test1", g.s.Node(big.NewInt(0)).Name) + g.Require().Equal(-1, int(g.s.NodesOffsetByAddress(addr2).Int64())) + + // 1st node Unstake. + input, err = abiObject.Pack("unstake") + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + g.Require().Equal(0, len(g.s.QualifiedNodes())) + g.Require().Equal(big.NewInt(0).String(), g.s.TotalStaked().String()) + + time.Sleep(time.Second * 2) + input, err = abiObject.Pack("withdraw", addr) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + + g.Require().Equal(0, int(g.s.LenNodes().Uint64())) + g.Require().Equal(-1, int(g.s.NodesOffsetByAddress(addr).Int64())) + g.Require().Equal(-1, int(g.s.DelegatorsOffset(addr, addr).Int64())) + + // Check balance. + g.Require().Equal(balanceBeforeStake, g.stateDB.GetBalance(addr)) + g.Require().Equal(balanceBeforeStake, g.stateDB.GetBalance(addr2)) + g.Require().Equal(big.NewInt(1), g.stateDB.GetBalance(GovernanceContractAddress)) +} + +func (g *GovernanceContractTestSuite) TestDelegateUndelegate() { + privKey, addr := g.newPrefundAccount() + pk := crypto.FromECDSAPub(&privKey.PublicKey) + + // Stake. + input, err := abiObject.Pack("stake", pk, "Test1", "test1@dexon.org", "Taipei, Taiwan", "https://dexon.org") + g.Require().NoError(err) + ownerStaked := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(5e5)) + _, err = g.call(addr, input, ownerStaked) + g.Require().NoError(err) + g.Require().Equal(0, len(g.s.QualifiedNodes())) + g.Require().Equal(addr, g.s.Delegator(addr, big.NewInt(0)).Owner) + g.Require().Equal(ownerStaked, g.s.Node(big.NewInt(0)).Staked) + + // 1st delegator delegate to 1st node. + _, addrDelegator := g.newPrefundAccount() + + balanceBeforeDelegate := g.stateDB.GetBalance(addrDelegator) + amount := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(3e5)) + input, err = abiObject.Pack("delegate", addr) + g.Require().NoError(err) + + _, err = g.call(addrDelegator, input, amount) + g.Require().NoError(err) + g.Require().Equal(new(big.Int).Sub(balanceBeforeDelegate, amount), g.stateDB.GetBalance(addrDelegator)) + g.Require().Equal(addrDelegator, g.s.Delegator(addr, big.NewInt(1)).Owner) + g.Require().Equal(new(big.Int).Add(amount, ownerStaked), g.s.Node(big.NewInt(0)).Staked) + g.Require().Equal(g.s.Node(big.NewInt(0)).Staked.String(), g.s.TotalStaked().String()) + g.Require().Equal(1, int(g.s.DelegatorsOffset(addr, addrDelegator).Int64())) + + // Same person delegate the 2nd time should fail. + _, err = g.call(addrDelegator, input, big.NewInt(1e18)) + g.Require().NotNil(err) + + // Not yet qualified. + g.Require().Equal(0, len(g.s.QualifiedNodes())) + + // 2nd delegator delegate to 1st node. + _, addrDelegator2 := g.newPrefundAccount() + _, err = g.call(addrDelegator2, input, amount) + g.Require().NoError(err) + g.Require().Equal(new(big.Int).Sub(balanceBeforeDelegate, amount), g.stateDB.GetBalance(addrDelegator2)) + g.Require().Equal(addrDelegator2, g.s.Delegator(addr, big.NewInt(2)).Owner) + g.Require().Equal(new(big.Int).Add(ownerStaked, new(big.Int).Mul(amount, big.NewInt(2))), + g.s.Node(big.NewInt(0)).Staked) + g.Require().Equal(g.s.Node(big.NewInt(0)).Staked.String(), g.s.TotalStaked().String()) + g.Require().Equal(2, int(g.s.DelegatorsOffset(addr, addrDelegator2).Int64())) + + // Qualified. + g.Require().Equal(1, len(g.s.QualifiedNodes())) + + // Undelegate addrDelegator. + balanceBeforeUnDelegate := g.stateDB.GetBalance(addrDelegator) + input, err = abiObject.Pack("undelegate", addr) + g.Require().NoError(err) + _, err = g.call(addrDelegator, input, big.NewInt(0)) + g.Require().Equal(new(big.Int).Add(amount, ownerStaked), g.s.Node(big.NewInt(0)).Staked) + g.Require().Equal(g.s.Node(big.NewInt(0)).Staked.String(), g.s.TotalStaked().String()) + g.Require().NoError(err) + + // Undelegate the second time should fail. + _, err = g.call(addrDelegator, input, big.NewInt(0)) + g.Require().Error(err) + + // Withdraw within lockup time should fail. + input, err = abiObject.Pack("withdraw", addr) + g.Require().NoError(err) + _, err = g.call(addrDelegator, input, big.NewInt(0)) + g.Require().NotNil(err) + + g.Require().Equal(3, int(g.s.LenDelegators(addr).Uint64())) + g.Require().Equal(balanceBeforeUnDelegate, g.stateDB.GetBalance(addrDelegator)) + g.Require().NotEqual(-1, int(g.s.DelegatorsOffset(addr, addrDelegator).Int64())) + + // Wait for lockup time than withdraw. + time.Sleep(time.Second * 2) + input, err = abiObject.Pack("withdraw", addr) + g.Require().NoError(err) + _, err = g.call(addrDelegator, input, big.NewInt(0)) + g.Require().NoError(err) + + g.Require().Equal(2, int(g.s.LenDelegators(addr).Uint64())) + g.Require().Equal(new(big.Int).Add(balanceBeforeUnDelegate, amount), g.stateDB.GetBalance(addrDelegator)) + g.Require().Equal(-1, int(g.s.DelegatorsOffset(addr, addrDelegator).Int64())) + + // Withdraw when their is no delegation should fail. + time.Sleep(time.Second) + input, err = abiObject.Pack("withdraw", addr) + g.Require().NoError(err) + _, err = g.call(addrDelegator, input, big.NewInt(0)) + g.Require().Error(err) + + // Undelegate addrDelegator2. + balanceBeforeUnDelegate = g.stateDB.GetBalance(addrDelegator2) + input, err = abiObject.Pack("undelegate", addr) + g.Require().NoError(err) + _, err = g.call(addrDelegator2, input, big.NewInt(0)) + g.Require().NoError(err) + + g.Require().Equal(ownerStaked, g.s.Node(big.NewInt(0)).Staked) + g.Require().Equal(g.s.Node(big.NewInt(0)).Staked.String(), g.s.TotalStaked().String()) + + // Wait for lockup time than withdraw. + time.Sleep(time.Second * 2) + input, err = abiObject.Pack("withdraw", addr) + g.Require().NoError(err) + _, err = g.call(addrDelegator2, input, big.NewInt(0)) + g.Require().NoError(err) + + g.Require().Equal(1, int(g.s.LenDelegators(addr).Uint64())) + g.Require().Equal(new(big.Int).Add(balanceBeforeUnDelegate, amount), g.stateDB.GetBalance(addrDelegator2)) + g.Require().Equal(-1, int(g.s.DelegatorsOffset(addr, addrDelegator2).Int64())) + + // Unqualified + g.Require().Equal(0, len(g.s.QualifiedNodes())) + + // Owner undelegate itself. + g.Require().Equal(1, int(g.s.LenNodes().Uint64())) + g.Require().Equal(1, int(g.s.LenDelegators(addr).Uint64())) + + input, err = abiObject.Pack("undelegate", addr) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + g.Require().Equal(big.NewInt(0).String(), g.s.Node(big.NewInt(0)).Staked.String()) + g.Require().Equal(big.NewInt(0).String(), g.s.TotalStaked().String()) + + time.Sleep(time.Second * 2) + input, err = abiObject.Pack("withdraw", addr) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + + g.Require().Equal(0, int(g.s.LenNodes().Uint64())) + g.Require().Equal(0, int(g.s.LenDelegators(addr).Uint64())) +} + +func (g *GovernanceContractTestSuite) TestFine() { + privKey, addr := g.newPrefundAccount() + pk := crypto.FromECDSAPub(&privKey.PublicKey) + + // Stake. + input, err := abiObject.Pack("stake", pk, "Test1", "test1@dexon.org", "Taipei, Taiwan", "https://dexon.org") + g.Require().NoError(err) + ownerStaked := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(5e5)) + _, err = g.call(addr, input, ownerStaked) + g.Require().NoError(err) + g.Require().Equal(0, len(g.s.QualifiedNodes())) + g.Require().Equal(addr, g.s.Delegator(addr, big.NewInt(0)).Owner) + g.Require().Equal(ownerStaked, g.s.Node(big.NewInt(0)).Staked) + + // 1st delegator delegate to 1st node. + _, addrDelegator := g.newPrefundAccount() + + balanceBeforeDelegate := g.stateDB.GetBalance(addrDelegator) + amount := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(5e5)) + input, err = abiObject.Pack("delegate", addr) + g.Require().NoError(err) + + _, err = g.call(addrDelegator, input, amount) + g.Require().NoError(err) + g.Require().Equal(new(big.Int).Sub(balanceBeforeDelegate, amount), g.stateDB.GetBalance(addrDelegator)) + g.Require().Equal(addrDelegator, g.s.Delegator(addr, big.NewInt(1)).Owner) + g.Require().Equal(new(big.Int).Add(amount, ownerStaked), g.s.Node(big.NewInt(0)).Staked) + g.Require().Equal(1, int(g.s.DelegatorsOffset(addr, addrDelegator).Int64())) + + // Qualified. + g.Require().Equal(1, len(g.s.QualifiedNodes())) + + // Paying to node without fine should fail. + input, err = abiObject.Pack("payFine", addr) + g.Require().NoError(err) + _, err = g.call(addrDelegator, input, amount) + g.Require().NotNil(err) + + // Fined. + offset := g.s.NodesOffsetByAddress(addr) + g.Require().True(offset.Cmp(big.NewInt(0)) >= 0) + node := g.s.Node(offset) + node.Fined = new(big.Int).Set(amount) + g.s.UpdateNode(offset, node) + node = g.s.Node(offset) + g.Require().Equal(0, node.Fined.Cmp(amount)) + + // Not qualified after fined. + g.Require().Equal(0, len(g.s.QualifiedNodes())) + + // Cannot undelegate before fines are paied. + input, err = abiObject.Pack("undelegate", addr) + g.Require().NoError(err) + _, err = g.call(addrDelegator, input, big.NewInt(0)) + g.Require().NotNil(err) + + // Only delegators can pay fine. + _, addrDelegator2 := g.newPrefundAccount() + input, err = abiObject.Pack("payFine", addr) + g.Require().NoError(err) + _, err = g.call(addrDelegator2, input, big.NewInt(5e5)) + g.Require().NotNil(err) + + // Paying more than fine should fail. + payAmount := new(big.Int).Add(amount, amount) + input, err = abiObject.Pack("payFine", addr) + g.Require().NoError(err) + _, err = g.call(addrDelegator, input, payAmount) + g.Require().NotNil(err) + + // Pay the fine. + input, err = abiObject.Pack("payFine", addr) + g.Require().NoError(err) + _, err = g.call(addrDelegator, input, amount) + g.Require().NoError(err) + + // Qualified. + g.Require().Equal(1, len(g.s.QualifiedNodes())) + + // Can undelegate after all fines are paied. + input, err = abiObject.Pack("undelegate", addr) + g.Require().NoError(err) + _, err = g.call(addrDelegator, input, big.NewInt(0)) + g.Require().NoError(err) +} + +func (g *GovernanceContractTestSuite) TestUnstakeWithExtraDelegators() { + privKey, addr := g.newPrefundAccount() + pk := crypto.FromECDSAPub(&privKey.PublicKey) + + // Stake. + amount := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(5e5)) + input, err := abiObject.Pack("stake", pk, "Test1", "test1@dexon.org", "Taipei, Taiwan", "https://dexon.org") + g.Require().NoError(err) + _, err = g.call(addr, input, amount) + g.Require().NoError(err) + + // 1st delegator delegate to 1st node. + _, addrDelegator := g.newPrefundAccount() + + balanceBeforeDelegate := g.stateDB.GetBalance(addrDelegator) + amount = new(big.Int).Mul(big.NewInt(1e18), big.NewInt(3e5)) + input, err = abiObject.Pack("delegate", addr) + g.Require().NoError(err) + + _, err = g.call(addrDelegator, input, amount) + g.Require().NoError(err) + g.Require().Equal(new(big.Int).Sub(balanceBeforeDelegate, amount), g.stateDB.GetBalance(addrDelegator)) + g.Require().Equal(addrDelegator, g.s.Delegator(addr, big.NewInt(1)).Owner) + g.Require().Equal(0, len(g.s.QualifiedNodes())) + + // 2st delegator delegate to 1st node. + _, addrDelegator2 := g.newPrefundAccount() + + balanceBeforeDelegate = g.stateDB.GetBalance(addrDelegator2) + input, err = abiObject.Pack("delegate", addr) + g.Require().NoError(err) + + _, err = g.call(addrDelegator2, input, amount) + g.Require().NoError(err) + g.Require().Equal(new(big.Int).Sub(balanceBeforeDelegate, amount), g.stateDB.GetBalance(addrDelegator2)) + g.Require().Equal(addrDelegator2, g.s.Delegator(addr, big.NewInt(2)).Owner) + + // Node is now qualified. + g.Require().Equal(1, len(g.s.QualifiedNodes())) + + // Unstake. + input, err = abiObject.Pack("unstake") + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + + time.Sleep(time.Second * 2) + input, err = abiObject.Pack("withdraw", addr) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + _, err = g.call(addrDelegator, input, big.NewInt(0)) + g.Require().NoError(err) + _, err = g.call(addrDelegator2, input, big.NewInt(0)) + g.Require().NoError(err) + + g.Require().Equal(0, int(g.s.LenDelegators(addr).Uint64())) + g.Require().Equal(0, int(g.s.LenNodes().Uint64())) + + // Check balance. + g.Require().Equal(balanceBeforeDelegate, g.stateDB.GetBalance(addr)) + g.Require().Equal(balanceBeforeDelegate, g.stateDB.GetBalance(addrDelegator)) + g.Require().Equal(balanceBeforeDelegate, g.stateDB.GetBalance(addrDelegator2)) + g.Require().Equal(big.NewInt(1), g.stateDB.GetBalance(GovernanceContractAddress)) +} + +func (g *GovernanceContractTestSuite) TestUpdateConfiguration() { + _, addr := g.newPrefundAccount() + + input, err := abiObject.Pack("updateConfiguration", + new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)), + big.NewInt(1000), + big.NewInt(0.1875*decimalMultiplier), + big.NewInt(1e18), + big.NewInt(1e18), + big.NewInt(8000000), + big.NewInt(6), + big.NewInt(250), + big.NewInt(2500), + big.NewInt(0), + big.NewInt(667000), + big.NewInt(4), + big.NewInt(4), + big.NewInt(600000), + big.NewInt(900), + []*big.Int{big.NewInt(1), big.NewInt(1), big.NewInt(1)}) + g.Require().NoError(err) + + // Call with non-owner. + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NotNil(err) + + // Call with owner. + _, err = g.call(g.config.Owner, input, big.NewInt(0)) + g.Require().NoError(err) +} + +func (g *GovernanceContractTestSuite) TestSnapshotRound() { + _, addr := g.newPrefundAccount() + + // Wrong height. + input, err := abiObject.Pack("snapshotRound", big.NewInt(1), big.NewInt(666)) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NotNil(err) + + // Invalid round. + input, err = abiObject.Pack("snapshotRound", big.NewInt(2), big.NewInt(2000)) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NotNil(err) + + // Correct. + input, err = abiObject.Pack("snapshotRound", big.NewInt(1), big.NewInt(1000)) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + + // Duplicate round. + input, err = abiObject.Pack("snapshotRound", big.NewInt(1), big.NewInt(1000)) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NotNil(err) + + // Invalid round. + input, err = abiObject.Pack("snapshotRound", big.NewInt(3), big.NewInt(3000)) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NotNil(err) + + // Correct. + input, err = abiObject.Pack("snapshotRound", big.NewInt(2), big.NewInt(2000)) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) +} + +func (g *GovernanceContractTestSuite) TestConfigurationReading() { + _, addr := g.newPrefundAccount() + + // CRS. + input, err := abiObject.Pack("crs", big.NewInt(0)) + g.Require().NoError(err) + res, err := g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + var crs0 [32]byte + err = abiObject.Unpack(&crs0, "crs", res) + g.Require().NoError(err) + g.Require().Equal(crypto.Keccak256Hash([]byte(g.config.GenesisCRSText)), common.BytesToHash(crs0[:])) + + // Owner. + input, err = abiObject.Pack("owner") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + var owner common.Address + err = abiObject.Unpack(&owner, "owner", res) + g.Require().NoError(err) + g.Require().Equal(g.config.Owner, owner) + + // MinStake. + input, err = abiObject.Pack("minStake") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + var value *big.Int + err = abiObject.Unpack(&value, "minStake", res) + g.Require().NoError(err) + g.Require().Equal(g.config.MinStake.String(), value.String()) + + // BlockReward. + input, err = abiObject.Pack("miningVelocity") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "miningVelocity", res) + g.Require().NoError(err) + g.Require().Equal(g.config.MiningVelocity, float32(value.Uint64())/decimalMultiplier) + + // BlockGasLimit. + input, err = abiObject.Pack("blockGasLimit") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "blockGasLimit", res) + g.Require().NoError(err) + g.Require().Equal(g.config.BlockGasLimit, value.Uint64()) + + // NumChains. + input, err = abiObject.Pack("numChains") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "numChains", res) + g.Require().NoError(err) + g.Require().Equal(g.config.NumChains, uint32(value.Uint64())) + + // LambdaBA. + input, err = abiObject.Pack("lambdaBA") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "lambdaBA", res) + g.Require().NoError(err) + g.Require().Equal(g.config.LambdaBA, value.Uint64()) + + // LambdaDKG. + input, err = abiObject.Pack("lambdaDKG") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "lambdaDKG", res) + g.Require().NoError(err) + g.Require().Equal(g.config.LambdaDKG, value.Uint64()) + + // K. + input, err = abiObject.Pack("k") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "k", res) + g.Require().NoError(err) + g.Require().Equal(g.config.K, uint32(value.Uint64())) + + // PhiRatio. + input, err = abiObject.Pack("phiRatio") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "phiRatio", res) + g.Require().NoError(err) + g.Require().Equal(g.config.PhiRatio, float32(value.Uint64())/decimalMultiplier) + + // NotarySetSize. + input, err = abiObject.Pack("notarySetSize") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "notarySetSize", res) + g.Require().NoError(err) + g.Require().Equal(g.config.NotarySetSize, uint32(value.Uint64())) + + // DKGSetSize. + input, err = abiObject.Pack("dkgSetSize") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "dkgSetSize", res) + g.Require().NoError(err) + g.Require().Equal(g.config.DKGSetSize, uint32(value.Uint64())) + + // RoundInterval. + input, err = abiObject.Pack("roundInterval") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "roundInterval", res) + g.Require().NoError(err) + g.Require().Equal(g.config.RoundInterval, value.Uint64()) + + // MinBlockInterval. + input, err = abiObject.Pack("minBlockInterval") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "minBlockInterval", res) + g.Require().NoError(err) + g.Require().Equal(g.config.MinBlockInterval, value.Uint64()) +} + +func (g *GovernanceContractTestSuite) TestReportForkVote() { + key, addr := g.newPrefundAccount() + pkBytes := crypto.FromECDSAPub(&key.PublicKey) + + // Stake. + amount := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(5e5)) + input, err := abiObject.Pack("stake", pkBytes, "Test1", "test1@dexon.org", "Taipei, Taiwan", "https://dexon.org") + g.Require().NoError(err) + _, err = g.call(addr, input, amount) + g.Require().NoError(err) + + pubKey := coreEcdsa.NewPublicKeyFromECDSA(&key.PublicKey) + privKey := coreEcdsa.NewPrivateKeyFromECDSA(key) + vote1 := coreTypes.NewVote(coreTypes.VoteCom, coreCommon.NewRandomHash(), uint64(0)) + vote1.ProposerID = coreTypes.NewNodeID(pubKey) + + vote2 := vote1.Clone() + for vote2.BlockHash == vote1.BlockHash { + vote2.BlockHash = coreCommon.NewRandomHash() + } + vote1.Signature, err = privKey.Sign(coreUtils.HashVote(vote1)) + g.Require().NoError(err) + vote2.Signature, err = privKey.Sign(coreUtils.HashVote(vote2)) + g.Require().NoError(err) + + vote1Bytes, err := rlp.EncodeToBytes(vote1) + g.Require().NoError(err) + vote2Bytes, err := rlp.EncodeToBytes(vote2) + g.Require().NoError(err) + + // Report wrong type (fork block) + input, err = abiObject.Pack("report", big.NewInt(2), vote1Bytes, vote2Bytes) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().Error(err) + + input, err = abiObject.Pack("report", big.NewInt(1), vote1Bytes, vote2Bytes) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + + node := g.s.Node(big.NewInt(0)) + g.Require().Equal(node.Fined, g.s.FineValue(big.NewInt(1))) + + // Duplicate report should fail. + input, err = abiObject.Pack("report", big.NewInt(1), vote1Bytes, vote2Bytes) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().Error(err) + + // Check if finedRecords is set. + payloads := [][]byte{vote1Bytes, vote2Bytes} + sort.Sort(sortBytes(payloads)) + + hash := Bytes32(crypto.Keccak256Hash(payloads...)) + input, err = abiObject.Pack("finedRecords", hash) + g.Require().NoError(err) + res, err := g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + + var value bool + err = abiObject.Unpack(&value, "finedRecords", res) + g.Require().NoError(err) + g.Require().True(value) +} + +func (g *GovernanceContractTestSuite) TestReportForkBlock() { + key, addr := g.newPrefundAccount() + pkBytes := crypto.FromECDSAPub(&key.PublicKey) + + // Stake. + amount := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(5e5)) + input, err := abiObject.Pack("stake", pkBytes, "Test1", "test1@dexon.org", "Taipei, Taiwan", "https://dexon.org") + g.Require().NoError(err) + _, err = g.call(addr, input, amount) + g.Require().NoError(err) + + privKey := coreEcdsa.NewPrivateKeyFromECDSA(key) + block1 := &coreTypes.Block{ + ProposerID: coreTypes.NewNodeID(privKey.PublicKey()), + ParentHash: coreCommon.NewRandomHash(), + Timestamp: time.Now(), + } + + block2 := block1.Clone() + for block2.ParentHash == block1.ParentHash { + block2.ParentHash = coreCommon.NewRandomHash() + } + + hashBlock := func(block *coreTypes.Block) coreCommon.Hash { + block.PayloadHash = coreCrypto.Keccak256Hash(block.Payload) + var err error + block.Hash, err = coreUtils.HashBlock(block) + g.Require().NoError(err) + return block.Hash + } + + block1.Signature, err = privKey.Sign(hashBlock(block1)) + g.Require().NoError(err) + block2.Signature, err = privKey.Sign(hashBlock(block2)) + g.Require().NoError(err) + + block1Bytes, err := rlp.EncodeToBytes(block1) + g.Require().NoError(err) + block2Bytes, err := rlp.EncodeToBytes(block2) + g.Require().NoError(err) + + // Report wrong type (fork vote) + input, err = abiObject.Pack("report", big.NewInt(1), block1Bytes, block2Bytes) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().Error(err) + + input, err = abiObject.Pack("report", big.NewInt(2), block1Bytes, block2Bytes) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + + node := g.s.Node(big.NewInt(0)) + g.Require().Equal(node.Fined, g.s.FineValue(big.NewInt(2))) + + // Duplicate report should fail. + input, err = abiObject.Pack("report", big.NewInt(2), block1Bytes, block2Bytes) + g.Require().NoError(err) + _, err = g.call(addr, input, big.NewInt(0)) + g.Require().Error(err) + + // Check if finedRecords is set. + payloads := [][]byte{block1Bytes, block2Bytes} + sort.Sort(sortBytes(payloads)) + + hash := Bytes32(crypto.Keccak256Hash(payloads...)) + input, err = abiObject.Pack("finedRecords", hash) + g.Require().NoError(err) + res, err := g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + + var value bool + err = abiObject.Unpack(&value, "finedRecords", res) + g.Require().NoError(err) + g.Require().True(value) +} + +func (g *GovernanceContractTestSuite) TestMiscVariableReading() { + privKey, addr := g.newPrefundAccount() + pk := crypto.FromECDSAPub(&privKey.PublicKey) + + input, err := abiObject.Pack("totalSupply") + g.Require().NoError(err) + res, err := g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + + input, err = abiObject.Pack("totalStaked") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + + // Stake. + amount := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(5e5)) + input, err = abiObject.Pack("stake", pk, "Test1", "test1@dexon.org", "Taipei, Taiwan", "https://dexon.org") + g.Require().NoError(err) + _, err = g.call(addr, input, amount) + g.Require().NoError(err) + + // 1st delegator delegate to 1st node. + _, addrDelegator := g.newPrefundAccount() + amount = new(big.Int).Mul(big.NewInt(1e18), big.NewInt(3e5)) + input, err = abiObject.Pack("delegate", addr) + g.Require().NoError(err) + _, err = g.call(addrDelegator, input, amount) + g.Require().NoError(err) + + // 2st delegator delegate to 1st node. + _, addrDelegator2 := g.newPrefundAccount() + input, err = abiObject.Pack("delegate", addr) + g.Require().NoError(err) + _, err = g.call(addrDelegator2, input, amount) + g.Require().NoError(err) + + input, err = abiObject.Pack("nodes", big.NewInt(0)) + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + + input, err = abiObject.Pack("nodesLength") + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + var value *big.Int + err = abiObject.Unpack(&value, "nodesLength", res) + g.Require().NoError(err) + g.Require().Equal(1, int(value.Uint64())) + + input, err = abiObject.Pack("nodesOffsetByAddress", addr) + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "nodesOffsetByAddress", res) + g.Require().NoError(err) + g.Require().Equal(0, int(value.Uint64())) + + id, err := publicKeyToNodeID(pk) + g.Require().NoError(err) + input, err = abiObject.Pack("nodesOffsetByID", id) + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "nodesOffsetByID", res) + g.Require().NoError(err) + g.Require().Equal(0, int(value.Uint64())) + + input, err = abiObject.Pack("delegators", addr, big.NewInt(0)) + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + + input, err = abiObject.Pack("delegatorsLength", addr) + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "delegatorsLength", res) + g.Require().NoError(err) + g.Require().Equal(3, int(value.Uint64())) + + input, err = abiObject.Pack("delegatorsOffset", addr, addrDelegator2) + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) + err = abiObject.Unpack(&value, "delegatorsOffset", res) + g.Require().NoError(err) + g.Require().Equal(2, int(value.Uint64())) + + input, err = abiObject.Pack("fineValues", big.NewInt(0)) + g.Require().NoError(err) + res, err = g.call(addr, input, big.NewInt(0)) + g.Require().NoError(err) +} + +func (g *GovernanceContractTestSuite) TestHalvingCondition() { + // TotalSupply 2.5B reached + g.s.MiningHalved() + g.Require().Equal(new(big.Int).Mul(big.NewInt(1e18), big.NewInt(3.25e9)).String(), + g.s.NextHalvingSupply().String()) + g.Require().Equal(new(big.Int).Mul(big.NewInt(1e18), big.NewInt(0.75e9)).String(), + g.s.LastHalvedAmount().String()) + + // TotalSupply 3.25B reached + g.s.MiningHalved() + g.Require().Equal(new(big.Int).Mul(big.NewInt(1e18), big.NewInt(3.625e9)).String(), + g.s.NextHalvingSupply().String()) + g.Require().Equal(new(big.Int).Mul(big.NewInt(1e18), big.NewInt(0.375e9)).String(), + g.s.LastHalvedAmount().String()) +} + +func TestGovernanceContract(t *testing.T) { + suite.Run(t, new(GovernanceContractTestSuite)) +} diff --git a/core/vm/evm/instructions.go b/core/vm/evm/instructions.go new file mode 100644 index 000000000..d37ab5e43 --- /dev/null +++ b/core/vm/evm/instructions.go @@ -0,0 +1,983 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "encoding/binary" + "errors" + "fmt" + "math/big" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/common/math" + "github.com/dexon-foundation/dexon/core/types" + "github.com/dexon-foundation/dexon/core/vm" + "github.com/dexon-foundation/dexon/crypto" + "github.com/dexon-foundation/dexon/params" + "golang.org/x/crypto/sha3" +) + +var ( + bigZero = new(big.Int) + big2 = big.NewInt(2) + big256 = big.NewInt(256) + tt255 = math.BigPow(2, 255) + errWriteProtection = errors.New("evm: write protection") + errReturnDataOutOfBounds = errors.New("evm: return data out of bounds") + errExecutionReverted = errors.New("evm: execution reverted") + errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded") + power2 = make([]*big.Int, 256) +) + +func init() { + cur := big.NewInt(1) + for i := 0; i < 256; i++ { + power2[i] = new(big.Int).Set(cur) + cur = new(big.Int).Mul(cur, big2) + } +} + +func opAdd(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := stack.Pop(), stack.Peek() + math.U256(y.Add(x, y)) + + interpreter.intPool.Put(x) + return nil, nil +} + +func opSub(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := stack.Pop(), stack.Peek() + math.U256(y.Sub(x, y)) + + interpreter.intPool.Put(x) + return nil, nil +} + +func opMul(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := stack.Pop(), stack.Pop() + stack.Push(math.U256(x.Mul(x, y))) + + interpreter.intPool.Put(y) + + return nil, nil +} + +func opDiv(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := stack.Pop(), stack.Peek() + if y.Sign() != 0 { + math.U256(y.Div(x, y)) + } else { + y.SetUint64(0) + } + interpreter.intPool.Put(x) + return nil, nil +} + +func opSdiv(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := math.S256(stack.Pop()), math.S256(stack.Pop()) + res := interpreter.intPool.GetZero() + + if y.Sign() == 0 || x.Sign() == 0 { + stack.Push(res) + } else { + if x.Sign() != y.Sign() { + res.Div(x.Abs(x), y.Abs(y)) + res.Neg(res) + } else { + res.Div(x.Abs(x), y.Abs(y)) + } + stack.Push(math.U256(res)) + } + interpreter.intPool.Put(x, y) + return nil, nil +} + +func opMod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := stack.Pop(), stack.Pop() + if y.Sign() == 0 { + stack.Push(x.SetUint64(0)) + } else { + stack.Push(math.U256(x.Mod(x, y))) + } + interpreter.intPool.Put(y) + return nil, nil +} + +func opSmod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := math.S256(stack.Pop()), math.S256(stack.Pop()) + res := interpreter.intPool.GetZero() + + if y.Sign() == 0 { + stack.Push(res) + } else { + if x.Sign() < 0 { + res.Mod(x.Abs(x), y.Abs(y)) + res.Neg(res) + } else { + res.Mod(x.Abs(x), y.Abs(y)) + } + stack.Push(math.U256(res)) + } + interpreter.intPool.Put(x, y) + return nil, nil +} + +func opExp(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + base, exponent := stack.Pop(), stack.Pop() + if base.Cmp(big2) == 0 && exponent.Cmp(big256) == -1 { + exp := exponent.Int64() + stack.Push(interpreter.intPool.Get().Set(power2[exp])) + } else { + stack.Push(math.Exp(base, exponent)) + } + + interpreter.intPool.Put(base, exponent) + + return nil, nil +} + +func opSignExtend(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + back := stack.Pop() + if back.Cmp(big.NewInt(31)) < 0 { + bit := uint(back.Uint64()*8 + 7) + num := stack.Pop() + mask := back.Lsh(common.Big1, bit) + mask.Sub(mask, common.Big1) + if num.Bit(int(bit)) > 0 { + num.Or(num, mask.Not(mask)) + } else { + num.And(num, mask) + } + + stack.Push(math.U256(num)) + } + + interpreter.intPool.Put(back) + return nil, nil +} + +func opNot(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x := stack.Peek() + math.U256(x.Not(x)) + return nil, nil +} + +func opLt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := stack.Pop(), stack.Peek() + if x.Cmp(y) < 0 { + y.SetUint64(1) + } else { + y.SetUint64(0) + } + interpreter.intPool.Put(x) + return nil, nil +} + +func opGt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := stack.Pop(), stack.Peek() + if x.Cmp(y) > 0 { + y.SetUint64(1) + } else { + y.SetUint64(0) + } + interpreter.intPool.Put(x) + return nil, nil +} + +func opSlt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := stack.Pop(), stack.Peek() + + xSign := x.Cmp(tt255) + ySign := y.Cmp(tt255) + + switch { + case xSign >= 0 && ySign < 0: + y.SetUint64(1) + + case xSign < 0 && ySign >= 0: + y.SetUint64(0) + + default: + if x.Cmp(y) < 0 { + y.SetUint64(1) + } else { + y.SetUint64(0) + } + } + interpreter.intPool.Put(x) + return nil, nil +} + +func opSgt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := stack.Pop(), stack.Peek() + + xSign := x.Cmp(tt255) + ySign := y.Cmp(tt255) + + switch { + case xSign >= 0 && ySign < 0: + y.SetUint64(0) + + case xSign < 0 && ySign >= 0: + y.SetUint64(1) + + default: + if x.Cmp(y) > 0 { + y.SetUint64(1) + } else { + y.SetUint64(0) + } + } + interpreter.intPool.Put(x) + return nil, nil +} + +func opEq(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := stack.Pop(), stack.Peek() + if x.Cmp(y) == 0 { + y.SetUint64(1) + } else { + y.SetUint64(0) + } + interpreter.intPool.Put(x) + return nil, nil +} + +func opIszero(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x := stack.Peek() + if x.Sign() > 0 { + x.SetUint64(0) + } else { + x.SetUint64(1) + } + return nil, nil +} + +func opAnd(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := stack.Pop(), stack.Pop() + stack.Push(x.And(x, y)) + + interpreter.intPool.Put(y) + return nil, nil +} + +func opOr(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := stack.Pop(), stack.Peek() + y.Or(x, y) + + interpreter.intPool.Put(x) + return nil, nil +} + +func opXor(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y := stack.Pop(), stack.Peek() + y.Xor(x, y) + + interpreter.intPool.Put(x) + return nil, nil +} + +func opByte(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + th, val := stack.Pop(), stack.Peek() + if th.Cmp(common.Big32) < 0 { + b := math.Byte(val, 32, int(th.Int64())) + val.SetUint64(uint64(b)) + } else { + val.SetUint64(0) + } + interpreter.intPool.Put(th) + return nil, nil +} + +func opAddmod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y, z := stack.Pop(), stack.Pop(), stack.Pop() + if z.Cmp(bigZero) > 0 { + x.Add(x, y) + x.Mod(x, z) + stack.Push(math.U256(x)) + } else { + stack.Push(x.SetUint64(0)) + } + interpreter.intPool.Put(y, z) + return nil, nil +} + +func opMulmod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + x, y, z := stack.Pop(), stack.Pop(), stack.Pop() + if z.Cmp(bigZero) > 0 { + x.Mul(x, y) + x.Mod(x, z) + stack.Push(math.U256(x)) + } else { + stack.Push(x.SetUint64(0)) + } + interpreter.intPool.Put(y, z) + return nil, nil +} + +// opSHL implements Shift Left +// The SHL instruction (shift left) pops 2 values from the stack, first arg1 and then arg2, +// and pushes on the stack arg2 shifted to the left by arg1 number of bits. +func opSHL(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards + shift, value := math.U256(stack.Pop()), math.U256(stack.Peek()) + defer interpreter.intPool.Put(shift) // First operand back into the pool + + if shift.Cmp(common.Big256) >= 0 { + value.SetUint64(0) + return nil, nil + } + n := uint(shift.Uint64()) + math.U256(value.Lsh(value, n)) + + return nil, nil +} + +// opSHR implements Logical Shift Right +// The SHR instruction (logical shift right) pops 2 values from the stack, first arg1 and then arg2, +// and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill. +func opSHR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards + shift, value := math.U256(stack.Pop()), math.U256(stack.Peek()) + defer interpreter.intPool.Put(shift) // First operand back into the pool + + if shift.Cmp(common.Big256) >= 0 { + value.SetUint64(0) + return nil, nil + } + n := uint(shift.Uint64()) + math.U256(value.Rsh(value, n)) + + return nil, nil +} + +// opSAR implements Arithmetic Shift Right +// The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2, +// and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension. +func opSAR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + // Note, S256 returns (potentially) a new bigint, so we're popping, not peeking this one + shift, value := math.U256(stack.Pop()), math.S256(stack.Pop()) + defer interpreter.intPool.Put(shift) // First operand back into the pool + + if shift.Cmp(common.Big256) >= 0 { + if value.Sign() >= 0 { + value.SetUint64(0) + } else { + value.SetInt64(-1) + } + stack.Push(math.U256(value)) + return nil, nil + } + n := uint(shift.Uint64()) + value.Rsh(value, n) + stack.Push(math.U256(value)) + + return nil, nil +} + +func opSha3(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + offset, size := stack.Pop(), stack.Pop() + data := memory.Get(offset.Int64(), size.Int64()) + + if interpreter.hasher == nil { + interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState) + } else { + interpreter.hasher.Reset() + } + interpreter.hasher.Write(data) + interpreter.hasher.Read(interpreter.hasherBuf[:]) + + evm := interpreter.evm + if evm.vmConfig.EnablePreimageRecording { + evm.StateDB.AddPreimage(interpreter.hasherBuf, data) + } + stack.Push(interpreter.intPool.Get().SetBytes(interpreter.hasherBuf[:])) + + interpreter.intPool.Put(offset, size) + return nil, nil +} + +func opRand(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + evm := interpreter.evm + + nonce := evm.StateDB.GetNonce(evm.Origin) + binaryOriginNonce := make([]byte, binary.MaxVarintLen64) + binary.PutUvarint(binaryOriginNonce, nonce) + + binaryUsedIndex := make([]byte, binary.MaxVarintLen64) + binary.PutUvarint(binaryUsedIndex, evm.RandCallIndex) + + evm.RandCallIndex += 1 + + hash := crypto.Keccak256( + evm.Randomness, + evm.Origin.Bytes(), + binaryOriginNonce, + binaryUsedIndex) + + stack.Push(interpreter.intPool.Get().SetBytes(hash)) + return nil, nil +} + +func opAddress(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(contract.Address().Big()) + return nil, nil +} + +func opBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + slot := stack.Peek() + slot.Set(interpreter.evm.StateDB.GetBalance(common.BigToAddress(slot))) + return nil, nil +} + +func opOrigin(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(interpreter.evm.Origin.Big()) + return nil, nil +} + +func opCaller(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(contract.Caller().Big()) + return nil, nil +} + +func opCallValue(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(interpreter.intPool.Get().Set(contract.value)) + return nil, nil +} + +func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(interpreter.intPool.Get().SetBytes(vm.GetDataBig(contract.Input, stack.Pop(), big32))) + return nil, nil +} + +func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(interpreter.intPool.Get().SetInt64(int64(len(contract.Input)))) + return nil, nil +} + +func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + var ( + memOffset = stack.Pop() + dataOffset = stack.Pop() + length = stack.Pop() + ) + memory.Set(memOffset.Uint64(), length.Uint64(), vm.GetDataBig(contract.Input, dataOffset, length)) + + interpreter.intPool.Put(memOffset, dataOffset, length) + return nil, nil +} + +func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(interpreter.intPool.Get().SetUint64(uint64(len(interpreter.returnData)))) + return nil, nil +} + +func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + var ( + memOffset = stack.Pop() + dataOffset = stack.Pop() + length = stack.Pop() + + end = interpreter.intPool.Get().Add(dataOffset, length) + ) + defer interpreter.intPool.Put(memOffset, dataOffset, length, end) + + if end.BitLen() > 64 || uint64(len(interpreter.returnData)) < end.Uint64() { + return nil, errReturnDataOutOfBounds + } + memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()]) + + return nil, nil +} + +func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + slot := stack.Peek() + slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.BigToAddress(slot)))) + + return nil, nil +} + +func opCodeSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + l := interpreter.intPool.Get().SetInt64(int64(len(contract.Code))) + stack.Push(l) + + return nil, nil +} + +func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + var ( + memOffset = stack.Pop() + codeOffset = stack.Pop() + length = stack.Pop() + ) + codeCopy := vm.GetDataBig(contract.Code, codeOffset, length) + memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) + + interpreter.intPool.Put(memOffset, codeOffset, length) + return nil, nil +} + +func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + var ( + addr = common.BigToAddress(stack.Pop()) + memOffset = stack.Pop() + codeOffset = stack.Pop() + length = stack.Pop() + ) + codeCopy := vm.GetDataBig(interpreter.evm.StateDB.GetCode(addr), codeOffset, length) + memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) + + interpreter.intPool.Put(memOffset, codeOffset, length) + return nil, nil +} + +// opExtCodeHash returns the code hash of a specified account. +// There are several cases when the function is called, while we can relay everything +// to `state.GetCodeHash` function to ensure the correctness. +// (1) Caller tries to get the code hash of a normal contract account, state +// should return the relative code hash and set it as the result. +// +// (2) Caller tries to get the code hash of a non-existent account, state should +// return common.Hash{} and zero will be set as the result. +// +// (3) Caller tries to get the code hash for an account without contract code, +// state should return emptyCodeHash(0xc5d246...) as the result. +// +// (4) Caller tries to get the code hash of a precompiled account, the result +// should be zero or emptyCodeHash. +// +// It is worth noting that in order to avoid unnecessary create and clean, +// all precompile accounts on mainnet have been transferred 1 wei, so the return +// here should be emptyCodeHash. +// If the precompile account is not transferred any amount on a private or +// customized chain, the return value will be zero. +// +// (5) Caller tries to get the code hash for an account which is marked as suicided +// in the current transaction, the code hash of this account should be returned. +// +// (6) Caller tries to get the code hash for an account which is marked as deleted, +// this account should be regarded as a non-existent account and zero should be returned. +func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + slot := stack.Peek() + address := common.BigToAddress(slot) + if interpreter.evm.StateDB.Empty(address) { + slot.SetUint64(0) + } else { + slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes()) + } + return nil, nil +} + +func opGasprice(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(interpreter.intPool.Get().Set(interpreter.evm.GasPrice)) + return nil, nil +} + +func opBlockhash(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + num := stack.Pop() + + n := interpreter.intPool.Get().Sub(interpreter.evm.BlockNumber, common.Big257) + if num.Cmp(n) > 0 && num.Cmp(interpreter.evm.BlockNumber) < 0 { + stack.Push(interpreter.evm.GetHash(num.Uint64()).Big()) + } else { + stack.Push(interpreter.intPool.GetZero()) + } + interpreter.intPool.Put(num, n) + return nil, nil +} + +func opCoinbase(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(interpreter.evm.Coinbase.Big()) + return nil, nil +} + +func opTimestamp(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(math.U256(interpreter.intPool.Get().Set(interpreter.evm.Time))) + return nil, nil +} + +func opNumber(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(math.U256(interpreter.intPool.Get().Set(interpreter.evm.BlockNumber))) + return nil, nil +} + +func opDifficulty(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(math.U256(interpreter.intPool.Get().Set(interpreter.evm.Difficulty))) + return nil, nil +} + +func opGasLimit(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(math.U256(interpreter.intPool.Get().SetUint64(interpreter.evm.GasLimit))) + return nil, nil +} + +func opPop(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + interpreter.intPool.Put(stack.Pop()) + return nil, nil +} + +func opMload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + offset := stack.Pop() + val := interpreter.intPool.Get().SetBytes(memory.Get(offset.Int64(), 32)) + stack.Push(val) + + interpreter.intPool.Put(offset) + return nil, nil +} + +func opMstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + // pop value of the stack + mStart, val := stack.Pop(), stack.Pop() + memory.Set32(mStart.Uint64(), val) + + interpreter.intPool.Put(mStart, val) + return nil, nil +} + +func opMstore8(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + off, val := stack.Pop().Int64(), stack.Pop().Int64() + memory.Store[off] = byte(val & 0xff) + + return nil, nil +} + +func opSload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + loc := stack.Peek() + val := interpreter.evm.StateDB.GetState(contract.Address(), common.BigToHash(loc)) + loc.SetBytes(val.Bytes()) + return nil, nil +} + +func opSstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + loc := common.BigToHash(stack.Pop()) + val := stack.Pop() + interpreter.evm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val)) + + interpreter.intPool.Put(val) + return nil, nil +} + +func opJump(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + pos := stack.Pop() + if !contract.validJumpdest(pos) { + nop := contract.GetOp(pos.Uint64()) + return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos) + } + *pc = pos.Uint64() + + interpreter.intPool.Put(pos) + return nil, nil +} + +func opJumpi(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + pos, cond := stack.Pop(), stack.Pop() + if cond.Sign() != 0 { + if !contract.validJumpdest(pos) { + nop := contract.GetOp(pos.Uint64()) + return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos) + } + *pc = pos.Uint64() + } else { + *pc++ + } + + interpreter.intPool.Put(pos, cond) + return nil, nil +} + +func opJumpdest(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + return nil, nil +} + +func opPc(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(interpreter.intPool.Get().SetUint64(*pc)) + return nil, nil +} + +func opMsize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(interpreter.intPool.Get().SetInt64(int64(memory.Len()))) + return nil, nil +} + +func opGas(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Push(interpreter.intPool.Get().SetUint64(contract.Gas)) + return nil, nil +} + +func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + var ( + value = stack.Pop() + offset, size = stack.Pop(), stack.Pop() + input = memory.Get(offset.Int64(), size.Int64()) + gas = contract.Gas + ) + if interpreter.evm.ChainConfig().IsEIP150(interpreter.evm.BlockNumber) { + gas -= gas / 64 + } + + contract.UseGas(gas) + res, addr, returnGas, suberr := interpreter.evm.Create(contract, input, gas, value) + // Push item on the stack based on the returned error. If the ruleset is + // homestead we must check for CodeStoreOutOfGasError (homestead only + // rule) and treat as an error, if the ruleset is frontier we must + // ignore this error and pretend the operation was successful. + if interpreter.evm.ChainConfig().IsHomestead(interpreter.evm.BlockNumber) && suberr == vm.ErrCodeStoreOutOfGas { + stack.Push(interpreter.intPool.GetZero()) + } else if suberr != nil && suberr != vm.ErrCodeStoreOutOfGas { + stack.Push(interpreter.intPool.GetZero()) + } else { + stack.Push(addr.Big()) + } + contract.Gas += returnGas + interpreter.intPool.Put(value, offset, size) + + if suberr == errExecutionReverted { + return res, nil + } + return nil, nil +} + +func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + var ( + endowment = stack.Pop() + offset, size = stack.Pop(), stack.Pop() + salt = stack.Pop() + input = memory.Get(offset.Int64(), size.Int64()) + gas = contract.Gas + ) + + // Apply EIP150 + gas -= gas / 64 + contract.UseGas(gas) + res, addr, returnGas, suberr := interpreter.evm.Create2(contract, input, gas, endowment, salt) + // Push item on the stack based on the returned error. + if suberr != nil { + stack.Push(interpreter.intPool.GetZero()) + } else { + stack.Push(addr.Big()) + } + contract.Gas += returnGas + interpreter.intPool.Put(endowment, offset, size, salt) + + if suberr == errExecutionReverted { + return res, nil + } + return nil, nil +} + +func opCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + // Pop gas. The actual gas in interpreter.evm.callGasTemp. + interpreter.intPool.Put(stack.Pop()) + gas := interpreter.evm.callGasTemp + // Pop other call parameters. + addr, value, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop() + toAddr := common.BigToAddress(addr) + value = math.U256(value) + // Get the arguments from the memory. + args := memory.Get(inOffset.Int64(), inSize.Int64()) + + if value.Sign() != 0 { + gas += params.CallStipend + } + ret, returnGas, err := interpreter.evm.Call(contract, toAddr, args, gas, value) + if err != nil { + stack.Push(interpreter.intPool.GetZero()) + } else { + stack.Push(interpreter.intPool.Get().SetUint64(1)) + } + if err == nil || err == errExecutionReverted { + memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) + } + contract.Gas += returnGas + + interpreter.intPool.Put(addr, value, inOffset, inSize, retOffset, retSize) + return ret, nil +} + +func opCallCode(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + // Pop gas. The actual gas is in interpreter.evm.callGasTemp. + interpreter.intPool.Put(stack.Pop()) + gas := interpreter.evm.callGasTemp + // Pop other call parameters. + addr, value, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop() + toAddr := common.BigToAddress(addr) + value = math.U256(value) + // Get arguments from the memory. + args := memory.Get(inOffset.Int64(), inSize.Int64()) + + if value.Sign() != 0 { + gas += params.CallStipend + } + ret, returnGas, err := interpreter.evm.CallCode(contract, toAddr, args, gas, value) + if err != nil { + stack.Push(interpreter.intPool.GetZero()) + } else { + stack.Push(interpreter.intPool.Get().SetUint64(1)) + } + if err == nil || err == errExecutionReverted { + memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) + } + contract.Gas += returnGas + + interpreter.intPool.Put(addr, value, inOffset, inSize, retOffset, retSize) + return ret, nil +} + +func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + // Pop gas. The actual gas is in interpreter.evm.callGasTemp. + interpreter.intPool.Put(stack.Pop()) + gas := interpreter.evm.callGasTemp + // Pop other call parameters. + addr, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop() + toAddr := common.BigToAddress(addr) + // Get arguments from the memory. + args := memory.Get(inOffset.Int64(), inSize.Int64()) + + ret, returnGas, err := interpreter.evm.DelegateCall(contract, toAddr, args, gas) + if err != nil { + stack.Push(interpreter.intPool.GetZero()) + } else { + stack.Push(interpreter.intPool.Get().SetUint64(1)) + } + if err == nil || err == errExecutionReverted { + memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) + } + contract.Gas += returnGas + + interpreter.intPool.Put(addr, inOffset, inSize, retOffset, retSize) + return ret, nil +} + +func opStaticCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + // Pop gas. The actual gas is in interpreter.evm.callGasTemp. + interpreter.intPool.Put(stack.Pop()) + gas := interpreter.evm.callGasTemp + // Pop other call parameters. + addr, inOffset, inSize, retOffset, retSize := stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop(), stack.Pop() + toAddr := common.BigToAddress(addr) + // Get arguments from the memory. + args := memory.Get(inOffset.Int64(), inSize.Int64()) + + ret, returnGas, err := interpreter.evm.StaticCall(contract, toAddr, args, gas) + if err != nil { + stack.Push(interpreter.intPool.GetZero()) + } else { + stack.Push(interpreter.intPool.Get().SetUint64(1)) + } + if err == nil || err == errExecutionReverted { + memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) + } + contract.Gas += returnGas + + interpreter.intPool.Put(addr, inOffset, inSize, retOffset, retSize) + return ret, nil +} + +func opReturn(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + offset, size := stack.Pop(), stack.Pop() + ret := memory.GetPtr(offset.Int64(), size.Int64()) + + interpreter.intPool.Put(offset, size) + return ret, nil +} + +func opRevert(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + offset, size := stack.Pop(), stack.Pop() + ret := memory.GetPtr(offset.Int64(), size.Int64()) + + interpreter.intPool.Put(offset, size) + return ret, nil +} + +func opStop(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + return nil, nil +} + +func opSuicide(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + balance := interpreter.evm.StateDB.GetBalance(contract.Address()) + interpreter.evm.StateDB.AddBalance(common.BigToAddress(stack.Pop()), balance) + + interpreter.evm.StateDB.Suicide(contract.Address()) + return nil, nil +} + +// following functions are used by the instruction jump table + +// make log instruction function +func makeLog(size int) executionFunc { + return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + topics := make([]common.Hash, size) + mStart, mSize := stack.Pop(), stack.Pop() + for i := 0; i < size; i++ { + topics[i] = common.BigToHash(stack.Pop()) + } + + d := memory.Get(mStart.Int64(), mSize.Int64()) + interpreter.evm.StateDB.AddLog(&types.Log{ + Address: contract.Address(), + Topics: topics, + Data: d, + // This is a non-consensus field, but assigned here because + // core/state doesn't know the current block number. + BlockNumber: interpreter.evm.BlockNumber.Uint64(), + }) + + interpreter.intPool.Put(mStart, mSize) + return nil, nil + } +} + +// make push instruction function +func makePush(size uint64, pushByteSize int) executionFunc { + return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + codeLen := len(contract.Code) + + startMin := codeLen + if int(*pc+1) < startMin { + startMin = int(*pc + 1) + } + + endMin := codeLen + if startMin+pushByteSize < endMin { + endMin = startMin + pushByteSize + } + + integer := interpreter.intPool.Get() + stack.Push(integer.SetBytes(common.RightPadBytes(contract.Code[startMin:endMin], pushByteSize))) + + *pc += size + return nil, nil + } +} + +// make dup instruction function +func makeDup(size int64) executionFunc { + return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Dup(interpreter.intPool, int(size)) + return nil, nil + } +} + +// make swap instruction function +func makeSwap(size int64) executionFunc { + // switch n + 1 otherwise n would be swapped with n + size++ + return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) { + stack.Swap(int(size)) + return nil, nil + } +} diff --git a/core/vm/evm/instructions_test.go b/core/vm/evm/instructions_test.go new file mode 100644 index 000000000..9e677dde3 --- /dev/null +++ b/core/vm/evm/instructions_test.go @@ -0,0 +1,590 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "bytes" + "math/big" + "testing" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/core/vm" + "github.com/dexon-foundation/dexon/crypto" + "github.com/dexon-foundation/dexon/params" +) + +type twoOperandTest struct { + x string + y string + expected string +} + +func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error)) { + var ( + env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + stack = NewStack() + pc = uint64(0) + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + ) + + env.interpreter = evmInterpreter + evmInterpreter.intPool = vm.PoolOfIntPools.Get() + for i, test := range tests { + x := new(big.Int).SetBytes(common.Hex2Bytes(test.x)) + shift := new(big.Int).SetBytes(common.Hex2Bytes(test.y)) + expected := new(big.Int).SetBytes(common.Hex2Bytes(test.expected)) + stack.Push(x) + stack.Push(shift) + opFn(&pc, evmInterpreter, nil, nil, stack) + actual := stack.Pop() + if actual.Cmp(expected) != 0 { + t.Errorf("Testcase %d, expected %v, got %v", i, expected, actual) + } + // Check pool usage + // 1.pool is not allowed to contain anything on the stack + // 2.pool is not allowed to contain the same pointers twice + if evmInterpreter.intPool.Pool.Len() > 0 { + + poolvals := make(map[*big.Int]struct{}) + poolvals[actual] = struct{}{} + + for evmInterpreter.intPool.Pool.Len() > 0 { + key := evmInterpreter.intPool.Get() + if _, exist := poolvals[key]; exist { + t.Errorf("Testcase %d, pool contains double-entry", i) + } + poolvals[key] = struct{}{} + } + } + } + vm.PoolOfIntPools.Put(evmInterpreter.intPool) +} + +func TestByteOp(t *testing.T) { + var ( + env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + stack = NewStack() + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + ) + + env.interpreter = evmInterpreter + evmInterpreter.intPool = vm.PoolOfIntPools.Get() + tests := []struct { + v string + th uint64 + expected *big.Int + }{ + {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 0, big.NewInt(0xAB)}, + {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 1, big.NewInt(0xCD)}, + {"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 0, big.NewInt(0x00)}, + {"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 1, big.NewInt(0xCD)}, + {"0000000000000000000000000000000000000000000000000000000000102030", 31, big.NewInt(0x30)}, + {"0000000000000000000000000000000000000000000000000000000000102030", 30, big.NewInt(0x20)}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 32, big.NewInt(0x0)}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0xFFFFFFFFFFFFFFFF, big.NewInt(0x0)}, + } + pc := uint64(0) + for _, test := range tests { + val := new(big.Int).SetBytes(common.Hex2Bytes(test.v)) + th := new(big.Int).SetUint64(test.th) + stack.Push(val) + stack.Push(th) + opByte(&pc, evmInterpreter, nil, nil, stack) + actual := stack.Pop() + if actual.Cmp(test.expected) != 0 { + t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.v, test.th, test.expected, actual) + } + } + vm.PoolOfIntPools.Put(evmInterpreter.intPool) +} + +func TestSHL(t *testing.T) { + // Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#shl-shift-left + tests := []twoOperandTest{ + {"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000002"}, + {"0000000000000000000000000000000000000000000000000000000000000001", "ff", "8000000000000000000000000000000000000000000000000000000000000000"}, + {"0000000000000000000000000000000000000000000000000000000000000001", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"0000000000000000000000000000000000000000000000000000000000000001", "0101", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "00", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "8000000000000000000000000000000000000000000000000000000000000000"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"}, + } + testTwoOperandOp(t, tests, opSHL) +} + +func TestSHR(t *testing.T) { + // Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#shr-logical-shift-right + tests := []twoOperandTest{ + {"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"8000000000000000000000000000000000000000000000000000000000000000", "01", "4000000000000000000000000000000000000000000000000000000000000000"}, + {"8000000000000000000000000000000000000000000000000000000000000000", "ff", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"8000000000000000000000000000000000000000000000000000000000000000", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"8000000000000000000000000000000000000000000000000000000000000000", "0101", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "00", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, + } + testTwoOperandOp(t, tests, opSHR) +} + +func TestSAR(t *testing.T) { + // Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#sar-arithmetic-shift-right + tests := []twoOperandTest{ + {"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"8000000000000000000000000000000000000000000000000000000000000000", "01", "c000000000000000000000000000000000000000000000000000000000000000"}, + {"8000000000000000000000000000000000000000000000000000000000000000", "ff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"8000000000000000000000000000000000000000000000000000000000000000", "0100", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"8000000000000000000000000000000000000000000000000000000000000000", "0101", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "00", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}, + {"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"4000000000000000000000000000000000000000000000000000000000000000", "fe", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "f8", "000000000000000000000000000000000000000000000000000000000000007f"}, + {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "fe", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ff", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, + } + + testTwoOperandOp(t, tests, opSAR) +} + +func TestSGT(t *testing.T) { + tests := []twoOperandTest{ + + {"0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"0000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"0000000000000000000000000000000000000000000000000000000000000001", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"8000000000000000000000000000000000000000000000000000000000000001", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"8000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "0000000000000000000000000000000000000000000000000000000000000000"}, + } + testTwoOperandOp(t, tests, opSgt) +} + +func TestSLT(t *testing.T) { + tests := []twoOperandTest{ + {"0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"0000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"0000000000000000000000000000000000000000000000000000000000000001", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"8000000000000000000000000000000000000000000000000000000000000001", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"8000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"}, + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "0000000000000000000000000000000000000000000000000000000000000000"}, + {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "0000000000000000000000000000000000000000000000000000000000000001"}, + } + testTwoOperandOp(t, tests, opSlt) +} + +func opBenchmark(bench *testing.B, op func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error), args ...string) { + var ( + env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + stack = NewStack() + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + ) + + env.interpreter = evmInterpreter + evmInterpreter.intPool = vm.PoolOfIntPools.Get() + // convert args + byteArgs := make([][]byte, len(args)) + for i, arg := range args { + byteArgs[i] = common.Hex2Bytes(arg) + } + pc := uint64(0) + bench.ResetTimer() + for i := 0; i < bench.N; i++ { + for _, arg := range byteArgs { + a := new(big.Int).SetBytes(arg) + stack.Push(a) + } + op(&pc, evmInterpreter, nil, nil, stack) + stack.Pop() + } + vm.PoolOfIntPools.Put(evmInterpreter.intPool) +} + +func BenchmarkOpAdd64(b *testing.B) { + x := "ffffffff" + y := "fd37f3e2bba2c4f" + + opBenchmark(b, opAdd, x, y) +} + +func BenchmarkOpAdd128(b *testing.B) { + x := "ffffffffffffffff" + y := "f5470b43c6549b016288e9a65629687" + + opBenchmark(b, opAdd, x, y) +} + +func BenchmarkOpAdd256(b *testing.B) { + x := "0802431afcbce1fc194c9eaa417b2fb67dc75a95db0bc7ec6b1c8af11df6a1da9" + y := "a1f5aac137876480252e5dcac62c354ec0d42b76b0642b6181ed099849ea1d57" + + opBenchmark(b, opAdd, x, y) +} + +func BenchmarkOpSub64(b *testing.B) { + x := "51022b6317003a9d" + y := "a20456c62e00753a" + + opBenchmark(b, opSub, x, y) +} + +func BenchmarkOpSub128(b *testing.B) { + x := "4dde30faaacdc14d00327aac314e915d" + y := "9bbc61f5559b829a0064f558629d22ba" + + opBenchmark(b, opSub, x, y) +} + +func BenchmarkOpSub256(b *testing.B) { + x := "4bfcd8bb2ac462735b48a17580690283980aa2d679f091c64364594df113ea37" + y := "97f9b1765588c4e6b69142eb00d20507301545acf3e1238c86c8b29be227d46e" + + opBenchmark(b, opSub, x, y) +} + +func BenchmarkOpMul(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opMul, x, y) +} + +func BenchmarkOpDiv256(b *testing.B) { + x := "ff3f9014f20db29ae04af2c2d265de17" + y := "fe7fb0d1f59dfe9492ffbf73683fd1e870eec79504c60144cc7f5fc2bad1e611" + opBenchmark(b, opDiv, x, y) +} + +func BenchmarkOpDiv128(b *testing.B) { + x := "fdedc7f10142ff97" + y := "fbdfda0e2ce356173d1993d5f70a2b11" + opBenchmark(b, opDiv, x, y) +} + +func BenchmarkOpDiv64(b *testing.B) { + x := "fcb34eb3" + y := "f97180878e839129" + opBenchmark(b, opDiv, x, y) +} + +func BenchmarkOpSdiv(b *testing.B) { + x := "ff3f9014f20db29ae04af2c2d265de17" + y := "fe7fb0d1f59dfe9492ffbf73683fd1e870eec79504c60144cc7f5fc2bad1e611" + + opBenchmark(b, opSdiv, x, y) +} + +func BenchmarkOpMod(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opMod, x, y) +} + +func BenchmarkOpSmod(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opSmod, x, y) +} + +func BenchmarkOpExp(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opExp, x, y) +} + +func BenchmarkOpSignExtend(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opSignExtend, x, y) +} + +func BenchmarkOpLt(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opLt, x, y) +} + +func BenchmarkOpGt(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opGt, x, y) +} + +func BenchmarkOpSlt(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opSlt, x, y) +} + +func BenchmarkOpSgt(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opSgt, x, y) +} + +func BenchmarkOpEq(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opEq, x, y) +} +func BenchmarkOpEq2(b *testing.B) { + x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff" + y := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201fffffffe" + opBenchmark(b, opEq, x, y) +} +func BenchmarkOpAnd(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opAnd, x, y) +} + +func BenchmarkOpOr(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opOr, x, y) +} + +func BenchmarkOpXor(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opXor, x, y) +} + +func BenchmarkOpByte(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opByte, x, y) +} + +func BenchmarkOpAddmod(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + z := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opAddmod, x, y, z) +} + +func BenchmarkOpMulmod(b *testing.B) { + x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + z := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + + opBenchmark(b, opMulmod, x, y, z) +} + +func BenchmarkOpSHL(b *testing.B) { + x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff" + y := "ff" + + opBenchmark(b, opSHL, x, y) +} +func BenchmarkOpSHR(b *testing.B) { + x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff" + y := "ff" + + opBenchmark(b, opSHR, x, y) +} +func BenchmarkOpSAR(b *testing.B) { + x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff" + y := "ff" + + opBenchmark(b, opSAR, x, y) +} +func BenchmarkOpIsZero(b *testing.B) { + x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff" + opBenchmark(b, opIszero, x) +} + +func TestOpMstore(t *testing.T) { + var ( + env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + stack = NewStack() + mem = vm.NewMemory() + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + ) + + env.interpreter = evmInterpreter + evmInterpreter.intPool = vm.PoolOfIntPools.Get() + mem.Resize(64) + pc := uint64(0) + v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" + stack.PushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0)) + opMstore(&pc, evmInterpreter, nil, mem, stack) + if got := common.Bytes2Hex(mem.Get(0, 32)); got != v { + t.Fatalf("Mstore fail, got %v, expected %v", got, v) + } + stack.PushN(big.NewInt(0x1), big.NewInt(0)) + opMstore(&pc, evmInterpreter, nil, mem, stack) + if common.Bytes2Hex(mem.Get(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { + t.Fatalf("Mstore failed to overwrite previous value") + } + vm.PoolOfIntPools.Put(evmInterpreter.intPool) +} + +func BenchmarkOpMstore(bench *testing.B) { + var ( + env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + stack = NewStack() + mem = vm.NewMemory() + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + ) + + env.interpreter = evmInterpreter + evmInterpreter.intPool = vm.PoolOfIntPools.Get() + mem.Resize(64) + pc := uint64(0) + memStart := big.NewInt(0) + value := big.NewInt(0x1337) + + bench.ResetTimer() + for i := 0; i < bench.N; i++ { + stack.PushN(value, memStart) + opMstore(&pc, evmInterpreter, nil, mem, stack) + } + vm.PoolOfIntPools.Put(evmInterpreter.intPool) +} + +func BenchmarkOpSHA3(bench *testing.B) { + var ( + env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + stack = NewStack() + mem = vm.NewMemory() + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + ) + env.interpreter = evmInterpreter + evmInterpreter.intPool = vm.PoolOfIntPools.Get() + mem.Resize(32) + pc := uint64(0) + start := big.NewInt(0) + + bench.ResetTimer() + for i := 0; i < bench.N; i++ { + stack.PushN(big.NewInt(32), start) + opSha3(&pc, evmInterpreter, nil, mem, stack) + } + vm.PoolOfIntPools.Put(evmInterpreter.intPool) +} + +func TestCreate2Addreses(t *testing.T) { + type testcase struct { + origin string + salt string + code string + expected string + } + + for i, tt := range []testcase{ + { + origin: "0x0000000000000000000000000000000000000000", + salt: "0x0000000000000000000000000000000000000000", + code: "0x00", + expected: "0x4d1a2e2bb4f88f0250f26ffff098b0b30b26bf38", + }, + { + origin: "0xdeadbeef00000000000000000000000000000000", + salt: "0x0000000000000000000000000000000000000000", + code: "0x00", + expected: "0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3", + }, + { + origin: "0xdeadbeef00000000000000000000000000000000", + salt: "0xfeed000000000000000000000000000000000000", + code: "0x00", + expected: "0xD04116cDd17beBE565EB2422F2497E06cC1C9833", + }, + { + origin: "0x0000000000000000000000000000000000000000", + salt: "0x0000000000000000000000000000000000000000", + code: "0xdeadbeef", + expected: "0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e", + }, + { + origin: "0x00000000000000000000000000000000deadbeef", + salt: "0xcafebabe", + code: "0xdeadbeef", + expected: "0x60f3f640a8508fC6a86d45DF051962668E1e8AC7", + }, + { + origin: "0x00000000000000000000000000000000deadbeef", + salt: "0xcafebabe", + code: "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", + expected: "0x1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C", + }, + { + origin: "0x0000000000000000000000000000000000000000", + salt: "0x0000000000000000000000000000000000000000", + code: "0x", + expected: "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0", + }, + } { + + origin := common.BytesToAddress(common.FromHex(tt.origin)) + salt := common.BytesToHash(common.FromHex(tt.salt)) + code := common.FromHex(tt.code) + codeHash := crypto.Keccak256(code) + address := crypto.CreateAddress2(origin, salt, codeHash) + /* + stack := NewStack() + // salt, but we don't need that for this test + stack.Push(big.NewInt(int64(len(code)))) //size + stack.Push(big.NewInt(0)) // memstart + stack.Push(big.NewInt(0)) // value + gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0) + fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String()) + */ + expected := common.BytesToAddress(common.FromHex(tt.expected)) + if !bytes.Equal(expected.Bytes(), address.Bytes()) { + t.Errorf("test %d: expected %s, got %s", i, expected.String(), address.String()) + } + + } +} diff --git a/core/vm/evm/interface.go b/core/vm/evm/interface.go new file mode 100644 index 000000000..20e5f34a9 --- /dev/null +++ b/core/vm/evm/interface.go @@ -0,0 +1,80 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "math/big" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/core/types" +) + +// StateDB is an EVM database for full state querying. +type StateDB interface { + CreateAccount(common.Address) + + SubBalance(common.Address, *big.Int) + AddBalance(common.Address, *big.Int) + GetBalance(common.Address) *big.Int + + GetNonce(common.Address) uint64 + SetNonce(common.Address, uint64) + + GetCodeHash(common.Address) common.Hash + GetCode(common.Address) []byte + SetCode(common.Address, []byte) + GetCodeSize(common.Address) int + + AddRefund(uint64) + SubRefund(uint64) + GetRefund() uint64 + + GetCommittedState(common.Address, common.Hash) common.Hash + GetState(common.Address, common.Hash) common.Hash + SetState(common.Address, common.Hash, common.Hash) + + Suicide(common.Address) bool + HasSuicided(common.Address) bool + + // Exist reports whether the given account exists in state. + // Notably this should also return true for suicided accounts. + Exist(common.Address) bool + // Empty returns whether the given account is empty. Empty + // is defined according to EIP161 (balance = nonce = code = 0). + Empty(common.Address) bool + + RevertToSnapshot(int) + Snapshot() int + + AddLog(*types.Log) + AddPreimage(common.Hash, []byte) + + ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) +} + +// CallContext provides a basic interface for the EVM calling conventions. The EVM +// depends on this context being implemented for doing subcalls and initialising new EVM contracts. +type CallContext interface { + // Call another contract + Call(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) + // Take another's contract code and execute within our own context + CallCode(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) + // Same as CallCode except sender and value is propagated from parent to child scope + DelegateCall(env *EVM, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) + // Create a new contract + Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) +} diff --git a/core/vm/evm/interpreter.go b/core/vm/evm/interpreter.go new file mode 100644 index 000000000..ca3ddd05a --- /dev/null +++ b/core/vm/evm/interpreter.go @@ -0,0 +1,294 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "fmt" + "hash" + "sync/atomic" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/common/math" + "github.com/dexon-foundation/dexon/core/vm" + "github.com/dexon-foundation/dexon/params" +) + +// Config are the configuration options for the Interpreter +type Config struct { + // Debug enabled debugging Interpreter options + Debug bool + // Tracer is the op code logger + Tracer Tracer + // NoRecursion disabled Interpreter call, callcode, + // delegate call and create. + NoRecursion bool + // Enable recording of SHA3/keccak preimages + EnablePreimageRecording bool + // JumpTable contains the EVM instruction table. This + // may be left uninitialised and will be set to the default + // table. + JumpTable [256]operation + + // Type of the EWASM interpreter + EWASMInterpreter string + // Type of the EVM interpreter + EVMInterpreter string + + // Whether or not we are a block proposer. + IsBlockProposer bool +} + +// Interpreter is used to run Ethereum based contracts and will utilise the +// passed environment to query external sources for state information. +// The Interpreter will run the byte code VM based on the passed +// configuration. +type Interpreter interface { + // Run loops and evaluates the contract's code with the given input data and returns + // the return byte-slice and an error if one occurred. + Run(contract *Contract, input []byte, static bool) ([]byte, error) + // CanRun tells if the contract, passed as an argument, can be + // run by the current interpreter. This is meant so that the + // caller can do something like: + // + // ```golang + // for _, interpreter := range interpreters { + // if interpreter.CanRun(contract.code) { + // interpreter.Run(contract.code, input) + // } + // } + // ``` + CanRun([]byte) bool +} + +// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports +// Read to get a variable amount of data from the hash state. Read is faster than Sum +// because it doesn't copy the internal state, but also modifies the internal state. +type keccakState interface { + hash.Hash + Read([]byte) (int, error) +} + +// EVMInterpreter represents an EVM interpreter +type EVMInterpreter struct { + evm *EVM + cfg Config + gasTable params.GasTable + + intPool *vm.IntPool + + hasher keccakState // Keccak256 hasher instance shared across opcodes + hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes + + readOnly bool // Whether to throw on stateful modifications + returnData []byte // Last CALL's return data for subsequent reuse +} + +// NewEVMInterpreter returns a new instance of the Interpreter. +func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { + // We use the STOP instruction whether to see + // the jump table was initialised. If it was not + // we'll set the default jump table. + if !cfg.JumpTable[STOP].valid { + switch { + case evm.ChainConfig().IsConstantinople(evm.BlockNumber): + cfg.JumpTable = constantinopleInstructionSet + case evm.ChainConfig().IsByzantium(evm.BlockNumber): + cfg.JumpTable = byzantiumInstructionSet + case evm.ChainConfig().IsHomestead(evm.BlockNumber): + cfg.JumpTable = homesteadInstructionSet + default: + cfg.JumpTable = frontierInstructionSet + } + } + + return &EVMInterpreter{ + evm: evm, + cfg: cfg, + gasTable: evm.ChainConfig().GasTable(evm.BlockNumber), + } +} + +func (in *EVMInterpreter) enforceRestrictions(op OpCode, operation operation, stack *vm.Stack) error { + if in.evm.chainRules.IsByzantium { + if in.readOnly { + // If the interpreter is operating in readonly mode, make sure no + // state-modifying operation is performed. The 3rd stack item + // for a call operation is the value. Transferring value from one + // account to the others means the state is modified and should also + // return with an error. + if operation.writes || (op == CALL && stack.Back(2).BitLen() > 0) { + return errWriteProtection + } + } + } + return nil +} + +// Run loops and evaluates the contract's code with the given input data and returns +// the return byte-slice and an error if one occurred. +// +// It's important to note that any errors returned by the interpreter should be +// considered a revert-and-consume-all-gas operation except for +// errExecutionReverted which means revert-and-keep-gas-left. +func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) { + if in.intPool == nil { + in.intPool = vm.PoolOfIntPools.Get() + defer func() { + vm.PoolOfIntPools.Put(in.intPool) + in.intPool = nil + }() + } + + // Increment the call depth which is restricted to 1024 + in.evm.depth++ + defer func() { in.evm.depth-- }() + + // Make sure the readOnly is only set if we aren't in readOnly yet. + // This makes also sure that the readOnly flag isn't removed for child calls. + if readOnly && !in.readOnly { + in.readOnly = true + defer func() { in.readOnly = false }() + } + + // Reset the previous call's return data. It's unimportant to preserve the old buffer + // as every returning call will return new data anyway. + in.returnData = nil + + // Don't bother with the execution if there's no code. + if len(contract.Code) == 0 { + return nil, nil + } + + var ( + op OpCode // current opcode + mem = vm.NewMemory() // bound memory + stack = NewStack() // local stack + // For optimisation reason we're using uint64 as the program counter. + // It's theoretically possible to go above 2^64. The YP defines the PC + // to be uint256. Practically much less so feasible. + pc = uint64(0) // program counter + cost uint64 + // copies used by tracer + pcCopy uint64 // needed for the deferred Tracer + gasCopy uint64 // for Tracer to log gas remaining before execution + logged bool // deferred Tracer should ignore already logged steps + ) + contract.Input = input + + // Reclaim the stack as an int pool when the execution stops + defer func() { + in.intPool.Put(stack.Data...) + Recyclestack(stack) + }() + + if in.cfg.Debug { + defer func() { + if err != nil { + if !logged { + in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) + } else { + in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) + } + } + }() + } + // The Interpreter main run loop (contextual). This loop runs until either an + // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during + // the execution of one of the operations or until the done flag is set by the + // parent context. + for atomic.LoadInt32(&in.evm.abort) == 0 { + if in.cfg.Debug { + // Capture pre-execution values for tracing. + logged, pcCopy, gasCopy = false, pc, contract.Gas + } + + // Get the operation from the jump table and validate the stack to ensure there are + // enough stack items available to perform the operation. + op = contract.GetOp(pc) + operation := in.cfg.JumpTable[op] + if !operation.valid { + return nil, fmt.Errorf("invalid opcode 0x%x", int(op)) + } + if err := operation.validateStack(stack); err != nil { + return nil, err + } + // If the operation is valid, enforce and write restrictions + if err := in.enforceRestrictions(op, operation, stack); err != nil { + return nil, err + } + + var memorySize uint64 + // calculate the new memory size and expand the memory to fit + // the operation + if operation.memorySize != nil { + memSize, overflow := vm.BigUint64(operation.memorySize(stack)) + if overflow { + return nil, errGasUintOverflow + } + // memory is expanded in words of 32 bytes. Gas + // is also calculated in words. + if memorySize, overflow = math.SafeMul(vm.ToWordSize(memSize), 32); overflow { + return nil, errGasUintOverflow + } + } + // consume the gas and return an error if not enough gas is available. + // cost is explicitly set so that the capture state defer method can get the proper cost + cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize) + if err != nil || !contract.UseGas(cost) { + return nil, vm.ErrOutOfGas + } + if memorySize > 0 { + mem.Resize(memorySize) + } + + if in.cfg.Debug { + in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err) + logged = true + } + + // execute the operation + res, err := operation.execute(&pc, in, contract, mem, stack) + // verifyPool is a build flag. Pool verification makes sure the integrity + // of the integer pool by comparing values to a default value. + if vm.VerifyPool { + vm.VerifyIntegerPool(in.intPool) + } + // if the operation clears the return data (e.g. it has returning data) + // set the last return to the result of the operation. + if operation.returns { + in.returnData = res + } + + switch { + case err != nil: + return nil, err + case operation.reverts: + return res, errExecutionReverted + case operation.halts: + return res, nil + case !operation.jumps: + pc++ + } + } + return nil, nil +} + +// CanRun tells if the contract, passed as an argument, can be +// run by the current interpreter. +func (in *EVMInterpreter) CanRun(code []byte) bool { + return true +} diff --git a/core/vm/evm/jump_table.go b/core/vm/evm/jump_table.go new file mode 100644 index 000000000..da2f50c5a --- /dev/null +++ b/core/vm/evm/jump_table.go @@ -0,0 +1,972 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "errors" + "math/big" + + "github.com/dexon-foundation/dexon/core/vm" + "github.com/dexon-foundation/dexon/params" +) + +type ( + executionFunc func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *vm.Memory, stack *vm.Stack) ([]byte, error) + gasFunc func(params.GasTable, *EVM, *Contract, *vm.Stack, *vm.Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64 + memorySizeFunc func(*vm.Stack) *big.Int +) + +var errGasUintOverflow = errors.New("gas uint64 overflow") + +type operation struct { + // execute is the operation function + execute executionFunc + // gasCost is the gas function and returns the gas required for execution + gasCost gasFunc + // validateStack validates the stack (size) for the operation + validateStack vm.StackValidationFunc + // memorySize returns the memory size required for the operation + memorySize memorySizeFunc + + halts bool // indicates whether the operation should halt further execution + jumps bool // indicates whether the program counter should not increment + writes bool // determines whether this a state modifying operation + valid bool // indication whether the retrieved operation is valid and known + reverts bool // determines whether the operation reverts state (implicitly halts) + returns bool // determines whether the operations sets the return data content +} + +var ( + frontierInstructionSet = newFrontierInstructionSet() + homesteadInstructionSet = newHomesteadInstructionSet() + byzantiumInstructionSet = newByzantiumInstructionSet() + constantinopleInstructionSet = newConstantinopleInstructionSet() +) + +// NewConstantinopleInstructionSet returns the frontier, homestead +// byzantium and contantinople instructions. +func newConstantinopleInstructionSet() [256]operation { + // instructions that can be executed during the byzantium phase. + instructionSet := newByzantiumInstructionSet() + instructionSet[SHL] = operation{ + execute: opSHL, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + } + instructionSet[SHR] = operation{ + execute: opSHR, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + } + instructionSet[SAR] = operation{ + execute: opSAR, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + } + instructionSet[EXTCODEHASH] = operation{ + execute: opExtCodeHash, + gasCost: gasExtCodeHash, + validateStack: vm.MakeStackFunc(1, 1), + valid: true, + } + instructionSet[CREATE2] = operation{ + execute: opCreate2, + gasCost: gasCreate2, + validateStack: vm.MakeStackFunc(4, 1), + memorySize: memoryCreate2, + valid: true, + writes: true, + returns: true, + } + return instructionSet +} + +// NewByzantiumInstructionSet returns the frontier, homestead and +// byzantium instructions. +func newByzantiumInstructionSet() [256]operation { + // instructions that can be executed during the homestead phase. + instructionSet := newHomesteadInstructionSet() + instructionSet[STATICCALL] = operation{ + execute: opStaticCall, + gasCost: gasStaticCall, + validateStack: vm.MakeStackFunc(6, 1), + memorySize: memoryStaticCall, + valid: true, + returns: true, + } + instructionSet[RETURNDATASIZE] = operation{ + execute: opReturnDataSize, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + } + instructionSet[RETURNDATACOPY] = operation{ + execute: opReturnDataCopy, + gasCost: gasReturnDataCopy, + validateStack: vm.MakeStackFunc(3, 0), + memorySize: memoryReturnDataCopy, + valid: true, + } + instructionSet[REVERT] = operation{ + execute: opRevert, + gasCost: gasRevert, + validateStack: vm.MakeStackFunc(2, 0), + memorySize: memoryRevert, + valid: true, + reverts: true, + returns: true, + } + return instructionSet +} + +// NewHomesteadInstructionSet returns the frontier and homestead +// instructions that can be executed during the homestead phase. +func newHomesteadInstructionSet() [256]operation { + instructionSet := newFrontierInstructionSet() + instructionSet[DELEGATECALL] = operation{ + execute: opDelegateCall, + gasCost: gasDelegateCall, + validateStack: vm.MakeStackFunc(6, 1), + memorySize: memoryDelegateCall, + valid: true, + returns: true, + } + return instructionSet +} + +// NewFrontierInstructionSet returns the frontier instructions +// that can be executed during the frontier phase. +func newFrontierInstructionSet() [256]operation { + return [256]operation{ + STOP: { + execute: opStop, + gasCost: constGasFunc(0), + validateStack: vm.MakeStackFunc(0, 0), + halts: true, + valid: true, + }, + ADD: { + execute: opAdd, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + MUL: { + execute: opMul, + gasCost: constGasFunc(GasFastStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + SUB: { + execute: opSub, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + DIV: { + execute: opDiv, + gasCost: constGasFunc(GasFastStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + SDIV: { + execute: opSdiv, + gasCost: constGasFunc(GasFastStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + MOD: { + execute: opMod, + gasCost: constGasFunc(GasFastStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + SMOD: { + execute: opSmod, + gasCost: constGasFunc(GasFastStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + ADDMOD: { + execute: opAddmod, + gasCost: constGasFunc(GasMidStep), + validateStack: vm.MakeStackFunc(3, 1), + valid: true, + }, + MULMOD: { + execute: opMulmod, + gasCost: constGasFunc(GasMidStep), + validateStack: vm.MakeStackFunc(3, 1), + valid: true, + }, + EXP: { + execute: opExp, + gasCost: gasExp, + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + SIGNEXTEND: { + execute: opSignExtend, + gasCost: constGasFunc(GasFastStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + LT: { + execute: opLt, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + GT: { + execute: opGt, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + SLT: { + execute: opSlt, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + SGT: { + execute: opSgt, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + EQ: { + execute: opEq, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + ISZERO: { + execute: opIszero, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(1, 1), + valid: true, + }, + AND: { + execute: opAnd, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + XOR: { + execute: opXor, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + OR: { + execute: opOr, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + NOT: { + execute: opNot, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(1, 1), + valid: true, + }, + BYTE: { + execute: opByte, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(2, 1), + valid: true, + }, + SHA3: { + execute: opSha3, + gasCost: gasSha3, + validateStack: vm.MakeStackFunc(2, 1), + memorySize: memorySha3, + valid: true, + }, + RAND: { + execute: opRand, + gasCost: constGasFunc(params.RandGas), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + ADDRESS: { + execute: opAddress, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + BALANCE: { + execute: opBalance, + gasCost: gasBalance, + validateStack: vm.MakeStackFunc(1, 1), + valid: true, + }, + ORIGIN: { + execute: opOrigin, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + CALLER: { + execute: opCaller, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + CALLVALUE: { + execute: opCallValue, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + CALLDATALOAD: { + execute: opCallDataLoad, + gasCost: constGasFunc(GasFastestStep), + validateStack: vm.MakeStackFunc(1, 1), + valid: true, + }, + CALLDATASIZE: { + execute: opCallDataSize, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + CALLDATACOPY: { + execute: opCallDataCopy, + gasCost: gasCallDataCopy, + validateStack: vm.MakeStackFunc(3, 0), + memorySize: memoryCallDataCopy, + valid: true, + }, + CODESIZE: { + execute: opCodeSize, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + CODECOPY: { + execute: opCodeCopy, + gasCost: gasCodeCopy, + validateStack: vm.MakeStackFunc(3, 0), + memorySize: memoryCodeCopy, + valid: true, + }, + GASPRICE: { + execute: opGasprice, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + EXTCODESIZE: { + execute: opExtCodeSize, + gasCost: gasExtCodeSize, + validateStack: vm.MakeStackFunc(1, 1), + valid: true, + }, + EXTCODECOPY: { + execute: opExtCodeCopy, + gasCost: gasExtCodeCopy, + validateStack: vm.MakeStackFunc(4, 0), + memorySize: memoryExtCodeCopy, + valid: true, + }, + BLOCKHASH: { + execute: opBlockhash, + gasCost: constGasFunc(GasExtStep), + validateStack: vm.MakeStackFunc(1, 1), + valid: true, + }, + COINBASE: { + execute: opCoinbase, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + TIMESTAMP: { + execute: opTimestamp, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + NUMBER: { + execute: opNumber, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + DIFFICULTY: { + execute: opDifficulty, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + GASLIMIT: { + execute: opGasLimit, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + POP: { + execute: opPop, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(1, 0), + valid: true, + }, + MLOAD: { + execute: opMload, + gasCost: gasMLoad, + validateStack: vm.MakeStackFunc(1, 1), + memorySize: memoryMLoad, + valid: true, + }, + MSTORE: { + execute: opMstore, + gasCost: gasMStore, + validateStack: vm.MakeStackFunc(2, 0), + memorySize: memoryMStore, + valid: true, + }, + MSTORE8: { + execute: opMstore8, + gasCost: gasMStore8, + memorySize: memoryMStore8, + validateStack: vm.MakeStackFunc(2, 0), + + valid: true, + }, + SLOAD: { + execute: opSload, + gasCost: gasSLoad, + validateStack: vm.MakeStackFunc(1, 1), + valid: true, + }, + SSTORE: { + execute: opSstore, + gasCost: gasSStore, + validateStack: vm.MakeStackFunc(2, 0), + valid: true, + writes: true, + }, + JUMP: { + execute: opJump, + gasCost: constGasFunc(GasMidStep), + validateStack: vm.MakeStackFunc(1, 0), + jumps: true, + valid: true, + }, + JUMPI: { + execute: opJumpi, + gasCost: constGasFunc(GasSlowStep), + validateStack: vm.MakeStackFunc(2, 0), + jumps: true, + valid: true, + }, + PC: { + execute: opPc, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + MSIZE: { + execute: opMsize, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + GAS: { + execute: opGas, + gasCost: constGasFunc(GasQuickStep), + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + JUMPDEST: { + execute: opJumpdest, + gasCost: constGasFunc(params.JumpdestGas), + validateStack: vm.MakeStackFunc(0, 0), + valid: true, + }, + PUSH1: { + execute: makePush(1, 1), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH2: { + execute: makePush(2, 2), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH3: { + execute: makePush(3, 3), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH4: { + execute: makePush(4, 4), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH5: { + execute: makePush(5, 5), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH6: { + execute: makePush(6, 6), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH7: { + execute: makePush(7, 7), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH8: { + execute: makePush(8, 8), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH9: { + execute: makePush(9, 9), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH10: { + execute: makePush(10, 10), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH11: { + execute: makePush(11, 11), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH12: { + execute: makePush(12, 12), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH13: { + execute: makePush(13, 13), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH14: { + execute: makePush(14, 14), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH15: { + execute: makePush(15, 15), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH16: { + execute: makePush(16, 16), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH17: { + execute: makePush(17, 17), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH18: { + execute: makePush(18, 18), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH19: { + execute: makePush(19, 19), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH20: { + execute: makePush(20, 20), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH21: { + execute: makePush(21, 21), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH22: { + execute: makePush(22, 22), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH23: { + execute: makePush(23, 23), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH24: { + execute: makePush(24, 24), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH25: { + execute: makePush(25, 25), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH26: { + execute: makePush(26, 26), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH27: { + execute: makePush(27, 27), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH28: { + execute: makePush(28, 28), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH29: { + execute: makePush(29, 29), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH30: { + execute: makePush(30, 30), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH31: { + execute: makePush(31, 31), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + PUSH32: { + execute: makePush(32, 32), + gasCost: gasPush, + validateStack: vm.MakeStackFunc(0, 1), + valid: true, + }, + DUP1: { + execute: makeDup(1), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(1), + valid: true, + }, + DUP2: { + execute: makeDup(2), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(2), + valid: true, + }, + DUP3: { + execute: makeDup(3), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(3), + valid: true, + }, + DUP4: { + execute: makeDup(4), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(4), + valid: true, + }, + DUP5: { + execute: makeDup(5), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(5), + valid: true, + }, + DUP6: { + execute: makeDup(6), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(6), + valid: true, + }, + DUP7: { + execute: makeDup(7), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(7), + valid: true, + }, + DUP8: { + execute: makeDup(8), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(8), + valid: true, + }, + DUP9: { + execute: makeDup(9), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(9), + valid: true, + }, + DUP10: { + execute: makeDup(10), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(10), + valid: true, + }, + DUP11: { + execute: makeDup(11), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(11), + valid: true, + }, + DUP12: { + execute: makeDup(12), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(12), + valid: true, + }, + DUP13: { + execute: makeDup(13), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(13), + valid: true, + }, + DUP14: { + execute: makeDup(14), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(14), + valid: true, + }, + DUP15: { + execute: makeDup(15), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(15), + valid: true, + }, + DUP16: { + execute: makeDup(16), + gasCost: gasDup, + validateStack: vm.MakeDupStackFunc(16), + valid: true, + }, + SWAP1: { + execute: makeSwap(1), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(2), + valid: true, + }, + SWAP2: { + execute: makeSwap(2), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(3), + valid: true, + }, + SWAP3: { + execute: makeSwap(3), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(4), + valid: true, + }, + SWAP4: { + execute: makeSwap(4), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(5), + valid: true, + }, + SWAP5: { + execute: makeSwap(5), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(6), + valid: true, + }, + SWAP6: { + execute: makeSwap(6), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(7), + valid: true, + }, + SWAP7: { + execute: makeSwap(7), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(8), + valid: true, + }, + SWAP8: { + execute: makeSwap(8), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(9), + valid: true, + }, + SWAP9: { + execute: makeSwap(9), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(10), + valid: true, + }, + SWAP10: { + execute: makeSwap(10), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(11), + valid: true, + }, + SWAP11: { + execute: makeSwap(11), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(12), + valid: true, + }, + SWAP12: { + execute: makeSwap(12), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(13), + valid: true, + }, + SWAP13: { + execute: makeSwap(13), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(14), + valid: true, + }, + SWAP14: { + execute: makeSwap(14), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(15), + valid: true, + }, + SWAP15: { + execute: makeSwap(15), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(16), + valid: true, + }, + SWAP16: { + execute: makeSwap(16), + gasCost: gasSwap, + validateStack: vm.MakeSwapStackFunc(17), + valid: true, + }, + LOG0: { + execute: makeLog(0), + gasCost: makeGasLog(0), + validateStack: vm.MakeStackFunc(2, 0), + memorySize: memoryLog, + valid: true, + writes: true, + }, + LOG1: { + execute: makeLog(1), + gasCost: makeGasLog(1), + validateStack: vm.MakeStackFunc(3, 0), + memorySize: memoryLog, + valid: true, + writes: true, + }, + LOG2: { + execute: makeLog(2), + gasCost: makeGasLog(2), + validateStack: vm.MakeStackFunc(4, 0), + memorySize: memoryLog, + valid: true, + writes: true, + }, + LOG3: { + execute: makeLog(3), + gasCost: makeGasLog(3), + validateStack: vm.MakeStackFunc(5, 0), + memorySize: memoryLog, + valid: true, + writes: true, + }, + LOG4: { + execute: makeLog(4), + gasCost: makeGasLog(4), + validateStack: vm.MakeStackFunc(6, 0), + memorySize: memoryLog, + valid: true, + writes: true, + }, + CREATE: { + execute: opCreate, + gasCost: gasCreate, + validateStack: vm.MakeStackFunc(3, 1), + memorySize: memoryCreate, + valid: true, + writes: true, + returns: true, + }, + CALL: { + execute: opCall, + gasCost: gasCall, + validateStack: vm.MakeStackFunc(7, 1), + memorySize: memoryCall, + valid: true, + returns: true, + }, + CALLCODE: { + execute: opCallCode, + gasCost: gasCallCode, + validateStack: vm.MakeStackFunc(7, 1), + memorySize: memoryCall, + valid: true, + returns: true, + }, + RETURN: { + execute: opReturn, + gasCost: gasReturn, + validateStack: vm.MakeStackFunc(2, 0), + memorySize: memoryReturn, + halts: true, + valid: true, + }, + SELFDESTRUCT: { + execute: opSuicide, + gasCost: gasSuicide, + validateStack: vm.MakeStackFunc(1, 0), + halts: true, + valid: true, + writes: true, + }, + } +} diff --git a/core/vm/evm/logger.go b/core/vm/evm/logger.go new file mode 100644 index 000000000..f51943b14 --- /dev/null +++ b/core/vm/evm/logger.go @@ -0,0 +1,257 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "encoding/hex" + "fmt" + "io" + "math/big" + "time" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/common/hexutil" + "github.com/dexon-foundation/dexon/common/math" + "github.com/dexon-foundation/dexon/core/types" + "github.com/dexon-foundation/dexon/core/vm" +) + +// Storage represents a contract's storage. +type Storage map[common.Hash]common.Hash + +// Copy duplicates the current storage. +func (s Storage) Copy() Storage { + cpy := make(Storage) + for key, value := range s { + cpy[key] = value + } + + return cpy +} + +// LogConfig are the configuration options for structured logger the EVM +type LogConfig struct { + DisableMemory bool // disable memory capture + DisableStack bool // disable stack capture + DisableStorage bool // disable storage capture + Debug bool // print output during capture end + Limit int // maximum length of output, but zero means unlimited +} + +//go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go + +// StructLog is emitted to the EVM each cycle and lists information about the current internal state +// prior to the execution of the statement. +type StructLog struct { + Pc uint64 `json:"pc"` + Op OpCode `json:"op"` + Gas uint64 `json:"gas"` + GasCost uint64 `json:"gasCost"` + Memory []byte `json:"memory"` + MemorySize int `json:"memSize"` + Stack []*big.Int `json:"stack"` + Storage map[common.Hash]common.Hash `json:"-"` + Depth int `json:"depth"` + RefundCounter uint64 `json:"refund"` + Err error `json:"-"` +} + +// overrides for gencodec +type structLogMarshaling struct { + Stack []*math.HexOrDecimal256 + Gas math.HexOrDecimal64 + GasCost math.HexOrDecimal64 + Memory hexutil.Bytes + OpName string `json:"opName"` // adds call to OpName() in MarshalJSON + ErrorString string `json:"error"` // adds call to ErrorString() in MarshalJSON +} + +// OpName formats the operand name in a human-readable format. +func (s *StructLog) OpName() string { + return s.Op.String() +} + +// ErrorString formats the log's error as a string. +func (s *StructLog) ErrorString() string { + if s.Err != nil { + return s.Err.Error() + } + return "" +} + +// Tracer is used to collect execution traces from an EVM transaction +// execution. CaptureState is called for each step of the VM with the +// current VM state. +// Note that reference types are actual VM data structures; make copies +// if you need to retain them beyond the current call. +type Tracer interface { + CaptureStart(from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error + CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *Contract, depth int, err error) error + CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *Contract, depth int, err error) error + CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error +} + +// StructLogger is an EVM state logger and implements Tracer. +// +// StructLogger can capture state based on the given Log configuration and also keeps +// a track record of modified storage which is used in reporting snapshots of the +// contract their storage. +type StructLogger struct { + cfg LogConfig + + logs []StructLog + changedValues map[common.Address]Storage + output []byte + err error +} + +// NewStructLogger returns a new logger +func NewStructLogger(cfg *LogConfig) *StructLogger { + logger := &StructLogger{ + changedValues: make(map[common.Address]Storage), + } + if cfg != nil { + logger.cfg = *cfg + } + return logger +} + +// CaptureStart implements the Tracer interface to initialize the tracing operation. +func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { + return nil +} + +// CaptureState logs a new structured log message and pushes it out to the environment +// +// CaptureState also tracks SSTORE ops to track dirty values. +func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *Contract, depth int, err error) error { + // check if already accumulated the specified number of logs + if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { + return vm.ErrTraceLimitReached + } + + // initialise new changed values storage container for this contract + // if not present. + if l.changedValues[contract.Address()] == nil { + l.changedValues[contract.Address()] = make(Storage) + } + + // capture SSTORE opcodes and determine the changed value and store + // it in the local storage container. + if op == SSTORE && stack.Len() >= 2 { + var ( + value = common.BigToHash(stack.Data[stack.Len()-2]) + address = common.BigToHash(stack.Data[stack.Len()-1]) + ) + l.changedValues[contract.Address()][address] = value + } + // Copy a snapstot of the current memory state to a new buffer + var mem []byte + if !l.cfg.DisableMemory { + mem = make([]byte, len(memory.Data())) + copy(mem, memory.Data()) + } + // Copy a snapshot of the current stack state to a new buffer + var stck []*big.Int + if !l.cfg.DisableStack { + stck = make([]*big.Int, len(stack.Data)) + for i, item := range stack.Data { + stck[i] = new(big.Int).Set(item) + } + } + // Copy a snapshot of the current storage to a new container + var storage Storage + if !l.cfg.DisableStorage { + storage = l.changedValues[contract.Address()].Copy() + } + // create a new snaptshot of the EVM. + log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err} + + l.logs = append(l.logs, log) + return nil +} + +// CaptureFault implements the Tracer interface to trace an execution fault +// while running an opcode. +func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *Contract, depth int, err error) error { + return nil +} + +// CaptureEnd is called after the call finishes to finalize the tracing. +func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { + l.output = output + l.err = err + if l.cfg.Debug { + fmt.Printf("0x%x\n", output) + if err != nil { + fmt.Printf(" error: %v\n", err) + } + } + return nil +} + +// StructLogs returns the captured log entries. +func (l *StructLogger) StructLogs() []StructLog { return l.logs } + +// Error returns the VM error captured by the trace. +func (l *StructLogger) Error() error { return l.err } + +// Output returns the VM return value captured by the trace. +func (l *StructLogger) Output() []byte { return l.output } + +// WriteTrace writes a formatted trace to the given writer +func WriteTrace(writer io.Writer, logs []StructLog) { + for _, log := range logs { + fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost) + if log.Err != nil { + fmt.Fprintf(writer, " ERROR: %v", log.Err) + } + fmt.Fprintln(writer) + + if len(log.Stack) > 0 { + fmt.Fprintln(writer, "Stack:") + for i := len(log.Stack) - 1; i >= 0; i-- { + fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32)) + } + } + if len(log.Memory) > 0 { + fmt.Fprintln(writer, "Memory:") + fmt.Fprint(writer, hex.Dump(log.Memory)) + } + if len(log.Storage) > 0 { + fmt.Fprintln(writer, "Storage:") + for h, item := range log.Storage { + fmt.Fprintf(writer, "%x: %x\n", h, item) + } + } + fmt.Fprintln(writer) + } +} + +// WriteLogs writes vm logs in a readable format to the given writer +func WriteLogs(writer io.Writer, logs []*types.Log) { + for _, log := range logs { + fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex) + + for i, topic := range log.Topics { + fmt.Fprintf(writer, "%08d %x\n", i, topic) + } + + fmt.Fprint(writer, hex.Dump(log.Data)) + fmt.Fprintln(writer) + } +} diff --git a/core/vm/evm/logger_json.go b/core/vm/evm/logger_json.go new file mode 100644 index 000000000..e96e435b3 --- /dev/null +++ b/core/vm/evm/logger_json.go @@ -0,0 +1,88 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "encoding/json" + "io" + "math/big" + "time" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/common/math" + "github.com/dexon-foundation/dexon/core/vm" +) + +type JSONLogger struct { + encoder *json.Encoder + cfg *LogConfig +} + +// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects +// into the provided stream. +func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger { + l := &JSONLogger{json.NewEncoder(writer), cfg} + if l.cfg == nil { + l.cfg = &LogConfig{} + } + return l +} + +func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error { + return nil +} + +// CaptureState outputs state information on the logger. +func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *Contract, depth int, err error) error { + log := StructLog{ + Pc: pc, + Op: op, + Gas: gas, + GasCost: cost, + MemorySize: memory.Len(), + Storage: nil, + Depth: depth, + RefundCounter: env.StateDB.GetRefund(), + Err: err, + } + if !l.cfg.DisableMemory { + log.Memory = memory.Data() + } + if !l.cfg.DisableStack { + log.Stack = stack.Data + } + return l.encoder.Encode(log) +} + +// CaptureFault outputs state information on the logger. +func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *Contract, depth int, err error) error { + return nil +} + +// CaptureEnd is triggered at end of execution. +func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error { + type endLog struct { + Output string `json:"output"` + GasUsed math.HexOrDecimal64 `json:"gasUsed"` + Time time.Duration `json:"time"` + Err string `json:"error,omitempty"` + } + if err != nil { + return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, err.Error()}) + } + return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""}) +} diff --git a/core/vm/evm/logger_test.go b/core/vm/evm/logger_test.go new file mode 100644 index 000000000..a8e0e9044 --- /dev/null +++ b/core/vm/evm/logger_test.go @@ -0,0 +1,71 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "math/big" + "testing" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/core/state" + "github.com/dexon-foundation/dexon/core/vm" + "github.com/dexon-foundation/dexon/params" +) + +type dummyContractRef struct { + calledForEach bool +} + +func (dummyContractRef) ReturnGas(*big.Int) {} +func (dummyContractRef) Address() common.Address { return common.Address{} } +func (dummyContractRef) Value() *big.Int { return new(big.Int) } +func (dummyContractRef) SetCode(common.Hash, []byte) {} +func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) { + d.calledForEach = true +} +func (d *dummyContractRef) SubBalance(amount *big.Int) {} +func (d *dummyContractRef) AddBalance(amount *big.Int) {} +func (d *dummyContractRef) SetBalance(*big.Int) {} +func (d *dummyContractRef) SetNonce(uint64) {} +func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) } + +type dummyStatedb struct { + state.StateDB +} + +func (*dummyStatedb) GetRefund() uint64 { return 1337 } + +func TestStoreCapture(t *testing.T) { + var ( + env = NewEVM(Context{}, &dummyStatedb{}, params.TestChainConfig, Config{}) + logger = NewStructLogger(nil) + mem = vm.NewMemory() + stack = NewStack() + contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) + ) + stack.Push(big.NewInt(1)) + stack.Push(big.NewInt(0)) + var index common.Hash + logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil) + if len(logger.changedValues[contract.Address()]) == 0 { + t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()])) + } + exp := common.BigToHash(big.NewInt(1)) + if logger.changedValues[contract.Address()][index] != exp { + t.Errorf("expected %x, got %x", exp, logger.changedValues[contract.Address()][index]) + } +} diff --git a/core/vm/evm/memory_table.go b/core/vm/evm/memory_table.go new file mode 100644 index 000000000..c27e95a4a --- /dev/null +++ b/core/vm/evm/memory_table.go @@ -0,0 +1,98 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "math/big" + + "github.com/dexon-foundation/dexon/common/math" + "github.com/dexon-foundation/dexon/core/vm" +) + +func memorySha3(stack *vm.Stack) *big.Int { + return vm.CalcMemSize(stack.Back(0), stack.Back(1)) +} + +func memoryCallDataCopy(stack *vm.Stack) *big.Int { + return vm.CalcMemSize(stack.Back(0), stack.Back(2)) +} + +func memoryReturnDataCopy(stack *vm.Stack) *big.Int { + return vm.CalcMemSize(stack.Back(0), stack.Back(2)) +} + +func memoryCodeCopy(stack *vm.Stack) *big.Int { + return vm.CalcMemSize(stack.Back(0), stack.Back(2)) +} + +func memoryExtCodeCopy(stack *vm.Stack) *big.Int { + return vm.CalcMemSize(stack.Back(1), stack.Back(3)) +} + +func memoryMLoad(stack *vm.Stack) *big.Int { + return vm.CalcMemSize(stack.Back(0), big32) +} + +func memoryMStore8(stack *vm.Stack) *big.Int { + return vm.CalcMemSize(stack.Back(0), big1) +} + +func memoryMStore(stack *vm.Stack) *big.Int { + return vm.CalcMemSize(stack.Back(0), big32) +} + +func memoryCreate(stack *vm.Stack) *big.Int { + return vm.CalcMemSize(stack.Back(1), stack.Back(2)) +} + +func memoryCreate2(stack *vm.Stack) *big.Int { + return vm.CalcMemSize(stack.Back(1), stack.Back(2)) +} + +func memoryCall(stack *vm.Stack) *big.Int { + x := vm.CalcMemSize(stack.Back(5), stack.Back(6)) + y := vm.CalcMemSize(stack.Back(3), stack.Back(4)) + + return math.BigMax(x, y) +} + +func memoryDelegateCall(stack *vm.Stack) *big.Int { + x := vm.CalcMemSize(stack.Back(4), stack.Back(5)) + y := vm.CalcMemSize(stack.Back(2), stack.Back(3)) + + return math.BigMax(x, y) +} + +func memoryStaticCall(stack *vm.Stack) *big.Int { + x := vm.CalcMemSize(stack.Back(4), stack.Back(5)) + y := vm.CalcMemSize(stack.Back(2), stack.Back(3)) + + return math.BigMax(x, y) +} + +func memoryReturn(stack *vm.Stack) *big.Int { + return vm.CalcMemSize(stack.Back(0), stack.Back(1)) +} + +func memoryRevert(stack *vm.Stack) *big.Int { + return vm.CalcMemSize(stack.Back(0), stack.Back(1)) +} + +func memoryLog(stack *vm.Stack) *big.Int { + mSize, mStart := stack.Back(1), stack.Back(0) + return vm.CalcMemSize(mStart, mSize) +} diff --git a/core/vm/evm/opcodes.go b/core/vm/evm/opcodes.go new file mode 100644 index 000000000..22ff00c7d --- /dev/null +++ b/core/vm/evm/opcodes.go @@ -0,0 +1,543 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package evm + +import ( + "fmt" +) + +// OpCode is an EVM opcode +type OpCode byte + +// IsPush specifies if an opcode is a PUSH opcode. +func (op OpCode) IsPush() bool { + 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: + return true + } + return false +} + +// IsStaticJump specifies if an opcode is JUMP. +func (op OpCode) IsStaticJump() bool { + return op == JUMP +} + +// 0x0 range - arithmetic ops. +const ( + STOP OpCode = iota + ADD + MUL + SUB + DIV + SDIV + MOD + SMOD + ADDMOD + MULMOD + EXP + SIGNEXTEND +) + +// 0x10 range - comparison ops. +const ( + LT OpCode = iota + 0x10 + GT + SLT + SGT + EQ + ISZERO + AND + OR + XOR + NOT + BYTE + SHL + SHR + SAR + + SHA3 = 0x20 + RAND = 0x2f +) + +// 0x30 range - closure state. +const ( + ADDRESS OpCode = 0x30 + iota + BALANCE + ORIGIN + CALLER + CALLVALUE + CALLDATALOAD + CALLDATASIZE + CALLDATACOPY + CODESIZE + CODECOPY + GASPRICE + EXTCODESIZE + EXTCODECOPY + RETURNDATASIZE + RETURNDATACOPY + EXTCODEHASH +) + +// 0x40 range - block operations. +const ( + BLOCKHASH OpCode = 0x40 + iota + COINBASE + TIMESTAMP + NUMBER + DIFFICULTY + GASLIMIT +) + +// 0x50 range - 'storage' and execution. +const ( + POP OpCode = 0x50 + iota + MLOAD + MSTORE + MSTORE8 + SLOAD + SSTORE + JUMP + JUMPI + PC + MSIZE + GAS + JUMPDEST +) + +// 0x60 range. +const ( + 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 +) + +// 0xa0 range - logging ops. +const ( + LOG0 OpCode = 0xa0 + iota + LOG1 + LOG2 + LOG3 + LOG4 +) + +// unofficial opcodes used for parsing. +const ( + PUSH OpCode = 0xb0 + iota + DUP + SWAP +) + +// 0xf0 range - closures. +const ( + CREATE OpCode = 0xf0 + iota + CALL + CALLCODE + RETURN + DELEGATECALL + CREATE2 + STATICCALL = 0xfa + + REVERT = 0xfd + SELFDESTRUCT = 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", + SHL: "SHL", + SHR: "SHR", + SAR: "SAR", + ADDMOD: "ADDMOD", + MULMOD: "MULMOD", + + // 0x20 range - crypto. + SHA3: "SHA3", + RAND: "RAND", + + // 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: "GASPRICE", + EXTCODESIZE: "EXTCODESIZE", + EXTCODECOPY: "EXTCODECOPY", + RETURNDATASIZE: "RETURNDATASIZE", + RETURNDATACOPY: "RETURNDATACOPY", + EXTCODEHASH: "EXTCODEHASH", + + // 0x40 range - block operations. + BLOCKHASH: "BLOCKHASH", + COINBASE: "COINBASE", + TIMESTAMP: "TIMESTAMP", + NUMBER: "NUMBER", + DIFFICULTY: "DIFFICULTY", + GASLIMIT: "GASLIMIT", + + // 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", + DELEGATECALL: "DELEGATECALL", + CREATE2: "CREATE2", + STATICCALL: "STATICCALL", + REVERT: "REVERT", + SELFDESTRUCT: "SELFDESTRUCT", + + PUSH: "PUSH", + DUP: "DUP", + SWAP: "SWAP", +} + +func (op OpCode) String() string { + str := opCodeToString[op] + if len(str) == 0 { + return fmt.Sprintf("Missing opcode 0x%x", int(op)) + } + + return str +} + +var stringToOp = map[string]OpCode{ + "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, + "AND": AND, + "OR": OR, + "XOR": XOR, + "BYTE": BYTE, + "SHL": SHL, + "SHR": SHR, + "SAR": SAR, + "ADDMOD": ADDMOD, + "MULMOD": MULMOD, + "SHA3": SHA3, + "RAND": RAND, + "ADDRESS": ADDRESS, + "BALANCE": BALANCE, + "ORIGIN": ORIGIN, + "CALLER": CALLER, + "CALLVALUE": CALLVALUE, + "CALLDATALOAD": CALLDATALOAD, + "CALLDATASIZE": CALLDATASIZE, + "CALLDATACOPY": CALLDATACOPY, + "DELEGATECALL": DELEGATECALL, + "STATICCALL": STATICCALL, + "CODESIZE": CODESIZE, + "CODECOPY": CODECOPY, + "GASPRICE": GASPRICE, + "EXTCODESIZE": EXTCODESIZE, + "EXTCODECOPY": EXTCODECOPY, + "RETURNDATASIZE": RETURNDATASIZE, + "RETURNDATACOPY": RETURNDATACOPY, + "EXTCODEHASH": EXTCODEHASH, + "BLOCKHASH": BLOCKHASH, + "COINBASE": COINBASE, + "TIMESTAMP": TIMESTAMP, + "NUMBER": NUMBER, + "DIFFICULTY": DIFFICULTY, + "GASLIMIT": GASLIMIT, + "POP": POP, + "MLOAD": MLOAD, + "MSTORE": MSTORE, + "MSTORE8": MSTORE8, + "SLOAD": SLOAD, + "SSTORE": SSTORE, + "JUMP": JUMP, + "JUMPI": JUMPI, + "PC": PC, + "MSIZE": MSIZE, + "GAS": GAS, + "JUMPDEST": JUMPDEST, + "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, + "CREATE": CREATE, + "CREATE2": CREATE2, + "CALL": CALL, + "RETURN": RETURN, + "CALLCODE": CALLCODE, + "REVERT": REVERT, + "SELFDESTRUCT": SELFDESTRUCT, +} + +// StringToOp finds the opcode whose name is stored in `str`. +func StringToOp(str string) OpCode { + return stringToOp[str] +} diff --git a/core/vm/evm/runtime/doc.go b/core/vm/evm/runtime/doc.go new file mode 100644 index 000000000..a3b464a7d --- /dev/null +++ b/core/vm/evm/runtime/doc.go @@ -0,0 +1,18 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// Package runtime provides a basic execution model for executing EVM code. +package runtime diff --git a/core/vm/evm/runtime/env.go b/core/vm/evm/runtime/env.go new file mode 100644 index 000000000..c46580140 --- /dev/null +++ b/core/vm/evm/runtime/env.go @@ -0,0 +1,41 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package runtime + +import ( + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/core" + vm "github.com/dexon-foundation/dexon/core/vm/evm" +) + +func NewEnv(cfg *Config) *vm.EVM { + context := vm.Context{ + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + GetHash: func(uint64) common.Hash { return common.Hash{} }, + + Origin: cfg.Origin, + Coinbase: cfg.Coinbase, + BlockNumber: cfg.BlockNumber, + Time: cfg.Time, + Difficulty: cfg.Difficulty, + GasLimit: cfg.GasLimit, + GasPrice: cfg.GasPrice, + } + + return vm.NewEVM(context, cfg.State, cfg.ChainConfig, cfg.EVMConfig) +} diff --git a/core/vm/evm/runtime/fuzz.go b/core/vm/evm/runtime/fuzz.go new file mode 100644 index 000000000..cb9ff08b5 --- /dev/null +++ b/core/vm/evm/runtime/fuzz.go @@ -0,0 +1,36 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// +build gofuzz + +package runtime + +// Fuzz is the basic entry point for the go-fuzz tool +// +// This returns 1 for valid parsable/runable code, 0 +// for invalid opcode. +func Fuzz(input []byte) int { + _, _, err := Execute(input, input, &Config{ + GasLimit: 3000000, + }) + + // invalid opcode + if err != nil && len(err.Error()) > 6 && string(err.Error()[:7]) == "invalid" { + return 0 + } + + return 1 +} diff --git a/core/vm/evm/runtime/runtime.go b/core/vm/evm/runtime/runtime.go new file mode 100644 index 000000000..93e6322f4 --- /dev/null +++ b/core/vm/evm/runtime/runtime.go @@ -0,0 +1,170 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package runtime + +import ( + "math" + "math/big" + "time" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/core/state" + vm "github.com/dexon-foundation/dexon/core/vm/evm" + "github.com/dexon-foundation/dexon/crypto" + "github.com/dexon-foundation/dexon/ethdb" + "github.com/dexon-foundation/dexon/params" +) + +// Config is a basic type specifying certain configuration flags for running +// the EVM. +type Config struct { + ChainConfig *params.ChainConfig + Difficulty *big.Int + Origin common.Address + Coinbase common.Address + BlockNumber *big.Int + Time *big.Int + GasLimit uint64 + GasPrice *big.Int + Value *big.Int + Debug bool + EVMConfig vm.Config + + State *state.StateDB + GetHashFn func(n uint64) common.Hash +} + +// sets defaults on the config +func setDefaults(cfg *Config) { + if cfg.ChainConfig == nil { + cfg.ChainConfig = ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + HomesteadBlock: new(big.Int), + DAOForkBlock: new(big.Int), + DAOForkSupport: false, + EIP150Block: new(big.Int), + EIP155Block: new(big.Int), + EIP158Block: new(big.Int), + } + } + + if cfg.Difficulty == nil { + cfg.Difficulty = new(big.Int) + } + if cfg.Time == nil { + cfg.Time = big.NewInt(time.Now().Unix()) + } + if cfg.GasLimit == 0 { + cfg.GasLimit = math.MaxUint64 + } + if cfg.GasPrice == nil { + cfg.GasPrice = new(big.Int) + } + if cfg.Value == nil { + cfg.Value = new(big.Int) + } + if cfg.BlockNumber == nil { + cfg.BlockNumber = new(big.Int) + } + if cfg.GetHashFn == nil { + cfg.GetHashFn = func(n uint64) common.Hash { + return common.BytesToHash(crypto.Keccak256([]byte(new(big.Int).SetUint64(n).String()))) + } + } +} + +// Execute executes the code using the input as call data during the execution. +// It returns the EVM's return value, the new state and an error if it failed. +// +// Executes sets up a in memory, temporarily, environment for the execution of +// the given code. It makes sure that it's restored to it's original state afterwards. +func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { + if cfg == nil { + cfg = new(Config) + } + setDefaults(cfg) + + if cfg.State == nil { + cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + } + var ( + address = common.BytesToAddress([]byte("contract")) + vmenv = NewEnv(cfg) + sender = vm.AccountRef(cfg.Origin) + ) + cfg.State.CreateAccount(address) + // set the receiver's (the executing contract) code for execution. + cfg.State.SetCode(address, code) + // Call the code with the given configuration. + ret, _, err := vmenv.Call( + sender, + common.BytesToAddress([]byte("contract")), + input, + cfg.GasLimit, + cfg.Value, + ) + + return ret, cfg.State, err +} + +// Create executes the code using the EVM create method +func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { + if cfg == nil { + cfg = new(Config) + } + setDefaults(cfg) + + if cfg.State == nil { + cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + } + var ( + vmenv = NewEnv(cfg) + sender = vm.AccountRef(cfg.Origin) + ) + + // Call the code with the given configuration. + code, address, leftOverGas, err := vmenv.Create( + sender, + input, + cfg.GasLimit, + cfg.Value, + ) + return code, address, leftOverGas, err +} + +// Call executes the code given by the contract's address. It will return the +// EVM's return value or an error if it failed. +// +// Call, unlike Execute, requires a config and also requires the State field to +// be set. +func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, error) { + setDefaults(cfg) + + vmenv := NewEnv(cfg) + + sender := cfg.State.GetOrNewStateObject(cfg.Origin) + // Call the code with the given configuration. + ret, leftOverGas, err := vmenv.Call( + sender, + address, + input, + cfg.GasLimit, + cfg.Value, + ) + + return ret, leftOverGas, err +} diff --git a/core/vm/evm/runtime/runtime_example_test.go b/core/vm/evm/runtime/runtime_example_test.go new file mode 100644 index 000000000..c6050a771 --- /dev/null +++ b/core/vm/evm/runtime/runtime_example_test.go @@ -0,0 +1,34 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package runtime_test + +import ( + "fmt" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/core/vm/evm/runtime" +) + +func ExampleExecute() { + ret, _, err := runtime.Execute(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00"), nil, nil) + if err != nil { + fmt.Println(err) + } + fmt.Println(ret) + // Output: + // [96 96 96 64 82 96 8 86 91 0] +} diff --git a/core/vm/evm/runtime/runtime_test.go b/core/vm/evm/runtime/runtime_test.go new file mode 100644 index 000000000..43b8da325 --- /dev/null +++ b/core/vm/evm/runtime/runtime_test.go @@ -0,0 +1,205 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package runtime + +import ( + "math/big" + "strings" + "testing" + + "github.com/dexon-foundation/dexon/accounts/abi" + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/core/state" + vm "github.com/dexon-foundation/dexon/core/vm/evm" + "github.com/dexon-foundation/dexon/ethdb" + "github.com/dexon-foundation/dexon/params" +) + +func TestDefaults(t *testing.T) { + cfg := new(Config) + setDefaults(cfg) + + if cfg.Difficulty == nil { + t.Error("expected difficulty to be non nil") + } + + if cfg.Time == nil { + t.Error("expected time to be non nil") + } + if cfg.GasLimit == 0 { + t.Error("didn't expect gaslimit to be zero") + } + if cfg.GasPrice == nil { + t.Error("expected time to be non nil") + } + if cfg.Value == nil { + t.Error("expected time to be non nil") + } + if cfg.GetHashFn == nil { + t.Error("expected time to be non nil") + } + if cfg.BlockNumber == nil { + t.Error("expected block number to be non nil") + } +} + +func TestEVM(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Fatalf("crashed with: %v", r) + } + }() + + Execute([]byte{ + byte(vm.DIFFICULTY), + byte(vm.TIMESTAMP), + byte(vm.GASLIMIT), + byte(vm.PUSH1), + byte(vm.ORIGIN), + byte(vm.BLOCKHASH), + byte(vm.COINBASE), + }, nil, nil) +} + +func TestExecute(t *testing.T) { + ret, _, err := Execute([]byte{ + byte(vm.PUSH1), 10, + byte(vm.PUSH1), 0, + byte(vm.MSTORE), + byte(vm.PUSH1), 32, + byte(vm.PUSH1), 0, + byte(vm.RETURN), + }, nil, nil) + if err != nil { + t.Fatal("didn't expect error", err) + } + + num := new(big.Int).SetBytes(ret) + if num.Cmp(big.NewInt(10)) != 0 { + t.Error("Expected 10, got", num) + } +} + +func TestCall(t *testing.T) { + state, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + address := common.HexToAddress("0x0a") + state.SetCode(address, []byte{ + byte(vm.PUSH1), 10, + byte(vm.PUSH1), 0, + byte(vm.MSTORE), + byte(vm.PUSH1), 32, + byte(vm.PUSH1), 0, + byte(vm.RETURN), + }) + + ret, _, err := Call(address, nil, &Config{State: state}) + if err != nil { + t.Fatal("didn't expect error", err) + } + + num := new(big.Int).SetBytes(ret) + if num.Cmp(big.NewInt(10)) != 0 { + t.Error("Expected 10, got", num) + } +} + +func BenchmarkCall(b *testing.B) { + var definition = `[{"constant":true,"inputs":[],"name":"seller","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"abort","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"refund","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"buyer","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmReceived","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmPurchase","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"},{"anonymous":false,"inputs":[],"name":"Aborted","type":"event"},{"anonymous":false,"inputs":[],"name":"PurchaseConfirmed","type":"event"},{"anonymous":false,"inputs":[],"name":"ItemReceived","type":"event"},{"anonymous":false,"inputs":[],"name":"Refunded","type":"event"}]` + + var code = common.Hex2Bytes("6060604052361561006c5760e060020a600035046308551a53811461007457806335a063b4146100865780633fa4f245146100a6578063590e1ae3146100af5780637150d8ae146100cf57806373fac6f0146100e1578063c19d93fb146100fe578063d696069714610112575b610131610002565b610133600154600160a060020a031681565b610131600154600160a060020a0390811633919091161461015057610002565b61014660005481565b610131600154600160a060020a039081163391909116146102d557610002565b610133600254600160a060020a031681565b610131600254600160a060020a0333811691161461023757610002565b61014660025460ff60a060020a9091041681565b61013160025460009060ff60a060020a9091041681146101cc57610002565b005b600160a060020a03166060908152602090f35b6060908152602090f35b60025460009060a060020a900460ff16811461016b57610002565b600154600160a060020a03908116908290301631606082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f72c874aeff0b183a56e2b79c71b46e1aed4dee5e09862134b8821ba2fddbf8bf9250a150565b80546002023414806101dd57610002565b6002805460a060020a60ff021973ffffffffffffffffffffffffffffffffffffffff1990911633171660a060020a1790557fd5d55c8a68912e9a110618df8d5e2e83b8d83211c57a8ddd1203df92885dc881826060a15050565b60025460019060a060020a900460ff16811461025257610002565b60025460008054600160a060020a0390921691606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517fe89152acd703c9d8c7d28829d443260b411454d45394e7995815140c8cbcbcf79250a150565b60025460019060a060020a900460ff1681146102f057610002565b6002805460008054600160a060020a0390921692909102606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f8616bbbbad963e4e65b1366f1d75dfb63f9e9704bbbf91fb01bec70849906cf79250a15056") + + abi, err := abi.JSON(strings.NewReader(definition)) + if err != nil { + b.Fatal(err) + } + + cpurchase, err := abi.Pack("confirmPurchase") + if err != nil { + b.Fatal(err) + } + creceived, err := abi.Pack("confirmReceived") + if err != nil { + b.Fatal(err) + } + refund, err := abi.Pack("refund") + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + for j := 0; j < 400; j++ { + Execute(code, cpurchase, nil) + Execute(code, creceived, nil) + Execute(code, refund, nil) + } + } +} +func benchmarkEVM_Create(bench *testing.B, code string) { + var ( + statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) + sender = common.BytesToAddress([]byte("sender")) + receiver = common.BytesToAddress([]byte("receiver")) + ) + + statedb.CreateAccount(sender) + statedb.SetCode(receiver, common.FromHex(code)) + runtimeConfig := Config{ + Origin: sender, + State: statedb, + GasLimit: 10000000, + Difficulty: big.NewInt(0x200000), + Time: new(big.Int).SetUint64(0), + Coinbase: common.Address{}, + BlockNumber: new(big.Int).SetUint64(1), + ChainConfig: ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + HomesteadBlock: new(big.Int), + ByzantiumBlock: new(big.Int), + ConstantinopleBlock: new(big.Int), + DAOForkBlock: new(big.Int), + DAOForkSupport: false, + EIP150Block: new(big.Int), + EIP155Block: new(big.Int), + EIP158Block: new(big.Int), + }, + EVMConfig: vm.Config{}, + } + // Warm up the intpools and stuff + bench.ResetTimer() + for i := 0; i < bench.N; i++ { + Call(receiver, []byte{}, &runtimeConfig) + } + bench.StopTimer() +} + +func BenchmarkEVM_CREATE_500(bench *testing.B) { + // initcode size 500K, repeatedly calls CREATE and then modifies the mem contents + benchmarkEVM_Create(bench, "5b6207a120600080f0600152600056") +} +func BenchmarkEVM_CREATE2_500(bench *testing.B) { + // initcode size 500K, repeatedly calls CREATE2 and then modifies the mem contents + benchmarkEVM_Create(bench, "5b586207a120600080f5600152600056") +} +func BenchmarkEVM_CREATE_1200(bench *testing.B) { + // initcode size 1200K, repeatedly calls CREATE and then modifies the mem contents + benchmarkEVM_Create(bench, "5b62124f80600080f0600152600056") +} +func BenchmarkEVM_CREATE2_1200(bench *testing.B) { + // initcode size 1200K, repeatedly calls CREATE2 and then modifies the mem contents + benchmarkEVM_Create(bench, "5b5862124f80600080f5600152600056") +} diff --git a/core/vm/evm/stack.go b/core/vm/evm/stack.go new file mode 100644 index 000000000..4545201ce --- /dev/null +++ b/core/vm/evm/stack.go @@ -0,0 +1,24 @@ +package evm + +import ( + "math/big" + "sync" + + "github.com/dexon-foundation/dexon/core/vm" +) + +var stackPool = sync.Pool{ + New: func() interface{} { + return &vm.Stack{Data: make([]*big.Int, 0, 1024)} + }, +} + +func NewStack() *vm.Stack { + stack := stackPool.Get().(*vm.Stack) + stack.Data = stack.Data[:0] + return stack +} + +func Recyclestack(stack *vm.Stack) { + stackPool.Put(stack) +} |