evm: don't return extcode for validators

Due to technical debt, validator information is stored in the code field
of the address. The code field can be accessed in Solidity for an
arbitrary address using `extcodesize`, `extcodehash`, and `extcodecopy`
or helper commands (such as `address.code.Length`). The presence of this
field is used by contract developers to (erroneously) deny smart
contract access to other smart contracts (and therefore, validators).
This PR fixes that oversight by returning the same values as other EOAs
for known validator addresses. Obviously, it needs a hard fork that will
be scheduled separately.
pull/4383/head
MaxMustermann2 2 years ago committed by Casey Gardiner
parent 3f0a11638b
commit ab4a10547a
  1. 32
      core/vm/instructions.go
  2. 23
      internal/params/config.go

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common/math"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/shard"
"golang.org/x/crypto/sha3"
)
@ -477,6 +478,16 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contrac
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
slot := stack.peek()
address := common.BigToAddress(slot)
fixValidatorCode := interpreter.evm.chainRules.IsValidatorCodeFix &&
interpreter.evm.ShardID == shard.BeaconChainShardID &&
interpreter.evm.StateDB.IsValidator(address)
if fixValidatorCode {
// https://github.com/ethereum/solidity/blob/develop/Changelog.md#081-2021-01-27
// per this link, <address>.code.length calls extcodesize on the address so this fix will work
slot.SetUint64(0)
return nil, nil
}
slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.BigToAddress(slot))))
return nil, nil
@ -509,7 +520,17 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract,
codeOffset = stack.pop()
length = stack.pop()
)
codeCopy := getDataBig(interpreter.evm.StateDB.GetCode(addr), codeOffset, length)
var code []byte
fixValidatorCode := interpreter.evm.chainRules.IsValidatorCodeFix &&
interpreter.evm.ShardID == shard.BeaconChainShardID &&
interpreter.evm.StateDB.IsValidator(addr)
if fixValidatorCode {
// for EOAs that are not validators, statedb returns nil
code = nil
} else {
code = interpreter.evm.StateDB.GetCode(addr)
}
codeCopy := getDataBig(code, codeOffset, length)
memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
interpreter.intPool.put(memOffset, codeOffset, length)
@ -555,7 +576,14 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, contract *Contract,
if interpreter.evm.StateDB.Empty(address) {
slot.SetUint64(0)
} else {
slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes())
fixValidatorCode := interpreter.evm.chainRules.IsValidatorCodeFix &&
interpreter.evm.ShardID == shard.BeaconChainShardID &&
interpreter.evm.StateDB.IsValidator(address)
if fixValidatorCode {
slot.SetBytes(emptyCodeHash.Bytes())
} else {
slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes())
}
}
return nil, nil
}

@ -72,6 +72,7 @@ var (
FeeCollectEpoch: EpochTBD,
LeaderRotationEpoch: EpochTBD,
LeaderRotationBlocksCount: 64,
ValidatorCodeFixEpoch: EpochTBD,
}
// TestnetChainConfig contains the chain parameters to run a node on the harmony test network.
@ -112,6 +113,7 @@ var (
LeaderRotationEpoch: EpochTBD,
LeaderRotationBlocksCount: 64,
FeeCollectEpoch: EpochTBD,
ValidatorCodeFixEpoch: EpochTBD,
}
// PangaeaChainConfig contains the chain parameters for the Pangaea network.
// All features except for CrossLink are enabled at launch.
@ -152,6 +154,7 @@ var (
LeaderRotationEpoch: EpochTBD,
LeaderRotationBlocksCount: 64,
FeeCollectEpoch: EpochTBD,
ValidatorCodeFixEpoch: EpochTBD,
}
// PartnerChainConfig contains the chain parameters for the Partner network.
@ -193,6 +196,7 @@ var (
FeeCollectEpoch: big.NewInt(574),
LeaderRotationEpoch: EpochTBD,
LeaderRotationBlocksCount: 64,
ValidatorCodeFixEpoch: EpochTBD,
}
// StressnetChainConfig contains the chain parameters for the Stress test network.
@ -234,6 +238,7 @@ var (
FeeCollectEpoch: EpochTBD,
LeaderRotationEpoch: EpochTBD,
LeaderRotationBlocksCount: 64,
ValidatorCodeFixEpoch: EpochTBD,
}
// LocalnetChainConfig contains the chain parameters to run for local development.
@ -274,6 +279,7 @@ var (
LeaderRotationEpoch: EpochTBD,
LeaderRotationBlocksCount: 5,
FeeCollectEpoch: big.NewInt(5),
ValidatorCodeFixEpoch: EpochTBD,
}
// AllProtocolChanges ...
@ -316,6 +322,7 @@ var (
big.NewInt(1), // LeaderRotationEpoch
64, // LeaderRotationBlocksCount
big.NewInt(0), // FeeCollectEpoch
big.NewInt(0), // ValidatorCodeFixEpoch
}
// TestChainConfig ...
@ -358,6 +365,7 @@ var (
big.NewInt(1), // LeaderRotationEpoch
64, // LeaderRotationBlocksCount
big.NewInt(0), // FeeCollectEpoch
big.NewInt(0), // ValidatorCodeFixEpoch
}
// TestRules ...
@ -507,6 +515,13 @@ type ChainConfig struct {
// Then before FeeCollectEpoch, txn fees are burned.
// After FeeCollectEpoch, txn fees paid to FeeCollector account.
FeeCollectEpoch *big.Int
// ValidatorCodeFixEpoch is the first epoch that fixes the issue of validator code
// being available in Solidity. This is a temporary fix until we have a better
// solution.
// Contracts can check the (presence of) validator code by calling the following:
// extcodesize, extcodecopy and extcodehash.
ValidatorCodeFixEpoch *big.Int `json:"validator-code-fix-epoch,omitempty"`
}
// String implements the fmt.Stringer interface.
@ -541,6 +556,8 @@ func (c *ChainConfig) mustValid() {
"must satisfy: StakingPrecompileEpoch >= PreStakingEpoch")
require(c.CrossShardXferPrecompileEpoch.Cmp(c.CrossTxEpoch) > 0,
"must satisfy: CrossShardXferPrecompileEpoch > CrossTxEpoch")
require(c.ValidatorCodeFixEpoch.Cmp(c.EthCompatibleEpoch) >= 0,
"must satisfy: ValidatorCodeFixEpoch >= EthCompatibleEpoch")
}
// IsEIP155 returns whether epoch is either equal to the EIP155 fork epoch or greater.
@ -716,6 +733,10 @@ func (c *ChainConfig) IsFeeCollectEpoch(epoch *big.Int) bool {
return isForked(c.FeeCollectEpoch, epoch)
}
func (c *ChainConfig) IsValidatorCodeFix(epoch *big.Int) bool {
return isForked(c.ValidatorCodeFixEpoch, epoch)
}
// UpdateEthChainIDByShard update the ethChainID based on shard ID.
func UpdateEthChainIDByShard(shardID uint32) {
once.Do(func() {
@ -773,6 +794,7 @@ type Rules struct {
IsStakingPrecompile, IsCrossShardXferPrecompile,
// eip-155 chain id fix
IsChainIdFix bool
IsValidatorCodeFix bool
}
// Rules ensures c's ChainID is not nil.
@ -797,5 +819,6 @@ func (c *ChainConfig) Rules(epoch *big.Int) Rules {
IsStakingPrecompile: c.IsStakingPrecompile(epoch),
IsCrossShardXferPrecompile: c.IsCrossShardXferPrecompile(epoch),
IsChainIdFix: c.IsChainIdFix(epoch),
IsValidatorCodeFix: c.IsValidatorCodeFix(epoch),
}
}

Loading…
Cancel
Save