From 368844ad2a28ec07848e3c0169cf2b83b579a2e8 Mon Sep 17 00:00:00 2001 From: Aaron Buchwald Date: Mon, 23 Nov 2020 20:46:35 -0500 Subject: Add native asset precompiled contracts for apricot release --- core/vm/contracts.go | 86 +++++++++++----------- core/vm/contracts_stateful.go | 165 ++++++++++++++++++++++++++++++++++++++++++ core/vm/evm.go | 22 +++--- core/vm/interpreter.go | 12 +++ core/vm/jump_table.go | 11 +++ core/vm/opcodes.go | 1 + 6 files changed, 245 insertions(+), 52 deletions(-) create mode 100644 core/vm/contracts_stateful.go (limited to 'core') diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 9827bac..6fc6334 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -44,61 +44,61 @@ type PrecompiledContract interface { // 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{}, +var PrecompiledContractsHomestead = map[common.Address]StatefulPrecompiledContract{ + common.BytesToAddress([]byte{1}): newWrappedPrecompiledContract(&ecrecover{}), + common.BytesToAddress([]byte{2}): newWrappedPrecompiledContract(&sha256hash{}), + common.BytesToAddress([]byte{3}): newWrappedPrecompiledContract(&ripemd160hash{}), + common.BytesToAddress([]byte{4}): newWrappedPrecompiledContract(&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}): &bn256AddByzantium{}, - common.BytesToAddress([]byte{7}): &bn256ScalarMulByzantium{}, - common.BytesToAddress([]byte{8}): &bn256PairingByzantium{}, +var PrecompiledContractsByzantium = map[common.Address]StatefulPrecompiledContract{ + common.BytesToAddress([]byte{1}): newWrappedPrecompiledContract(&ecrecover{}), + common.BytesToAddress([]byte{2}): newWrappedPrecompiledContract(&sha256hash{}), + common.BytesToAddress([]byte{3}): newWrappedPrecompiledContract(&ripemd160hash{}), + common.BytesToAddress([]byte{4}): newWrappedPrecompiledContract(&dataCopy{}), + common.BytesToAddress([]byte{5}): newWrappedPrecompiledContract(&bigModExp{}), + common.BytesToAddress([]byte{6}): newWrappedPrecompiledContract(&bn256AddByzantium{}), + common.BytesToAddress([]byte{7}): newWrappedPrecompiledContract(&bn256ScalarMulByzantium{}), + common.BytesToAddress([]byte{8}): newWrappedPrecompiledContract(&bn256PairingByzantium{}), } // PrecompiledContractsIstanbul contains the default set of pre-compiled Ethereum // contracts used in the Istanbul release. -var PrecompiledContractsIstanbul = 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}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{9}): &blake2F{}, +var PrecompiledContractsIstanbul = map[common.Address]StatefulPrecompiledContract{ + common.BytesToAddress([]byte{1}): newWrappedPrecompiledContract(&ecrecover{}), + common.BytesToAddress([]byte{2}): newWrappedPrecompiledContract(&sha256hash{}), + common.BytesToAddress([]byte{3}): newWrappedPrecompiledContract(&ripemd160hash{}), + common.BytesToAddress([]byte{4}): newWrappedPrecompiledContract(&dataCopy{}), + common.BytesToAddress([]byte{5}): newWrappedPrecompiledContract(&bigModExp{}), + common.BytesToAddress([]byte{6}): newWrappedPrecompiledContract(&bn256AddIstanbul{}), + common.BytesToAddress([]byte{7}): newWrappedPrecompiledContract(&bn256ScalarMulIstanbul{}), + common.BytesToAddress([]byte{8}): newWrappedPrecompiledContract(&bn256PairingIstanbul{}), + common.BytesToAddress([]byte{9}): newWrappedPrecompiledContract(&blake2F{}), } // PrecompiledContractsYoloV1 contains the default set of pre-compiled Ethereum // contracts used in the Yolo v1 test release. -var PrecompiledContractsYoloV1 = 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}): &bn256AddIstanbul{}, - common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, - common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, - common.BytesToAddress([]byte{9}): &blake2F{}, - common.BytesToAddress([]byte{10}): &bls12381G1Add{}, - common.BytesToAddress([]byte{11}): &bls12381G1Mul{}, - common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{}, - common.BytesToAddress([]byte{13}): &bls12381G2Add{}, - common.BytesToAddress([]byte{14}): &bls12381G2Mul{}, - common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{}, - common.BytesToAddress([]byte{16}): &bls12381Pairing{}, - common.BytesToAddress([]byte{17}): &bls12381MapG1{}, - common.BytesToAddress([]byte{18}): &bls12381MapG2{}, +var PrecompiledContractsYoloV1 = map[common.Address]StatefulPrecompiledContract{ + common.BytesToAddress([]byte{1}): newWrappedPrecompiledContract(&ecrecover{}), + common.BytesToAddress([]byte{2}): newWrappedPrecompiledContract(&sha256hash{}), + common.BytesToAddress([]byte{3}): newWrappedPrecompiledContract(&ripemd160hash{}), + common.BytesToAddress([]byte{4}): newWrappedPrecompiledContract(&dataCopy{}), + common.BytesToAddress([]byte{5}): newWrappedPrecompiledContract(&bigModExp{}), + common.BytesToAddress([]byte{6}): newWrappedPrecompiledContract(&bn256AddIstanbul{}), + common.BytesToAddress([]byte{7}): newWrappedPrecompiledContract(&bn256ScalarMulIstanbul{}), + common.BytesToAddress([]byte{8}): newWrappedPrecompiledContract(&bn256PairingIstanbul{}), + common.BytesToAddress([]byte{9}): newWrappedPrecompiledContract(&blake2F{}), + common.BytesToAddress([]byte{10}): newWrappedPrecompiledContract(&bls12381G1Add{}), + common.BytesToAddress([]byte{11}): newWrappedPrecompiledContract(&bls12381G1Mul{}), + common.BytesToAddress([]byte{12}): newWrappedPrecompiledContract(&bls12381G1MultiExp{}), + common.BytesToAddress([]byte{13}): newWrappedPrecompiledContract(&bls12381G2Add{}), + common.BytesToAddress([]byte{14}): newWrappedPrecompiledContract(&bls12381G2Mul{}), + common.BytesToAddress([]byte{15}): newWrappedPrecompiledContract(&bls12381G2MultiExp{}), + common.BytesToAddress([]byte{16}): newWrappedPrecompiledContract(&bls12381Pairing{}), + common.BytesToAddress([]byte{17}): newWrappedPrecompiledContract(&bls12381MapG1{}), + common.BytesToAddress([]byte{18}): newWrappedPrecompiledContract(&bls12381MapG2{}), } // RunPrecompiledContract runs and evaluates the output of a precompiled contract. diff --git a/core/vm/contracts_stateful.go b/core/vm/contracts_stateful.go new file mode 100644 index 0000000..3ef7a9e --- /dev/null +++ b/core/vm/contracts_stateful.go @@ -0,0 +1,165 @@ +package vm + +import ( + "errors" + "fmt" + "math/big" + + "github.com/ava-labs/coreth/params" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/holiman/uint256" +) + +// PrecompiledContractsApricot contains the default set of pre-compiled Ethereum +// contracts used in the Istanbul release and the stateful precompiled contracts +// added for the Avalanche Apricot release. +// Apricot is incompatible with the YoloV1 Release since it does not include the +// BLS12-381 Curve Operations added to the set of precompiled contracts +var PrecompiledContractsApricot = map[common.Address]StatefulPrecompiledContract{ + common.BytesToAddress([]byte{1}): newWrappedPrecompiledContract(&ecrecover{}), + common.BytesToAddress([]byte{2}): newWrappedPrecompiledContract(&sha256hash{}), + common.BytesToAddress([]byte{3}): newWrappedPrecompiledContract(&ripemd160hash{}), + common.BytesToAddress([]byte{4}): newWrappedPrecompiledContract(&dataCopy{}), + common.BytesToAddress([]byte{5}): newWrappedPrecompiledContract(&bigModExp{}), + common.BytesToAddress([]byte{6}): newWrappedPrecompiledContract(&bn256AddIstanbul{}), + common.BytesToAddress([]byte{7}): newWrappedPrecompiledContract(&bn256ScalarMulIstanbul{}), + common.BytesToAddress([]byte{8}): newWrappedPrecompiledContract(&bn256PairingIstanbul{}), + common.BytesToAddress([]byte{9}): newWrappedPrecompiledContract(&blake2F{}), + common.HexToAddress("0x0100000000000000000000000000000000000000"): &deprecatedContract{msg: "hardcoded genesis contract has been deprecated"}, + common.HexToAddress("0xffffffffffffffffffffffffffffffffffffff"): &nativeAssetBalance{gasCost: params.AssetBalanceApricot}, + common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffe"): &nativeAssetCall{gasCost: params.AssetCallApricot}, +} + +// StatefulPrecompiledContract is the interface for executing a precompiled contract +// This wraps the PrecompiledContracts native to Ethereum and allows adding in stateful +// precompiled contracts to support native Avalanche asset transfers. +type StatefulPrecompiledContract interface { + // Run executes a precompiled contract in the current state + // assumes that it has already been verified that [caller] can + // transfer [value]. + Run(evm *EVM, caller ContractRef, addr common.Address, value *big.Int, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) +} + +// wrappedPrecompiledContract implements StatefulPrecompiledContract by wrapping stateless native precompiled contracts +// in Ethereum. +type wrappedPrecompiledContract struct { + p PrecompiledContract +} + +func newWrappedPrecompiledContract(p PrecompiledContract) StatefulPrecompiledContract { + return &wrappedPrecompiledContract{p: p} +} + +// Run ... +func (w *wrappedPrecompiledContract) Run(evm *EVM, caller ContractRef, addr common.Address, value *big.Int, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + evm.Transfer(evm.StateDB, caller.Address(), addr, value) + return RunPrecompiledContract(w.p, input, suppliedGas) +} + +// nativeAssetBalance is a precompiled contract used to retrieve the native asset balance +type nativeAssetBalance struct { + gasCost uint64 +} + +// Run implements StatefulPrecompiledContract +func (b *nativeAssetBalance) Run(evm *EVM, caller ContractRef, addr common.Address, value *big.Int, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + // input: encodePacked(address 20 bytes, assetID 32 bytes) + if value.Sign() != 0 { + return nil, suppliedGas, errors.New("cannot transfer value to native asset balance pre-compiled contract") + } + + if suppliedGas < b.gasCost { + return nil, 0, ErrOutOfGas + } + remainingGas = suppliedGas - b.gasCost + + if len(input) != 52 { + return nil, remainingGas, fmt.Errorf("input to native asset balance must be 52 bytes, containing address [20 bytes] and assetID [32 bytes], but found length: %d", len(input)) + } + address := common.BytesToAddress(input[:20]) + assetID := new(common.Hash) + assetID.SetBytes(input[20:52]) + + res, overflow := uint256.FromBig(evm.StateDB.GetBalanceMultiCoin(address, *assetID)) + log.Info("nativeAssetBalance", "address", address, "assetID", assetID.Hex(), "res", res, "overflow", overflow) + if overflow { + return nil, remainingGas, errors.New("balance overflow") + } + return common.LeftPadBytes(res.Bytes(), 32), remainingGas, nil +} + +// nativeAssetCall atomically transfers a native asset to a recipient address as well as calling that +// address +type nativeAssetCall struct { + gasCost uint64 +} + +// Run implements StatefulPrecompiledContract +func (c *nativeAssetCall) Run(evm *EVM, caller ContractRef, addr common.Address, value *big.Int, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + // input: encodePacked(address 20 bytes, assetID 32 bytes, assetAmount 32 bytes, callData variable length bytes) + if suppliedGas < c.gasCost { + return nil, 0, ErrOutOfGas + } + remainingGas = suppliedGas - c.gasCost + + if readOnly { + return nil, remainingGas, errors.New("cannot execute native asset transfer within read only call") + } + + if len(input) < 84 { + return nil, remainingGas, fmt.Errorf("missing inputs to native asset transfer, expected input of length at least 84, but found %d", len(input)) + } + to := common.BytesToAddress(input[:20]) + assetID := new(common.Hash) + assetID.SetBytes(input[20:52]) + assetAmount := new(big.Int).SetBytes(input[52:84]) + callData := input[84:] + log.Info("nativeAssetCall", "input", fmt.Sprintf("0x%x", input), "to", to.Hex(), "assetID", assetID.Hex(), "assetAmount", assetAmount, "callData", fmt.Sprintf("0x%x", callData)) + + mcerr := evm.Context.CanTransferMC(evm.StateDB, caller.Address(), to, assetID, assetAmount) + if mcerr == 1 { + log.Info("Insufficient balance for mc transfer") + return nil, remainingGas, ErrInsufficientBalance + } else if mcerr != 0 { + log.Info("incompatible account for mc transfer") + return nil, remainingGas, ErrIncompatibleAccount + } + + snapshot := evm.StateDB.Snapshot() + + if !evm.StateDB.Exist(to) { + log.Info("Creating account entry", "address", to.Hex()) + remainingGas -= params.CallNewAccountGas + evm.StateDB.CreateAccount(to) + } + + // Send [value] to [to] address + evm.Transfer(evm.StateDB, caller.Address(), to, value) + evm.TransferMultiCoin(evm.StateDB, caller.Address(), to, assetID, assetAmount) + ret, remainingGas, err = evm.Call(caller, to, callData, remainingGas, big.NewInt(0)) + log.Info("Finished TransferMultiCoin and call", "return", ret, "remainingGas", remainingGas, "error", err) + + // 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 { + remainingGas = 0 + } + // TODO: consider clearing up unused snapshots: + //} else { + // evm.StateDB.DiscardSnapshot(snapshot) + } + return ret, remainingGas, err +} + +type deprecatedContract struct { + msg string +} + +// Run ... +func (d *deprecatedContract) Run(evm *EVM, caller ContractRef, addr common.Address, value *big.Int, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) { + return nil, suppliedGas, errors.New(d.msg) +} diff --git a/core/vm/evm.go b/core/vm/evm.go index 54c9c7f..ab04790 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -34,19 +34,23 @@ var emptyCodeHash = crypto.Keccak256Hash(nil) type ( // CanTransferFunc is the signature of a transfer guard function - CanTransferFunc func(StateDB, common.Address, *big.Int) bool + CanTransferFunc func(StateDB, common.Address, *big.Int) bool + // CanTransferMCFunc is the signature of a transfer native asset function CanTransferMCFunc func(StateDB, common.Address, common.Address, *common.Hash, *big.Int) int // TransferFunc is the signature of a transfer function - TransferFunc func(StateDB, common.Address, common.Address, *big.Int) + TransferFunc func(StateDB, common.Address, common.Address, *big.Int) + // TransferMCFunc is the signature of a transfer native asset function TransferMCFunc func(StateDB, common.Address, common.Address, *common.Hash, *big.Int) // GetHashFunc returns the n'th block hash in the blockchain // and is used by the BLOCKHASH EVM op code. GetHashFunc func(uint64) common.Hash ) -func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { - var precompiles map[common.Address]PrecompiledContract +func (evm *EVM) precompile(addr common.Address) (StatefulPrecompiledContract, bool) { + var precompiles map[common.Address]StatefulPrecompiledContract switch { + case evm.chainRules.IsApricot: + precompiles = PrecompiledContractsApricot case evm.chainRules.IsYoloV1: precompiles = PrecompiledContractsYoloV1 case evm.chainRules.IsIstanbul: @@ -222,7 +226,6 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } evm.StateDB.CreateAccount(addr) } - evm.Transfer(evm.StateDB, caller.Address(), addr, value) // Capture the tracer start/end events in debug mode if evm.vmConfig.Debug && evm.depth == 0 { @@ -233,8 +236,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } if isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = p.Run(evm, caller, addr, value, input, gas, false) } else { + evm.Transfer(evm.StateDB, caller.Address(), addr, 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. code := evm.StateDB.GetCode(addr) @@ -371,7 +375,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = p.Run(evm, caller, addr, value, input, gas, false) } else { addrCopy := addr // Initialise a new contract and set the code that is to be used by the EVM. @@ -407,7 +411,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = p.Run(evm, caller, addr, big0, input, gas, false) } else { addrCopy := addr // Initialise a new contract and make initialise the delegate values @@ -451,7 +455,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte evm.StateDB.AddBalance(addr, big0) if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = p.Run(evm, caller, addr, big0, input, gas, true) } else { // At this point, we use a copy of address. If we don't, the go compiler will // leak the 'contract' to the outer scope, and make allocation for 'contract' diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index a114a8d..1292650 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -26,6 +26,7 @@ import ( ) var ( + // BuiltinAddr ... BuiltinAddr = common.Address{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -106,6 +107,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { if cfg.JumpTable[STOP] == nil { var jt JumpTable switch { + case evm.chainRules.IsApricot: + jt = apricotInstructionSet case evm.chainRules.IsYoloV1: jt = yoloV1InstructionSet case evm.chainRules.IsIstanbul: @@ -146,6 +149,15 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // 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) { + // TODO remove after Apricot network upgrade. + // [BuiltinAddr] is overridden by a precompile in Apricot. + // This makes any call to [BuiltinAddr] a no-op once the network + // upgrade occurs. Then in the upgrade after the hard fork, this + // code can be safely removed. + // This can't occur before the hard fork because it would change + // the behavior of upgraded nodes before the hard fork occurs + // since there is no mechanism to change this code based on the + // block height. if contract.Address() == BuiltinAddr { self := AccountRef(contract.Caller()) if _, ok := contract.caller.(*Contract); ok { diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 9b538d4..fc12f93 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -49,6 +49,7 @@ type operation struct { } var ( + apricotInstructionSet = newApricotInstructionSet() frontierInstructionSet = newFrontierInstructionSet() homesteadInstructionSet = newHomesteadInstructionSet() tangerineWhistleInstructionSet = newTangerineWhistleInstructionSet() @@ -1043,3 +1044,13 @@ func newFrontierInstructionSet() JumpTable { }, } } + +// newApricotInstructionSet returns a new instruction set +// compatible with the Apricot release, which deprecates +// added instructions: CALLEX and BALANCEMC +func newApricotInstructionSet() JumpTable { + instructionSet := newFrontierInstructionSet() + instructionSet[CALLEX] = nil + instructionSet[BALANCEMC] = nil + return instructionSet +} diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index c5097f8..cc9518e 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -208,6 +208,7 @@ const ( SWAP ) +// Constants supporting native asset operations const ( BALANCEMC = 0xcd //EMC = 0xce -- cgit v1.2.3-70-g09d2 From 34da2a9bbc1cad12e5d1ab14799290e4b4987f99 Mon Sep 17 00:00:00 2001 From: Aaron Buchwald Date: Tue, 15 Dec 2020 17:05:51 -0500 Subject: Fix apricot instruction set --- core/vm/contracts_stateful.go | 4 ++-- core/vm/jump_table.go | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'core') diff --git a/core/vm/contracts_stateful.go b/core/vm/contracts_stateful.go index 3ef7a9e..b91acfb 100644 --- a/core/vm/contracts_stateful.go +++ b/core/vm/contracts_stateful.go @@ -27,8 +27,8 @@ var PrecompiledContractsApricot = map[common.Address]StatefulPrecompiledContract common.BytesToAddress([]byte{8}): newWrappedPrecompiledContract(&bn256PairingIstanbul{}), common.BytesToAddress([]byte{9}): newWrappedPrecompiledContract(&blake2F{}), common.HexToAddress("0x0100000000000000000000000000000000000000"): &deprecatedContract{msg: "hardcoded genesis contract has been deprecated"}, - common.HexToAddress("0xffffffffffffffffffffffffffffffffffffff"): &nativeAssetBalance{gasCost: params.AssetBalanceApricot}, - common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffe"): &nativeAssetCall{gasCost: params.AssetCallApricot}, + common.HexToAddress("0x0100000000000000000000000000000000000001"): &nativeAssetBalance{gasCost: params.AssetBalanceApricot}, + common.HexToAddress("0x0100000000000000000000000000000000000002"): &nativeAssetCall{gasCost: params.AssetCallApricot}, } // StatefulPrecompiledContract is the interface for executing a precompiled contract diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index fc12f93..44e2ebc 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -63,6 +63,16 @@ var ( // JumpTable contains the EVM opcodes supported at a given fork. type JumpTable [256]*operation +// newApricotInstructionSet returns a new instruction set +// compatible with the Apricot release, which deprecates +// added instructions: CALLEX and BALANCEMC +func newApricotInstructionSet() JumpTable { + instructionSet := newIstanbulInstructionSet() + instructionSet[CALLEX] = nil + instructionSet[BALANCEMC] = nil + return instructionSet +} + func newYoloV1InstructionSet() JumpTable { instructionSet := newIstanbulInstructionSet() @@ -1044,13 +1054,3 @@ func newFrontierInstructionSet() JumpTable { }, } } - -// newApricotInstructionSet returns a new instruction set -// compatible with the Apricot release, which deprecates -// added instructions: CALLEX and BALANCEMC -func newApricotInstructionSet() JumpTable { - instructionSet := newFrontierInstructionSet() - instructionSet[CALLEX] = nil - instructionSet[BALANCEMC] = nil - return instructionSet -} -- cgit v1.2.3-70-g09d2 From cc2a13f91f95bd377f5c3efb86ebbcc7096f6d97 Mon Sep 17 00:00:00 2001 From: Aaron Buchwald Date: Tue, 15 Dec 2020 17:30:03 -0500 Subject: Set chain rules based on block times for mainnet/fuji --- contracts/arc20.sol | 2 +- core/vm/evm.go | 2 +- params/config.go | 84 +++++++++++++++++++++++++++++++---------------------- plugin/evm/vm.go | 5 ++-- 4 files changed, 53 insertions(+), 40 deletions(-) (limited to 'core') diff --git a/contracts/arc20.sol b/contracts/arc20.sol index a7fc997..d1af6a3 100644 --- a/contracts/arc20.sol +++ b/contracts/arc20.sol @@ -2,7 +2,7 @@ pragma solidity >=0.6.0 <0.8.0; -import {NativeAssets} from "./NativeAssets.sol"; +import {NativeAssets} from "./nativeAssets.sol"; contract ARC20 { diff --git a/core/vm/evm.go b/core/vm/evm.go index ab04790..fe840da 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -152,7 +152,7 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmCon StateDB: statedb, vmConfig: vmConfig, chainConfig: chainConfig, - chainRules: chainConfig.Rules(ctx.BlockNumber), + chainRules: chainConfig.AvalancheRules(ctx.BlockNumber, ctx.Time), interpreters: make([]Interpreter, 0, 1), } diff --git a/params/config.go b/params/config.go index 1e14c40..d5db94f 100644 --- a/params/config.go +++ b/params/config.go @@ -20,6 +20,7 @@ import ( "encoding/binary" "fmt" "math/big" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -62,40 +63,38 @@ var ( var ( // AvalancheApricotMainnetChainConfig is the configuration for Avalanche Main Network AvalancheApricotMainnetChainConfig = &ChainConfig{ - ChainID: AvalancheMainnetChainID, - HomesteadBlock: big.NewInt(0), - DAOForkBlock: big.NewInt(0), - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), - EIP155Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - ApricotBlock: big.NewInt(0), // TODO specify correct block height + ChainID: AvalancheMainnetChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotBlockTimestamp: new(big.Int).SetUint64(uint64(time.Date(2021, 1, 7, 5, 00, 0, 0, time.UTC).Unix())), } // AvalancheApricotFujiChainConfig is the configuration for the Fuji Test Network AvalancheApricotFujiChainConfig = &ChainConfig{ - ChainID: AvalancheFujiChainID, - HomesteadBlock: big.NewInt(0), - DAOForkBlock: big.NewInt(0), - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), - EIP155Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - ApricotBlock: big.NewInt(0), // TODO specify correct block height + ChainID: AvalancheFujiChainID, + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + ApricotBlockTimestamp: new(big.Int).SetUint64(uint64(time.Date(2020, 12, 23, 5, 00, 0, 0, time.UTC).Unix())), } - // TODO update local network genesis to enable Apricot - // MainnetChainConfig is the chain parameters to run a node on the main network. MainnetChainConfig = &ChainConfig{ ChainID: big.NewInt(1), @@ -366,7 +365,11 @@ type ChainConfig struct { YoloV1Block *big.Int `json:"yoloV1Block,omitempty"` // YOLO v1: https://github.com/ethereum/EIPs/pull/2657 (Ephemeral testnet) EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated) - ApricotBlock *big.Int `json:"apricotBlock,omitempty"` // Apricot switch block (nil = no fork, 0 = already activated) + // Avalanche Network Upgrade Block Timestamps: + + // Apricot switch block timestamp (nil = no fork, 0 = already activated) + // Apricot rules go into effect for blocks with a timestamp >= ApricotBlockTimestamp + ApricotBlockTimestamp *big.Int `json:"apricotBlock,omitempty"` // Various consensus engines Ethash *EthashConfig `json:"ethash,omitempty"` @@ -403,7 +406,7 @@ func (c *ChainConfig) String() string { default: engine = "unknown" } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, YOLO v1: %v, Apricot: %v, Engine: %v}", + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, YOLO v1: %v, ApricotTimestamp: %v, Engine: %v}", c.ChainID, c.HomesteadBlock, c.DAOForkBlock, @@ -417,7 +420,7 @@ func (c *ChainConfig) String() string { c.IstanbulBlock, c.MuirGlacierBlock, c.YoloV1Block, - c.ApricotBlock, + c.ApricotBlockTimestamp, engine, ) } @@ -484,9 +487,12 @@ func (c *ChainConfig) IsEWASM(num *big.Int) bool { return isForked(c.EWASMBlock, num) } -// IsApricot returns whether num represents a block number after the Apricot fork -func (c *ChainConfig) IsApricot(num *big.Int) bool { - return isForked(c.ApricotBlock, num) +// Avalanche Forks: + +// IsApricot returns whether num represents a block with a timestamp +// after the Apricot fork time. +func (c *ChainConfig) IsApricot(blockTimestamp *big.Int) bool { + return isForked(c.ApricotBlockTimestamp, blockTimestamp) } // TODO review how this works and see if it will work for a live transition @@ -687,6 +693,14 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { IsPetersburg: c.IsPetersburg(num), IsIstanbul: c.IsIstanbul(num), IsYoloV1: c.IsYoloV1(num), - IsApricot: c.IsApricot(num), } } + +// AvalancheRules returns the Avalanche modified rules to support Avalanche +// network upgrades +func (c *ChainConfig) AvalancheRules(blockNum, blockTimestamp *big.Int) Rules { + rules := c.Rules(blockNum) + + rules.IsApricot = c.IsApricot(blockTimestamp) + return rules +} diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go index fc571b9..2732a79 100644 --- a/plugin/evm/vm.go +++ b/plugin/evm/vm.go @@ -243,11 +243,10 @@ func (vm *VM) Initialize( // TODO switch to correct block number of the fork switch { case g.Config.ChainID.Cmp(params.AvalancheMainnetChainID) == 0: - g.Config.ApricotBlock = nil + g.Config = params.AvalancheApricotMainnetChainConfig case g.Config.ChainID.Cmp(params.AvalancheFujiChainID) == 0: - g.Config.ApricotBlock = nil + g.Config = params.AvalancheApricotFujiChainConfig } - vm.acceptedDB = prefixdb.New([]byte(acceptedPrefix), db) vm.chainID = g.Config.ChainID -- cgit v1.2.3-70-g09d2