The core protocol of WoopChain
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
woop/core/vm/contracts_write.go

146 lines
5.0 KiB

package vm
import (
"errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/staking"
stakingTypes "github.com/harmony-one/harmony/staking/types"
)
// WriteCapablePrecompiledContractsStaking lists out the write capable precompiled contracts
// which are available after the StakingPrecompileEpoch
// for now, we have only one contract at 252 or 0xfc - which is the staking precompile
var WriteCapablePrecompiledContractsStaking = map[common.Address]WriteCapablePrecompiledContract{
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
// Note that these contracts have the capability to alter the state
// while those in contracts.go do not
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)
}
// RunWriteCapablePrecompiledContract runs and evaluates the output of a write capable precompiled contract.
func RunWriteCapablePrecompiledContract(
p WriteCapablePrecompiledContract,
evm *EVM,
contract *Contract,
input []byte,
readOnly bool,
) ([]byte, error) {
// immediately error out if readOnly
if readOnly {
return nil, errWriteProtection
}
gas, err := p.RequiredGas(evm, contract, input)
if err != nil {
return nil, err
}
if !contract.UseGas(gas) {
return nil, ErrOutOfGas
}
return p.RunWriteCapable(evm, contract, input)
}
type stakingPrecompile 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 *stakingPrecompile) RequiredGas(
evm *EVM,
contract *Contract,
input []byte,
) (uint64, error) {
// if invalid data or invalid shard
// set payload to blank and charge minimum gas
var payload []byte = make([]byte, 0)
// availability of staking and precompile has already been checked
if evm.Context.ShardID == shard.BeaconChainShardID {
// check that input is well formed
// meaning all the expected parameters are available
// and that we are only trying to perform staking tx
// on behalf of the correct entity
stakeMsg, err := staking.ParseStakeMsg(contract.Caller(), input)
if err == nil {
// otherwise charge similar to a regular staking tx
if migrationMsg, ok := stakeMsg.(*stakingTypes.MigrationMsg); ok {
// charge per delegation to migrate
return evm.CalculateMigrationGas(evm.StateDB,
migrationMsg,
evm.ChainConfig().IsS3(evm.EpochNumber),
evm.ChainConfig().IsIstanbul(evm.EpochNumber),
)
} else if encoded, err := rlp.EncodeToBytes(stakeMsg); 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 (that is it performs the staking)
func (c *stakingPrecompile) RunWriteCapable(
evm *EVM,
contract *Contract,
input []byte,
) ([]byte, error) {
if evm.Context.ShardID != shard.BeaconChainShardID {
return nil, errors.New("Staking not supported on this shard")
}
stakeMsg, err := staking.ParseStakeMsg(contract.Caller(), input)
if err != nil {
return nil, err
}
if delegate, ok := stakeMsg.(*stakingTypes.Delegate); ok {
if err := evm.Delegate(evm.StateDB, delegate); err != nil {
return nil, err
} else {
evm.StakeMsgs = append(evm.StakeMsgs, delegate)
return nil, nil
}
}
if undelegate, ok := stakeMsg.(*stakingTypes.Undelegate); ok {
return nil, evm.Undelegate(evm.StateDB, undelegate)
}
if collectRewards, ok := stakeMsg.(*stakingTypes.CollectRewards); ok {
return nil, evm.CollectRewards(evm.StateDB, collectRewards)
}
// Migrate is not supported in precompile and will be done in a batch hard fork
//if migrationMsg, ok := stakeMsg.(*stakingTypes.MigrationMsg); ok {
// stakeMsgs, err := evm.MigrateDelegations(evm.StateDB, migrationMsg)
// if err != nil {
// return nil, err
// } else {
// for _, stakeMsg := range stakeMsgs {
// if delegate, ok := stakeMsg.(*stakingTypes.Delegate); ok {
// evm.StakeMsgs = append(evm.StakeMsgs, delegate)
// } else {
// return nil, errors.New("[StakingPrecompile] Received incompatible stakeMsg from evm.MigrateDelegations")
// }
// }
// return nil, nil
// }
//}
return nil, errors.New("[StakingPrecompile] Received incompatible stakeMsg from staking.ParseStakeMsg")
}