From 455fef7d2ff427d428ed596d4c9c3dc616260426 Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Wed, 14 Feb 2024 17:09:30 +0100 Subject: [PATCH 01/16] validator set precompile --- consensus/polybft/polybft.go | 7 + consensus/polybft/validators_snapshot.go | 16 +- contracts/system_addresses.go | 4 +- helper/predeployment/predeployment.go | 2 +- state/executor.go | 17 +- state/executor_test.go | 4 +- .../precompiled/native_transfer_test.go | 5 + state/runtime/precompiled/precompiled.go | 11 +- .../precompiled/validator_set_precompile.go | 95 +++++++++++ .../validator_set_precompile_test.go | 158 ++++++++++++++++++ tests/tests | 2 +- 11 files changed, 300 insertions(+), 21 deletions(-) create mode 100644 state/runtime/precompiled/validator_set_precompile.go create mode 100644 state/runtime/precompiled/validator_set_precompile_test.go diff --git a/consensus/polybft/polybft.go b/consensus/polybft/polybft.go index a9f2d5328d..46291941fc 100644 --- a/consensus/polybft/polybft.go +++ b/consensus/polybft/polybft.go @@ -471,6 +471,9 @@ func (p *Polybft) Initialize() error { executor: p.config.Executor, } + // enable validatorset precompile + p.config.Executor.SetValidatorSetBackend(p) + // create bridge and consensus topics if err = p.createTopics(); err != nil { return fmt.Errorf("cannot create topics: %w", err) @@ -754,6 +757,10 @@ func (p *Polybft) GetValidators(blockNumber uint64, parents []*types.Header) (va return p.validatorsCache.GetSnapshot(blockNumber, parents, nil) } +func (p *Polybft) GetValidatorsForBlock(blockNumber uint64) (validator.AccountSet, error) { + return p.validatorsCache.GetSnapshot(blockNumber, nil, nil) +} + func (p *Polybft) GetValidatorsWithTx(blockNumber uint64, parents []*types.Header, dbTx *bolt.Tx) (validator.AccountSet, error) { return p.validatorsCache.GetSnapshot(blockNumber, parents, dbTx) diff --git a/consensus/polybft/validators_snapshot.go b/consensus/polybft/validators_snapshot.go index 8d8fecf7fa..1a6e0b15cc 100644 --- a/consensus/polybft/validators_snapshot.go +++ b/consensus/polybft/validators_snapshot.go @@ -182,15 +182,13 @@ func (v *validatorsSnapshotCache) computeSnapshot( v.logger.Trace("Compute snapshot started...", "BlockNumber", nextEpochEndBlockNumber) - if len(parents) > 0 { - for i := len(parents) - 1; i >= 0; i-- { - parentHeader := parents[i] - if parentHeader.Number == nextEpochEndBlockNumber { - v.logger.Trace("Compute snapshot. Found header in parents", "Header", parentHeader.Number) - header = parentHeader - - break - } + for i := len(parents) - 1; i >= 0; i-- { + parentHeader := parents[i] + if parentHeader.Number == nextEpochEndBlockNumber { + v.logger.Trace("Compute snapshot. Found header in parents", "Header", parentHeader.Number) + header = parentHeader + + break } } diff --git a/contracts/system_addresses.go b/contracts/system_addresses.go index 1e8bf3daf6..f3b45a2b13 100644 --- a/contracts/system_addresses.go +++ b/contracts/system_addresses.go @@ -95,8 +95,10 @@ var ( // NativeTransferPrecompile is an address of native transfer precompile NativeTransferPrecompile = types.StringToAddress("0x2020") - // BLSAggSigsVerificationPrecompile is an address of BLS aggregated signatures verificatin precompile + // BLSAggSigsVerificationPrecompile is an address of BLS aggregated signatures verification precompile BLSAggSigsVerificationPrecompile = types.StringToAddress("0x2030") + // ValidatorSetPrecompile is an address of precompile which provides some validatorSet functionalities to smart contracts + ValidatorSetPrecompile = types.StringToAddress("0x2040") // ConsolePrecompile is and address of Hardhat console precompile ConsolePrecompile = types.StringToAddress("0x000000000000000000636F6e736F6c652e6c6f67") // AllowListContractsAddr is the address of the contract deployer allow list diff --git a/helper/predeployment/predeployment.go b/helper/predeployment/predeployment.go index ed152d7de5..324186f386 100644 --- a/helper/predeployment/predeployment.go +++ b/helper/predeployment/predeployment.go @@ -68,7 +68,7 @@ func getPredeployAccount(address types.Address, input []byte, config := chain.AllForksEnabled.At(0) // Create a transition - transition := state.NewTransition(hclog.NewNullLogger(), config, snapshot, radix) + transition := state.NewTransition(hclog.NewNullLogger(), config, snapshot, radix, nil) transition.ContextPtr().ChainID = chainID // Run the transition through the EVM diff --git a/state/executor.go b/state/executor.go index 4f094c0c86..adf209dd59 100644 --- a/state/executor.go +++ b/state/executor.go @@ -45,6 +45,10 @@ type Executor struct { PostHook func(txn *Transition) GenesisPostHook func(*Transition) error + + // this value should be set if we want to enable validator set precompile + // NOTE that this precompile wont be enabled for WriteGenesis + validatorSetBackend precompiled.ValidatoSetPrecompiledBackend } // NewExecutor creates a new executor @@ -81,7 +85,7 @@ func (e *Executor) WriteGenesis( ChainID: e.config.ChainID, } - transition := NewTransition(e.logger, config, snap, txn) + transition := NewTransition(e.logger, config, snap, txn, nil) transition.ctx = env for addr, account := range alloc { @@ -199,7 +203,7 @@ func (e *Executor) BeginTxn( BurnContract: burnContract, } - t := NewTransition(e.logger, forkConfig, snap, newTxn) + t := NewTransition(e.logger, forkConfig, snap, newTxn, e.validatorSetBackend) t.PostHook = e.PostHook t.getHash = e.GetHash(header) t.ctx = txCtx @@ -235,6 +239,10 @@ func (e *Executor) BeginTxn( return t, nil } +func (e *Executor) SetValidatorSetBackend(validatorSetBackend precompiled.ValidatoSetPrecompiledBackend) { + e.validatorSetBackend = validatorSetBackend +} + type Transition struct { logger hclog.Logger @@ -272,14 +280,15 @@ type Transition struct { accessList *runtime.AccessList } -func NewTransition(logger hclog.Logger, config chain.ForksInTime, snap Snapshot, radix *Txn) *Transition { +func NewTransition(logger hclog.Logger, config chain.ForksInTime, + snap Snapshot, radix *Txn, validatorSetBackend precompiled.ValidatoSetPrecompiledBackend) *Transition { return &Transition{ logger: logger, config: config, state: radix, snap: snap, evm: evm.NewEVM(), - precompiles: precompiled.NewPrecompiled(), + precompiles: precompiled.NewPrecompiled(validatorSetBackend), journal: &runtime.Journal{}, accessList: runtime.NewAccessList(), } diff --git a/state/executor_test.go b/state/executor_test.go index cb04b09b2b..ce2183c7bc 100644 --- a/state/executor_test.go +++ b/state/executor_test.go @@ -37,7 +37,7 @@ func TestOverride(t *testing.T) { balance := big.NewInt(2) code := []byte{0x1} - tt := NewTransition(hclog.NewNullLogger(), chain.ForksInTime{}, state, newTxn(state)) + tt := NewTransition(hclog.NewNullLogger(), chain.ForksInTime{}, state, newTxn(state), nil) require.Empty(t, tt.state.GetCode(types.ZeroAddress)) @@ -256,7 +256,7 @@ func Test_Transition_EIP2929(t *testing.T) { txn.SetCode(addr, tt.code) enabledForks := chain.AllForksEnabled.At(0) - transition := NewTransition(hclog.NewNullLogger(), enabledForks, state, txn) + transition := NewTransition(hclog.NewNullLogger(), enabledForks, state, txn, nil) initialAccessList := runtime.NewAccessList() initialAccessList.PrepareAccessList(transition.ctx.Origin, &addr, transition.precompiles.Addrs, nil) transition.accessList = initialAccessList diff --git a/state/runtime/precompiled/native_transfer_test.go b/state/runtime/precompiled/native_transfer_test.go index 4bdf95b4fa..f06e286f33 100644 --- a/state/runtime/precompiled/native_transfer_test.go +++ b/state/runtime/precompiled/native_transfer_test.go @@ -59,6 +59,7 @@ type dummyHost struct { t *testing.T balances map[types.Address]*big.Int + context *runtime.TxContext } func newDummyHost(t *testing.T) *dummyHost { @@ -138,6 +139,10 @@ func (d dummyHost) Selfdestruct(addr types.Address, beneficiary types.Address) { } func (d dummyHost) GetTxContext() runtime.TxContext { + if d.context != nil { + return *d.context + } + d.t.Fatalf("GetTxContext is not implemented") return runtime.TxContext{} diff --git a/state/runtime/precompiled/precompiled.go b/state/runtime/precompiled/precompiled.go index f5f8347a27..e7430bc5d5 100644 --- a/state/runtime/precompiled/precompiled.go +++ b/state/runtime/precompiled/precompiled.go @@ -49,14 +49,14 @@ type Precompiled struct { } // NewPrecompiled creates a new runtime for the precompiled contracts -func NewPrecompiled() *Precompiled { +func NewPrecompiled(validatorSetBackend ValidatoSetPrecompiledBackend) *Precompiled { p := &Precompiled{} - p.setupContracts() + p.setupContracts(validatorSetBackend) return p } -func (p *Precompiled) setupContracts() { +func (p *Precompiled) setupContracts(validatorSetBackend ValidatoSetPrecompiledBackend) { p.register("1", &ecrecover{p}) p.register("2", &sha256h{}) p.register("3", &ripemd160h{p}) @@ -79,6 +79,11 @@ func (p *Precompiled) setupContracts() { // BLS aggregated signatures verification precompile p.register(contracts.BLSAggSigsVerificationPrecompile.String(), &blsAggSignsVerification{}) + + // ValidatorSet precompile + p.register(contracts.ValidatorSetPrecompile.String(), &validatorSetPrecompile{ + backend: validatorSetBackend, + }) } func (p *Precompiled) register(precompileAddrRaw string, b contract) { diff --git a/state/runtime/precompiled/validator_set_precompile.go b/state/runtime/precompiled/validator_set_precompile.go new file mode 100644 index 0000000000..b51816793c --- /dev/null +++ b/state/runtime/precompiled/validator_set_precompile.go @@ -0,0 +1,95 @@ +package precompiled + +import ( + "errors" + + "github.com/0xPolygon/polygon-edge/chain" + "github.com/0xPolygon/polygon-edge/consensus/polybft/validator" + "github.com/0xPolygon/polygon-edge/state/runtime" + "github.com/0xPolygon/polygon-edge/types" + "github.com/hashicorp/go-hclog" + "github.com/umbracle/ethgo" + "github.com/umbracle/ethgo/abi" +) + +var ( + hasQuorumAbiType = abi.MustNewType("address[]") + + errValidatorSetPrecompileNotEnabled = errors.New("validator set precompile is not enabled") +) + +type ValidatoSetPrecompiledBackend interface { + GetValidatorsForBlock(blockNumber uint64) (validator.AccountSet, error) +} + +type validatorSetPrecompile struct { + backend ValidatoSetPrecompiledBackend +} + +// gas returns the gas required to execute the pre-compiled contract +func (c *validatorSetPrecompile) gas(input []byte, _ *chain.ForksInTime) uint64 { + return 150000 +} + +// Run runs the precompiled contract with the given input. +// There are two functions: +// isValidator(address addr) bool +// hasConsensus(address[] addrs) bool +// Input must be ABI encoded: address or (address[]) +// Output could be an error or ABI encoded "bool" value +func (c *validatorSetPrecompile) run(input []byte, caller types.Address, host runtime.Host) ([]byte, error) { + // isValidator case + if len(input) == 32 { + validatorSet, err := createValidatorSet(host, c.backend) + if err != nil { + return nil, err + } + + addr := types.BytesToAddress(input[0:32]) + + if validatorSet.Includes(addr) { + return abiBoolTrue, nil + } + + return abiBoolFalse, nil + } + + rawData, err := abi.Decode(hasQuorumAbiType, input) + if err != nil { + return nil, err + } + + addresses, ok := rawData.([]ethgo.Address) + if !ok { + return nil, errBLSVerifyAggSignsInputs + } + + validatorSet, err := createValidatorSet(host, c.backend) + if err != nil { + return nil, err + } + + signers := make(map[types.Address]struct{}, len(addresses)) + for _, x := range addresses { + signers[types.Address(x)] = struct{}{} + } + + if validatorSet.HasQuorum(uint64(host.GetTxContext().Number), signers) { + return abiBoolTrue, nil + } + + return abiBoolFalse, nil +} + +func createValidatorSet(host runtime.Host, backend ValidatoSetPrecompiledBackend) (validator.ValidatorSet, error) { + if backend == nil { + return nil, errValidatorSetPrecompileNotEnabled + } + + accounts, err := backend.GetValidatorsForBlock(uint64(host.GetTxContext().Number)) + if err != nil { + return nil, err + } + + return validator.NewValidatorSet(accounts, hclog.NewNullLogger()), nil +} diff --git a/state/runtime/precompiled/validator_set_precompile_test.go b/state/runtime/precompiled/validator_set_precompile_test.go new file mode 100644 index 0000000000..984e797b86 --- /dev/null +++ b/state/runtime/precompiled/validator_set_precompile_test.go @@ -0,0 +1,158 @@ +package precompiled + +import ( + "errors" + "math/big" + "testing" + + "github.com/0xPolygon/polygon-edge/consensus/polybft/validator" + "github.com/0xPolygon/polygon-edge/helper/common" + "github.com/0xPolygon/polygon-edge/state/runtime" + "github.com/0xPolygon/polygon-edge/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func Test_ValidatorSetPrecompile_gas(t *testing.T) { + assert.Equal(t, uint64(150000), (&validatorSetPrecompile{}).gas(nil, nil)) +} + +func Test_ValidatorSetPrecompile_run_BackendNotSet(t *testing.T) { + addr := types.StringToAddress("aaff") + host := newDummyHost(t) + host.context = &runtime.TxContext{ + Number: 100, + } + + p := &validatorSetPrecompile{} + _, err := p.run(common.PadLeftOrTrim(addr.Bytes(), 32), types.Address{}, host) + + assert.ErrorIs(t, err, errValidatorSetPrecompileNotEnabled) +} + +func Test_ValidatorSetPrecompile_run_GetValidatorsForBlockError(t *testing.T) { + desiredErr := errors.New("aaabbb") + addr := types.StringToAddress("aaff") + host := newDummyHost(t) + host.context = &runtime.TxContext{ + Number: 100, + } + backendMock := &validatorSetBackendMock{} + + backendMock.On("GetValidatorsForBlock", uint64(100)).Return((validator.AccountSet)(nil), desiredErr) + + p := &validatorSetPrecompile{ + backend: backendMock, + } + _, err := p.run(common.PadLeftOrTrim(addr.Bytes(), 32), types.Address{}, host) + + assert.ErrorIs(t, err, desiredErr) +} + +func Test_ValidatorSetPrecompile_run_IsValidator(t *testing.T) { + addrGood := types.StringToAddress("a") + addrBad := types.StringToAddress("1") + host := newDummyHost(t) + host.context = &runtime.TxContext{ + Number: 100, + } + backendMock := &validatorSetBackendMock{} + + backendMock.On("GetValidatorsForBlock", uint64(100)).Return(getDummyAccountSet(), error(nil)) + + p := &validatorSetPrecompile{ + backend: backendMock, + } + + v, err := p.run(common.PadLeftOrTrim(addrGood.Bytes(), 32), types.Address{}, host) + require.NoError(t, err) + assert.Equal(t, abiBoolTrue, v) + + v, err = p.run(common.PadLeftOrTrim(addrBad.Bytes(), 32), types.Address{}, host) + require.NoError(t, err) + assert.Equal(t, abiBoolFalse, v) +} + +func Test_ValidatorSetPrecompile_run_HasQuorum(t *testing.T) { + addrGood := [][]byte{ + types.StringToAddress("a").Bytes(), + types.StringToAddress("b").Bytes(), + types.StringToAddress("d").Bytes(), + } + addrBad1 := [][]byte{ + types.StringToAddress("a").Bytes(), + } + addrBad2 := [][]byte{ + types.StringToAddress("a").Bytes(), + types.StringToAddress("0").Bytes(), + types.StringToAddress("d").Bytes(), + } + host := newDummyHost(t) + host.context = &runtime.TxContext{ + Number: 200, + } + backendMock := &validatorSetBackendMock{} + + backendMock.On("GetValidatorsForBlock", uint64(200)).Return(getDummyAccountSet(), error(nil)) + + p := &validatorSetPrecompile{ + backend: backendMock, + } + + bytes, err := hasQuorumAbiType.Encode(addrGood) + require.NoError(t, err) + + v, err := p.run(bytes, types.Address{}, host) + require.NoError(t, err) + assert.Equal(t, abiBoolTrue, v) + + bytes, err = hasQuorumAbiType.Encode(addrBad1) + require.NoError(t, err) + + v, err = p.run(bytes, types.Address{}, host) + require.NoError(t, err) + assert.Equal(t, abiBoolFalse, v) + + bytes, err = hasQuorumAbiType.Encode(addrBad2) + require.NoError(t, err) + + v, err = p.run(bytes, types.Address{}, host) + require.NoError(t, err) + assert.Equal(t, abiBoolFalse, v) +} + +type validatorSetBackendMock struct { + mock.Mock +} + +func (m *validatorSetBackendMock) GetValidatorsForBlock(blockNumber uint64) (validator.AccountSet, error) { + call := m.Called(blockNumber) + + return call.Get(0).(validator.AccountSet), call.Error(1) +} + +func getDummyAccountSet() validator.AccountSet { + return validator.AccountSet{ + &validator.ValidatorMetadata{ + Address: types.StringToAddress("a"), + VotingPower: new(big.Int).SetUint64(1), + IsActive: true, + }, + &validator.ValidatorMetadata{ + Address: types.StringToAddress("b"), + VotingPower: new(big.Int).SetUint64(1), + IsActive: true, + }, + &validator.ValidatorMetadata{ + Address: types.StringToAddress("c"), + VotingPower: new(big.Int).SetUint64(1), + IsActive: true, + }, + &validator.ValidatorMetadata{ + Address: types.StringToAddress("d"), + VotingPower: new(big.Int).SetUint64(1), + IsActive: true, + }, + } +} diff --git a/tests/tests b/tests/tests index 853b1e03b1..5bf1fff257 160000 --- a/tests/tests +++ b/tests/tests @@ -1 +1 @@ -Subproject commit 853b1e03b1078d370614002851ba1ee9803d9fcf +Subproject commit 5bf1fff257c0662bc526c27d7546d470025b3239 From e580b569e0a206f0833516969bf1285cea89fb9c Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Thu, 15 Feb 2024 09:43:05 +0100 Subject: [PATCH 02/16] lint fix --- contracts/system_addresses.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/system_addresses.go b/contracts/system_addresses.go index f3b45a2b13..b5d27eddf4 100644 --- a/contracts/system_addresses.go +++ b/contracts/system_addresses.go @@ -97,7 +97,7 @@ var ( NativeTransferPrecompile = types.StringToAddress("0x2020") // BLSAggSigsVerificationPrecompile is an address of BLS aggregated signatures verification precompile BLSAggSigsVerificationPrecompile = types.StringToAddress("0x2030") - // ValidatorSetPrecompile is an address of precompile which provides some validatorSet functionalities to smart contracts + // ValidatorSetPrecompile is an address of precompile which provides some validatorSet functionalities ValidatorSetPrecompile = types.StringToAddress("0x2040") // ConsolePrecompile is and address of Hardhat console precompile ConsolePrecompile = types.StringToAddress("0x000000000000000000636F6e736F6c652e6c6f67") From d0ba68255c184518db971631794604abf13b7c24 Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Thu, 15 Feb 2024 13:09:51 +0100 Subject: [PATCH 03/16] e2e test --- consensus/polybft/contractsapi/init.go | 21 ++-- .../TestValidatorSetPrecompile.json | 104 ++++++++++++++++++ .../TestValidatorSetPrecompile.sol | 35 ++++++ e2e-polybft/e2e/consensus_test.go | 78 +++++++++++++ state/executor.go | 6 +- state/runtime/precompiled/precompiled.go | 4 +- .../precompiled/validator_set_precompile.go | 6 +- 7 files changed, 239 insertions(+), 15 deletions(-) create mode 100644 consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json create mode 100644 consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol diff --git a/consensus/polybft/contractsapi/init.go b/consensus/polybft/contractsapi/init.go index aebae82f40..afe4469582 100644 --- a/consensus/polybft/contractsapi/init.go +++ b/consensus/polybft/contractsapi/init.go @@ -66,13 +66,14 @@ var ( // test smart contracts //go:embed test-contracts/* - testContracts embed.FS - TestWriteBlockMetadata *contracts.Artifact - RootERC20 *contracts.Artifact - TestSimple *contracts.Artifact - TestRewardToken *contracts.Artifact - Wrapper *contracts.Artifact - NumberPersister *contracts.Artifact + testContracts embed.FS + TestWriteBlockMetadata *contracts.Artifact + RootERC20 *contracts.Artifact + TestSimple *contracts.Artifact + TestRewardToken *contracts.Artifact + Wrapper *contracts.Artifact + NumberPersister *contracts.Artifact + TestValidatorSetPrecompile *contracts.Artifact contractArtifacts map[string]*contracts.Artifact ) @@ -270,6 +271,11 @@ func init() { log.Fatal(err) } + TestValidatorSetPrecompile, err = contracts.DecodeArtifact(readTestContractContent("TestValidatorSetPrecompile.json")) + if err != nil { + log.Fatal(err) + } + StakeManager, err = contracts.DecodeArtifact([]byte(StakeManagerArtifact)) if err != nil { log.Fatal(err) @@ -381,6 +387,7 @@ func init() { "RootERC20": RootERC20, "TestSimple": TestSimple, "TestRewardToken": TestRewardToken, + "TestValidatorSetPrecompile": TestValidatorSetPrecompile, } } diff --git a/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json new file mode 100644 index 0000000000..bc9cb31c93 --- /dev/null +++ b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json @@ -0,0 +1,104 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestValidatorSetPrecompile", + "sourceName": "consensus/polybft/contractsapi/test-contracts/TestValidatorSetContract.sol", + "abi": [ + { + "inputs":[ + + ], + "name":"VALIDATOR_SET_PRECOMPILE", + "outputs":[ + { + "internalType":"address", + "name":"", + "type":"address" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + + ], + "name":"VALIDATOR_SET_PRECOMPILE_GAS", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + + ], + "name":"hasQuorum", + "outputs":[ + { + "internalType":"bool", + "name":"", + "type":"bool" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + + ], + "name":"inc", + "outputs":[ + + ], + "stateMutability":"nonpayable", + "type":"function" + }, + { + "inputs":[ + { + "internalType":"address", + "name":"", + "type":"address" + } + ], + "name":"voteMap", + "outputs":[ + { + "internalType":"bool", + "name":"", + "type":"bool" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "name":"votes", + "outputs":[ + { + "internalType":"address", + "name":"", + "type":"address" + } + ], + "stateMutability":"view", + "type":"function" + } + ], + "bytecode": "0x608060405234801561000f575f80fd5b5061085c8061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610060575f3560e01c8063371303c0146100645780635df813301461006e5780635e4f94881461009e578063815b4d1b146100bc57806384005d56146100da578063965844a6146100f8575b5f80fd5b61006c610128565b005b6100886004803603810190610083919061046e565b61031e565b60405161009591906104d8565b60405180910390f35b6100a6610359565b6040516100b391906104d8565b60405180910390f35b6100c461035f565b6040516100d1919061050b565b60405180910390f35b6100e2610414565b6040516100ef9190610533565b60405180910390f35b610112600480360381019061010d9190610576565b61041b565b60405161011f919061050b565b60405180910390f35b5f8061204073ffffffffffffffffffffffffffffffffffffffff16620249f03360405160200161015891906104d8565b604051602081830303815290604052604051610174919061060d565b5f604051808303818686fa925050503d805f81146101ad576040519150601f19603f3d011682016040523d82523d5f602084013e6101b2565b606091505b50915091505f8280156101d55750818060200190518101906101d4919061064d565b5b905080610217576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161020e906106d2565b60405180910390fd5b5f803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff1661031957600133908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060015f803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055505b505050565b6001818154811061032d575f80fd5b905f5260205f20015f915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61204081565b5f805f61204073ffffffffffffffffffffffffffffffffffffffff16620249f060016040516020016103919190610806565b6040516020818303038152906040526040516103ad919061060d565b5f604051808303818686fa925050503d805f81146103e6576040519150601f19603f3d011682016040523d82523d5f602084013e6103eb565b606091505b509150915081801561040d57508080602001905181019061040c919061064d565b5b9250505090565b620249f081565b5f602052805f5260405f205f915054906101000a900460ff1681565b5f80fd5b5f819050919050565b61044d8161043b565b8114610457575f80fd5b50565b5f8135905061046881610444565b92915050565b5f6020828403121561048357610482610437565b5b5f6104908482850161045a565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6104c282610499565b9050919050565b6104d2816104b8565b82525050565b5f6020820190506104eb5f8301846104c9565b92915050565b5f8115159050919050565b610505816104f1565b82525050565b5f60208201905061051e5f8301846104fc565b92915050565b61052d8161043b565b82525050565b5f6020820190506105465f830184610524565b92915050565b610555816104b8565b811461055f575f80fd5b50565b5f813590506105708161054c565b92915050565b5f6020828403121561058b5761058a610437565b5b5f61059884828501610562565b91505092915050565b5f81519050919050565b5f81905092915050565b5f5b838110156105d25780820151818401526020810190506105b7565b5f8484015250505050565b5f6105e7826105a1565b6105f181856105ab565b93506106018185602086016105b5565b80840191505092915050565b5f61061882846105dd565b915081905092915050565b61062c816104f1565b8114610636575f80fd5b50565b5f8151905061064781610623565b92915050565b5f6020828403121561066257610661610437565b5b5f61066f84828501610639565b91505092915050565b5f82825260208201905092915050565b7f76616c696461746f72206f6e6c790000000000000000000000000000000000005f82015250565b5f6106bc600e83610678565b91506106c782610688565b602082019050919050565b5f6020820190508181035f8301526106e9816106b0565b9050919050565b5f81549050919050565b5f82825260208201905092915050565b5f819050815f5260205f209050919050565b610725816104b8565b82525050565b5f610736838361071c565b60208301905092915050565b5f815f1c9050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61077e61077983610742565b61074d565b9050919050565b5f610790825461076c565b9050919050565b5f600182019050919050565b5f6107ad826106f0565b6107b781856106fa565b93506107c28361070a565b805f5b838110156107f9576107d682610785565b6107e0888261072b565b97506107eb83610797565b9250506001810190506107c5565b5085935050505092915050565b5f6020820190508181035f83015261081e81846107a3565b90509291505056fea26469706673582212204da3517de3a7d78ba0f5c424a698f8d7833d8bf6d56911c8075e8e6546a8649264736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} \ No newline at end of file diff --git a/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol new file mode 100644 index 0000000000..add4df58ff --- /dev/null +++ b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +// TestValidatorSetPrecompile.sol +// Contract which testst ValidatorSet precompile +pragma solidity ^0.8.0; + +contract TestValidatorSetPrecompile { + address public constant VALIDATOR_SET_PRECOMPILE = 0x0000000000000000000000000000000000002040; + uint256 public constant VALIDATOR_SET_PRECOMPILE_GAS = 150000; + + mapping(address => bool) public voteMap; + address[] public votes; + + modifier onlyValidator() { + (bool callSuccess, bytes memory returnData) = VALIDATOR_SET_PRECOMPILE.staticcall{ + gas: VALIDATOR_SET_PRECOMPILE_GAS + }(abi.encode(msg.sender)); + bool isValidator = callSuccess && abi.decode(returnData, (bool)); + require(isValidator, "validator only"); + _; + } + + function inc() public onlyValidator { + if (!voteMap[msg.sender]) { + votes.push(msg.sender); + voteMap[msg.sender] = true; + } + } + + function hasQuorum() public view returns (bool) { + (bool callSuccess, bytes memory returnData) = VALIDATOR_SET_PRECOMPILE.staticcall{ + gas: VALIDATOR_SET_PRECOMPILE_GAS + }(abi.encode(votes)); + return callSuccess && abi.decode(returnData, (bool)); + } +} diff --git a/e2e-polybft/e2e/consensus_test.go b/e2e-polybft/e2e/consensus_test.go index 0115d6bcb1..041d9a5b64 100644 --- a/e2e-polybft/e2e/consensus_test.go +++ b/e2e-polybft/e2e/consensus_test.go @@ -823,3 +823,81 @@ func TestE2E_Deploy_Nested_Contract(t *testing.T) { require.Equal(t, numberToPersist, parsedResponse) } + +func TestE2E_TestValidatorSetPrecompile(t *testing.T) { + admin, err := wallet.GenerateKey() + require.NoError(t, err) + + validators := []types.Address(nil) + adminAddr := types.Address(admin.Address()) + + cluster := framework.NewTestCluster(t, 4, + framework.WithBladeAdmin(adminAddr.String()), + framework.WithSecretsCallback(func(addrs []types.Address, _ *framework.TestClusterConfig) { + validators = addrs + })) + defer cluster.Stop() + + cluster.WaitForReady(t) + + txRelayer, err := txrelayer.NewTxRelayer(txrelayer.WithClient(cluster.Servers[0].JSONRPC())) + require.NoError(t, err) + + // deploy Wrapper contract + receipt, err := txRelayer.SendTransaction( + ðgo.Transaction{ + To: nil, + Input: contractsapi.TestValidatorSetPrecompile.Bytecode, + }, + admin) + require.NoError(t, err) + + validatorSetPrecompileTestAddr := receipt.ContractAddress + + hasQuorum := func() bool { + t.Helper() + + hasQuorumFn := contractsapi.TestValidatorSetPrecompile.Abi.GetMethod("hasQuorum") + + hasQuorumFnBytes, err := hasQuorumFn.Encode([]interface{}{}) + require.NoError(t, err) + + response, err := txRelayer.Call(ethgo.ZeroAddress, validatorSetPrecompileTestAddr, hasQuorumFnBytes) + require.NoError(t, err) + + return response == "true" + } + + sendIncTx := func(addr types.Address) { + t.Helper() + + incFn := contractsapi.TestValidatorSetPrecompile.Abi.GetMethod("inc") + + incFnBytes, err := incFn.Encode([]interface{}{}) + require.NoError(t, err) + + txn := ðgo.Transaction{ + From: ethgo.Address(addr), + To: &validatorSetPrecompileTestAddr, + Input: incFnBytes, + } + + receipt, err = txRelayer.SendTransaction(txn, admin) + require.NoError(t, err) + require.Equal(t, uint64(types.ReceiptSuccess), receipt.Status) + } + + require.False(t, hasQuorum()) + + sendIncTx(validators[0]) + require.False(t, hasQuorum()) + + sendIncTx(validators[1]) + require.False(t, hasQuorum()) + + sendIncTx(validators[1]) + require.False(t, hasQuorum()) + + sendIncTx(validators[3]) + require.True(t, hasQuorum()) +} diff --git a/state/executor.go b/state/executor.go index adf209dd59..220085ea0f 100644 --- a/state/executor.go +++ b/state/executor.go @@ -47,8 +47,8 @@ type Executor struct { GenesisPostHook func(*Transition) error // this value should be set if we want to enable validator set precompile - // NOTE that this precompile wont be enabled for WriteGenesis - validatorSetBackend precompiled.ValidatoSetPrecompiledBackend + // note that this precompile WONT be enabled for WriteGenesis + validatorSetBackend precompiled.ValidatoSetPrecompileBackend } // NewExecutor creates a new executor @@ -239,7 +239,7 @@ func (e *Executor) BeginTxn( return t, nil } -func (e *Executor) SetValidatorSetBackend(validatorSetBackend precompiled.ValidatoSetPrecompiledBackend) { +func (e *Executor) SetValidatorSetBackend(validatorSetBackend precompiled.ValidatoSetPrecompileBackend) { e.validatorSetBackend = validatorSetBackend } diff --git a/state/runtime/precompiled/precompiled.go b/state/runtime/precompiled/precompiled.go index e7430bc5d5..94ff1e7b64 100644 --- a/state/runtime/precompiled/precompiled.go +++ b/state/runtime/precompiled/precompiled.go @@ -49,14 +49,14 @@ type Precompiled struct { } // NewPrecompiled creates a new runtime for the precompiled contracts -func NewPrecompiled(validatorSetBackend ValidatoSetPrecompiledBackend) *Precompiled { +func NewPrecompiled(validatorSetBackend ValidatoSetPrecompileBackend) *Precompiled { p := &Precompiled{} p.setupContracts(validatorSetBackend) return p } -func (p *Precompiled) setupContracts(validatorSetBackend ValidatoSetPrecompiledBackend) { +func (p *Precompiled) setupContracts(validatorSetBackend ValidatoSetPrecompileBackend) { p.register("1", &ecrecover{p}) p.register("2", &sha256h{}) p.register("3", &ripemd160h{p}) diff --git a/state/runtime/precompiled/validator_set_precompile.go b/state/runtime/precompiled/validator_set_precompile.go index b51816793c..9df7e93f8c 100644 --- a/state/runtime/precompiled/validator_set_precompile.go +++ b/state/runtime/precompiled/validator_set_precompile.go @@ -18,12 +18,12 @@ var ( errValidatorSetPrecompileNotEnabled = errors.New("validator set precompile is not enabled") ) -type ValidatoSetPrecompiledBackend interface { +type ValidatoSetPrecompileBackend interface { GetValidatorsForBlock(blockNumber uint64) (validator.AccountSet, error) } type validatorSetPrecompile struct { - backend ValidatoSetPrecompiledBackend + backend ValidatoSetPrecompileBackend } // gas returns the gas required to execute the pre-compiled contract @@ -81,7 +81,7 @@ func (c *validatorSetPrecompile) run(input []byte, caller types.Address, host ru return abiBoolFalse, nil } -func createValidatorSet(host runtime.Host, backend ValidatoSetPrecompiledBackend) (validator.ValidatorSet, error) { +func createValidatorSet(host runtime.Host, backend ValidatoSetPrecompileBackend) (validator.ValidatorSet, error) { if backend == nil { return nil, errValidatorSetPrecompileNotEnabled } From 74e58421018f6d1141f36ac8ac31d9c68d1d6699 Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Thu, 15 Feb 2024 17:03:41 +0100 Subject: [PATCH 04/16] additional --- state/runtime/precompiled/validator_set_precompile.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/state/runtime/precompiled/validator_set_precompile.go b/state/runtime/precompiled/validator_set_precompile.go index 9df7e93f8c..2c641c1851 100644 --- a/state/runtime/precompiled/validator_set_precompile.go +++ b/state/runtime/precompiled/validator_set_precompile.go @@ -40,7 +40,7 @@ func (c *validatorSetPrecompile) gas(input []byte, _ *chain.ForksInTime) uint64 func (c *validatorSetPrecompile) run(input []byte, caller types.Address, host runtime.Host) ([]byte, error) { // isValidator case if len(input) == 32 { - validatorSet, err := createValidatorSet(host, c.backend) + validatorSet, err := createValidatorSet(host, c.backend) // we are calling validators for previous block if err != nil { return nil, err } @@ -86,7 +86,13 @@ func createValidatorSet(host runtime.Host, backend ValidatoSetPrecompileBackend) return nil, errValidatorSetPrecompileNotEnabled } - accounts, err := backend.GetValidatorsForBlock(uint64(host.GetTxContext().Number)) + // if its payable tx we need to look for validator in previous block + blockNumber := uint64(host.GetTxContext().Number) + if !host.GetTxContext().NonPayable { + blockNumber-- + } + + accounts, err := backend.GetValidatorsForBlock(blockNumber) if err != nil { return nil, err } From 00eb4f8b5410f8c71d40828a51bfe9718bfa8c44 Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Thu, 15 Feb 2024 19:04:15 +0100 Subject: [PATCH 05/16] test fix --- .../precompiled/validator_set_precompile_test.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/state/runtime/precompiled/validator_set_precompile_test.go b/state/runtime/precompiled/validator_set_precompile_test.go index 984e797b86..3724cf339c 100644 --- a/state/runtime/precompiled/validator_set_precompile_test.go +++ b/state/runtime/precompiled/validator_set_precompile_test.go @@ -36,11 +36,12 @@ func Test_ValidatorSetPrecompile_run_GetValidatorsForBlockError(t *testing.T) { addr := types.StringToAddress("aaff") host := newDummyHost(t) host.context = &runtime.TxContext{ - Number: 100, + Number: 100, + NonPayable: true, } backendMock := &validatorSetBackendMock{} - backendMock.On("GetValidatorsForBlock", uint64(100)).Return((validator.AccountSet)(nil), desiredErr) + backendMock.On("GetValidatorsForBlock", uint64(host.context.Number)).Return((validator.AccountSet)(nil), desiredErr) p := &validatorSetPrecompile{ backend: backendMock, @@ -55,11 +56,12 @@ func Test_ValidatorSetPrecompile_run_IsValidator(t *testing.T) { addrBad := types.StringToAddress("1") host := newDummyHost(t) host.context = &runtime.TxContext{ - Number: 100, + Number: 100, + NonPayable: false, } backendMock := &validatorSetBackendMock{} - backendMock.On("GetValidatorsForBlock", uint64(100)).Return(getDummyAccountSet(), error(nil)) + backendMock.On("GetValidatorsForBlock", uint64(host.context.Number-1)).Return(getDummyAccountSet(), error(nil)) p := &validatorSetPrecompile{ backend: backendMock, @@ -90,11 +92,12 @@ func Test_ValidatorSetPrecompile_run_HasQuorum(t *testing.T) { } host := newDummyHost(t) host.context = &runtime.TxContext{ - Number: 200, + Number: 200, + NonPayable: true, } backendMock := &validatorSetBackendMock{} - backendMock.On("GetValidatorsForBlock", uint64(200)).Return(getDummyAccountSet(), error(nil)) + backendMock.On("GetValidatorsForBlock", uint64(host.context.Number)).Return(getDummyAccountSet(), error(nil)) p := &validatorSetPrecompile{ backend: backendMock, From c6970bd160f471a47d4566515ee518580f987950 Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Thu, 15 Feb 2024 19:49:26 +0100 Subject: [PATCH 06/16] additional --- .../TestValidatorSetPrecompile.json | 138 +++++++----------- .../TestValidatorSetPrecompile.sol | 11 +- e2e-polybft/e2e/consensus_test.go | 30 ++-- 3 files changed, 76 insertions(+), 103 deletions(-) diff --git a/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json index bc9cb31c93..73056c5be3 100644 --- a/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json +++ b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json @@ -3,102 +3,64 @@ "contractName": "TestValidatorSetPrecompile", "sourceName": "consensus/polybft/contractsapi/test-contracts/TestValidatorSetContract.sol", "abi": [ - { - "inputs":[ - - ], - "name":"VALIDATOR_SET_PRECOMPILE", - "outputs":[ - { - "internalType":"address", - "name":"", - "type":"address" - } - ], - "stateMutability":"view", - "type":"function" - }, - { - "inputs":[ - - ], - "name":"VALIDATOR_SET_PRECOMPILE_GAS", - "outputs":[ - { - "internalType":"uint256", - "name":"", - "type":"uint256" - } - ], - "stateMutability":"view", - "type":"function" - }, - { - "inputs":[ - - ], - "name":"hasQuorum", - "outputs":[ - { - "internalType":"bool", - "name":"", - "type":"bool" - } - ], - "stateMutability":"view", - "type":"function" + { + "inputs":[ + + ], + "name":"VALIDATOR_SET_PRECOMPILE", + "outputs":[ + { + "internalType":"address", + "name":"", + "type":"address" + } + ], + "stateMutability":"view", + "type":"function" }, { - "inputs":[ - - ], - "name":"inc", - "outputs":[ - - ], - "stateMutability":"nonpayable", - "type":"function" + "inputs":[ + + ], + "name":"VALIDATOR_SET_PRECOMPILE_GAS", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" }, { - "inputs":[ - { - "internalType":"address", - "name":"", - "type":"address" - } - ], - "name":"voteMap", - "outputs":[ - { - "internalType":"bool", - "name":"", - "type":"bool" - } - ], - "stateMutability":"view", - "type":"function" + "inputs":[ + + ], + "name":"hasQuorum", + "outputs":[ + { + "internalType":"bool", + "name":"", + "type":"bool" + } + ], + "stateMutability":"view", + "type":"function" }, { - "inputs":[ - { - "internalType":"uint256", - "name":"", - "type":"uint256" - } - ], - "name":"votes", - "outputs":[ - { - "internalType":"address", - "name":"", - "type":"address" - } - ], - "stateMutability":"view", - "type":"function" + "inputs":[ + + ], + "name":"inc", + "outputs":[ + + ], + "stateMutability":"nonpayable", + "type":"function" } ], - "bytecode": "0x608060405234801561000f575f80fd5b5061085c8061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610060575f3560e01c8063371303c0146100645780635df813301461006e5780635e4f94881461009e578063815b4d1b146100bc57806384005d56146100da578063965844a6146100f8575b5f80fd5b61006c610128565b005b6100886004803603810190610083919061046e565b61031e565b60405161009591906104d8565b60405180910390f35b6100a6610359565b6040516100b391906104d8565b60405180910390f35b6100c461035f565b6040516100d1919061050b565b60405180910390f35b6100e2610414565b6040516100ef9190610533565b60405180910390f35b610112600480360381019061010d9190610576565b61041b565b60405161011f919061050b565b60405180910390f35b5f8061204073ffffffffffffffffffffffffffffffffffffffff16620249f03360405160200161015891906104d8565b604051602081830303815290604052604051610174919061060d565b5f604051808303818686fa925050503d805f81146101ad576040519150601f19603f3d011682016040523d82523d5f602084013e6101b2565b606091505b50915091505f8280156101d55750818060200190518101906101d4919061064d565b5b905080610217576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161020e906106d2565b60405180910390fd5b5f803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff1661031957600133908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060015f803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055505b505050565b6001818154811061032d575f80fd5b905f5260205f20015f915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61204081565b5f805f61204073ffffffffffffffffffffffffffffffffffffffff16620249f060016040516020016103919190610806565b6040516020818303038152906040526040516103ad919061060d565b5f604051808303818686fa925050503d805f81146103e6576040519150601f19603f3d011682016040523d82523d5f602084013e6103eb565b606091505b509150915081801561040d57508080602001905181019061040c919061064d565b5b9250505090565b620249f081565b5f602052805f5260405f205f915054906101000a900460ff1681565b5f80fd5b5f819050919050565b61044d8161043b565b8114610457575f80fd5b50565b5f8135905061046881610444565b92915050565b5f6020828403121561048357610482610437565b5b5f6104908482850161045a565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6104c282610499565b9050919050565b6104d2816104b8565b82525050565b5f6020820190506104eb5f8301846104c9565b92915050565b5f8115159050919050565b610505816104f1565b82525050565b5f60208201905061051e5f8301846104fc565b92915050565b61052d8161043b565b82525050565b5f6020820190506105465f830184610524565b92915050565b610555816104b8565b811461055f575f80fd5b50565b5f813590506105708161054c565b92915050565b5f6020828403121561058b5761058a610437565b5b5f61059884828501610562565b91505092915050565b5f81519050919050565b5f81905092915050565b5f5b838110156105d25780820151818401526020810190506105b7565b5f8484015250505050565b5f6105e7826105a1565b6105f181856105ab565b93506106018185602086016105b5565b80840191505092915050565b5f61061882846105dd565b915081905092915050565b61062c816104f1565b8114610636575f80fd5b50565b5f8151905061064781610623565b92915050565b5f6020828403121561066257610661610437565b5b5f61066f84828501610639565b91505092915050565b5f82825260208201905092915050565b7f76616c696461746f72206f6e6c790000000000000000000000000000000000005f82015250565b5f6106bc600e83610678565b91506106c782610688565b602082019050919050565b5f6020820190508181035f8301526106e9816106b0565b9050919050565b5f81549050919050565b5f82825260208201905092915050565b5f819050815f5260205f209050919050565b610725816104b8565b82525050565b5f610736838361071c565b60208301905092915050565b5f815f1c9050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61077e61077983610742565b61074d565b9050919050565b5f610790825461076c565b9050919050565b5f600182019050919050565b5f6107ad826106f0565b6107b781856106fa565b93506107c28361070a565b805f5b838110156107f9576107d682610785565b6107e0888261072b565b97506107eb83610797565b9250506001810190506107c5565b5085935050505092915050565b5f6020820190508181035f83015261081e81846107a3565b90509291505056fea26469706673582212204da3517de3a7d78ba0f5c424a698f8d7833d8bf6d56911c8075e8e6546a8649264736f6c63430008180033", + "bytecode": "0x608060405234801561000f575f80fd5b506106ea8061001d5f395ff3fe608060405234801561000f575f80fd5b506004361061004a575f3560e01c8063371303c01461004e5780635e4f948814610058578063815b4d1b1461007657806384005d5614610094575b5f80fd5b6100566100b2565b005b6100606102a8565b60405161006d91906103ae565b60405180910390f35b61007e6102ae565b60405161008b91906103e1565b60405180910390f35b61009c610368565b6040516100a99190610412565b60405180910390f35b5f8061204073ffffffffffffffffffffffffffffffffffffffff16620249f0336040516020016100e291906103ae565b6040516020818303038152906040526040516100fe9190610497565b5f604051808303818686fa925050503d805f8114610137576040519150601f19603f3d011682016040523d82523d5f602084013e61013c565b606091505b50915091505f8180602001905181019061015691906104db565b90508280156101625750805b6101a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161019890610560565b60405180910390fd5b5f803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff166102a357600133908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060015f803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055505b505050565b61204081565b5f805f61204073ffffffffffffffffffffffffffffffffffffffff16620249f060016040516020016102e09190610694565b6040516020818303038152906040526040516102fc9190610497565b5f604051808303818686fa925050503d805f8114610335576040519150601f19603f3d011682016040523d82523d5f602084013e61033a565b606091505b50915091505f8180602001905181019061035491906104db565b90508280156103605750805b935050505090565b620249f081565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6103988261036f565b9050919050565b6103a88161038e565b82525050565b5f6020820190506103c15f83018461039f565b92915050565b5f8115159050919050565b6103db816103c7565b82525050565b5f6020820190506103f45f8301846103d2565b92915050565b5f819050919050565b61040c816103fa565b82525050565b5f6020820190506104255f830184610403565b92915050565b5f81519050919050565b5f81905092915050565b5f5b8381101561045c578082015181840152602081019050610441565b5f8484015250505050565b5f6104718261042b565b61047b8185610435565b935061048b81856020860161043f565b80840191505092915050565b5f6104a28284610467565b915081905092915050565b5f80fd5b6104ba816103c7565b81146104c4575f80fd5b50565b5f815190506104d5816104b1565b92915050565b5f602082840312156104f0576104ef6104ad565b5b5f6104fd848285016104c7565b91505092915050565b5f82825260208201905092915050565b7f76616c696461746f72206f6e6c790000000000000000000000000000000000005f82015250565b5f61054a600e83610506565b915061055582610516565b602082019050919050565b5f6020820190508181035f8301526105778161053e565b9050919050565b5f81549050919050565b5f82825260208201905092915050565b5f819050815f5260205f209050919050565b6105b38161038e565b82525050565b5f6105c483836105aa565b60208301905092915050565b5f815f1c9050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61060c610607836105d0565b6105db565b9050919050565b5f61061e82546105fa565b9050919050565b5f600182019050919050565b5f61063b8261057e565b6106458185610588565b935061065083610598565b805f5b838110156106875761066482610613565b61066e88826105b9565b975061067983610625565b925050600181019050610653565b5085935050505092915050565b5f6020820190508181035f8301526106ac8184610631565b90509291505056fea2646970667358221220efe83875fb1246787277c88a460f768e19f019c8e2e557e6d05731425a3210f864736f6c63430008180033", "linkReferences": {}, "deployedLinkReferences": {} } \ No newline at end of file diff --git a/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol index add4df58ff..ba376ba208 100644 --- a/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol +++ b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol @@ -7,15 +7,15 @@ contract TestValidatorSetPrecompile { address public constant VALIDATOR_SET_PRECOMPILE = 0x0000000000000000000000000000000000002040; uint256 public constant VALIDATOR_SET_PRECOMPILE_GAS = 150000; - mapping(address => bool) public voteMap; - address[] public votes; + mapping(address => bool) voteMap; + address[] votes; modifier onlyValidator() { (bool callSuccess, bytes memory returnData) = VALIDATOR_SET_PRECOMPILE.staticcall{ gas: VALIDATOR_SET_PRECOMPILE_GAS }(abi.encode(msg.sender)); - bool isValidator = callSuccess && abi.decode(returnData, (bool)); - require(isValidator, "validator only"); + bool state = abi.decode(returnData, (bool)); + require(callSuccess && state, "validator only"); _; } @@ -30,6 +30,7 @@ contract TestValidatorSetPrecompile { (bool callSuccess, bytes memory returnData) = VALIDATOR_SET_PRECOMPILE.staticcall{ gas: VALIDATOR_SET_PRECOMPILE_GAS }(abi.encode(votes)); - return callSuccess && abi.decode(returnData, (bool)); + bool state = abi.decode(returnData, (bool)); + return callSuccess && state; } } diff --git a/e2e-polybft/e2e/consensus_test.go b/e2e-polybft/e2e/consensus_test.go index 041d9a5b64..ef34ff7fec 100644 --- a/e2e-polybft/e2e/consensus_test.go +++ b/e2e-polybft/e2e/consensus_test.go @@ -17,6 +17,7 @@ import ( "github.com/0xPolygon/polygon-edge/command" "github.com/0xPolygon/polygon-edge/command/genesis" + "github.com/0xPolygon/polygon-edge/command/validator/helper" validatorHelper "github.com/0xPolygon/polygon-edge/command/validator/helper" "github.com/0xPolygon/polygon-edge/consensus/polybft" "github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi" @@ -828,22 +829,31 @@ func TestE2E_TestValidatorSetPrecompile(t *testing.T) { admin, err := wallet.GenerateKey() require.NoError(t, err) - validators := []types.Address(nil) + validatorAddrs := []types.Address(nil) adminAddr := types.Address(admin.Address()) cluster := framework.NewTestCluster(t, 4, framework.WithBladeAdmin(adminAddr.String()), framework.WithSecretsCallback(func(addrs []types.Address, _ *framework.TestClusterConfig) { - validators = addrs + validatorAddrs = addrs })) defer cluster.Stop() cluster.WaitForReady(t) + validatorKeys := make([]*wallet.Key, len(validatorAddrs)) + + for i, s := range cluster.Servers { + voterAcc, err := helper.GetAccountFromDir(s.DataDir()) + require.NoError(t, err) + + validatorKeys[i] = voterAcc.Ecdsa + } + txRelayer, err := txrelayer.NewTxRelayer(txrelayer.WithClient(cluster.Servers[0].JSONRPC())) require.NoError(t, err) - // deploy Wrapper contract + // deploy contract receipt, err := txRelayer.SendTransaction( ðgo.Transaction{ To: nil, @@ -868,7 +878,7 @@ func TestE2E_TestValidatorSetPrecompile(t *testing.T) { return response == "true" } - sendIncTx := func(addr types.Address) { + sendIncTx := func(validatorID int) { t.Helper() incFn := contractsapi.TestValidatorSetPrecompile.Abi.GetMethod("inc") @@ -877,27 +887,27 @@ func TestE2E_TestValidatorSetPrecompile(t *testing.T) { require.NoError(t, err) txn := ðgo.Transaction{ - From: ethgo.Address(addr), + From: ethgo.Address(validatorAddrs[validatorID]), To: &validatorSetPrecompileTestAddr, Input: incFnBytes, } - receipt, err = txRelayer.SendTransaction(txn, admin) + receipt, err = txRelayer.SendTransaction(txn, validatorKeys[validatorID]) require.NoError(t, err) require.Equal(t, uint64(types.ReceiptSuccess), receipt.Status) } require.False(t, hasQuorum()) - sendIncTx(validators[0]) + sendIncTx(0) require.False(t, hasQuorum()) - sendIncTx(validators[1]) + sendIncTx(1) require.False(t, hasQuorum()) - sendIncTx(validators[1]) + sendIncTx(1) require.False(t, hasQuorum()) - sendIncTx(validators[3]) + sendIncTx(3) require.True(t, hasQuorum()) } From 45e36a953dd146d801a1b899128df412c55db482 Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Thu, 15 Feb 2024 20:31:55 +0100 Subject: [PATCH 07/16] custom abi.(d)encode(address[]) --- e2e-polybft/e2e/consensus_test.go | 3 +- .../precompiled/validator_set_precompile.go | 64 +++++++++++++++---- .../validator_set_precompile_test.go | 37 +++++------ 3 files changed, 69 insertions(+), 35 deletions(-) diff --git a/e2e-polybft/e2e/consensus_test.go b/e2e-polybft/e2e/consensus_test.go index ef34ff7fec..d68e5894a8 100644 --- a/e2e-polybft/e2e/consensus_test.go +++ b/e2e-polybft/e2e/consensus_test.go @@ -17,7 +17,6 @@ import ( "github.com/0xPolygon/polygon-edge/command" "github.com/0xPolygon/polygon-edge/command/genesis" - "github.com/0xPolygon/polygon-edge/command/validator/helper" validatorHelper "github.com/0xPolygon/polygon-edge/command/validator/helper" "github.com/0xPolygon/polygon-edge/consensus/polybft" "github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi" @@ -844,7 +843,7 @@ func TestE2E_TestValidatorSetPrecompile(t *testing.T) { validatorKeys := make([]*wallet.Key, len(validatorAddrs)) for i, s := range cluster.Servers { - voterAcc, err := helper.GetAccountFromDir(s.DataDir()) + voterAcc, err := validatorHelper.GetAccountFromDir(s.DataDir()) require.NoError(t, err) validatorKeys[i] = voterAcc.Ecdsa diff --git a/state/runtime/precompiled/validator_set_precompile.go b/state/runtime/precompiled/validator_set_precompile.go index 2c641c1851..4ea5aed19e 100644 --- a/state/runtime/precompiled/validator_set_precompile.go +++ b/state/runtime/precompiled/validator_set_precompile.go @@ -1,20 +1,19 @@ package precompiled import ( + "bytes" + "encoding/binary" "errors" + "fmt" "github.com/0xPolygon/polygon-edge/chain" "github.com/0xPolygon/polygon-edge/consensus/polybft/validator" "github.com/0xPolygon/polygon-edge/state/runtime" "github.com/0xPolygon/polygon-edge/types" "github.com/hashicorp/go-hclog" - "github.com/umbracle/ethgo" - "github.com/umbracle/ethgo/abi" ) var ( - hasQuorumAbiType = abi.MustNewType("address[]") - errValidatorSetPrecompileNotEnabled = errors.New("validator set precompile is not enabled") ) @@ -54,24 +53,25 @@ func (c *validatorSetPrecompile) run(input []byte, caller types.Address, host ru return abiBoolFalse, nil } - rawData, err := abi.Decode(hasQuorumAbiType, input) + addresses, err := abiDecodeAddresses(input) if err != nil { return nil, err } - addresses, ok := rawData.([]ethgo.Address) - if !ok { - return nil, errBLSVerifyAggSignsInputs - } - validatorSet, err := createValidatorSet(host, c.backend) if err != nil { return nil, err } + fmt.Println("QUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU") signers := make(map[types.Address]struct{}, len(addresses)) for _, x := range addresses { - signers[types.Address(x)] = struct{}{} + signers[x] = struct{}{} + fmt.Println("HAS QUOORUM", x) + } + + for _, x := range validatorSet.Accounts() { + fmt.Println(x.Address.String(), x.VotingPower) } if validatorSet.HasQuorum(uint64(host.GetTxContext().Number), signers) { @@ -99,3 +99,45 @@ func createValidatorSet(host runtime.Host, backend ValidatoSetPrecompileBackend) return validator.NewValidatorSet(accounts, hclog.NewNullLogger()), nil } + +func abiDecodeAddresses(input []byte) ([]types.Address, error) { + if len(input) < 32 || len(input)%32 != 0 { + return nil, runtime.ErrInvalidInputData + } + + // abi.encode encodes addresses[] with slice of bytes where initial 31 bytes + // are set to 0, and 32nd is 32 + // then goes length of slice (32 bytes) + // then each address is 32 bytes + dummy := [32]byte{} + dummy[31] = 32 + + if bytes.Equal(dummy[:], input[:32]) { + input = input[32:] + } + + size := binary.BigEndian.Uint32(input[28:32]) + if uint32(len(input)) != size*32+32 { + return nil, runtime.ErrInvalidInputData + } + + res := make([]types.Address, size) + for i, offset := 0, 32; offset < len(input); i, offset = i+1, offset+32 { + res[i] = types.Address(input[offset+12 : offset+32]) + } + + return res, nil +} + +func abiEncodeAddresses(addrs []types.Address) []byte { + res := make([]byte, len(addrs)*32+64) + res[31] = 32 + + binary.BigEndian.PutUint32(res[32+28:64], uint32(len(addrs))) + + for i, a := range addrs { + copy(res[64+i*32+12:64+(i+1)*32], a.Bytes()) + } + + return res +} diff --git a/state/runtime/precompiled/validator_set_precompile_test.go b/state/runtime/precompiled/validator_set_precompile_test.go index 3724cf339c..a9d31da098 100644 --- a/state/runtime/precompiled/validator_set_precompile_test.go +++ b/state/runtime/precompiled/validator_set_precompile_test.go @@ -77,18 +77,20 @@ func Test_ValidatorSetPrecompile_run_IsValidator(t *testing.T) { } func Test_ValidatorSetPrecompile_run_HasQuorum(t *testing.T) { - addrGood := [][]byte{ - types.StringToAddress("a").Bytes(), - types.StringToAddress("b").Bytes(), - types.StringToAddress("d").Bytes(), + dummy := [32]byte{} + dummy[31] = 32 + addrGood := []types.Address{ + types.StringToAddress("a"), + types.StringToAddress("b"), + types.StringToAddress("d"), } - addrBad1 := [][]byte{ - types.StringToAddress("a").Bytes(), + addrBad1 := []types.Address{ + types.StringToAddress("a"), } - addrBad2 := [][]byte{ - types.StringToAddress("a").Bytes(), - types.StringToAddress("0").Bytes(), - types.StringToAddress("d").Bytes(), + addrBad2 := []types.Address{ + types.StringToAddress("a"), + types.StringToAddress("0"), + types.StringToAddress("d"), } host := newDummyHost(t) host.context = &runtime.TxContext{ @@ -103,24 +105,15 @@ func Test_ValidatorSetPrecompile_run_HasQuorum(t *testing.T) { backend: backendMock, } - bytes, err := hasQuorumAbiType.Encode(addrGood) - require.NoError(t, err) - - v, err := p.run(bytes, types.Address{}, host) + v, err := p.run(abiEncodeAddresses(addrGood), types.Address{}, host) require.NoError(t, err) assert.Equal(t, abiBoolTrue, v) - bytes, err = hasQuorumAbiType.Encode(addrBad1) - require.NoError(t, err) - - v, err = p.run(bytes, types.Address{}, host) + v, err = p.run(abiEncodeAddresses(addrBad1), types.Address{}, host) require.NoError(t, err) assert.Equal(t, abiBoolFalse, v) - bytes, err = hasQuorumAbiType.Encode(addrBad2) - require.NoError(t, err) - - v, err = p.run(bytes, types.Address{}, host) + v, err = p.run(abiEncodeAddresses(addrBad2), types.Address{}, host) require.NoError(t, err) assert.Equal(t, abiBoolFalse, v) } From 371d6aea60de5017d90436e8110a3c00d7393f6a Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Thu, 15 Feb 2024 23:49:57 +0100 Subject: [PATCH 08/16] e2e works --- e2e-polybft/e2e/consensus_test.go | 2 +- .../precompiled/validator_set_precompile.go | 27 ++++------ .../validator_set_precompile_test.go | 49 ++++++++++--------- 3 files changed, 37 insertions(+), 41 deletions(-) diff --git a/e2e-polybft/e2e/consensus_test.go b/e2e-polybft/e2e/consensus_test.go index d68e5894a8..c3a9ff0902 100644 --- a/e2e-polybft/e2e/consensus_test.go +++ b/e2e-polybft/e2e/consensus_test.go @@ -874,7 +874,7 @@ func TestE2E_TestValidatorSetPrecompile(t *testing.T) { response, err := txRelayer.Call(ethgo.ZeroAddress, validatorSetPrecompileTestAddr, hasQuorumFnBytes) require.NoError(t, err) - return response == "true" + return response == "0x0000000000000000000000000000000000000000000000000000000000000001" } sendIncTx := func(validatorID int) { diff --git a/state/runtime/precompiled/validator_set_precompile.go b/state/runtime/precompiled/validator_set_precompile.go index 4ea5aed19e..f17022cb60 100644 --- a/state/runtime/precompiled/validator_set_precompile.go +++ b/state/runtime/precompiled/validator_set_precompile.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/binary" "errors" - "fmt" "github.com/0xPolygon/polygon-edge/chain" "github.com/0xPolygon/polygon-edge/consensus/polybft/validator" @@ -37,9 +36,15 @@ func (c *validatorSetPrecompile) gas(input []byte, _ *chain.ForksInTime) uint64 // Input must be ABI encoded: address or (address[]) // Output could be an error or ABI encoded "bool" value func (c *validatorSetPrecompile) run(input []byte, caller types.Address, host runtime.Host) ([]byte, error) { + // if its payable tx we need to look for validator in previous block + blockNumber := uint64(host.GetTxContext().Number) + if !host.GetTxContext().NonPayable { + blockNumber-- + } + // isValidator case if len(input) == 32 { - validatorSet, err := createValidatorSet(host, c.backend) // we are calling validators for previous block + validatorSet, err := createValidatorSet(blockNumber, c.backend) // we are calling validators for previous block if err != nil { return nil, err } @@ -58,40 +63,28 @@ func (c *validatorSetPrecompile) run(input []byte, caller types.Address, host ru return nil, err } - validatorSet, err := createValidatorSet(host, c.backend) + validatorSet, err := createValidatorSet(blockNumber, c.backend) if err != nil { return nil, err } - fmt.Println("QUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU") signers := make(map[types.Address]struct{}, len(addresses)) for _, x := range addresses { signers[x] = struct{}{} - fmt.Println("HAS QUOORUM", x) - } - - for _, x := range validatorSet.Accounts() { - fmt.Println(x.Address.String(), x.VotingPower) } - if validatorSet.HasQuorum(uint64(host.GetTxContext().Number), signers) { + if validatorSet.HasQuorum(blockNumber, signers) { return abiBoolTrue, nil } return abiBoolFalse, nil } -func createValidatorSet(host runtime.Host, backend ValidatoSetPrecompileBackend) (validator.ValidatorSet, error) { +func createValidatorSet(blockNumber uint64, backend ValidatoSetPrecompileBackend) (validator.ValidatorSet, error) { if backend == nil { return nil, errValidatorSetPrecompileNotEnabled } - // if its payable tx we need to look for validator in previous block - blockNumber := uint64(host.GetTxContext().Number) - if !host.GetTxContext().NonPayable { - blockNumber-- - } - accounts, err := backend.GetValidatorsForBlock(blockNumber) if err != nil { return nil, err diff --git a/state/runtime/precompiled/validator_set_precompile_test.go b/state/runtime/precompiled/validator_set_precompile_test.go index a9d31da098..6b959bff2d 100644 --- a/state/runtime/precompiled/validator_set_precompile_test.go +++ b/state/runtime/precompiled/validator_set_precompile_test.go @@ -52,7 +52,7 @@ func Test_ValidatorSetPrecompile_run_GetValidatorsForBlockError(t *testing.T) { } func Test_ValidatorSetPrecompile_run_IsValidator(t *testing.T) { - addrGood := types.StringToAddress("a") + accounts := getDummyAccountSet() addrBad := types.StringToAddress("1") host := newDummyHost(t) host.context = &runtime.TxContext{ @@ -61,36 +61,37 @@ func Test_ValidatorSetPrecompile_run_IsValidator(t *testing.T) { } backendMock := &validatorSetBackendMock{} - backendMock.On("GetValidatorsForBlock", uint64(host.context.Number-1)).Return(getDummyAccountSet(), error(nil)) + backendMock.On("GetValidatorsForBlock", uint64(host.context.Number-1)).Return(accounts, error(nil)) p := &validatorSetPrecompile{ backend: backendMock, } - v, err := p.run(common.PadLeftOrTrim(addrGood.Bytes(), 32), types.Address{}, host) - require.NoError(t, err) - assert.Equal(t, abiBoolTrue, v) + for _, x := range accounts { + v, err := p.run(common.PadLeftOrTrim(x.Address[:], 32), types.Address{}, host) + require.NoError(t, err) + assert.Equal(t, abiBoolTrue, v) + } - v, err = p.run(common.PadLeftOrTrim(addrBad.Bytes(), 32), types.Address{}, host) + v, err := p.run(common.PadLeftOrTrim(addrBad.Bytes(), 32), types.Address{}, host) require.NoError(t, err) assert.Equal(t, abiBoolFalse, v) } func Test_ValidatorSetPrecompile_run_HasQuorum(t *testing.T) { - dummy := [32]byte{} - dummy[31] = 32 + accounts := getDummyAccountSet() addrGood := []types.Address{ - types.StringToAddress("a"), - types.StringToAddress("b"), - types.StringToAddress("d"), + accounts[0].Address, + accounts[1].Address, + accounts[3].Address, } addrBad1 := []types.Address{ - types.StringToAddress("a"), + accounts[0].Address, } addrBad2 := []types.Address{ - types.StringToAddress("a"), + accounts[0].Address, types.StringToAddress("0"), - types.StringToAddress("d"), + accounts[3].Address, } host := newDummyHost(t) host.context = &runtime.TxContext{ @@ -99,7 +100,7 @@ func Test_ValidatorSetPrecompile_run_HasQuorum(t *testing.T) { } backendMock := &validatorSetBackendMock{} - backendMock.On("GetValidatorsForBlock", uint64(host.context.Number)).Return(getDummyAccountSet(), error(nil)) + backendMock.On("GetValidatorsForBlock", uint64(host.context.Number)).Return(accounts, error(nil)) p := &validatorSetPrecompile{ backend: backendMock, @@ -129,25 +130,27 @@ func (m *validatorSetBackendMock) GetValidatorsForBlock(blockNumber uint64) (val } func getDummyAccountSet() validator.AccountSet { + v, _ := new(big.Int).SetString("1000000000000000000000", 10) + return validator.AccountSet{ &validator.ValidatorMetadata{ - Address: types.StringToAddress("a"), - VotingPower: new(big.Int).SetUint64(1), + Address: types.StringToAddress("0xd29f66FEd147B26925DE44Ba468670e921012B6f"), + VotingPower: new(big.Int).Set(v), IsActive: true, }, &validator.ValidatorMetadata{ - Address: types.StringToAddress("b"), - VotingPower: new(big.Int).SetUint64(1), + Address: types.StringToAddress("0x72aB93bbbc38E90d962dcbf41d973f8C434978e1"), + VotingPower: new(big.Int).Set(v), IsActive: true, }, &validator.ValidatorMetadata{ - Address: types.StringToAddress("c"), - VotingPower: new(big.Int).SetUint64(1), + Address: types.StringToAddress("0x3b5c82720835d3BAA42eB34E3e4acEE6042A830D"), + VotingPower: new(big.Int).Set(v), IsActive: true, }, &validator.ValidatorMetadata{ - Address: types.StringToAddress("d"), - VotingPower: new(big.Int).SetUint64(1), + Address: types.StringToAddress("0x87558A1abE10E41a328086474c93449e8A1F8f4e"), + VotingPower: new(big.Int).Set(v), IsActive: true, }, } From 350b9f6d05fa2b00b822062d2901413cbfb1da4e Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Mon, 19 Feb 2024 09:48:52 +0100 Subject: [PATCH 09/16] a little bit more of a readability --- .../TestValidatorSetPrecompile.json | 102 +++++++++--------- .../TestValidatorSetPrecompile.sol | 10 +- .../precompiled/validator_set_precompile.go | 13 ++- 3 files changed, 64 insertions(+), 61 deletions(-) diff --git a/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json index 73056c5be3..58eabe1f22 100644 --- a/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json +++ b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json @@ -3,64 +3,64 @@ "contractName": "TestValidatorSetPrecompile", "sourceName": "consensus/polybft/contractsapi/test-contracts/TestValidatorSetContract.sol", "abi": [ - { - "inputs":[ - - ], - "name":"VALIDATOR_SET_PRECOMPILE", - "outputs":[ - { - "internalType":"address", - "name":"", - "type":"address" - } - ], - "stateMutability":"view", - "type":"function" + { + "inputs":[ + + ], + "name":"VALIDATOR_SET_PRECOMPILE", + "outputs":[ + { + "internalType":"address", + "name":"", + "type":"address" + } + ], + "stateMutability":"view", + "type":"function" }, { - "inputs":[ - - ], - "name":"VALIDATOR_SET_PRECOMPILE_GAS", - "outputs":[ - { - "internalType":"uint256", - "name":"", - "type":"uint256" - } - ], - "stateMutability":"view", - "type":"function" + "inputs":[ + + ], + "name":"VALIDATOR_SET_PRECOMPILE_GAS", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" }, { - "inputs":[ - - ], - "name":"hasQuorum", - "outputs":[ - { - "internalType":"bool", - "name":"", - "type":"bool" - } - ], - "stateMutability":"view", - "type":"function" + "inputs":[ + + ], + "name":"hasQuorum", + "outputs":[ + { + "internalType":"bool", + "name":"", + "type":"bool" + } + ], + "stateMutability":"view", + "type":"function" }, { - "inputs":[ - - ], - "name":"inc", - "outputs":[ - - ], - "stateMutability":"nonpayable", - "type":"function" + "inputs":[ + + ], + "name":"inc", + "outputs":[ + + ], + "stateMutability":"nonpayable", + "type":"function" } - ], - "bytecode": "0x608060405234801561000f575f80fd5b506106ea8061001d5f395ff3fe608060405234801561000f575f80fd5b506004361061004a575f3560e01c8063371303c01461004e5780635e4f948814610058578063815b4d1b1461007657806384005d5614610094575b5f80fd5b6100566100b2565b005b6100606102a8565b60405161006d91906103ae565b60405180910390f35b61007e6102ae565b60405161008b91906103e1565b60405180910390f35b61009c610368565b6040516100a99190610412565b60405180910390f35b5f8061204073ffffffffffffffffffffffffffffffffffffffff16620249f0336040516020016100e291906103ae565b6040516020818303038152906040526040516100fe9190610497565b5f604051808303818686fa925050503d805f8114610137576040519150601f19603f3d011682016040523d82523d5f602084013e61013c565b606091505b50915091505f8180602001905181019061015691906104db565b90508280156101625750805b6101a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161019890610560565b60405180910390fd5b5f803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff166102a357600133908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060015f803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055505b505050565b61204081565b5f805f61204073ffffffffffffffffffffffffffffffffffffffff16620249f060016040516020016102e09190610694565b6040516020818303038152906040526040516102fc9190610497565b5f604051808303818686fa925050503d805f8114610335576040519150601f19603f3d011682016040523d82523d5f602084013e61033a565b606091505b50915091505f8180602001905181019061035491906104db565b90508280156103605750805b935050505090565b620249f081565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6103988261036f565b9050919050565b6103a88161038e565b82525050565b5f6020820190506103c15f83018461039f565b92915050565b5f8115159050919050565b6103db816103c7565b82525050565b5f6020820190506103f45f8301846103d2565b92915050565b5f819050919050565b61040c816103fa565b82525050565b5f6020820190506104255f830184610403565b92915050565b5f81519050919050565b5f81905092915050565b5f5b8381101561045c578082015181840152602081019050610441565b5f8484015250505050565b5f6104718261042b565b61047b8185610435565b935061048b81856020860161043f565b80840191505092915050565b5f6104a28284610467565b915081905092915050565b5f80fd5b6104ba816103c7565b81146104c4575f80fd5b50565b5f815190506104d5816104b1565b92915050565b5f602082840312156104f0576104ef6104ad565b5b5f6104fd848285016104c7565b91505092915050565b5f82825260208201905092915050565b7f76616c696461746f72206f6e6c790000000000000000000000000000000000005f82015250565b5f61054a600e83610506565b915061055582610516565b602082019050919050565b5f6020820190508181035f8301526105778161053e565b9050919050565b5f81549050919050565b5f82825260208201905092915050565b5f819050815f5260205f209050919050565b6105b38161038e565b82525050565b5f6105c483836105aa565b60208301905092915050565b5f815f1c9050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61060c610607836105d0565b6105db565b9050919050565b5f61061e82546105fa565b9050919050565b5f600182019050919050565b5f61063b8261057e565b6106458185610588565b935061065083610598565b805f5b838110156106875761066482610613565b61066e88826105b9565b975061067983610625565b925050600181019050610653565b5085935050505092915050565b5f6020820190508181035f8301526106ac8184610631565b90509291505056fea2646970667358221220efe83875fb1246787277c88a460f768e19f019c8e2e557e6d05731425a3210f864736f6c63430008180033", + ], + "bytecode": "0x608060405234801561000f575f80fd5b506106508061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c8063371303c014610038578063815b4d1b14610042575b5f80fd5b610040610060565b005b61004a610251565b6040516100579190610320565b60405180910390f35b5f8061204073ffffffffffffffffffffffffffffffffffffffff16620249f0336040516020016100909190610378565b6040516020818303038152906040526040516100ac91906103fd565b5f604051808303818686fa925050503d805f81146100e5576040519150601f19603f3d011682016040523d82523d5f602084013e6100ea565b606091505b509150915081801561010c57508080602001905181019061010b9190610441565b5b61014b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610142906104c6565b60405180910390fd5b5f803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff1661024d57600133908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060015f803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055505b5050565b5f805f61204073ffffffffffffffffffffffffffffffffffffffff16620249f0600160405160200161028391906105fa565b60405160208183030381529060405260405161029f91906103fd565b5f604051808303818686fa925050503d805f81146102d8576040519150601f19603f3d011682016040523d82523d5f602084013e6102dd565b606091505b50915091508180156102ff5750808060200190518101906102fe9190610441565b5b9250505090565b5f8115159050919050565b61031a81610306565b82525050565b5f6020820190506103335f830184610311565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61036282610339565b9050919050565b61037281610358565b82525050565b5f60208201905061038b5f830184610369565b92915050565b5f81519050919050565b5f81905092915050565b5f5b838110156103c25780820151818401526020810190506103a7565b5f8484015250505050565b5f6103d782610391565b6103e1818561039b565b93506103f18185602086016103a5565b80840191505092915050565b5f61040882846103cd565b915081905092915050565b5f80fd5b61042081610306565b811461042a575f80fd5b50565b5f8151905061043b81610417565b92915050565b5f6020828403121561045657610455610413565b5b5f6104638482850161042d565b91505092915050565b5f82825260208201905092915050565b7f76616c696461746f7200000000000000000000000000000000000000000000005f82015250565b5f6104b060098361046c565b91506104bb8261047c565b602082019050919050565b5f6020820190508181035f8301526104dd816104a4565b9050919050565b5f81549050919050565b5f82825260208201905092915050565b5f819050815f5260205f209050919050565b61051981610358565b82525050565b5f61052a8383610510565b60208301905092915050565b5f815f1c9050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61057261056d83610536565b610541565b9050919050565b5f6105848254610560565b9050919050565b5f600182019050919050565b5f6105a1826104e4565b6105ab81856104ee565b93506105b6836104fe565b805f5b838110156105ed576105ca82610579565b6105d4888261051f565b97506105df8361058b565b9250506001810190506105b9565b5085935050505092915050565b5f6020820190508181035f8301526106128184610597565b90509291505056fea26469706673582212207ce9734a4cb70866c45912245079c5f01a9c3f3b2cae0888b8afbbec2c224b6c64736f6c63430008180033", "linkReferences": {}, "deployedLinkReferences": {} } \ No newline at end of file diff --git a/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol index ba376ba208..c44fc1a05a 100644 --- a/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol +++ b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol @@ -4,8 +4,8 @@ pragma solidity ^0.8.0; contract TestValidatorSetPrecompile { - address public constant VALIDATOR_SET_PRECOMPILE = 0x0000000000000000000000000000000000002040; - uint256 public constant VALIDATOR_SET_PRECOMPILE_GAS = 150000; + address constant VALIDATOR_SET_PRECOMPILE = 0x0000000000000000000000000000000000002040; + uint256 constant VALIDATOR_SET_PRECOMPILE_GAS = 150000; mapping(address => bool) voteMap; address[] votes; @@ -14,8 +14,7 @@ contract TestValidatorSetPrecompile { (bool callSuccess, bytes memory returnData) = VALIDATOR_SET_PRECOMPILE.staticcall{ gas: VALIDATOR_SET_PRECOMPILE_GAS }(abi.encode(msg.sender)); - bool state = abi.decode(returnData, (bool)); - require(callSuccess && state, "validator only"); + require(callSuccess && abi.decode(returnData, (bool)), "validator"); _; } @@ -30,7 +29,6 @@ contract TestValidatorSetPrecompile { (bool callSuccess, bytes memory returnData) = VALIDATOR_SET_PRECOMPILE.staticcall{ gas: VALIDATOR_SET_PRECOMPILE_GAS }(abi.encode(votes)); - bool state = abi.decode(returnData, (bool)); - return callSuccess && state; + return callSuccess && abi.decode(returnData, (bool)); } } diff --git a/state/runtime/precompiled/validator_set_precompile.go b/state/runtime/precompiled/validator_set_precompile.go index f17022cb60..e1e4d4e845 100644 --- a/state/runtime/precompiled/validator_set_precompile.go +++ b/state/runtime/precompiled/validator_set_precompile.go @@ -12,6 +12,8 @@ import ( "github.com/hashicorp/go-hclog" ) +const addrOffset = 32 - types.AddressLength + var ( errValidatorSetPrecompileNotEnabled = errors.New("validator set precompile is not enabled") ) @@ -116,7 +118,7 @@ func abiDecodeAddresses(input []byte) ([]types.Address, error) { res := make([]types.Address, size) for i, offset := 0, 32; offset < len(input); i, offset = i+1, offset+32 { - res[i] = types.Address(input[offset+12 : offset+32]) + res[i] = types.Address(input[offset+addrOffset : offset+32]) } return res, nil @@ -126,10 +128,13 @@ func abiEncodeAddresses(addrs []types.Address) []byte { res := make([]byte, len(addrs)*32+64) res[31] = 32 - binary.BigEndian.PutUint32(res[32+28:64], uint32(len(addrs))) + binary.BigEndian.PutUint32(res[60:64], uint32(len(addrs))) // 60 == 32 + 28 + + offset := 64 - for i, a := range addrs { - copy(res[64+i*32+12:64+(i+1)*32], a.Bytes()) + for _, addr := range addrs { + copy(res[offset+addrOffset:offset+32], addr.Bytes()) + offset += 32 } return res From b668bd232509d27a0c55e149a8bf17d741fc2d3f Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Mon, 19 Feb 2024 10:32:36 +0100 Subject: [PATCH 10/16] simplified --- state/runtime/precompiled/validator_set_precompile.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/state/runtime/precompiled/validator_set_precompile.go b/state/runtime/precompiled/validator_set_precompile.go index e1e4d4e845..7beb793f63 100644 --- a/state/runtime/precompiled/validator_set_precompile.go +++ b/state/runtime/precompiled/validator_set_precompile.go @@ -1,7 +1,6 @@ package precompiled import ( - "bytes" "encoding/binary" "errors" @@ -96,7 +95,7 @@ func createValidatorSet(blockNumber uint64, backend ValidatoSetPrecompileBackend } func abiDecodeAddresses(input []byte) ([]types.Address, error) { - if len(input) < 32 || len(input)%32 != 0 { + if len(input) < 64 || len(input)%32 != 0 { return nil, runtime.ErrInvalidInputData } @@ -107,9 +106,7 @@ func abiDecodeAddresses(input []byte) ([]types.Address, error) { dummy := [32]byte{} dummy[31] = 32 - if bytes.Equal(dummy[:], input[:32]) { - input = input[32:] - } + input = input[32:] size := binary.BigEndian.Uint32(input[28:32]) if uint32(len(input)) != size*32+32 { From cd9bbd52978235e4f375cf6e6a99093bd3c2102b Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Mon, 19 Feb 2024 10:36:55 +0100 Subject: [PATCH 11/16] additional --- state/runtime/precompiled/validator_set_precompile.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/state/runtime/precompiled/validator_set_precompile.go b/state/runtime/precompiled/validator_set_precompile.go index 7beb793f63..7b53fe76b4 100644 --- a/state/runtime/precompiled/validator_set_precompile.go +++ b/state/runtime/precompiled/validator_set_precompile.go @@ -1,6 +1,7 @@ package precompiled import ( + "bytes" "encoding/binary" "errors" @@ -106,15 +107,17 @@ func abiDecodeAddresses(input []byte) ([]types.Address, error) { dummy := [32]byte{} dummy[31] = 32 - input = input[32:] + if !bytes.Equal(dummy[:], input[:32]) { + return nil, runtime.ErrInvalidInputData + } - size := binary.BigEndian.Uint32(input[28:32]) - if uint32(len(input)) != size*32+32 { + size := binary.BigEndian.Uint32(input[60:64]) + if uint32(len(input)) != size*32+64 { return nil, runtime.ErrInvalidInputData } res := make([]types.Address, size) - for i, offset := 0, 32; offset < len(input); i, offset = i+1, offset+32 { + for i, offset := 0, 64; offset < len(input); i, offset = i+1, offset+32 { res[i] = types.Address(input[offset+addrOffset : offset+32]) } From 3d5ecef76a6436bf003228e938da6c1248dafb10 Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Mon, 19 Feb 2024 12:27:32 +0100 Subject: [PATCH 12/16] fix --- state/executor.go | 6 +++--- state/runtime/precompiled/precompiled.go | 4 ++-- .../precompiled/validator_set_precompile.go | 6 +++--- .../validator_set_precompile_test.go | 21 +++++++++++++++++++ 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/state/executor.go b/state/executor.go index 220085ea0f..39bb4d77ff 100644 --- a/state/executor.go +++ b/state/executor.go @@ -48,7 +48,7 @@ type Executor struct { // this value should be set if we want to enable validator set precompile // note that this precompile WONT be enabled for WriteGenesis - validatorSetBackend precompiled.ValidatoSetPrecompileBackend + validatorSetBackend precompiled.ValidatorSetPrecompileBackend } // NewExecutor creates a new executor @@ -239,7 +239,7 @@ func (e *Executor) BeginTxn( return t, nil } -func (e *Executor) SetValidatorSetBackend(validatorSetBackend precompiled.ValidatoSetPrecompileBackend) { +func (e *Executor) SetValidatorSetBackend(validatorSetBackend precompiled.ValidatorSetPrecompileBackend) { e.validatorSetBackend = validatorSetBackend } @@ -281,7 +281,7 @@ type Transition struct { } func NewTransition(logger hclog.Logger, config chain.ForksInTime, - snap Snapshot, radix *Txn, validatorSetBackend precompiled.ValidatoSetPrecompiledBackend) *Transition { + snap Snapshot, radix *Txn, validatorSetBackend precompiled.ValidatorSetPrecompileBackend) *Transition { return &Transition{ logger: logger, config: config, diff --git a/state/runtime/precompiled/precompiled.go b/state/runtime/precompiled/precompiled.go index 94ff1e7b64..91501f0e79 100644 --- a/state/runtime/precompiled/precompiled.go +++ b/state/runtime/precompiled/precompiled.go @@ -49,14 +49,14 @@ type Precompiled struct { } // NewPrecompiled creates a new runtime for the precompiled contracts -func NewPrecompiled(validatorSetBackend ValidatoSetPrecompileBackend) *Precompiled { +func NewPrecompiled(validatorSetBackend ValidatorSetPrecompileBackend) *Precompiled { p := &Precompiled{} p.setupContracts(validatorSetBackend) return p } -func (p *Precompiled) setupContracts(validatorSetBackend ValidatoSetPrecompileBackend) { +func (p *Precompiled) setupContracts(validatorSetBackend ValidatorSetPrecompileBackend) { p.register("1", &ecrecover{p}) p.register("2", &sha256h{}) p.register("3", &ripemd160h{p}) diff --git a/state/runtime/precompiled/validator_set_precompile.go b/state/runtime/precompiled/validator_set_precompile.go index 7b53fe76b4..f928f1078e 100644 --- a/state/runtime/precompiled/validator_set_precompile.go +++ b/state/runtime/precompiled/validator_set_precompile.go @@ -18,12 +18,12 @@ var ( errValidatorSetPrecompileNotEnabled = errors.New("validator set precompile is not enabled") ) -type ValidatoSetPrecompileBackend interface { +type ValidatorSetPrecompileBackend interface { GetValidatorsForBlock(blockNumber uint64) (validator.AccountSet, error) } type validatorSetPrecompile struct { - backend ValidatoSetPrecompileBackend + backend ValidatorSetPrecompileBackend } // gas returns the gas required to execute the pre-compiled contract @@ -82,7 +82,7 @@ func (c *validatorSetPrecompile) run(input []byte, caller types.Address, host ru return abiBoolFalse, nil } -func createValidatorSet(blockNumber uint64, backend ValidatoSetPrecompileBackend) (validator.ValidatorSet, error) { +func createValidatorSet(blockNumber uint64, backend ValidatorSetPrecompileBackend) (validator.ValidatorSet, error) { if backend == nil { return nil, errValidatorSetPrecompileNotEnabled } diff --git a/state/runtime/precompiled/validator_set_precompile_test.go b/state/runtime/precompiled/validator_set_precompile_test.go index 6b959bff2d..15ea510c06 100644 --- a/state/runtime/precompiled/validator_set_precompile_test.go +++ b/state/runtime/precompiled/validator_set_precompile_test.go @@ -119,6 +119,27 @@ func Test_ValidatorSetPrecompile_run_HasQuorum(t *testing.T) { assert.Equal(t, abiBoolFalse, v) } +func Test_abiDecodeAddresses_Error(t *testing.T) { + dummy1 := [31]byte{} + dummy2 := [62]byte{} + dummy3 := [64]byte{} + dummy4 := [96]byte{} + dummy4[31] = 32 + dummy4[63] = 10 + + _, err := abiDecodeAddresses(dummy1[:]) + require.ErrorIs(t, err, runtime.ErrInvalidInputData) + + _, err = abiDecodeAddresses(dummy2[:]) + require.ErrorIs(t, err, runtime.ErrInvalidInputData) + + _, err = abiDecodeAddresses(dummy3[:]) + require.ErrorIs(t, err, runtime.ErrInvalidInputData) + + _, err = abiDecodeAddresses(dummy4[:]) + require.ErrorIs(t, err, runtime.ErrInvalidInputData) +} + type validatorSetBackendMock struct { mock.Mock } From 5f2520ccbb8f663848015efee19f003a2151aa9e Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Thu, 29 Feb 2024 10:58:19 +0100 Subject: [PATCH 13/16] cr fix --- e2e-polybft/e2e/consensus_test.go | 51 +++++++++++++++---- .../precompiled/validator_set_precompile.go | 9 +++- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/e2e-polybft/e2e/consensus_test.go b/e2e-polybft/e2e/consensus_test.go index c3a9ff0902..2941b5322d 100644 --- a/e2e-polybft/e2e/consensus_test.go +++ b/e2e-polybft/e2e/consensus_test.go @@ -825,22 +825,35 @@ func TestE2E_Deploy_Nested_Contract(t *testing.T) { } func TestE2E_TestValidatorSetPrecompile(t *testing.T) { + var ( + premineBalance = ethgo.Ether(2e6) // 2M native tokens (so that we have enough balance to fund new validator) + stakeAmount = ethgo.Ether(500) + ) + admin, err := wallet.GenerateKey() require.NoError(t, err) - validatorAddrs := []types.Address(nil) - adminAddr := types.Address(admin.Address()) + dummyKey, err := wallet.GenerateKey() + require.NoError(t, err) + // start cluster with 'validatorSize' validators cluster := framework.NewTestCluster(t, 4, - framework.WithBladeAdmin(adminAddr.String()), - framework.WithSecretsCallback(func(addrs []types.Address, _ *framework.TestClusterConfig) { - validatorAddrs = addrs - })) + framework.WithBladeAdmin(admin.Address().String()), + framework.WithSecretsCallback(func(addresses []types.Address, config *framework.TestClusterConfig) { + for _, a := range addresses { + config.Premine = append(config.Premine, fmt.Sprintf("%s:%s", a, premineBalance)) + config.StakeAmounts = append(config.StakeAmounts, stakeAmount) + } + + config.Premine = append(config.Premine, fmt.Sprintf("%s:%s", dummyKey.Address(), premineBalance)) + }), + ) + defer cluster.Stop() cluster.WaitForReady(t) - validatorKeys := make([]*wallet.Key, len(validatorAddrs)) + validatorKeys := make([]*wallet.Key, len(cluster.Servers)) for i, s := range cluster.Servers { voterAcc, err := validatorHelper.GetAccountFromDir(s.DataDir()) @@ -880,20 +893,33 @@ func TestE2E_TestValidatorSetPrecompile(t *testing.T) { sendIncTx := func(validatorID int) { t.Helper() + isValidator := validatorID >= 0 && validatorID < len(validatorKeys) incFn := contractsapi.TestValidatorSetPrecompile.Abi.GetMethod("inc") incFnBytes, err := incFn.Encode([]interface{}{}) require.NoError(t, err) + var key *wallet.Key + if isValidator { + key = validatorKeys[validatorID] + } else { + key = dummyKey + } + txn := ðgo.Transaction{ - From: ethgo.Address(validatorAddrs[validatorID]), + From: key.Address(), To: &validatorSetPrecompileTestAddr, Input: incFnBytes, } - receipt, err = txRelayer.SendTransaction(txn, validatorKeys[validatorID]) - require.NoError(t, err) - require.Equal(t, uint64(types.ReceiptSuccess), receipt.Status) + receipt, err = txRelayer.SendTransaction(txn, key) + + if isValidator { + require.NoError(t, err) + require.Equal(t, uint64(types.ReceiptSuccess), receipt.Status) + } else { + require.ErrorContains(t, err, "unable to apply transaction even for the highest gas limit") + } } require.False(t, hasQuorum()) @@ -907,6 +933,9 @@ func TestE2E_TestValidatorSetPrecompile(t *testing.T) { sendIncTx(1) require.False(t, hasQuorum()) + sendIncTx(-1) // non validator + require.False(t, hasQuorum()) + sendIncTx(3) require.True(t, hasQuorum()) } diff --git a/state/runtime/precompiled/validator_set_precompile.go b/state/runtime/precompiled/validator_set_precompile.go index f928f1078e..c8c47a8a6d 100644 --- a/state/runtime/precompiled/validator_set_precompile.go +++ b/state/runtime/precompiled/validator_set_precompile.go @@ -18,10 +18,17 @@ var ( errValidatorSetPrecompileNotEnabled = errors.New("validator set precompile is not enabled") ) +// ValidatorSetPrecompileBackend is an interface defining the contract for a precompile backend +// responsible for retrieving validators (current account set) for a specific block number type ValidatorSetPrecompileBackend interface { GetValidatorsForBlock(blockNumber uint64) (validator.AccountSet, error) } +// validatorSetPrecompile is a concrete implementation of the contract interface. +// The struct implements two functionalities through the `run` method: +// - isValidator(address addr) bool: Returns true if addr is the address of a validator. +// - hasQuorum(address[] addrs) bool: Returns true if the array of validators is sufficient to constitute a quorum +// It encapsulates a backend that provides the functionality to retrieve validators for a specific block number type validatorSetPrecompile struct { backend ValidatorSetPrecompileBackend } @@ -34,7 +41,7 @@ func (c *validatorSetPrecompile) gas(input []byte, _ *chain.ForksInTime) uint64 // Run runs the precompiled contract with the given input. // There are two functions: // isValidator(address addr) bool -// hasConsensus(address[] addrs) bool +// hasQuorum(address[] addrs) bool // Input must be ABI encoded: address or (address[]) // Output could be an error or ABI encoded "bool" value func (c *validatorSetPrecompile) run(input []byte, caller types.Address, host runtime.Host) ([]byte, error) { From 6bb60cac723a227084e1ac7818b996cbfbddc6ef Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Fri, 1 Mar 2024 10:02:09 +0100 Subject: [PATCH 14/16] gas higher --- .../contractsapi/test-contracts/TestValidatorSetPrecompile.json | 2 +- .../contractsapi/test-contracts/TestValidatorSetPrecompile.sol | 2 +- state/runtime/precompiled/validator_set_precompile.go | 2 +- state/runtime/precompiled/validator_set_precompile_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json index 58eabe1f22..94fb9c4c27 100644 --- a/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json +++ b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.json @@ -60,7 +60,7 @@ "type":"function" } ], - "bytecode": "0x608060405234801561000f575f80fd5b506106508061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c8063371303c014610038578063815b4d1b14610042575b5f80fd5b610040610060565b005b61004a610251565b6040516100579190610320565b60405180910390f35b5f8061204073ffffffffffffffffffffffffffffffffffffffff16620249f0336040516020016100909190610378565b6040516020818303038152906040526040516100ac91906103fd565b5f604051808303818686fa925050503d805f81146100e5576040519150601f19603f3d011682016040523d82523d5f602084013e6100ea565b606091505b509150915081801561010c57508080602001905181019061010b9190610441565b5b61014b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610142906104c6565b60405180910390fd5b5f803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff1661024d57600133908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060015f803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055505b5050565b5f805f61204073ffffffffffffffffffffffffffffffffffffffff16620249f0600160405160200161028391906105fa565b60405160208183030381529060405260405161029f91906103fd565b5f604051808303818686fa925050503d805f81146102d8576040519150601f19603f3d011682016040523d82523d5f602084013e6102dd565b606091505b50915091508180156102ff5750808060200190518101906102fe9190610441565b5b9250505090565b5f8115159050919050565b61031a81610306565b82525050565b5f6020820190506103335f830184610311565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61036282610339565b9050919050565b61037281610358565b82525050565b5f60208201905061038b5f830184610369565b92915050565b5f81519050919050565b5f81905092915050565b5f5b838110156103c25780820151818401526020810190506103a7565b5f8484015250505050565b5f6103d782610391565b6103e1818561039b565b93506103f18185602086016103a5565b80840191505092915050565b5f61040882846103cd565b915081905092915050565b5f80fd5b61042081610306565b811461042a575f80fd5b50565b5f8151905061043b81610417565b92915050565b5f6020828403121561045657610455610413565b5b5f6104638482850161042d565b91505092915050565b5f82825260208201905092915050565b7f76616c696461746f7200000000000000000000000000000000000000000000005f82015250565b5f6104b060098361046c565b91506104bb8261047c565b602082019050919050565b5f6020820190508181035f8301526104dd816104a4565b9050919050565b5f81549050919050565b5f82825260208201905092915050565b5f819050815f5260205f209050919050565b61051981610358565b82525050565b5f61052a8383610510565b60208301905092915050565b5f815f1c9050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61057261056d83610536565b610541565b9050919050565b5f6105848254610560565b9050919050565b5f600182019050919050565b5f6105a1826104e4565b6105ab81856104ee565b93506105b6836104fe565b805f5b838110156105ed576105ca82610579565b6105d4888261051f565b97506105df8361058b565b9250506001810190506105b9565b5085935050505092915050565b5f6020820190508181035f8301526106128184610597565b90509291505056fea26469706673582212207ce9734a4cb70866c45912245079c5f01a9c3f3b2cae0888b8afbbec2c224b6c64736f6c63430008180033", + "bytecode": "0x608060405234801561000f575f80fd5b506106508061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c8063371303c014610038578063815b4d1b14610042575b5f80fd5b610040610060565b005b61004a610251565b6040516100579190610320565b60405180910390f35b5f8061204073ffffffffffffffffffffffffffffffffffffffff166203a980336040516020016100909190610378565b6040516020818303038152906040526040516100ac91906103fd565b5f604051808303818686fa925050503d805f81146100e5576040519150601f19603f3d011682016040523d82523d5f602084013e6100ea565b606091505b509150915081801561010c57508080602001905181019061010b9190610441565b5b61014b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610142906104c6565b60405180910390fd5b5f803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff1661024d57600133908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060015f803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055505b5050565b5f805f61204073ffffffffffffffffffffffffffffffffffffffff166203a980600160405160200161028391906105fa565b60405160208183030381529060405260405161029f91906103fd565b5f604051808303818686fa925050503d805f81146102d8576040519150601f19603f3d011682016040523d82523d5f602084013e6102dd565b606091505b50915091508180156102ff5750808060200190518101906102fe9190610441565b5b9250505090565b5f8115159050919050565b61031a81610306565b82525050565b5f6020820190506103335f830184610311565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61036282610339565b9050919050565b61037281610358565b82525050565b5f60208201905061038b5f830184610369565b92915050565b5f81519050919050565b5f81905092915050565b5f5b838110156103c25780820151818401526020810190506103a7565b5f8484015250505050565b5f6103d782610391565b6103e1818561039b565b93506103f18185602086016103a5565b80840191505092915050565b5f61040882846103cd565b915081905092915050565b5f80fd5b61042081610306565b811461042a575f80fd5b50565b5f8151905061043b81610417565b92915050565b5f6020828403121561045657610455610413565b5b5f6104638482850161042d565b91505092915050565b5f82825260208201905092915050565b7f76616c696461746f7200000000000000000000000000000000000000000000005f82015250565b5f6104b060098361046c565b91506104bb8261047c565b602082019050919050565b5f6020820190508181035f8301526104dd816104a4565b9050919050565b5f81549050919050565b5f82825260208201905092915050565b5f819050815f5260205f209050919050565b61051981610358565b82525050565b5f61052a8383610510565b60208301905092915050565b5f815f1c9050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61057261056d83610536565b610541565b9050919050565b5f6105848254610560565b9050919050565b5f600182019050919050565b5f6105a1826104e4565b6105ab81856104ee565b93506105b6836104fe565b805f5b838110156105ed576105ca82610579565b6105d4888261051f565b97506105df8361058b565b9250506001810190506105b9565b5085935050505092915050565b5f6020820190508181035f8301526106128184610597565b90509291505056fea26469706673582212206efd0d43fe03760dad6d6d6027319396055df00430d6654ecf53622a2dc85a4764736f6c63430008180033", "linkReferences": {}, "deployedLinkReferences": {} } \ No newline at end of file diff --git a/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol index c44fc1a05a..a90c320ff2 100644 --- a/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol +++ b/consensus/polybft/contractsapi/test-contracts/TestValidatorSetPrecompile.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.0; contract TestValidatorSetPrecompile { address constant VALIDATOR_SET_PRECOMPILE = 0x0000000000000000000000000000000000002040; - uint256 constant VALIDATOR_SET_PRECOMPILE_GAS = 150000; + uint256 constant VALIDATOR_SET_PRECOMPILE_GAS = 240000; mapping(address => bool) voteMap; address[] votes; diff --git a/state/runtime/precompiled/validator_set_precompile.go b/state/runtime/precompiled/validator_set_precompile.go index c8c47a8a6d..6ab9fdd98a 100644 --- a/state/runtime/precompiled/validator_set_precompile.go +++ b/state/runtime/precompiled/validator_set_precompile.go @@ -35,7 +35,7 @@ type validatorSetPrecompile struct { // gas returns the gas required to execute the pre-compiled contract func (c *validatorSetPrecompile) gas(input []byte, _ *chain.ForksInTime) uint64 { - return 150000 + return 240000 } // Run runs the precompiled contract with the given input. diff --git a/state/runtime/precompiled/validator_set_precompile_test.go b/state/runtime/precompiled/validator_set_precompile_test.go index 15ea510c06..32c81be933 100644 --- a/state/runtime/precompiled/validator_set_precompile_test.go +++ b/state/runtime/precompiled/validator_set_precompile_test.go @@ -15,7 +15,7 @@ import ( ) func Test_ValidatorSetPrecompile_gas(t *testing.T) { - assert.Equal(t, uint64(150000), (&validatorSetPrecompile{}).gas(nil, nil)) + assert.Equal(t, uint64(240000), (&validatorSetPrecompile{}).gas(nil, nil)) } func Test_ValidatorSetPrecompile_run_BackendNotSet(t *testing.T) { From f486ac4b843fb96b8576cdbffd5739ce8a7608eb Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Fri, 1 Mar 2024 10:10:40 +0100 Subject: [PATCH 15/16] revert --- tests/tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests b/tests/tests index 5bf1fff257..853b1e03b1 160000 --- a/tests/tests +++ b/tests/tests @@ -1 +1 @@ -Subproject commit 5bf1fff257c0662bc526c27d7546d470025b3239 +Subproject commit 853b1e03b1078d370614002851ba1ee9803d9fcf From 9696d4f14e914a49e3cbca68d7afb7f01df288f5 Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Wed, 6 Mar 2024 10:06:57 +0100 Subject: [PATCH 16/16] expand gas cost interface, limit number of addresses for hasQuorum --- consensus/polybft/polybft.go | 14 +++++++++++ state/runtime/precompiled/base.go | 8 +++--- state/runtime/precompiled/base_test.go | 2 +- state/runtime/precompiled/blake2f.go | 2 +- .../precompiled/bls_agg_sigs_verification.go | 2 +- state/runtime/precompiled/bn256.go | 6 ++--- state/runtime/precompiled/modexp.go | 2 +- state/runtime/precompiled/native_transfer.go | 2 +- state/runtime/precompiled/precompiled.go | 4 +-- .../precompiled/validator_set_precompile.go | 25 ++++++++++++++++--- .../validator_set_precompile_test.go | 18 +++++++++---- 11 files changed, 62 insertions(+), 23 deletions(-) diff --git a/consensus/polybft/polybft.go b/consensus/polybft/polybft.go index 46291941fc..1f6b87df68 100644 --- a/consensus/polybft/polybft.go +++ b/consensus/polybft/polybft.go @@ -826,6 +826,20 @@ func (p *Polybft) GetLatestChainConfig() (*chain.Params, error) { return nil, nil } +func (p *Polybft) GetMaxValidatorSetSize() (uint64, error) { + params, err := p.GetLatestChainConfig() + if err != nil { + return 0, err + } + + polyBFTConfig, err := GetPolyBFTConfig(params) + if err != nil { + return 0, err + } + + return polyBFTConfig.MaxValidatorSetSize, nil +} + // GetBridgeProvider is an implementation of Consensus interface // Returns an instance of BridgeDataProvider func (p *Polybft) GetBridgeProvider() consensus.BridgeDataProvider { diff --git a/state/runtime/precompiled/base.go b/state/runtime/precompiled/base.go index 9997d4ebdc..3d081ce2d8 100644 --- a/state/runtime/precompiled/base.go +++ b/state/runtime/precompiled/base.go @@ -17,7 +17,7 @@ type ecrecover struct { p *Precompiled } -func (e *ecrecover) gas(input []byte, config *chain.ForksInTime) uint64 { +func (e *ecrecover) gas(_ []byte, _ types.Address, _ *chain.ForksInTime) uint64 { return 3000 } @@ -57,7 +57,7 @@ func (e *ecrecover) run(input []byte, caller types.Address, _ runtime.Host) ([]b type identity struct { } -func (i *identity) gas(input []byte, config *chain.ForksInTime) uint64 { +func (i *identity) gas(input []byte, _ types.Address, _ *chain.ForksInTime) uint64 { return baseGasCalc(input, 15, 3) } @@ -68,7 +68,7 @@ func (i *identity) run(input []byte, _ types.Address, _ runtime.Host) ([]byte, e type sha256h struct { } -func (s *sha256h) gas(input []byte, config *chain.ForksInTime) uint64 { +func (s *sha256h) gas(input []byte, _ types.Address, _ *chain.ForksInTime) uint64 { return baseGasCalc(input, 60, 12) } @@ -82,7 +82,7 @@ type ripemd160h struct { p *Precompiled } -func (r *ripemd160h) gas(input []byte, config *chain.ForksInTime) uint64 { +func (r *ripemd160h) gas(input []byte, _ types.Address, _ *chain.ForksInTime) uint64 { return baseGasCalc(input, 600, 120) } diff --git a/state/runtime/precompiled/base_test.go b/state/runtime/precompiled/base_test.go index dc2aeada73..a548d19345 100644 --- a/state/runtime/precompiled/base_test.go +++ b/state/runtime/precompiled/base_test.go @@ -25,7 +25,7 @@ func testPrecompiled(t *testing.T, p contract, cases []precompiledTest, enabledF h, _ := hex.DecodeString(c.Input) if c.Gas != 0 && len(enabledForks) > 0 { - gas := p.gas(h, enabledForks[0]) + gas := p.gas(h, types.Address{}, enabledForks[0]) assert.Equal(t, c.Gas, gas, "Incorrect gas estimation") } diff --git a/state/runtime/precompiled/blake2f.go b/state/runtime/precompiled/blake2f.go index 62ad2872b1..a54f0e88bd 100644 --- a/state/runtime/precompiled/blake2f.go +++ b/state/runtime/precompiled/blake2f.go @@ -14,7 +14,7 @@ type blake2f struct { p *Precompiled } -func (e *blake2f) gas(input []byte, config *chain.ForksInTime) uint64 { +func (e *blake2f) gas(input []byte, _ types.Address, _ *chain.ForksInTime) uint64 { if len(input) != 213 { return 0 } diff --git a/state/runtime/precompiled/bls_agg_sigs_verification.go b/state/runtime/precompiled/bls_agg_sigs_verification.go index 06e21cfc61..d343b1d962 100644 --- a/state/runtime/precompiled/bls_agg_sigs_verification.go +++ b/state/runtime/precompiled/bls_agg_sigs_verification.go @@ -32,7 +32,7 @@ type blsAggSignsVerification struct { } // gas returns the gas required to execute the pre-compiled contract -func (c *blsAggSignsVerification) gas(input []byte, _ *chain.ForksInTime) uint64 { +func (c *blsAggSignsVerification) gas(_ []byte, _ types.Address, _ *chain.ForksInTime) uint64 { return 150000 } diff --git a/state/runtime/precompiled/bn256.go b/state/runtime/precompiled/bn256.go index 7181a3a647..07c3d4e38b 100644 --- a/state/runtime/precompiled/bn256.go +++ b/state/runtime/precompiled/bn256.go @@ -14,7 +14,7 @@ type bn256Add struct { p *Precompiled } -func (b *bn256Add) gas(input []byte, config *chain.ForksInTime) uint64 { +func (b *bn256Add) gas(_ []byte, _ types.Address, config *chain.ForksInTime) uint64 { if config.Istanbul { return 150 } @@ -48,7 +48,7 @@ type bn256Mul struct { p *Precompiled } -func (b *bn256Mul) gas(input []byte, config *chain.ForksInTime) uint64 { +func (b *bn256Mul) gas(_ []byte, _ types.Address, config *chain.ForksInTime) uint64 { if config.Istanbul { return 6000 } @@ -79,7 +79,7 @@ type bn256Pairing struct { p *Precompiled } -func (b *bn256Pairing) gas(input []byte, config *chain.ForksInTime) uint64 { +func (b *bn256Pairing) gas(input []byte, _ types.Address, config *chain.ForksInTime) uint64 { baseGas, pointGas := uint64(100000), uint64(80000) if config.Istanbul { baseGas, pointGas = 45000, 34000 diff --git a/state/runtime/precompiled/modexp.go b/state/runtime/precompiled/modexp.go index c8d3f02edb..ee3661602b 100644 --- a/state/runtime/precompiled/modexp.go +++ b/state/runtime/precompiled/modexp.go @@ -83,7 +83,7 @@ func multComplexity(x *big.Int) *big.Int { return x } -func (m *modExp) gas(input []byte, config *chain.ForksInTime) uint64 { +func (m *modExp) gas(input []byte, _ types.Address, config *chain.ForksInTime) uint64 { var val, tail []byte val, tail = m.p.get(input, 32) diff --git a/state/runtime/precompiled/native_transfer.go b/state/runtime/precompiled/native_transfer.go index 1be4a94618..74d0b0ad2c 100644 --- a/state/runtime/precompiled/native_transfer.go +++ b/state/runtime/precompiled/native_transfer.go @@ -11,7 +11,7 @@ import ( type nativeTransfer struct{} -func (c *nativeTransfer) gas(input []byte, _ *chain.ForksInTime) uint64 { +func (c *nativeTransfer) gas(_ []byte, _ types.Address, _ *chain.ForksInTime) uint64 { return 21000 } diff --git a/state/runtime/precompiled/precompiled.go b/state/runtime/precompiled/precompiled.go index 91501f0e79..93626f953b 100644 --- a/state/runtime/precompiled/precompiled.go +++ b/state/runtime/precompiled/precompiled.go @@ -37,7 +37,7 @@ func init() { } type contract interface { - gas(input []byte, config *chain.ForksInTime) uint64 + gas(input []byte, caller types.Address, config *chain.ForksInTime) uint64 run(input []byte, caller types.Address, host runtime.Host) ([]byte, error) } @@ -139,7 +139,7 @@ func (p *Precompiled) Name() string { // Run runs an execution func (p *Precompiled) Run(c *runtime.Contract, host runtime.Host, config *chain.ForksInTime) *runtime.ExecutionResult { contract := p.contracts[c.CodeAddress] - gasCost := contract.gas(c.Input, config) + gasCost := contract.gas(c.Input, c.Caller, config) // In the case of not enough gas for precompiled execution we return ErrOutOfGas if c.Gas < gasCost { diff --git a/state/runtime/precompiled/validator_set_precompile.go b/state/runtime/precompiled/validator_set_precompile.go index 6ab9fdd98a..8932f0117f 100644 --- a/state/runtime/precompiled/validator_set_precompile.go +++ b/state/runtime/precompiled/validator_set_precompile.go @@ -12,7 +12,10 @@ import ( "github.com/hashicorp/go-hclog" ) -const addrOffset = 32 - types.AddressLength +const ( + addrOffset = 32 - types.AddressLength + defaultMaxHasQuorumAddresses = 100 +) var ( errValidatorSetPrecompileNotEnabled = errors.New("validator set precompile is not enabled") @@ -22,6 +25,7 @@ var ( // responsible for retrieving validators (current account set) for a specific block number type ValidatorSetPrecompileBackend interface { GetValidatorsForBlock(blockNumber uint64) (validator.AccountSet, error) + GetMaxValidatorSetSize() (uint64, error) } // validatorSetPrecompile is a concrete implementation of the contract interface. @@ -34,7 +38,7 @@ type validatorSetPrecompile struct { } // gas returns the gas required to execute the pre-compiled contract -func (c *validatorSetPrecompile) gas(input []byte, _ *chain.ForksInTime) uint64 { +func (c *validatorSetPrecompile) gas(_ []byte, _ types.Address, config *chain.ForksInTime) uint64 { return 240000 } @@ -67,7 +71,16 @@ func (c *validatorSetPrecompile) run(input []byte, caller types.Address, host ru return abiBoolFalse, nil } - addresses, err := abiDecodeAddresses(input) + maxAddressesCount, err := c.backend.GetMaxValidatorSetSize() + if err != nil { + return nil, err + } + + if maxAddressesCount == 0 { + maxAddressesCount = defaultMaxHasQuorumAddresses + } + + addresses, err := abiDecodeAddresses(input, uint32(maxAddressesCount)) if err != nil { return nil, err } @@ -102,7 +115,7 @@ func createValidatorSet(blockNumber uint64, backend ValidatorSetPrecompileBacken return validator.NewValidatorSet(accounts, hclog.NewNullLogger()), nil } -func abiDecodeAddresses(input []byte) ([]types.Address, error) { +func abiDecodeAddresses(input []byte, maxCount uint32) ([]types.Address, error) { if len(input) < 64 || len(input)%32 != 0 { return nil, runtime.ErrInvalidInputData } @@ -122,6 +135,10 @@ func abiDecodeAddresses(input []byte) ([]types.Address, error) { if uint32(len(input)) != size*32+64 { return nil, runtime.ErrInvalidInputData } + // sanitize the input if it contains too many addresses. + if maxCount != 0 && size > maxCount { + return nil, runtime.ErrInvalidInputData + } res := make([]types.Address, size) for i, offset := 0, 64; offset < len(input); i, offset = i+1, offset+32 { diff --git a/state/runtime/precompiled/validator_set_precompile_test.go b/state/runtime/precompiled/validator_set_precompile_test.go index 32c81be933..fc0062dd49 100644 --- a/state/runtime/precompiled/validator_set_precompile_test.go +++ b/state/runtime/precompiled/validator_set_precompile_test.go @@ -15,7 +15,7 @@ import ( ) func Test_ValidatorSetPrecompile_gas(t *testing.T) { - assert.Equal(t, uint64(240000), (&validatorSetPrecompile{}).gas(nil, nil)) + assert.Equal(t, uint64(240000), (&validatorSetPrecompile{}).gas(nil, types.Address{}, nil)) } func Test_ValidatorSetPrecompile_run_BackendNotSet(t *testing.T) { @@ -101,6 +101,8 @@ func Test_ValidatorSetPrecompile_run_HasQuorum(t *testing.T) { backendMock := &validatorSetBackendMock{} backendMock.On("GetValidatorsForBlock", uint64(host.context.Number)).Return(accounts, error(nil)) + backendMock.On("GetMaxValidatorSetSize").Return(uint64(100), error(nil)).Twice() + backendMock.On("GetMaxValidatorSetSize").Return(uint64(0), error(nil)).Once() p := &validatorSetPrecompile{ backend: backendMock, @@ -127,16 +129,16 @@ func Test_abiDecodeAddresses_Error(t *testing.T) { dummy4[31] = 32 dummy4[63] = 10 - _, err := abiDecodeAddresses(dummy1[:]) + _, err := abiDecodeAddresses(dummy1[:], 0) require.ErrorIs(t, err, runtime.ErrInvalidInputData) - _, err = abiDecodeAddresses(dummy2[:]) + _, err = abiDecodeAddresses(dummy2[:], 0) require.ErrorIs(t, err, runtime.ErrInvalidInputData) - _, err = abiDecodeAddresses(dummy3[:]) + _, err = abiDecodeAddresses(dummy3[:], 0) require.ErrorIs(t, err, runtime.ErrInvalidInputData) - _, err = abiDecodeAddresses(dummy4[:]) + _, err = abiDecodeAddresses(dummy4[:], 0) require.ErrorIs(t, err, runtime.ErrInvalidInputData) } @@ -150,6 +152,12 @@ func (m *validatorSetBackendMock) GetValidatorsForBlock(blockNumber uint64) (val return call.Get(0).(validator.AccountSet), call.Error(1) } +func (m *validatorSetBackendMock) GetMaxValidatorSetSize() (uint64, error) { + call := m.Called() + + return call.Get(0).(uint64), call.Error(1) +} + func getDummyAccountSet() validator.AccountSet { v, _ := new(big.Int).SetString("1000000000000000000000", 10)