Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Evm 785: enable and disable access lists in the runtime #1839

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions chain/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Params struct {
BlockGasTarget uint64 `json:"blockGasTarget"`

// Access control configuration
AccessListsOwner *types.Address `json:"accessListsOwner,omitempty"`
ContractDeployerAllowList *AddressListConfig `json:"contractDeployerAllowList,omitempty"`
ContractDeployerBlockList *AddressListConfig `json:"contractDeployerBlockList,omitempty"`
TransactionsAllowList *AddressListConfig `json:"transactionsAllowList,omitempty"`
Expand Down
7 changes: 7 additions & 0 deletions command/genesis/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,13 @@ func setFlags(cmd *cobra.Command) {
[]string{},
"list of addresses to enable by default in the bridge block list",
)

cmd.Flags().StringVar(
&params.accessListsOwner,
accessListsOwnerFlag,
"",
"owner for all allow and block lists",
)
}
}

Expand Down
1 change: 1 addition & 0 deletions command/genesis/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ type genesisParams struct {
bridgeAllowListEnabled []string
bridgeBlockListAdmin []string
bridgeBlockListEnabled []string
accessListsOwner string

nativeTokenConfigRaw string
nativeTokenConfig *polybft.TokenConfig
Expand Down
8 changes: 7 additions & 1 deletion command/genesis/polybft_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const (
defaultEpochReward = 1
defaultBlockTimeDrift = uint64(10)

accessListsOwnerFlag = "access-lists-owner" // #nosec G101
contractDeployerAllowListAdminFlag = "contract-deployer-allow-list-admin"
contractDeployerAllowListEnabledFlag = "contract-deployer-allow-list-enabled"
contractDeployerBlockListAdminFlag = "contract-deployer-block-list-admin"
Expand Down Expand Up @@ -296,6 +297,11 @@ func (p *genesisParams) generatePolyBftChainConfig(o command.OutputFormatter) er
}
}

if p.accessListsOwner != "" {
value := types.StringToAddress(p.accessListsOwner)
chainConfig.Params.AccessListsOwner = &value
}

if p.isBurnContractEnabled() {
// only populate base fee and base fee multiplier values if burn contract(s)
// is provided
Expand Down Expand Up @@ -386,7 +392,7 @@ func (p *genesisParams) deployContracts(
})
}

if len(params.bridgeAllowListAdmin) != 0 || len(params.bridgeBlockListAdmin) != 0 {
if len(p.bridgeAllowListAdmin) != 0 || len(p.bridgeBlockListAdmin) != 0 || p.accessListsOwner != "" {
// rootchain originated tokens predicates (with access lists)
genesisContracts = append(genesisContracts,
&contractInfo{
Expand Down
16 changes: 10 additions & 6 deletions consensus/polybft/polybft.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,20 @@ func GenesisPostHookFactory(config *chain.Chain, engineName string) func(txn *st
bridgeBlockListAdmin = config.Params.BridgeBlockList.AdminAddresses[0]
}

hasOwner := config.Params.AccessListsOwner != nil
useBridgeAllowList := bridgeAllowListAdmin != types.ZeroAddress
useBridgeBlockList := bridgeBlockListAdmin != types.ZeroAddress

// initialize Predicate SCs
if bridgeAllowListAdmin != types.ZeroAddress || bridgeBlockListAdmin != types.ZeroAddress {
if hasOwner || useBridgeAllowList || useBridgeBlockList {
// The owner of the contract will be the allow list admin or the block list admin, if any of them is set.
owner := contracts.SystemCaller
useBridgeAllowList := bridgeAllowListAdmin != types.ZeroAddress
useBridgeBlockList := bridgeBlockListAdmin != types.ZeroAddress
var owner types.Address

if bridgeAllowListAdmin != types.ZeroAddress {
if hasOwner {
owner = *config.Params.AccessListsOwner
} else if useBridgeAllowList {
owner = bridgeAllowListAdmin
} else if bridgeBlockListAdmin != types.ZeroAddress {
} else {
owner = bridgeBlockListAdmin
}

Expand Down
151 changes: 142 additions & 9 deletions e2e-polybft/e2e/acls_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package e2e

import (
"encoding/hex"
"fmt"
"math/big"
"testing"

"github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi"
"github.com/0xPolygon/polygon-edge/contracts"
"github.com/0xPolygon/polygon-edge/e2e-polybft/framework"
"github.com/0xPolygon/polygon-edge/helper/hex"
"github.com/0xPolygon/polygon-edge/state/runtime/addresslist"
"github.com/0xPolygon/polygon-edge/types"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -53,9 +54,7 @@ func TestE2E_AllowList_ContractDeployment(t *testing.T) {

cluster.WaitForReady(t)

// bytecode for an empty smart contract
bytecode, err := hex.DecodeString("608060405234801561001057600080fd5b506103db806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80635e9f13f81461003b578063d78bca6914610059575b600080fd5b610043610089565b6040516100509190610217565b60405180910390f35b610073600480360381019061006e9190610263565b6100a1565b60405161008091906102a9565b60405180910390f35b73020000000000000000000000000000000000000081565b600080600073020000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16846040516024016100e29190610217565b6040516020818303038152906040527fd78bca69000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161016c9190610335565b6000604051808303816000865af19150503d80600081146101a9576040519150601f19603f3d011682016040523d82523d6000602084013e6101ae565b606091505b50915091506000818060200190518101906101c99190610378565b9050809350505050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610201826101d6565b9050919050565b610211816101f6565b82525050565b600060208201905061022c6000830184610208565b92915050565b600080fd5b610240816101f6565b811461024b57600080fd5b50565b60008135905061025d81610237565b92915050565b60006020828403121561027957610278610232565b5b60006102878482850161024e565b91505092915050565b6000819050919050565b6102a381610290565b82525050565b60006020820190506102be600083018461029a565b92915050565b600081519050919050565b600081905092915050565b60005b838110156102f85780820151818401526020810190506102dd565b60008484015250505050565b600061030f826102c4565b61031981856102cf565b93506103298185602086016102da565b80840191505092915050565b60006103418284610304565b915081905092915050565b61035581610290565b811461036057600080fd5b50565b6000815190506103728161034c565b92915050565b60006020828403121561038e5761038d610232565b5b600061039c84828501610363565b9150509291505056fea264697066735822122035f391b5c3dbf9a5e31072167a4ada71e9ba762650849f6320bc6150fa45aa9564736f6c63430008110033")
require.NoError(t, err)
bytecode := getDummySmartContract(t) // this test requires custom proxy smart contract

{
// Step 0. Check the role of both accounts
Expand Down Expand Up @@ -152,9 +151,7 @@ func TestE2E_BlockList_ContractDeployment(t *testing.T) {

cluster.WaitForReady(t)

// bytecode for an empty smart contract
bytecode, err := hex.DecodeString("608060405234801561001057600080fd5b506103db806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80635e9f13f81461003b578063d78bca6914610059575b600080fd5b610043610089565b6040516100509190610217565b60405180910390f35b610073600480360381019061006e9190610263565b6100a1565b60405161008091906102a9565b60405180910390f35b73020000000000000000000000000000000000000081565b600080600073020000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16846040516024016100e29190610217565b6040516020818303038152906040527fd78bca69000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161016c9190610335565b6000604051808303816000865af19150503d80600081146101a9576040519150601f19603f3d011682016040523d82523d6000602084013e6101ae565b606091505b50915091506000818060200190518101906101c99190610378565b9050809350505050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610201826101d6565b9050919050565b610211816101f6565b82525050565b600060208201905061022c6000830184610208565b92915050565b600080fd5b610240816101f6565b811461024b57600080fd5b50565b60008135905061025d81610237565b92915050565b60006020828403121561027957610278610232565b5b60006102878482850161024e565b91505092915050565b6000819050919050565b6102a381610290565b82525050565b60006020820190506102be600083018461029a565b92915050565b600081519050919050565b600081905092915050565b60005b838110156102f85780820151818401526020810190506102dd565b60008484015250505050565b600061030f826102c4565b61031981856102cf565b93506103298185602086016102da565b80840191505092915050565b60006103418284610304565b915081905092915050565b61035581610290565b811461036057600080fd5b50565b6000815190506103728161034c565b92915050565b60006020828403121561038e5761038d610232565b5b600061039c84828501610363565b9150509291505056fea264697066735822122035f391b5c3dbf9a5e31072167a4ada71e9ba762650849f6320bc6150fa45aa9564736f6c63430008110033")
require.NoError(t, err)
bytecode := contractsapi.TestSimple.Bytecode

{
// Step 0. Check the role of accounts
Expand Down Expand Up @@ -236,8 +233,7 @@ func TestE2E_AllowList_Transactions(t *testing.T) {

cluster.WaitForReady(t)

// bytecode for an empty smart contract
bytecode, _ := hex.DecodeString("608060405234801561001057600080fd5b506103db806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80635e9f13f81461003b578063d78bca6914610059575b600080fd5b610043610089565b6040516100509190610217565b60405180910390f35b610073600480360381019061006e9190610263565b6100a1565b60405161008091906102a9565b60405180910390f35b73020000000000000000000000000000000000000081565b600080600073020000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16846040516024016100e29190610217565b6040516020818303038152906040527fd78bca69000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161016c9190610335565b6000604051808303816000865af19150503d80600081146101a9576040519150601f19603f3d011682016040523d82523d6000602084013e6101ae565b606091505b50915091506000818060200190518101906101c99190610378565b9050809350505050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610201826101d6565b9050919050565b610211816101f6565b82525050565b600060208201905061022c6000830184610208565b92915050565b600080fd5b610240816101f6565b811461024b57600080fd5b50565b60008135905061025d81610237565b92915050565b60006020828403121561027957610278610232565b5b60006102878482850161024e565b91505092915050565b6000819050919050565b6102a381610290565b82525050565b60006020820190506102be600083018461029a565b92915050565b600081519050919050565b600081905092915050565b60005b838110156102f85780820151818401526020810190506102dd565b60008484015250505050565b600061030f826102c4565b61031981856102cf565b93506103298185602086016102da565b80840191505092915050565b60006103418284610304565b915081905092915050565b61035581610290565b811461036057600080fd5b50565b6000815190506103728161034c565b92915050565b60006020828403121561038e5761038d610232565b5b600061039c84828501610363565b9150509291505056fea264697066735822122035f391b5c3dbf9a5e31072167a4ada71e9ba762650849f6320bc6150fa45aa9564736f6c63430008110033")
bytecode := contractsapi.TestSimple.Bytecode

{
// Step 0. Check the role of both accounts
Expand Down Expand Up @@ -413,3 +409,140 @@ func TestE2E_AddressLists_Bridge(t *testing.T) {
expectRole(t, cluster, contracts.BlockListBridgeAddr, otherAddr, addresslist.EnabledRole)
}
}

func TestE2E_AccessListsOwner(t *testing.T) {
owner, _ := wallet.GenerateKey()
admin, _ := wallet.GenerateKey()
rndUser, _ := wallet.GenerateKey()

adminAddr := types.Address(admin.Address())
rndUserAddress := types.Address(rndUser.Address())

cluster := framework.NewTestCluster(t, 5,
framework.WithNativeTokenConfig(fmt.Sprintf(nativeTokenMintableTestCfg, adminAddr)),
framework.WithPremine(adminAddr, rndUserAddress),
framework.WithAccessListsOwner(types.Address(owner.Address())),
)
defer cluster.Stop()

cluster.WaitForReady(t)

{
// Step 0. allow lists should be disabled
expectIsEnabled(t, cluster, contracts.AllowListTransactionsAddr, false)
expectIsEnabled(t, cluster, contracts.AllowListContractsAddr, false)
}

bytecode := contractsapi.TestSimple.Bytecode

{
// Step 1. anyone can send normal transaction or deploy smart contract because lists are disabled
tx := cluster.Transfer(t, admin, types.ZeroAddress, big.NewInt(1))
require.NoError(t, tx.Wait())
require.True(t, tx.Succeed())

tx = cluster.Deploy(t, rndUser, bytecode)
require.NoError(t, tx.Wait())
require.True(t, tx.Succeed())
require.True(t, cluster.ExistsCode(t, tx.Receipt().ContractAddress))
}

{
// Step 2. owner enables allow tx list and allow contracts list
input, _ := addresslist.SetListEnabledFunc.Encode([]interface{}{true})

saTxn := cluster.MethodTxn(t, owner, contracts.AllowListTransactionsAddr, input)
require.NoError(t, saTxn.Wait())
require.False(t, saTxn.Failed())

input, _ = addresslist.SetListEnabledFunc.Encode([]interface{}{true})

saTxn = cluster.MethodTxn(t, owner, contracts.AllowListContractsAddr, input)
require.NoError(t, saTxn.Wait())
require.False(t, saTxn.Failed())
}

{
// Step 3. future admin or rndUser can not send transaction or deploy smart contract because they are not in the lists yet
tx := cluster.Transfer(t, rndUser, types.ZeroAddress, big.NewInt(1))
require.NoError(t, tx.Wait())
require.False(t, tx.Succeed())

tx = cluster.Deploy(t, admin, bytecode)
require.NoError(t, tx.Wait())
require.False(t, tx.Succeed())
require.False(t, cluster.ExistsCode(t, tx.Receipt().ContractAddress))
}

{
// Step 4. add two roles by owner
input, _ := addresslist.SetAdminFunc.Encode([]interface{}{adminAddr})

saTxn := cluster.MethodTxn(t, owner, contracts.AllowListTransactionsAddr, input)
require.NoError(t, saTxn.Wait())
require.True(t, saTxn.Succeed())

input, _ = addresslist.SetEnabledFunc.Encode([]interface{}{rndUserAddress})

saTxn = cluster.MethodTxn(t, owner, contracts.AllowListContractsAddr, input)
require.NoError(t, saTxn.Wait())
require.True(t, saTxn.Succeed())

// add one more role by admin
input, _ = addresslist.SetEnabledFunc.Encode([]interface{}{rndUserAddress})

saTxn = cluster.MethodTxn(t, admin, contracts.AllowListTransactionsAddr, input)
require.NoError(t, saTxn.Wait())
require.True(t, saTxn.Succeed())

// adding role by normal user should fail
input, _ = addresslist.SetEnabledFunc.Encode([]interface{}{adminAddr})

saTxn = cluster.MethodTxn(t, rndUser, contracts.AllowListContractsAddr, input)
require.NoError(t, saTxn.Wait())
require.True(t, saTxn.Failed())

expectRole(t, cluster, contracts.AllowListTransactionsAddr, adminAddr, addresslist.AdminRole)
expectRole(t, cluster, contracts.AllowListContractsAddr, rndUserAddress, addresslist.EnabledRole)
expectRole(t, cluster, contracts.AllowListTransactionsAddr, rndUserAddress, addresslist.EnabledRole)
expectRole(t, cluster, contracts.AllowListContractsAddr, adminAddr, addresslist.NoRole)
}

{
// Step 4. admin address can send normal transaction, user can deploy contract...
tx := cluster.Transfer(t, admin, types.ZeroAddress, big.NewInt(1))
require.NoError(t, tx.Wait())
require.True(t, tx.Succeed())

tx = cluster.Deploy(t, rndUser, bytecode)
require.NoError(t, tx.Wait())
require.True(t, tx.Succeed())
require.True(t, cluster.ExistsCode(t, tx.Receipt().ContractAddress))

// ... but admin address is not in deployment allow list
tx = cluster.Deploy(t, admin, bytecode)
require.NoError(t, tx.Wait())
require.True(t, tx.Reverted())
require.False(t, cluster.ExistsCode(t, tx.Receipt().ContractAddress))

// remove rndUser address from tx allow list so that address can not send transactions anymore
input, _ := addresslist.SetNoneFunc.Encode([]interface{}{rndUserAddress})

saTxn := cluster.MethodTxn(t, admin, contracts.AllowListTransactionsAddr, input)
require.NoError(t, saTxn.Wait())
require.True(t, saTxn.Succeed())

tx = cluster.Transfer(t, rndUser, types.ZeroAddress, big.NewInt(1))
require.NoError(t, tx.Wait())
require.True(t, tx.Reverted())
}
}

func getDummySmartContract(t *testing.T) []byte {
t.Helper()

bytecode, err := hex.DecodeString("608060405234801561001057600080fd5b506103db806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80635e9f13f81461003b578063d78bca6914610059575b600080fd5b610043610089565b6040516100509190610217565b60405180910390f35b610073600480360381019061006e9190610263565b6100a1565b60405161008091906102a9565b60405180910390f35b73020000000000000000000000000000000000000081565b600080600073020000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16846040516024016100e29190610217565b6040516020818303038152906040527fd78bca69000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161016c9190610335565b6000604051808303816000865af19150503d80600081146101a9576040519150601f19603f3d011682016040523d82523d6000602084013e6101ae565b606091505b50915091506000818060200190518101906101c99190610378565b9050809350505050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610201826101d6565b9050919050565b610211816101f6565b82525050565b600060208201905061022c6000830184610208565b92915050565b600080fd5b610240816101f6565b811461024b57600080fd5b50565b60008135905061025d81610237565b92915050565b60006020828403121561027957610278610232565b5b60006102878482850161024e565b91505092915050565b6000819050919050565b6102a381610290565b82525050565b60006020820190506102be600083018461029a565b92915050565b600081519050919050565b600081905092915050565b60005b838110156102f85780820151818401526020810190506102dd565b60008484015250505050565b600061030f826102c4565b61031981856102cf565b93506103298185602086016102da565b80840191505092915050565b60006103418284610304565b915081905092915050565b61035581610290565b811461036057600080fd5b50565b6000815190506103728161034c565b92915050565b60006020828403121561038e5761038d610232565b5b600061039c84828501610363565b9150509291505056fea264697066735822122035f391b5c3dbf9a5e31072167a4ada71e9ba762650849f6320bc6150fa45aa9564736f6c63430008110033")
require.NoError(t, err)

return bytecode
}
14 changes: 11 additions & 3 deletions e2e-polybft/e2e/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,21 @@ func expectRole(t *testing.T, cluster *framework.TestCluster, contract types.Add
out := cluster.Call(t, contract, addresslist.ReadAddressListFunc, addr)

num, ok := out["0"].(*big.Int)
if !ok {
t.Fatal("unexpected")
}

require.True(t, ok)
require.Equal(t, role.Uint64(), num.Uint64())
}

func expectIsEnabled(t *testing.T, cluster *framework.TestCluster, contract types.Address, value bool) {
t.Helper()
out := cluster.Call(t, contract, addresslist.GetListEnabledFunc)

num, ok := out["0"].(bool)

require.True(t, ok)
require.Equal(t, value, num)
}

// getFilteredLogs retrieves Ethereum logs, described by event signature within the block range
func getFilteredLogs(eventSig ethgo.Hash, startBlock, endBlock uint64,
ethEndpoint *jsonrpc.Eth) ([]*ethgo.Log, error) {
Expand Down
11 changes: 11 additions & 0 deletions e2e-polybft/framework/test-cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ type TestClusterConfig struct {
NativeTokenConfigRaw string
SecretsCallback func([]types.Address, *TestClusterConfig)

AccessListsOwner *types.Address
ContractDeployerAllowListAdmin []types.Address
ContractDeployerAllowListEnabled []types.Address
ContractDeployerBlockListAdmin []types.Address
Expand Down Expand Up @@ -266,6 +267,12 @@ func WithNumBlockConfirmations(numBlockConfirmations uint64) ClusterOption {
}
}

func WithAccessListsOwner(addr types.Address) ClusterOption {
return func(h *TestClusterConfig) {
h.AccessListsOwner = &addr
}
}

func WithContractDeployerAllowListAdmin(addr types.Address) ClusterOption {
return func(h *TestClusterConfig) {
h.ContractDeployerAllowListAdmin = append(h.ContractDeployerAllowListAdmin, addr)
Expand Down Expand Up @@ -489,6 +496,10 @@ func NewTestCluster(t *testing.T, validatorsCount int, opts ...ClusterOption) *T
}
}

if cluster.Config.AccessListsOwner != nil {
args = append(args, "--access-lists-owner", cluster.Config.AccessListsOwner.String())
}

if len(cluster.Config.ContractDeployerAllowListAdmin) != 0 {
args = append(args, "--contract-deployer-allow-list-admin",
strings.Join(sliceAddressToSliceString(cluster.Config.ContractDeployerAllowListAdmin), ","))
Expand Down
Loading
Loading