Files
evm/precompile/contracts/rewardmanager/contract_test.go
2026-01-08 20:39:54 -08:00

493 lines
15 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package rewardmanager
import (
"testing"
"github.com/luxfi/geth/common"
ethtypes "github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/core/vm"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"github.com/luxfi/constants"
"github.com/luxfi/evm/commontype"
"github.com/luxfi/evm/core/state"
"github.com/luxfi/evm/precompile/allowlist/allowlisttest"
"github.com/luxfi/evm/precompile/precompileconfig"
"github.com/luxfi/evm/precompile/precompiletest"
"github.com/luxfi/evm/precompile/testutils"
)
var (
rewardAddress = common.HexToAddress("0x0123")
tests = map[string]precompiletest.PrecompileTest{
"set allow fee recipients from no role fails": {
Caller: allowlisttest.TestNoRoleAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackAllowFeeRecipients()
require.NoError(t, err)
return input
},
SuppliedGas: AllowFeeRecipientsGasCost,
ReadOnly: false,
ExpectedErr: ErrCannotAllowFeeRecipients.Error(),
},
"set reward address from no role fails": {
Caller: allowlisttest.TestNoRoleAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackSetRewardAddress(rewardAddress)
require.NoError(t, err)
return input
},
SuppliedGas: SetRewardAddressGasCost,
ReadOnly: false,
ExpectedErr: ErrCannotSetRewardAddress.Error(),
},
"disable rewards from no role fails": {
Caller: allowlisttest.TestNoRoleAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackDisableRewards()
require.NoError(t, err)
return input
},
SuppliedGas: DisableRewardsGasCost,
ReadOnly: false,
ExpectedErr: ErrCannotDisableRewards.Error(),
},
"set allow fee recipients from enabled succeeds": {
Caller: allowlisttest.TestEnabledAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackAllowFeeRecipients()
require.NoError(t, err)
return input
},
SuppliedGas: AllowFeeRecipientsGasCost + FeeRecipientsAllowedEventGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, state *state.StateDB) {
_, isFeeRecipients := GetStoredRewardAddress(state)
require.True(t, isFeeRecipients)
logs := state.Logs()
assertFeeRecipientsAllowed(t, logs, allowlisttest.TestEnabledAddr)
},
},
"set fee recipients should not emit events pre-Durango": {
Caller: allowlisttest.TestEnabledAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackAllowFeeRecipients()
require.NoError(t, err)
return input
},
ChainConfigFn: func(ctrl *gomock.Controller) precompileconfig.ChainConfig {
mockChainConfig := precompileconfig.NewMockChainConfig(ctrl)
mockChainConfig.EXPECT().GetFeeConfig().AnyTimes().Return(commontype.ValidTestFeeConfig)
mockChainConfig.EXPECT().AllowedFeeRecipients().AnyTimes().Return(false)
mockChainConfig.EXPECT().IsDurango(gomock.Any()).AnyTimes().Return(false)
return mockChainConfig
},
SuppliedGas: AllowFeeRecipientsGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, stateDB *state.StateDB) {
// Check no logs are stored in state
logs := stateDB.Logs()
require.Empty(t, logs)
},
},
"set reward address from enabled succeeds": {
Caller: allowlisttest.TestEnabledAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackSetRewardAddress(rewardAddress)
require.NoError(t, err)
return input
},
SuppliedGas: SetRewardAddressGasCost + RewardAddressChangedEventGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, state *state.StateDB) {
address, isFeeRecipients := GetStoredRewardAddress(state)
require.Equal(t, rewardAddress, address)
require.False(t, isFeeRecipients)
logs := state.Logs()
assertRewardAddressChanged(t, logs, allowlisttest.TestEnabledAddr, common.Address{}, rewardAddress)
},
},
"set allow fee recipients from manager succeeds": {
Caller: allowlisttest.TestManagerAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackAllowFeeRecipients()
require.NoError(t, err)
return input
},
SuppliedGas: AllowFeeRecipientsGasCost + FeeRecipientsAllowedEventGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, state *state.StateDB) {
_, isFeeRecipients := GetStoredRewardAddress(state)
require.True(t, isFeeRecipients)
logs := state.Logs()
assertFeeRecipientsAllowed(t, logs, allowlisttest.TestManagerAddr)
},
},
"set reward address from manager succeeds": {
Caller: allowlisttest.TestManagerAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackSetRewardAddress(rewardAddress)
require.NoError(t, err)
return input
},
SuppliedGas: SetRewardAddressGasCost + RewardAddressChangedEventGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, state *state.StateDB) {
address, isFeeRecipients := GetStoredRewardAddress(state)
require.Equal(t, rewardAddress, address)
require.False(t, isFeeRecipients)
logs := state.Logs()
assertRewardAddressChanged(t, logs, allowlisttest.TestManagerAddr, common.Address{}, rewardAddress)
},
},
"change reward address should not emit events pre-Durango": {
Caller: allowlisttest.TestManagerAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackSetRewardAddress(rewardAddress)
require.NoError(t, err)
return input
},
ChainConfigFn: func(ctrl *gomock.Controller) precompileconfig.ChainConfig {
mockChainConfig := precompileconfig.NewMockChainConfig(ctrl)
mockChainConfig.EXPECT().GetFeeConfig().AnyTimes().Return(commontype.ValidTestFeeConfig)
mockChainConfig.EXPECT().AllowedFeeRecipients().AnyTimes().Return(false)
mockChainConfig.EXPECT().IsDurango(gomock.Any()).AnyTimes().Return(false)
return mockChainConfig
},
SuppliedGas: SetRewardAddressGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, stateDB *state.StateDB) {
// Check no logs are stored in state
logs := stateDB.Logs()
require.Empty(t, logs)
},
},
"disable rewards from manager succeeds": {
Caller: allowlisttest.TestManagerAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackDisableRewards()
require.NoError(t, err)
return input
},
SuppliedGas: DisableRewardsGasCost + RewardsDisabledEventGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, state *state.StateDB) {
address, isFeeRecipients := GetStoredRewardAddress(state)
require.False(t, isFeeRecipients)
require.Equal(t, constants.BlackholeAddr, address)
logs := state.Logs()
assertRewardsDisabled(t, logs, allowlisttest.TestManagerAddr)
},
},
"disable rewards from enabled succeeds": {
Caller: allowlisttest.TestEnabledAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackDisableRewards()
require.NoError(t, err)
return input
},
SuppliedGas: DisableRewardsGasCost + RewardsDisabledEventGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, state *state.StateDB) {
address, isFeeRecipients := GetStoredRewardAddress(state)
require.False(t, isFeeRecipients)
require.Equal(t, constants.BlackholeAddr, address)
logs := state.Logs()
assertRewardsDisabled(t, logs, allowlisttest.TestEnabledAddr)
},
},
"disable rewards should not emit event pre-Durango": {
Caller: allowlisttest.TestManagerAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackDisableRewards()
require.NoError(t, err)
return input
},
ChainConfigFn: func(ctrl *gomock.Controller) precompileconfig.ChainConfig {
mockChainConfig := precompileconfig.NewMockChainConfig(ctrl)
mockChainConfig.EXPECT().GetFeeConfig().AnyTimes().Return(commontype.ValidTestFeeConfig)
mockChainConfig.EXPECT().AllowedFeeRecipients().AnyTimes().Return(false)
mockChainConfig.EXPECT().IsDurango(gomock.Any()).AnyTimes().Return(false)
return mockChainConfig
},
SuppliedGas: SetRewardAddressGasCost,
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, stateDB *state.StateDB) {
// Check logs are not stored in state
logs := stateDB.Logs()
require.Empty(t, logs)
},
},
"get current reward address from no role succeeds": {
Caller: allowlisttest.TestNoRoleAddr,
BeforeHook: func(t testing.TB, state *state.StateDB) {
allowlisttest.SetDefaultRoles(Module.Address)(t, state)
StoreRewardAddress(testutils.WrapStateDB(state), rewardAddress)
},
InputFn: func(t testing.TB) []byte {
input, err := PackCurrentRewardAddress()
require.NoError(t, err)
return input
},
SuppliedGas: CurrentRewardAddressGasCost,
ReadOnly: false,
ExpectedRes: func() []byte {
res, err := PackCurrentRewardAddressOutput(rewardAddress)
if err != nil {
panic(err)
}
return res
}(),
},
"get are fee recipients allowed from no role succeeds": {
Caller: allowlisttest.TestNoRoleAddr,
BeforeHook: func(t testing.TB, state *state.StateDB) {
allowlisttest.SetDefaultRoles(Module.Address)(t, state)
EnableAllowFeeRecipients(testutils.WrapStateDB(state))
},
InputFn: func(t testing.TB) []byte {
input, err := PackAreFeeRecipientsAllowed()
require.NoError(t, err)
return input
},
SuppliedGas: AreFeeRecipientsAllowedGasCost,
ReadOnly: false,
ExpectedRes: func() []byte {
res, err := PackAreFeeRecipientsAllowedOutput(true)
if err != nil {
panic(err)
}
return res
}(),
},
"get initial config with address": {
Caller: allowlisttest.TestNoRoleAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackCurrentRewardAddress()
require.NoError(t, err)
return input
},
SuppliedGas: CurrentRewardAddressGasCost,
Config: &Config{
InitialRewardConfig: &InitialRewardConfig{
RewardAddress: rewardAddress,
},
},
ReadOnly: false,
ExpectedRes: func() []byte {
res, err := PackCurrentRewardAddressOutput(rewardAddress)
if err != nil {
panic(err)
}
return res
}(),
},
"get initial config with allow fee recipients enabled": {
Caller: allowlisttest.TestNoRoleAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackAreFeeRecipientsAllowed()
require.NoError(t, err)
return input
},
SuppliedGas: AreFeeRecipientsAllowedGasCost,
Config: &Config{
InitialRewardConfig: &InitialRewardConfig{
AllowFeeRecipients: true,
},
},
ReadOnly: false,
ExpectedRes: func() []byte {
res, err := PackAreFeeRecipientsAllowedOutput(true)
if err != nil {
panic(err)
}
return res
}(),
},
"readOnly allow fee recipients with allowed role fails": {
Caller: allowlisttest.TestEnabledAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackAllowFeeRecipients()
require.NoError(t, err)
return input
},
SuppliedGas: AllowFeeRecipientsGasCost,
ReadOnly: true,
ExpectedErr: vm.ErrWriteProtection.Error(),
},
"readOnly set reward address with allowed role fails": {
Caller: allowlisttest.TestEnabledAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackSetRewardAddress(rewardAddress)
require.NoError(t, err)
return input
},
SuppliedGas: SetRewardAddressGasCost,
ReadOnly: true,
ExpectedErr: vm.ErrWriteProtection.Error(),
},
"insufficient gas set reward address from allowed role": {
Caller: allowlisttest.TestEnabledAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackSetRewardAddress(rewardAddress)
require.NoError(t, err)
return input
},
SuppliedGas: SetRewardAddressGasCost + RewardAddressChangedEventGasCost - 1,
ReadOnly: false,
ExpectedErr: vm.ErrOutOfGas.Error(),
},
"insufficient gas allow fee recipients from allowed role": {
Caller: allowlisttest.TestEnabledAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackAllowFeeRecipients()
require.NoError(t, err)
return input
},
SuppliedGas: AllowFeeRecipientsGasCost + FeeRecipientsAllowedEventGasCost - 1,
ReadOnly: false,
ExpectedErr: vm.ErrOutOfGas.Error(),
},
"insufficient gas read current reward address from allowed role": {
Caller: allowlisttest.TestEnabledAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackCurrentRewardAddress()
require.NoError(t, err)
return input
},
SuppliedGas: CurrentRewardAddressGasCost - 1,
ReadOnly: false,
ExpectedErr: vm.ErrOutOfGas.Error(),
},
"insufficient gas are fee recipients allowed from allowed role": {
Caller: allowlisttest.TestEnabledAddr,
BeforeHook: allowlisttest.SetDefaultRoles(Module.Address),
InputFn: func(t testing.TB) []byte {
input, err := PackAreFeeRecipientsAllowed()
require.NoError(t, err)
return input
},
SuppliedGas: AreFeeRecipientsAllowedGasCost - 1,
ReadOnly: false,
ExpectedErr: vm.ErrOutOfGas.Error(),
},
}
)
func TestRewardManagerRun(t *testing.T) {
allowlisttest.RunPrecompileWithAllowListTests(t, Module, tests)
}
func assertRewardAddressChanged(
t testing.TB,
logs []*ethtypes.Log,
caller,
oldAddress,
newAddress common.Address) {
require.Len(t, logs, 1)
log := logs[0]
require.Equal(
t,
[]common.Hash{
RewardManagerABI.Events["RewardAddressChanged"].ID,
common.BytesToHash(caller[:]),
common.BytesToHash(oldAddress[:]),
common.BytesToHash(newAddress[:]),
},
log.Topics,
)
require.Empty(t, log.Data)
}
func assertRewardsDisabled(
t testing.TB,
logs []*ethtypes.Log,
caller common.Address) {
require.Len(t, logs, 1)
log := logs[0]
require.Equal(
t,
[]common.Hash{
RewardManagerABI.Events["RewardsDisabled"].ID,
common.BytesToHash(caller[:]),
},
log.Topics,
)
require.Empty(t, log.Data)
}
func assertFeeRecipientsAllowed(
t testing.TB,
logs []*ethtypes.Log,
caller common.Address) {
require.Len(t, logs, 1)
log := logs[0]
require.Equal(
t,
[]common.Hash{
RewardManagerABI.Events["FeeRecipientsAllowed"].ID,
common.BytesToHash(caller[:]),
},
log.Topics,
)
require.Empty(t, log.Data)
}