[core] feat: Add cross shard transfer precompile

...for native tokens only, and not for smart contracts (which will be
added later). Resolves #4132 and requires a hard fork, currently
scheduled at EpochTBD on main and test nets, but epoch 1 (after
AcceptsCrossTx) on other nets.

The precompile is at address 249 and the method has the signature
`crossShardTransfer(uint256 amount, address to, uint32 toShardID)`. It
requires a transfer of the `amount` to the precompile address first, in
the form of `msg.value`, and cascades a cross shard receipt to the
network when executed. At this stage, it is blocked for use by smart
contracts by checking that there is no code at the address, and that the
address is not a validator. Documentation for Python integration tests
to follow.
pull/4165/head
MaxMustermann2 3 years ago
parent e1f079597a
commit 75017bfbad
No known key found for this signature in database
GPG Key ID: 4F4AB9DB6FF24C94
  1. 36
      accounts/abi/abi.go
  2. 36
      accounts/abi/abi_test.go
  3. 32
      core/evm.go
  4. 25
      core/state_processor.go
  5. 188
      core/vm/contracts_write.go
  6. 136
      core/vm/contracts_write_test.go
  7. 36
      core/vm/evm.go
  8. 1
      core/vm/evm_test.go
  9. 417
      internal/params/config.go
  10. 31
      staking/precompile.go
  11. 12
      staking/precompile_test.go

@ -19,9 +19,11 @@ package abi
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"math/big"
"github.com/pkg/errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
@ -277,3 +279,35 @@ func UnpackRevert(data []byte) (string, error) {
}
return unpacked[0].(string), nil
}
// ParseAddressFromKey pulls out the address value from a map with provided key,
// andv alidates the data type for it
func ParseAddressFromKey(args map[string]interface{}, key string) (common.Address, error) {
if address, ok := args[key].(common.Address); ok {
return address, nil
} else {
return common.Address{}, errors.Errorf("Cannot parse address from %v", args[key])
}
}
// ParseBigIntFromKey pulls out the *big.Int value from a map with provided key,
// and validates the data type for it
func ParseBigIntFromKey(args map[string]interface{}, key string) (*big.Int, error) {
bigInt, ok := args[key].(*big.Int)
if !ok {
return nil, errors.Errorf(
"Cannot parse BigInt from %v", args[key])
} else {
return bigInt, nil
}
}
// ParseUint32FromKey pulls out the uint64 value from a map with provided key,
// and validates the data type for it
func ParseUint32FromKey(args map[string]interface{}, key string) (uint32, error) {
if val, ok := args[key].(uint32); ok {
return val, nil
} else {
return 0, errors.Errorf("Cannot parse uint32 from %v", args[key])
}
}

@ -1143,3 +1143,39 @@ func TestUnpackRevert(t *testing.T) {
})
}
}
func TestParseBigIntFromKey(t *testing.T) {
args := map[string]interface{}{}
expectedError := errors.New("Cannot parse BigInt from <nil>")
if _, err := ParseBigIntFromKey(args, "PotentialBigInt"); err != nil {
if expectedError.Error() != err.Error() {
t.Errorf("Expected error %v, got %v", expectedError, err)
}
} else {
t.Errorf("Expected error %v, got result", expectedError)
}
}
func TestParseAddressFromKey(t *testing.T) {
args := map[string]interface{}{}
expectedError := errors.New("Cannot parse address from <nil>")
if _, err := ParseAddressFromKey(args, "PotentialAddress"); err != nil {
if expectedError.Error() != err.Error() {
t.Errorf("Expected error %v, got %v", expectedError, err)
}
} else {
t.Errorf("Expected error %v, got result", expectedError)
}
}
func TestParseUint32FromKey(t *testing.T) {
args := map[string]interface{}{}
expectedError := errors.New("Cannot parse uint32 from <nil>")
if _, err := ParseUint32FromKey(args, "PotentialUint32"); err != nil {
if expectedError.Error() != err.Error() {
t.Errorf("Expected error %v, got %v", expectedError, err)
}
} else {
t.Errorf("Expected error %v, got result", expectedError)
}
}

@ -31,6 +31,7 @@ import (
"github.com/harmony-one/harmony/core/vm"
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/shard"
staking "github.com/harmony-one/harmony/staking"
stakingTypes "github.com/harmony-one/harmony/staking/types"
)
@ -77,27 +78,28 @@ func NewEVMContext(msg Message, header *block.Header, chain ChainContext, author
copy(vrf[:], vrfAndProof[:32])
}
return vm.Context{
CanTransfer: CanTransfer,
Transfer: Transfer,
IsValidator: IsValidator,
GetHash: GetHashFn(header, chain),
GetVRF: GetVRFFn(header, chain),
CreateValidator: CreateValidatorFn(header, chain),
EditValidator: EditValidatorFn(header, chain),
Delegate: DelegateFn(header, chain),
Undelegate: UndelegateFn(header, chain),
CollectRewards: CollectRewardsFn(header, chain),
//MigrateDelegations: MigrateDelegationsFn(header, chain),
CalculateMigrationGas: CalculateMigrationGasFn(chain),
CanTransfer: CanTransfer,
Transfer: Transfer,
GetHash: GetHashFn(header, chain),
GetVRF: GetVRFFn(header, chain),
IsValidator: IsValidator,
Origin: msg.From(),
GasPrice: new(big.Int).Set(msg.GasPrice()),
Coinbase: beneficiary,
GasLimit: header.GasLimit(),
BlockNumber: header.Number(),
EpochNumber: header.Epoch(),
VRF: vrf,
Time: header.Time(),
GasLimit: header.GasLimit(),
GasPrice: new(big.Int).Set(msg.GasPrice()),
VRF: vrf,
TxType: 0,
CreateValidator: CreateValidatorFn(header, chain),
EditValidator: EditValidatorFn(header, chain),
Delegate: DelegateFn(header, chain),
Undelegate: UndelegateFn(header, chain),
CollectRewards: CollectRewardsFn(header, chain),
CalculateMigrationGas: CalculateMigrationGasFn(chain),
ShardID: chain.ShardID(),
NumShards: shard.Schedule.InstanceForEpoch(header.Epoch()).NumShards(),
}
}

@ -299,9 +299,30 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
var cxReceipt *types.CXReceipt
// Do not create cxReceipt if EVM call failed
if txType == types.SubtractionOnly && !failedExe {
cxReceipt = &types.CXReceipt{TxHash: tx.Hash(), From: msg.From(), To: msg.To(), ShardID: tx.ShardID(), ToShardID: tx.ToShardID(), Amount: msg.Value()}
cxReceipt = &types.CXReceipt{
TxHash: tx.Hash(),
From: msg.From(),
To: msg.To(),
ShardID: tx.ShardID(),
ToShardID: tx.ToShardID(),
Amount: msg.Value(),
}
if vmenv.CXReceipt != nil {
return nil, nil, nil, 0, errors.New("cannot have cross shard receipt via precompile and directly")
}
} else {
cxReceipt = nil
if !failedExe {
if vmenv.CXReceipt != nil {
cxReceipt = vmenv.CXReceipt
// this tx.Hash needs to be the "original" tx.Hash
// since, in effect, we have added
// support for cross shard txs
// to eth txs
cxReceipt.TxHash = tx.HashByType()
}
} else {
cxReceipt = nil
}
}
return receipt, cxReceipt, vmenv.StakeMsgs, result.UsedGas, err

@ -2,9 +2,13 @@ package vm
import (
"errors"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/accounts/abi"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/staking"
stakingTypes "github.com/harmony-one/harmony/staking/types"
@ -17,6 +21,19 @@ var WriteCapablePrecompiledContractsStaking = map[common.Address]WriteCapablePre
common.BytesToAddress([]byte{252}): &stakingPrecompile{},
}
var (
// reserve 250 for read only staking precompile and 251 for epoch
crossShardXferPrecompileAddress = common.BytesToAddress([]byte{249})
)
// WriteCapablePrecompiledContractsCrossXfer lists out the write capable precompiled contracts
// which are available after the CrossShardXferPrecompileEpoch
// It includes the staking precompile and the cross-shard transfer precompile
var WriteCapablePrecompiledContractsCrossXfer = map[common.Address]WriteCapablePrecompiledContract{
crossShardXferPrecompileAddress: &crossShardXferPrecompile{address: crossShardXferPrecompileAddress},
common.BytesToAddress([]byte{252}): &stakingPrecompile{},
}
// WriteCapablePrecompiledContract represents the interface for Native Go contracts
// which are available as a precompile in the EVM
// As with (read-only) PrecompiledContracts, these need a RequiredGas function
@ -26,7 +43,7 @@ type WriteCapablePrecompiledContract interface {
// RequiredGas calculates the contract gas use
RequiredGas(evm *EVM, contract *Contract, input []byte) (uint64, error)
// use a different name from read-only contracts to be safe
RunWriteCapable(evm *EVM, contract *Contract, input []byte) ([]byte, error)
RunWriteCapable(evm *EVM, contract *Contract, input []byte, value *big.Int) ([]byte, error)
}
// RunWriteCapablePrecompiledContract runs and evaluates the output of a write capable precompiled contract.
@ -36,6 +53,7 @@ func RunWriteCapablePrecompiledContract(
contract *Contract,
input []byte,
readOnly bool,
value *big.Int,
) ([]byte, error) {
// immediately error out if readOnly
if readOnly {
@ -48,7 +66,7 @@ func RunWriteCapablePrecompiledContract(
if !contract.UseGas(gas) {
return nil, ErrOutOfGas
}
return p.RunWriteCapable(evm, contract, input)
return p.RunWriteCapable(evm, contract, input, value)
}
type stakingPrecompile struct{}
@ -104,6 +122,7 @@ func (c *stakingPrecompile) RunWriteCapable(
evm *EVM,
contract *Contract,
input []byte,
value *big.Int,
) ([]byte, error) {
if evm.Context.ShardID != shard.BeaconChainShardID {
return nil, errors.New("Staking not supported on this shard")
@ -150,3 +169,168 @@ func (c *stakingPrecompile) RunWriteCapable(
//}
return nil, errors.New("[StakingPrecompile] Received incompatible stakeMsg from staking.ParseStakeMsg")
}
var abiCrossShardXfer abi.ABI
func init() {
// msg.Value is used for transfer and is also a parameter
// otherwise it might be possible for a user to retrieve money from the precompile
// that was sent by someone else prior to the hard fork
// contract.Caller is used as fromAddress, not a parameter
// originating ShardID is pulled from the EVM object, not a parameter
crossShardXferABIJSON := `
[
{
"inputs": [
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint64",
"name": "toShardID",
"type": "uint32"
}
],
"name": "crossShardTransfer",
"outputs": [],
"stateMutability": "payable",
"type": "function"
}
]`
var err error
abiCrossShardXfer, err = abi.JSON(strings.NewReader(crossShardXferABIJSON))
if err != nil {
// means an error in the code
panic("Invalid cross shard transfer ABI JSON")
}
}
type crossShardXferPrecompile struct {
address common.Address
}
// 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 *crossShardXferPrecompile) RequiredGas(
evm *EVM,
contract *Contract,
input []byte,
) (uint64, error) {
// we just charge the intrinsic gas here
// using data that includes: fromAddress, toAddress, fromShardID, toShardID, value
var payload []byte = make([]byte, 0)
fromAddress, toAddress, fromShardID, toShardID, value, err := parseCrossShardXferData(evm, contract, input)
if err == nil {
data := struct {
fromAddress common.Address
toAddress common.Address
fromShardID uint32
toShardID uint32
value *big.Int
}{
fromAddress: fromAddress,
toAddress: toAddress,
fromShardID: fromShardID,
toShardID: toShardID,
value: value,
}
if encoded, err := rlp.EncodeToBytes(data); err == nil {
payload = encoded
}
}
if gas, err := IntrinsicGas(
payload,
false, // contractCreation
evm.ChainConfig().IsS3(evm.EpochNumber), // homestead
evm.ChainConfig().IsIstanbul(evm.EpochNumber), // istanbul
false, // isValidatorCreation
); err != nil {
return 0, err // ErrOutOfGas occurs when gas payable > uint64
} else {
return gas, nil
}
}
// RunWriteCapable runs the actual contract
func (c *crossShardXferPrecompile) RunWriteCapable(
evm *EVM,
contract *Contract,
input []byte,
sentValue *big.Int,
) ([]byte, error) {
fromAddress, toAddress, fromShardID, toShardID, value, err := parseCrossShardXferData(evm, contract, input)
if err != nil {
return nil, err
}
// validate not a contract
if len(evm.StateDB.GetCode(fromAddress)) > 0 && !evm.IsValidator(evm.StateDB, fromAddress) {
return nil, errors.New("cross shard xfer not yet implemented for contracts")
}
// validate not a contract
if len(evm.StateDB.GetCode(toAddress)) > 0 && !evm.IsValidator(evm.StateDB, toAddress) {
return nil, errors.New("cross shard xfer not yet implemented for contracts")
}
// can't have too many shards
if toShardID >= evm.Context.NumShards {
return nil, errors.New("toShardId out of bounds")
}
// not for simple transfers
if fromShardID == toShardID {
return nil, errors.New("from and to shard id can't be equal")
}
// make sure nobody sends extra or less money
// no need to check `nil` because that only happens in readOnly
// which has already been checked
if sentValue.Cmp(value) != 0 {
return nil, errors.New("argument value and msg.value not equal")
}
// now do the actual transfer
// step 1 -> remove funds from the precompile address
evm.Transfer(evm.StateDB, c.address, toAddress, value, types.SubtractionOnly)
// step 2 -> make a cross link
// note that the transaction hash is added by state_processor.go to this receipt
evm.CXReceipt = &types.CXReceipt{
From: fromAddress,
To: &toAddress,
ShardID: fromShardID,
ToShardID: toShardID,
Amount: value,
}
return nil, nil
}
// parseCrossShardXferData does a simple parse with only data types validation
func parseCrossShardXferData(evm *EVM, contract *Contract, input []byte) (
common.Address, common.Address, uint32, uint32, *big.Int, error) {
method, err := abiCrossShardXfer.MethodById(input)
if err != nil {
return common.Address{}, common.Address{}, 0, 0, nil, err
}
input = input[4:]
args := map[string]interface{}{}
if err = method.Inputs.UnpackIntoMap(args, input); err != nil {
return common.Address{}, common.Address{}, 0, 0, nil, err
}
value, err := abi.ParseBigIntFromKey(args, "value")
if err != nil {
return common.Address{}, common.Address{}, 0, 0, nil, err
}
toAddress, err := abi.ParseAddressFromKey(args, "to")
if err != nil {
return common.Address{}, common.Address{}, 0, 0, nil, err
}
toShardID, err := abi.ParseUint32FromKey(args, "toShardID")
if err != nil {
return common.Address{}, common.Address{}, 0, 0, nil, err
}
return contract.Caller(), toAddress, evm.ShardID, toShardID, value, nil
}

@ -8,6 +8,10 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/internal/params"
stakingTypes "github.com/harmony-one/harmony/staking/types"
)
@ -17,6 +21,7 @@ type writeCapablePrecompileTest struct {
name string
expectedError error
p *WriteCapablePrecompiledContract
value *big.Int
}
func CollectRewardsFn() CollectRewardsFunc {
@ -61,18 +66,7 @@ func CalculateMigrationGasFn() CalculateMigrationGasFunc {
}
}
func testStakingPrecompile(test writeCapablePrecompileTest, t *testing.T) {
var env = NewEVM(Context{CollectRewards: CollectRewardsFn(),
Delegate: DelegateFn(),
Undelegate: UndelegateFn(),
CreateValidator: CreateValidatorFn(),
EditValidator: EditValidatorFn(),
ShardID: 0,
//MigrateDelegations: MigrateDelegationsFn(),
CalculateMigrationGas: CalculateMigrationGasFn(),
}, nil, params.TestChainConfig, Config{})
// use required gas to avoid out of gas errors
p := &stakingPrecompile{}
func testWriteCapablePrecompile(test writeCapablePrecompileTest, t *testing.T, env *EVM, p WriteCapablePrecompiledContract) {
t.Run(fmt.Sprintf("%s", test.name), func(t *testing.T) {
contract := NewContract(AccountRef(common.HexToAddress("1337")), AccountRef(common.HexToAddress("1338")), new(big.Int), 0)
gas, err := p.RequiredGas(env, contract, test.input)
@ -80,7 +74,7 @@ func testStakingPrecompile(test writeCapablePrecompileTest, t *testing.T) {
t.Error(err)
}
contract.Gas = gas
if res, err := RunWriteCapablePrecompiledContract(p, env, contract, test.input, false); err != nil {
if res, err := RunWriteCapablePrecompiledContract(p, env, contract, test.input, false, test.value); err != nil {
if test.expectedError != nil {
if test.expectedError.Error() != err.Error() {
t.Errorf("Expected error %v, got %v", test.expectedError, err)
@ -99,23 +93,18 @@ func testStakingPrecompile(test writeCapablePrecompileTest, t *testing.T) {
})
}
func TestStakingPrecompiles(t *testing.T) {
for _, test := range StakingPrecompileTests {
testStakingPrecompile(test, t)
}
}
func TestWriteCapablePrecompilesReadOnly(t *testing.T) {
func testStakingPrecompile(test writeCapablePrecompileTest, t *testing.T) {
var env = NewEVM(Context{CollectRewards: CollectRewardsFn(),
Delegate: DelegateFn(),
Undelegate: UndelegateFn(),
CreateValidator: CreateValidatorFn(),
EditValidator: EditValidatorFn(),
ShardID: 0,
//MigrateDelegations: MigrateDelegationsFn(),
CalculateMigrationGas: CalculateMigrationGasFn(),
}, nil, params.TestChainConfig, Config{})
p := &stakingPrecompile{}
expectedError := errWriteProtection
res, err := RunWriteCapablePrecompiledContract(p, nil, nil, []byte{}, true)
if err != nil {
if err.Error() != expectedError.Error() {
t.Errorf("Expected error %v, got %v", expectedError, err)
}
} else {
t.Errorf("Expected an error %v but instead got result %v", expectedError, res)
}
testWriteCapablePrecompile(test, t, env, p)
}
var StakingPrecompileTests = []writeCapablePrecompileTest{
@ -211,3 +200,92 @@ var StakingPrecompileTests = []writeCapablePrecompileTest{
// name: "migrationAddressMismatch",
//},
}
func TestStakingPrecompiles(t *testing.T) {
for _, test := range StakingPrecompileTests {
testStakingPrecompile(test, t)
}
}
func TestWriteCapablePrecompilesReadOnly(t *testing.T) {
p := &stakingPrecompile{}
expectedError := errWriteProtection
res, err := RunWriteCapablePrecompiledContract(p, nil, nil, []byte{}, true, nil)
if err != nil {
if err.Error() != expectedError.Error() {
t.Errorf("Expected error %v, got %v", expectedError, err)
}
} else {
t.Errorf("Expected an error %v but instead got result %v", expectedError, res)
}
}
func transfer(db StateDB, sender, recipient common.Address, amount *big.Int, txType types.TransactionType) {
}
func testCrossShardXferPrecompile(test writeCapablePrecompileTest, t *testing.T) {
// this EVM needs stateDB, Transfer, and NumShards
var db ethdb.Database
var err error
defer func() {
if db != nil {
db.Close()
}
}()
if db, err = rawdb.NewLevelDBDatabase("/tmp/harmony_shard_0", 256, 1024, ""); err != nil {
db = nil
t.Fatalf("Could not initialize db %s", err)
}
stateCache := state.NewDatabase(db)
state, err := state.New(common.Hash{}, stateCache)
if err != nil {
t.Fatalf("Error while initializing state %s", err)
}
var env = NewEVM(Context{
NumShards: 4,
Transfer: transfer,
}, state, params.TestChainConfig, Config{})
p := &crossShardXferPrecompile{}
testWriteCapablePrecompile(test, t, env, p)
}
func TestCrossShardXferPrecompile(t *testing.T) {
for _, test := range CrossShardXferPrecompileTests {
testCrossShardXferPrecompile(test, t)
}
}
var CrossShardXferPrecompileTests = []writeCapablePrecompileTest{
{
input: []byte{40, 72, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 107, 199, 94, 45, 99, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, 36, 21, 19, 218, 159, 68, 99, 241, 212, 135, 75, 84, 141, 251, 172, 41, 217, 31, 52, 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},
expected: nil,
name: "crossShardSuccess",
value: new(big.Int).Mul(big.NewInt(100), big.NewInt(1e18)),
},
{
input: []byte{40, 72, 8, 1},
expectedError: errors.New("no method with id: 0x28480801"),
name: "crossShardMethodSigFail",
},
{
input: []byte{40, 72, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 107, 199, 94, 45, 99, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, 36, 21, 19, 218, 159, 68, 99, 241, 212, 135, 75, 84, 141, 251, 172, 41, 217, 31, 52, 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},
expectedError: errors.New("abi: cannot marshal in to go type: length insufficient 95 require 96"),
name: "crossShardMalformedInputFail",
},
{
input: []byte{40, 72, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 107, 199, 94, 45, 99, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, 36, 21, 19, 218, 159, 68, 99, 241, 212, 135, 75, 84, 141, 251, 172, 41, 217, 31, 52, 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, 6},
expectedError: errors.New("toShardId out of bounds"),
name: "crossShardOutOfBoundsFail",
},
{
input: []byte{40, 72, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 107, 199, 94, 45, 99, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, 36, 21, 19, 218, 159, 68, 99, 241, 212, 135, 75, 84, 141, 251, 172, 41, 217, 31, 52, 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, 0},
expectedError: errors.New("from and to shard id can't be equal"),
name: "crossShardSameShardFail",
},
{
input: []byte{40, 72, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 107, 199, 94, 45, 99, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, 36, 21, 19, 218, 159, 68, 99, 241, 212, 135, 75, 84, 141, 251, 172, 41, 217, 31, 52, 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},
expectedError: errors.New("argument value and msg.value not equal"),
name: "crossShardDiffValueFail",
value: new(big.Int).Mul(big.NewInt(1000), big.NewInt(1e18)),
},
}

@ -66,11 +66,11 @@ type (
)
// 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) {
func run(evm *EVM, contract *Contract, input []byte, readOnly bool, value *big.Int) ([]byte, error) {
if contract.CodeAddr != nil {
precompiles := PrecompiledContractsHomestead
// assign empty write capable precompiles till they are available in the fork
writeCapablePrecompiles := make(map[common.Address]WriteCapablePrecompiledContract)
var writeCapablePrecompiles map[common.Address]WriteCapablePrecompiledContract
if evm.ChainConfig().IsS3(evm.EpochNumber) {
precompiles = PrecompiledContractsByzantium
}
@ -87,6 +87,9 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
precompiles = PrecompiledContractsStaking
writeCapablePrecompiles = WriteCapablePrecompiledContractsStaking
}
if evm.chainRules.IsCrossShardXferPrecompile {
writeCapablePrecompiles = WriteCapablePrecompiledContractsCrossXfer
}
if p := precompiles[*contract.CodeAddr]; p != nil {
if _, ok := p.(*vrf); ok {
if evm.chainRules.IsPrevVRF {
@ -111,8 +114,10 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
}
return RunPrecompiledContract(p, input, contract)
}
if p := writeCapablePrecompiles[*contract.CodeAddr]; p != nil {
return RunWriteCapablePrecompiledContract(p, evm, contract, input, readOnly)
if len(writeCapablePrecompiles) > 0 {
if p := writeCapablePrecompiles[*contract.CodeAddr]; p != nil {
return RunWriteCapablePrecompiledContract(p, evm, contract, input, readOnly, value)
}
}
}
for _, interpreter := range evm.interpreters {
@ -174,8 +179,8 @@ type Context struct {
CollectRewards CollectRewardsFunc
CalculateMigrationGas CalculateMigrationGasFunc
// staking precompile checks this before proceeding forward
ShardID uint32
ShardID uint32 // Used by staking and cross shard transfer precompile
NumShards uint32 // Used by cross shard transfer precompile
}
// EVM is the Ethereum Virtual Machine base object and provides
@ -216,6 +221,7 @@ type EVM struct {
// stored temporarily by stakingPrecompile and cleared immediately after return
// (although the EVM object itself is ephemeral)
StakeMsgs []stakingTypes.StakeMsg
CXReceipt *types.CXReceipt
}
// NewEVM returns a new EVM. The returned EVM is not thread safe and should
@ -297,7 +303,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
)
if !evm.StateDB.Exist(addr) && txType != types.SubtractionOnly {
precompiles := PrecompiledContractsHomestead
writeCapablePrecompiles := make(map[common.Address]WriteCapablePrecompiledContract)
var writeCapablePrecompiles map[common.Address]WriteCapablePrecompiledContract
if evm.ChainConfig().IsS3(evm.EpochNumber) {
precompiles = PrecompiledContractsByzantium
}
@ -314,8 +320,10 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
precompiles = PrecompiledContractsStaking
writeCapablePrecompiles = WriteCapablePrecompiledContractsStaking
}
if writeCapablePrecompiles[addr] == nil && precompiles[addr] == nil && evm.ChainConfig().IsS3(evm.EpochNumber) && value.Sign() == 0 {
if evm.chainRules.IsCrossShardXferPrecompile {
writeCapablePrecompiles = WriteCapablePrecompiledContractsCrossXfer
}
if (len(writeCapablePrecompiles) == 0 || writeCapablePrecompiles[addr] == nil) && precompiles[addr] == nil && evm.ChainConfig().IsS3(evm.EpochNumber) && 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(evm, caller.Address(), addr, false, input, gas, value)
@ -351,7 +359,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
}()
}
ret, err = run(evm, contract, input, false)
ret, err = run(evm, contract, input, false, value)
// 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
@ -396,7 +404,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
ret, err = run(evm, contract, input, false)
ret, err = run(evm, contract, input, false, value)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
@ -429,7 +437,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
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)
ret, err = run(evm, contract, input, false, nil)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
@ -471,7 +479,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// 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)
ret, err = run(evm, contract, input, true, nil)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
@ -534,7 +542,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
start := time.Now()
ret, err := run(evm, contract, nil, false)
ret, err := run(evm, contract, nil, false, value)
// check whether the max code size has been exceeded
maxCodeSizeExceeded := evm.ChainConfig().IsEIP155(evm.EpochNumber) && len(ret) > params.MaxCodeSize

@ -24,6 +24,7 @@ func TestEpochPrecompile(t *testing.T) {
&contract,
input,
true,
nil,
)
if err != nil {
t.Fatalf("Got error%v\n", err)

@ -36,209 +36,215 @@ var once sync.Once
var (
// MainnetChainConfig is the chain parameters to run a node on the main network.
MainnetChainConfig = &ChainConfig{
ChainID: MainnetChainID,
EthCompatibleChainID: EthMainnetShard0ChainID,
EthCompatibleShard0ChainID: EthMainnetShard0ChainID,
EthCompatibleEpoch: big.NewInt(442), // Around Thursday Feb 4th 2020, 10AM PST
CrossTxEpoch: big.NewInt(28),
CrossLinkEpoch: big.NewInt(186),
AggregatedRewardEpoch: big.NewInt(689), // Around Wed Sept 15th 2021 with 3.5s block time
StakingEpoch: big.NewInt(186),
PreStakingEpoch: big.NewInt(185),
QuickUnlockEpoch: big.NewInt(191),
FiveSecondsEpoch: big.NewInt(230),
TwoSecondsEpoch: big.NewInt(366), // Around Tuesday Dec 8th 2020, 8AM PST
SixtyPercentEpoch: big.NewInt(530), // Around Monday Apr 12th 2021, 22:30 UTC
RedelegationEpoch: big.NewInt(290),
NoEarlyUnlockEpoch: big.NewInt(530), // Around Monday Apr 12th 2021, 22:30 UTC
VRFEpoch: big.NewInt(631), // Around Wed July 7th 2021
PrevVRFEpoch: big.NewInt(689), // Around Wed Sept 15th 2021 with 3.5s block time
MinDelegation100Epoch: big.NewInt(631), // Around Wed July 7th 2021
MinCommissionRateEpoch: big.NewInt(631), // Around Wed July 7th 2021
MinCommissionPromoPeriod: big.NewInt(100),
EPoSBound35Epoch: big.NewInt(631), // Around Wed July 7th 2021
EIP155Epoch: big.NewInt(28),
S3Epoch: big.NewInt(28),
DataCopyFixEpoch: big.NewInt(689), // Around Wed Sept 15th 2021 with 3.5s block time
IstanbulEpoch: big.NewInt(314),
ReceiptLogEpoch: big.NewInt(101),
SHA3Epoch: big.NewInt(725), // Around Mon Oct 11 2021, 19:00 UTC
HIP6And8Epoch: big.NewInt(725), // Around Mon Oct 11 2021, 19:00 UTC
StakingPrecompileEpoch: big.NewInt(871), // Around Tue Feb 11 2022
SlotsLimitedEpoch: EpochTBD, // epoch to enable HIP-16
ChainID: MainnetChainID,
EthCompatibleChainID: EthMainnetShard0ChainID,
EthCompatibleShard0ChainID: EthMainnetShard0ChainID,
EthCompatibleEpoch: big.NewInt(442), // Around Thursday Feb 4th 2020, 10AM PST
CrossTxEpoch: big.NewInt(28),
CrossLinkEpoch: big.NewInt(186),
AggregatedRewardEpoch: big.NewInt(689), // Around Wed Sept 15th 2021 with 3.5s block time
StakingEpoch: big.NewInt(186),
PreStakingEpoch: big.NewInt(185),
QuickUnlockEpoch: big.NewInt(191),
FiveSecondsEpoch: big.NewInt(230),
TwoSecondsEpoch: big.NewInt(366), // Around Tuesday Dec 8th 2020, 8AM PST
SixtyPercentEpoch: big.NewInt(530), // Around Monday Apr 12th 2021, 22:30 UTC
RedelegationEpoch: big.NewInt(290),
NoEarlyUnlockEpoch: big.NewInt(530), // Around Monday Apr 12th 2021, 22:30 UTC
VRFEpoch: big.NewInt(631), // Around Wed July 7th 2021
PrevVRFEpoch: big.NewInt(689), // Around Wed Sept 15th 2021 with 3.5s block time
MinDelegation100Epoch: big.NewInt(631), // Around Wed July 7th 2021
MinCommissionRateEpoch: big.NewInt(631), // Around Wed July 7th 2021
MinCommissionPromoPeriod: big.NewInt(100),
EPoSBound35Epoch: big.NewInt(631), // Around Wed July 7th 2021
EIP155Epoch: big.NewInt(28),
S3Epoch: big.NewInt(28),
DataCopyFixEpoch: big.NewInt(689), // Around Wed Sept 15th 2021 with 3.5s block time
IstanbulEpoch: big.NewInt(314),
ReceiptLogEpoch: big.NewInt(101),
SHA3Epoch: big.NewInt(725), // Around Mon Oct 11 2021, 19:00 UTC
HIP6And8Epoch: big.NewInt(725), // Around Mon Oct 11 2021, 19:00 UTC
StakingPrecompileEpoch: big.NewInt(871), // Around Tue Feb 11 2022
SlotsLimitedEpoch: EpochTBD, // epoch to enable HIP-16
CrossShardXferPrecompileEpoch: EpochTBD,
}
// TestnetChainConfig contains the chain parameters to run a node on the harmony test network.
TestnetChainConfig = &ChainConfig{
ChainID: TestnetChainID,
EthCompatibleChainID: EthTestnetShard0ChainID,
EthCompatibleShard0ChainID: EthTestnetShard0ChainID,
EthCompatibleEpoch: big.NewInt(73290),
CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2),
AggregatedRewardEpoch: big.NewInt(74275),
StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(1),
QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(16500),
TwoSecondsEpoch: big.NewInt(73000),
SixtyPercentEpoch: big.NewInt(73282),
RedelegationEpoch: big.NewInt(36500),
NoEarlyUnlockEpoch: big.NewInt(73580),
VRFEpoch: big.NewInt(73880),
PrevVRFEpoch: big.NewInt(74384),
MinDelegation100Epoch: big.NewInt(73880),
MinCommissionRateEpoch: big.NewInt(73880),
MinCommissionPromoPeriod: big.NewInt(10),
EPoSBound35Epoch: big.NewInt(73880),
EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0),
DataCopyFixEpoch: big.NewInt(74412),
IstanbulEpoch: big.NewInt(43800),
ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: big.NewInt(74570),
HIP6And8Epoch: big.NewInt(74570),
StakingPrecompileEpoch: big.NewInt(75175),
SlotsLimitedEpoch: big.NewInt(75684), // epoch to enable HIP-16, around Mon, 02 May 2022 08:18:45 UTC with 2s block time
ChainID: TestnetChainID,
EthCompatibleChainID: EthTestnetShard0ChainID,
EthCompatibleShard0ChainID: EthTestnetShard0ChainID,
EthCompatibleEpoch: big.NewInt(73290),
CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2),
AggregatedRewardEpoch: big.NewInt(74275),
StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(1),
QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(16500),
TwoSecondsEpoch: big.NewInt(73000),
SixtyPercentEpoch: big.NewInt(73282),
RedelegationEpoch: big.NewInt(36500),
NoEarlyUnlockEpoch: big.NewInt(73580),
VRFEpoch: big.NewInt(73880),
PrevVRFEpoch: big.NewInt(74384),
MinDelegation100Epoch: big.NewInt(73880),
MinCommissionRateEpoch: big.NewInt(73880),
MinCommissionPromoPeriod: big.NewInt(10),
EPoSBound35Epoch: big.NewInt(73880),
EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0),
DataCopyFixEpoch: big.NewInt(74412),
IstanbulEpoch: big.NewInt(43800),
ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: big.NewInt(74570),
HIP6And8Epoch: big.NewInt(74570),
StakingPrecompileEpoch: big.NewInt(75175),
SlotsLimitedEpoch: big.NewInt(75684), // epoch to enable HIP-16, around Mon, 02 May 2022 08:18:45 UTC with 2s block time
CrossShardXferPrecompileEpoch: EpochTBD,
}
// PangaeaChainConfig contains the chain parameters for the Pangaea network.
// All features except for CrossLink are enabled at launch.
PangaeaChainConfig = &ChainConfig{
ChainID: PangaeaChainID,
EthCompatibleChainID: EthPangaeaShard0ChainID,
EthCompatibleShard0ChainID: EthPangaeaShard0ChainID,
EthCompatibleEpoch: big.NewInt(0),
CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2),
AggregatedRewardEpoch: big.NewInt(3),
StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(1),
QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(0),
TwoSecondsEpoch: big.NewInt(0),
SixtyPercentEpoch: big.NewInt(0),
RedelegationEpoch: big.NewInt(0),
NoEarlyUnlockEpoch: big.NewInt(0),
VRFEpoch: big.NewInt(0),
PrevVRFEpoch: big.NewInt(0),
MinDelegation100Epoch: big.NewInt(0),
MinCommissionRateEpoch: big.NewInt(0),
MinCommissionPromoPeriod: big.NewInt(10),
EPoSBound35Epoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0),
DataCopyFixEpoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: big.NewInt(0),
HIP6And8Epoch: big.NewInt(0),
StakingPrecompileEpoch: big.NewInt(2), // same as staking
SlotsLimitedEpoch: EpochTBD, // epoch to enable HIP-16
ChainID: PangaeaChainID,
EthCompatibleChainID: EthPangaeaShard0ChainID,
EthCompatibleShard0ChainID: EthPangaeaShard0ChainID,
EthCompatibleEpoch: big.NewInt(0),
CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2),
AggregatedRewardEpoch: big.NewInt(3),
StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(1),
QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(0),
TwoSecondsEpoch: big.NewInt(0),
SixtyPercentEpoch: big.NewInt(0),
RedelegationEpoch: big.NewInt(0),
NoEarlyUnlockEpoch: big.NewInt(0),
VRFEpoch: big.NewInt(0),
PrevVRFEpoch: big.NewInt(0),
MinDelegation100Epoch: big.NewInt(0),
MinCommissionRateEpoch: big.NewInt(0),
MinCommissionPromoPeriod: big.NewInt(10),
EPoSBound35Epoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0),
DataCopyFixEpoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: big.NewInt(0),
HIP6And8Epoch: big.NewInt(0),
StakingPrecompileEpoch: big.NewInt(2), // same as staking
SlotsLimitedEpoch: EpochTBD, // epoch to enable HIP-16
CrossShardXferPrecompileEpoch: big.NewInt(1), // cross tx + 1
}
// PartnerChainConfig contains the chain parameters for the Partner network.
// All features except for CrossLink are enabled at launch.
PartnerChainConfig = &ChainConfig{
ChainID: PartnerChainID,
EthCompatibleChainID: EthPartnerShard0ChainID,
EthCompatibleShard0ChainID: EthPartnerShard0ChainID,
EthCompatibleEpoch: big.NewInt(0),
CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2),
AggregatedRewardEpoch: big.NewInt(3),
StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(1),
QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(0),
TwoSecondsEpoch: big.NewInt(0),
SixtyPercentEpoch: big.NewInt(4),
RedelegationEpoch: big.NewInt(0),
NoEarlyUnlockEpoch: big.NewInt(0),
VRFEpoch: big.NewInt(0),
PrevVRFEpoch: big.NewInt(0),
MinDelegation100Epoch: big.NewInt(0),
MinCommissionRateEpoch: big.NewInt(0),
MinCommissionPromoPeriod: big.NewInt(10),
EPoSBound35Epoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0),
DataCopyFixEpoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: big.NewInt(0),
HIP6And8Epoch: big.NewInt(0),
StakingPrecompileEpoch: big.NewInt(2),
SlotsLimitedEpoch: EpochTBD, // epoch to enable HIP-16
ChainID: PartnerChainID,
EthCompatibleChainID: EthPartnerShard0ChainID,
EthCompatibleShard0ChainID: EthPartnerShard0ChainID,
EthCompatibleEpoch: big.NewInt(0),
CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2),
AggregatedRewardEpoch: big.NewInt(3),
StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(1),
QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(0),
TwoSecondsEpoch: big.NewInt(0),
SixtyPercentEpoch: big.NewInt(4),
RedelegationEpoch: big.NewInt(0),
NoEarlyUnlockEpoch: big.NewInt(0),
VRFEpoch: big.NewInt(0),
PrevVRFEpoch: big.NewInt(0),
MinDelegation100Epoch: big.NewInt(0),
MinCommissionRateEpoch: big.NewInt(0),
MinCommissionPromoPeriod: big.NewInt(10),
EPoSBound35Epoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0),
DataCopyFixEpoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: big.NewInt(0),
HIP6And8Epoch: big.NewInt(0),
StakingPrecompileEpoch: big.NewInt(2),
SlotsLimitedEpoch: EpochTBD, // epoch to enable HIP-16
CrossShardXferPrecompileEpoch: big.NewInt(1), // cross tx + 1
}
// StressnetChainConfig contains the chain parameters for the Stress test network.
// All features except for CrossLink are enabled at launch.
StressnetChainConfig = &ChainConfig{
ChainID: StressnetChainID,
EthCompatibleChainID: EthStressnetShard0ChainID,
EthCompatibleShard0ChainID: EthStressnetShard0ChainID,
EthCompatibleEpoch: big.NewInt(0),
CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2),
AggregatedRewardEpoch: big.NewInt(3),
StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(1),
QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(0),
TwoSecondsEpoch: big.NewInt(0),
SixtyPercentEpoch: big.NewInt(10),
RedelegationEpoch: big.NewInt(0),
NoEarlyUnlockEpoch: big.NewInt(0),
VRFEpoch: big.NewInt(0),
PrevVRFEpoch: big.NewInt(0),
MinDelegation100Epoch: big.NewInt(0),
MinCommissionRateEpoch: big.NewInt(0),
MinCommissionPromoPeriod: big.NewInt(10),
EPoSBound35Epoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0),
DataCopyFixEpoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: big.NewInt(0),
HIP6And8Epoch: big.NewInt(0),
StakingPrecompileEpoch: big.NewInt(2),
SlotsLimitedEpoch: EpochTBD, // epoch to enable HIP-16
ChainID: StressnetChainID,
EthCompatibleChainID: EthStressnetShard0ChainID,
EthCompatibleShard0ChainID: EthStressnetShard0ChainID,
EthCompatibleEpoch: big.NewInt(0),
CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2),
AggregatedRewardEpoch: big.NewInt(3),
StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(1),
QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(0),
TwoSecondsEpoch: big.NewInt(0),
SixtyPercentEpoch: big.NewInt(10),
RedelegationEpoch: big.NewInt(0),
NoEarlyUnlockEpoch: big.NewInt(0),
VRFEpoch: big.NewInt(0),
PrevVRFEpoch: big.NewInt(0),
MinDelegation100Epoch: big.NewInt(0),
MinCommissionRateEpoch: big.NewInt(0),
MinCommissionPromoPeriod: big.NewInt(10),
EPoSBound35Epoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0),
DataCopyFixEpoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: big.NewInt(0),
HIP6And8Epoch: big.NewInt(0),
StakingPrecompileEpoch: big.NewInt(2),
SlotsLimitedEpoch: EpochTBD, // epoch to enable HIP-16
CrossShardXferPrecompileEpoch: big.NewInt(1), // cross tx + 1
}
// LocalnetChainConfig contains the chain parameters to run for local development.
LocalnetChainConfig = &ChainConfig{
ChainID: TestnetChainID,
EthCompatibleChainID: EthTestnetShard0ChainID,
EthCompatibleShard0ChainID: EthTestnetShard0ChainID,
EthCompatibleEpoch: big.NewInt(0),
CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2),
AggregatedRewardEpoch: big.NewInt(3),
StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(0),
QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(0),
TwoSecondsEpoch: big.NewInt(3),
SixtyPercentEpoch: EpochTBD, // Never enable it for localnet as localnet has no external validator setup
RedelegationEpoch: big.NewInt(0),
NoEarlyUnlockEpoch: big.NewInt(0),
VRFEpoch: big.NewInt(0),
PrevVRFEpoch: big.NewInt(0),
MinDelegation100Epoch: big.NewInt(0),
MinCommissionRateEpoch: big.NewInt(0),
MinCommissionPromoPeriod: big.NewInt(10),
EPoSBound35Epoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0),
DataCopyFixEpoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: big.NewInt(0),
HIP6And8Epoch: EpochTBD, // Never enable it for localnet as localnet has no external validator setup
StakingPrecompileEpoch: big.NewInt(2),
SlotsLimitedEpoch: EpochTBD, // epoch to enable HIP-16
ChainID: TestnetChainID,
EthCompatibleChainID: EthTestnetShard0ChainID,
EthCompatibleShard0ChainID: EthTestnetShard0ChainID,
EthCompatibleEpoch: big.NewInt(0),
CrossTxEpoch: big.NewInt(0),
CrossLinkEpoch: big.NewInt(2),
AggregatedRewardEpoch: big.NewInt(3),
StakingEpoch: big.NewInt(2),
PreStakingEpoch: big.NewInt(0),
QuickUnlockEpoch: big.NewInt(0),
FiveSecondsEpoch: big.NewInt(0),
TwoSecondsEpoch: big.NewInt(3),
SixtyPercentEpoch: EpochTBD, // Never enable it for localnet as localnet has no external validator setup
RedelegationEpoch: big.NewInt(0),
NoEarlyUnlockEpoch: big.NewInt(0),
VRFEpoch: big.NewInt(0),
PrevVRFEpoch: big.NewInt(0),
MinDelegation100Epoch: big.NewInt(0),
MinCommissionRateEpoch: big.NewInt(0),
MinCommissionPromoPeriod: big.NewInt(10),
EPoSBound35Epoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0),
DataCopyFixEpoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0),
SHA3Epoch: big.NewInt(0),
HIP6And8Epoch: EpochTBD, // Never enable it for localnet as localnet has no external validator setup
StakingPrecompileEpoch: big.NewInt(2),
SlotsLimitedEpoch: EpochTBD, // epoch to enable HIP-16
CrossShardXferPrecompileEpoch: big.NewInt(1), // cross tx + 1
}
// AllProtocolChanges ...
@ -275,6 +281,7 @@ var (
big.NewInt(0), // HIP6And8Epoch
big.NewInt(0), // StakingPrecompileEpoch
big.NewInt(0), // SlotsLimitedEpoch
big.NewInt(1), // CrossShardXferPrecompileEpoch
}
// TestChainConfig ...
@ -311,6 +318,7 @@ var (
big.NewInt(0), // HIP6And8Epoch
big.NewInt(0), // StakingPrecompileEpoch
big.NewInt(0), // SlotsLimitedEpoch
big.NewInt(1), // CrossShardXferPrecompileEpoch
}
// TestRules ...
@ -431,11 +439,14 @@ type ChainConfig struct {
// SlotsLimitedEpoch is the first epoch to enable HIP-16.
SlotsLimitedEpoch *big.Int `json:"slots-limit-epoch,omitempty"`
// CrossShardXferPrecompileEpoch is the first epoch to feature cross shard transfer precompile
CrossShardXferPrecompileEpoch *big.Int `json:"cross-shard-xfer-precompile-epoch,omitempty"`
}
// String implements the fmt.Stringer interface.
func (c *ChainConfig) String() string {
return fmt.Sprintf("{ChainID: %v EthCompatibleChainID: %v EIP155: %v CrossTx: %v Staking: %v CrossLink: %v ReceiptLog: %v SHA3Epoch: %v StakingPrecompileEpoch: %v}",
return fmt.Sprintf("{ChainID: %v EthCompatibleChainID: %v EIP155: %v CrossTx: %v Staking: %v CrossLink: %v ReceiptLog: %v SHA3Epoch: %v StakingPrecompileEpoch: %v CrossShardXferPrecompileEpoch: %v}",
c.ChainID,
c.EthCompatibleChainID,
c.EIP155Epoch,
@ -445,6 +456,7 @@ func (c *ChainConfig) String() string {
c.ReceiptLogEpoch,
c.SHA3Epoch,
c.StakingPrecompileEpoch,
c.CrossShardXferPrecompileEpoch,
)
}
@ -596,6 +608,12 @@ func (c *ChainConfig) IsStakingPrecompile(epoch *big.Int) bool {
return isForked(c.StakingPrecompileEpoch, epoch)
}
// IsCrossShardXferPrecompile determines whether the
// Cross Shard Transfer Precompile is available in the EVM
func (c *ChainConfig) IsCrossShardXferPrecompile(epoch *big.Int) bool {
return isForked(c.CrossShardXferPrecompileEpoch, epoch)
}
// UpdateEthChainIDByShard update the ethChainID based on shard ID.
func UpdateEthChainIDByShard(shardID uint32) {
once.Do(func() {
@ -644,16 +662,22 @@ func isForked(s, epoch *big.Int) bool {
// Rules is a one time interface meaning that it shouldn't be used in between transition
// phases.
type Rules struct {
ChainID *big.Int
EthChainID *big.Int
IsCrossLink, IsEIP155, IsS3, IsReceiptLog, IsIstanbul, IsVRF, IsPrevVRF, IsSHA3, IsStakingPrecompile bool
ChainID *big.Int
EthChainID *big.Int
IsCrossLink, IsEIP155, IsS3, IsReceiptLog, IsIstanbul, IsVRF, IsPrevVRF, IsSHA3,
IsStakingPrecompile, IsCrossShardXferPrecompile bool
}
// Rules ensures c's ChainID is not nil.
func (c *ChainConfig) Rules(epoch *big.Int) Rules {
if c.IsStakingPrecompile(epoch) {
if !c.IsPreStaking(epoch) {
panic("Cannot have staking precompile epoch if not prestaking epoch")
panic("cannot have staking precompile epoch if not prestaking epoch")
}
}
if c.IsCrossShardXferPrecompile(epoch) {
if !c.AcceptsCrossTx(epoch) {
panic("cannot have cross shard xfer precompile epoch if not accepting cross tx")
}
}
chainID := c.ChainID
@ -665,16 +689,17 @@ func (c *ChainConfig) Rules(epoch *big.Int) Rules {
ethChainID = new(big.Int)
}
return Rules{
ChainID: new(big.Int).Set(chainID),
EthChainID: new(big.Int).Set(ethChainID),
IsCrossLink: c.IsCrossLink(epoch),
IsEIP155: c.IsEIP155(epoch),
IsS3: c.IsS3(epoch),
IsReceiptLog: c.IsReceiptLog(epoch),
IsIstanbul: c.IsIstanbul(epoch),
IsVRF: c.IsVRF(epoch),
IsPrevVRF: c.IsPrevVRF(epoch),
IsSHA3: c.IsSHA3(epoch),
IsStakingPrecompile: c.IsStakingPrecompile(epoch),
ChainID: new(big.Int).Set(chainID),
EthChainID: new(big.Int).Set(ethChainID),
IsCrossLink: c.IsCrossLink(epoch),
IsEIP155: c.IsEIP155(epoch),
IsS3: c.IsS3(epoch),
IsReceiptLog: c.IsReceiptLog(epoch),
IsIstanbul: c.IsIstanbul(epoch),
IsVRF: c.IsVRF(epoch),
IsPrevVRF: c.IsPrevVRF(epoch),
IsSHA3: c.IsSHA3(epoch),
IsStakingPrecompile: c.IsStakingPrecompile(epoch),
IsCrossShardXferPrecompile: c.IsCrossShardXferPrecompile(epoch),
}
}

@ -2,7 +2,6 @@ package staking
import (
"bytes"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/common"
@ -126,11 +125,11 @@ func ParseStakeMsg(contractCaller common.Address, input []byte) (interface{}, er
if err != nil {
return nil, err
}
validatorAddress, err := ParseAddressFromKey(args, "validatorAddress")
validatorAddress, err := abi.ParseAddressFromKey(args, "validatorAddress")
if err != nil {
return nil, err
}
amount, err := ParseBigIntFromKey(args, "amount")
amount, err := abi.ParseBigIntFromKey(args, "amount")
if err != nil {
return nil, err
}
@ -148,12 +147,12 @@ func ParseStakeMsg(contractCaller common.Address, input []byte) (interface{}, er
if err != nil {
return nil, err
}
validatorAddress, err := ParseAddressFromKey(args, "validatorAddress")
validatorAddress, err := abi.ParseAddressFromKey(args, "validatorAddress")
if err != nil {
return nil, err
}
// this type assertion is needed by Golang
amount, err := ParseBigIntFromKey(args, "amount")
amount, err := abi.ParseBigIntFromKey(args, "amount")
if err != nil {
return nil, err
}
@ -201,7 +200,7 @@ func ParseStakeMsg(contractCaller common.Address, input []byte) (interface{}, er
// used to ensure caller == delegatorAddress
func ValidateContractAddress(contractCaller common.Address, args map[string]interface{}, key string) (common.Address, error) {
address, err := ParseAddressFromKey(args, key)
address, err := abi.ParseAddressFromKey(args, key)
if err != nil {
return common.Address{}, err
}
@ -214,23 +213,3 @@ func ValidateContractAddress(contractCaller common.Address, args map[string]inte
return address, nil
}
}
// used for both delegatorAddress and validatorAddress
func ParseAddressFromKey(args map[string]interface{}, key string) (common.Address, error) {
if address, ok := args[key].(common.Address); ok {
return address, nil
} else {
return common.Address{}, errors.Errorf("Cannot parse address from %v", args[key])
}
}
// used for amounts
func ParseBigIntFromKey(args map[string]interface{}, key string) (*big.Int, error) {
bigInt, ok := args[key].(*big.Int)
if !ok {
return nil, errors.Errorf(
"Cannot parse BigInt from %v", args[key])
} else {
return bigInt, nil
}
}

@ -24,18 +24,6 @@ func TestValidateContractAddress(t *testing.T) {
}
}
func TestParseBigIntFromKey(t *testing.T) {
args := map[string]interface{}{}
expectedError := errors.New("Cannot parse BigInt from <nil>")
if _, err := ParseBigIntFromKey(args, "PotentialBigInt"); err != nil {
if expectedError.Error() != err.Error() {
t.Errorf("Expected error %v, got %v", expectedError, err)
}
} else {
t.Errorf("Expected error %v, got result", expectedError)
}
}
type parseTest struct {
input []byte
name string

Loading…
Cancel
Save