Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

precompiled: adjust the gas cost of consortium precompiled contract #376

Merged
merged 2 commits into from
Dec 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 115 additions & 51 deletions core/vm/consortium_precompiled_contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,34 @@ import (
"golang.org/x/crypto/sha3"
)

const (
LogContract = iota
SortValidator
VerifyHeaders
PickValidatorSet
GetDoubleSignSlashingConfig
ValidateFinalityVoteProof
NumOfAbis
)

var (
consortiumLogAbi = `[{"inputs":[{"internalType":"string","name":"message","type":"string"}],"name":"log","outputs":[],"stateMutability":"nonpayable","type":"function"}]`
consortiumSortValidatorAbi = `[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address[]","name":"validators","type":"address[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"}],"name":"sortValidators","outputs":[{"internalType":"address[]","name":"_validators","type":"address[]"}],"stateMutability":"view","type":"function"}]`
consortiumVerifyHeadersAbi = `[{"outputs":[],"name":"getHeader","inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"bytes32","name":"parentHash","type":"bytes32"},{"internalType":"bytes32","name":"ommersHash","type":"bytes32"},{"internalType":"address","name":"coinbase","type":"address"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"bytes32","name":"transactionsRoot","type":"bytes32"},{"internalType":"bytes32","name":"receiptsRoot","type":"bytes32"},{"internalType":"uint8[256]","name":"logsBloom","type":"uint8[256]"},{"internalType":"uint256","name":"difficulty","type":"uint256"},{"internalType":"uint256","name":"number","type":"uint256"},{"internalType":"uint64","name":"gasLimit","type":"uint64"},{"internalType":"uint64","name":"gasUsed","type":"uint64"},{"internalType":"uint64","name":"timestamp","type":"uint64"},{"internalType":"bytes","name":"extraData","type":"bytes"},{"internalType":"bytes32","name":"mixHash","type":"bytes32"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"consensusAddr","type":"address"},{"internalType":"bytes","name":"header1","type":"bytes"},{"internalType":"bytes","name":"header2","type":"bytes"}],"name":"validatingDoubleSignProof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]`
consortiumPickValidatorSetAbi = `[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address[]","name":"_candidates","type":"address[]"},{"internalType":"uint256[]","name":"_weights","type":"uint256[]"},{"internalType":"uint256[]","name":"_trustedWeights","type":"uint256[]"},{"internalType":"uint256","name":"_maxValidatorNumber","type":"uint256"},{"internalType":"uint256","name":"_maxPrioritizedValidatorNumber","type":"uint256"}],"name":"pickValidatorSet","outputs":[{"internalType":"address[]","name":"_validators","type":"address[]"}],"stateMutability":"view","type":"function"}]`
getDoubleSignSlashingConfigsAbi = `[{"inputs":[],"name":"getDoubleSignSlashingConfigs","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]`
validateFinalityVoteProofAbi = `[{"inputs":[{"internalType":"bytes","name":"voterPublicKey","type":"bytes"},{"internalType":"uint256","name":"targetBlockNumber","type":"uint256"},{"internalType":"bytes32[2]","name":"targetBlockHash","type":"bytes32[2]"},{"internalType":"bytes[][2]","name":"listOfPublicKey","type":"bytes[][2]"},{"internalType":"bytes[2]","name":"aggregatedSignature","type":"bytes[2]"}],"name":"validateFinalityVoteProof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]`
rawConsortiumLogAbi = `[{"inputs":[{"internalType":"string","name":"message","type":"string"}],"name":"log","outputs":[],"stateMutability":"nonpayable","type":"function"}]`
rawConsortiumSortValidatorAbi = `[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address[]","name":"validators","type":"address[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"}],"name":"sortValidators","outputs":[{"internalType":"address[]","name":"_validators","type":"address[]"}],"stateMutability":"view","type":"function"}]`
rawConsortiumVerifyHeadersAbi = `[{"outputs":[],"name":"getHeader","inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"bytes32","name":"parentHash","type":"bytes32"},{"internalType":"bytes32","name":"ommersHash","type":"bytes32"},{"internalType":"address","name":"coinbase","type":"address"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"bytes32","name":"transactionsRoot","type":"bytes32"},{"internalType":"bytes32","name":"receiptsRoot","type":"bytes32"},{"internalType":"uint8[256]","name":"logsBloom","type":"uint8[256]"},{"internalType":"uint256","name":"difficulty","type":"uint256"},{"internalType":"uint256","name":"number","type":"uint256"},{"internalType":"uint64","name":"gasLimit","type":"uint64"},{"internalType":"uint64","name":"gasUsed","type":"uint64"},{"internalType":"uint64","name":"timestamp","type":"uint64"},{"internalType":"bytes","name":"extraData","type":"bytes"},{"internalType":"bytes32","name":"mixHash","type":"bytes32"},{"internalType":"uint64","name":"nonce","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"consensusAddr","type":"address"},{"internalType":"bytes","name":"header1","type":"bytes"},{"internalType":"bytes","name":"header2","type":"bytes"}],"name":"validatingDoubleSignProof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]`
rawConsortiumPickValidatorSetAbi = `[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address[]","name":"_candidates","type":"address[]"},{"internalType":"uint256[]","name":"_weights","type":"uint256[]"},{"internalType":"uint256[]","name":"_trustedWeights","type":"uint256[]"},{"internalType":"uint256","name":"_maxValidatorNumber","type":"uint256"},{"internalType":"uint256","name":"_maxPrioritizedValidatorNumber","type":"uint256"}],"name":"pickValidatorSet","outputs":[{"internalType":"address[]","name":"_validators","type":"address[]"}],"stateMutability":"view","type":"function"}]`
rawGetDoubleSignSlashingConfigsAbi = `[{"inputs":[],"name":"getDoubleSignSlashingConfigs","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]`
rawValidateFinalityVoteProofAbi = `[{"inputs":[{"internalType":"bytes","name":"voterPublicKey","type":"bytes"},{"internalType":"uint256","name":"targetBlockNumber","type":"uint256"},{"internalType":"bytes32[2]","name":"targetBlockHash","type":"bytes32[2]"},{"internalType":"bytes[][2]","name":"listOfPublicKey","type":"bytes[][2]"},{"internalType":"bytes[2]","name":"aggregatedSignature","type":"bytes[2]"}],"name":"validateFinalityVoteProof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]`

rawABIs = [NumOfAbis]string{
LogContract: rawConsortiumLogAbi,
SortValidator: rawConsortiumSortValidatorAbi,
VerifyHeaders: rawConsortiumVerifyHeadersAbi,
PickValidatorSet: rawConsortiumPickValidatorSetAbi,
GetDoubleSignSlashingConfig: rawGetDoubleSignSlashingConfigsAbi,
ValidateFinalityVoteProof: rawValidateFinalityVoteProofAbi,
}

unmarshalledABIs = [NumOfAbis]*abi.ABI{}
)

const (
Expand All @@ -48,6 +69,17 @@ const (
maxBlsPublicKeyListLength = 100
)

func init() {
for i, rawABI := range rawABIs {
unmarshalledABI, err := abi.JSON(strings.NewReader(rawABI))
if err != nil {
log.Error("Failed to unmarshalled precompiled ABI", "num", i)
} else {
unmarshalledABIs[i] = &unmarshalledABI
}
}
}

func PrecompiledContractsConsortium(caller ContractRef, evm *EVM) map[common.Address]PrecompiledContract {
return map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{101}): &consortiumLog{},
Expand All @@ -68,7 +100,7 @@ func (c *consortiumLog) Run(input []byte) ([]byte, error) {
if os.Getenv("DEBUG") != "true" {
return input, nil
}
_, method, args, err := loadMethodAndArgs(consortiumLogAbi, input)
_, method, args, err := loadMethodAndArgs(LogContract, input)
if err != nil {
return nil, err
}
Expand All @@ -84,24 +116,44 @@ func (c *consortiumLog) Run(input []byte) ([]byte, error) {
return input, nil
}

func isSystemContractCaller(caller ContractRef, evm *EVM) error {
// These 2 fields are nil in benchmark only
if caller != nil && evm != nil {
if evm.ChainConfig().ConsortiumV2Contracts == nil {
return errors.New("cannot find consortium v2 contracts")
}
if !evm.ChainConfig().ConsortiumV2Contracts.IsSystemContract(caller.Address()) {
return errors.New("unauthorized sender")
}
}

return nil
}

type consortiumPickValidatorSet struct {
caller ContractRef
evm *EVM
}

func (c *consortiumPickValidatorSet) RequiredGas(_ []byte) uint64 {
return 0
func (c *consortiumPickValidatorSet) RequiredGas(input []byte) uint64 {
// c.evm is nil in benchmark
if c.evm == nil || c.evm.chainRules.IsMiko {
// We approximate the number of validators by dividing the length of input by
// length of address (20). This is likely an overestimation because there are
// slices of weight, maxValidatorNumber and maxPrioritizedValidatorNumber in
// the input too.
return uint64((len(input) / common.AddressLength)) * params.ValidatorSortingBaseGas
} else {
return 0
}
}

func (c *consortiumPickValidatorSet) Run(input []byte) ([]byte, error) {
if c.evm.ChainConfig().ConsortiumV2Contracts == nil {
return nil, errors.New("cannot find consortium v2 contracts")
}
if !c.evm.ChainConfig().ConsortiumV2Contracts.IsSystemContract(c.caller.Address()) {
return nil, errors.New("unauthorized sender")
if err := isSystemContractCaller(c.caller, c.evm); err != nil {
return nil, err
}
// get method, args from abi
_, method, args, err := loadMethodAndArgs(consortiumPickValidatorSetAbi, input)
_, method, args, err := loadMethodAndArgs(PickValidatorSet, input)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -216,19 +268,24 @@ type consortiumValidatorSorting struct {
evm *EVM
}

func (c *consortiumValidatorSorting) RequiredGas(_ []byte) uint64 {
return 0
func (c *consortiumValidatorSorting) RequiredGas(input []byte) uint64 {
// c.evm is nil in benchmark
if c.evm == nil || c.evm.chainRules.IsMiko {
// We approximate the number of validators by dividing the length of input by
// length of address (20). This is likely an overestimation because there is
// a slice of weight in the input too.
return uint64((len(input) / common.AddressLength)) * params.ValidatorSortingBaseGas
} else {
return 0
}
}

func (c *consortiumValidatorSorting) Run(input []byte) ([]byte, error) {
if c.evm.ChainConfig().ConsortiumV2Contracts == nil {
return nil, errors.New("cannot find consortium v2 contracts")
}
if !c.evm.ChainConfig().ConsortiumV2Contracts.IsSystemContract(c.caller.Address()) {
return nil, errors.New("unauthorized sender")
if err := isSystemContractCaller(c.caller, c.evm); err != nil {
return nil, err
}
// get method, args from abi
_, method, args, err := loadMethodAndArgs(consortiumSortValidatorAbi, input)
_, method, args, err := loadMethodAndArgs(SortValidator, input)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -271,8 +328,12 @@ func (s *SortableValidators) Len() int {

func (s *SortableValidators) Less(i, j int) bool {
cmp := s.weights[i].Cmp(s.weights[j])
addrsCmp := big.NewInt(0).SetBytes(s.validators[i].Bytes()).Cmp(big.NewInt(0).SetBytes(s.validators[j].Bytes())) > 0
return cmp > 0 || (cmp == 0 && addrsCmp)

if cmp == 0 {
return new(big.Int).SetBytes(s.validators[i].Bytes()).Cmp(new(big.Int).SetBytes(s.validators[j].Bytes())) > 0
}

return cmp > 0
}

func (s *SortableValidators) Swap(i, j int) {
Expand Down Expand Up @@ -335,16 +396,17 @@ func staticCall(evm *EVM, smcAbi abi.ABI, method string, contract, sender common
return out, nil
}

func loadMethodAndArgs(smcAbi string, input []byte) (abi.ABI, *abi.Method, []interface{}, error) {
func loadMethodAndArgs(contractIndex int, input []byte) (abi.ABI, *abi.Method, []interface{}, error) {
var (
pAbi abi.ABI
err error
method *abi.Method
args []interface{}
)
if pAbi, err = abi.JSON(strings.NewReader(smcAbi)); err != nil {
return abi.ABI{}, nil, nil, err
if contractIndex < 0 || contractIndex >= len(unmarshalledABIs) || unmarshalledABIs[contractIndex] == nil {
return abi.ABI{}, nil, nil, errors.New("invalid contract index")
}
pAbi = *unmarshalledABIs[contractIndex]
if method, err = pAbi.MethodById(input); err != nil {
return abi.ABI{}, nil, nil, err
}
Expand All @@ -364,18 +426,20 @@ type consortiumVerifyHeaders struct {
}

func (c *consortiumVerifyHeaders) RequiredGas(_ []byte) uint64 {
return 0
// c.evm is nil in benchmark
if c.evm == nil || c.evm.chainRules.IsMiko {
return params.VerifyFinalityHeadersProofGas
} else {
return 0
}
}

func (c *consortiumVerifyHeaders) Run(input []byte) ([]byte, error) {
if c.evm.ChainConfig().ConsortiumV2Contracts == nil {
return nil, errors.New("cannot find consortium v2 contracts")
}
if !c.evm.ChainConfig().ConsortiumV2Contracts.IsSystemContract(c.caller.Address()) {
return nil, errors.New("unauthorized sender")
if err := isSystemContractCaller(c.caller, c.evm); err != nil {
return nil, err
}
// get method, args from abi
smcAbi, method, args, err := loadMethodAndArgs(consortiumVerifyHeadersAbi, input)
smcAbi, method, args, err := loadMethodAndArgs(VerifyHeaders, input)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -438,7 +502,8 @@ func (c *consortiumVerifyHeaders) getSigner(header types.BlockHeader) (common.Ad
func (c *consortiumVerifyHeaders) verify(consensusAddr common.Address, header1, header2 types.BlockHeader) bool {
var maxOffset *big.Int

if !c.evm.chainConfig.IsConsortiumV2(header1.Number) {
// c.evm s nil in benchmark, so we skip this check in benchmark
if c.evm != nil && !c.evm.chainConfig.IsConsortiumV2(header1.Number) {
return false
}
if header1.ToHeader().ParentHash.Hex() != header2.ToHeader().ParentHash.Hex() {
Expand All @@ -460,7 +525,10 @@ func (c *consortiumVerifyHeaders) verify(consensusAddr common.Address, header1,
log.Trace("[consortiumVerifyHeaders][verify] error while getting signer from header2", "err", err)
return false
}
methodAbi, _ := abi.JSON(strings.NewReader(getDoubleSignSlashingConfigsAbi))
if unmarshalledABIs[GetDoubleSignSlashingConfig] == nil {
return false
}
methodAbi := *unmarshalledABIs[GetDoubleSignSlashingConfig]

if c.test {
maxOffset = big.NewInt(doubleSigningOffsetTest)
Expand All @@ -487,10 +555,13 @@ func (c *consortiumVerifyHeaders) verify(consensusAddr common.Address, header1,
}
}

currentBlock := c.evm.Context.BlockNumber
// What if current block < header1.Number?
if currentBlock.Cmp(header1.Number) > 0 && new(big.Int).Sub(currentBlock, header1.Number).Cmp(maxOffset) > 0 {
return false
// c.evm is nil in benchmark, so we skip this check in benchmark
if c.evm != nil {
currentBlock := c.evm.Context.BlockNumber
// What if current block < header1.Number?
if currentBlock.Cmp(header1.Number) > 0 && new(big.Int).Sub(currentBlock, header1.Number).Cmp(maxOffset) > 0 {
return false
}
}

return signer1.Hex() == signer2.Hex() &&
Expand Down Expand Up @@ -540,17 +611,10 @@ func (contract *consortiumValidateFinalityProof) RequiredGas(input []byte) uint6
}

func (contract *consortiumValidateFinalityProof) Run(input []byte) ([]byte, error) {
// These 2 fields are nil in testing only
if contract.caller != nil && contract.evm != nil {
if contract.evm.ChainConfig().ConsortiumV2Contracts == nil {
return nil, errors.New("cannot find consortium v2 contracts")
}
if !contract.evm.ChainConfig().ConsortiumV2Contracts.IsSystemContract(contract.caller.Address()) {
return nil, errors.New("unauthorized sender")
}
if err := isSystemContractCaller(contract.caller, contract.evm); err != nil {
return nil, err
}

_, method, args, err := loadMethodAndArgs(validateFinalityVoteProofAbi, input)
_, method, args, err := loadMethodAndArgs(ValidateFinalityVoteProof, input)
if err != nil {
return nil, err
}
Expand Down
Loading