Skip to content

Commit

Permalink
Reward Manager precompile (ava-labs#260)
Browse files Browse the repository at this point in the history
* add initial precompile configs and handle them

* add verify to precompile configs

* add unit tests for initial configs and verify

* add a nil check for initial fee config verification

* conditionally verify allowlist

* Update precompile/stateful_precompile_config.go

Co-authored-by: aaronbuchwald <[email protected]>

* add config test to precompile package

* add 0 test case for gas limit

* add comments for allowlist storage keys

* move allow list role to different file

* Update precompile/allow_list.go

Co-authored-by: aaronbuchwald <[email protected]>

* rename test

* Update precompile/allow_list.go

Co-authored-by: Darioush Jalali <[email protected]>

* rename test & use func to create config

* Remove unnecessary role checks

* use config constructor for precompile tests

* Update accounts/abi/bind/precompile_template.go

Co-authored-by: Darioush Jalali <[email protected]>

* Update precompile/contract_native_minter.go

Co-authored-by: Darioush Jalali <[email protected]>

* Update precompile/contract_deployer_allow_list.go

Co-authored-by: Darioush Jalali <[email protected]>

* Update precompile/allow_list.go

Co-authored-by: Darioush Jalali <[email protected]>

* add equals methods

* initialize reward manager precompile by precompilegen

* add reward upgrade to block verification

* handle empty case in configure

* use single field to decide allow fee recipieints

* add coinbase verification

* configure miner with coinbase in state accordingly

* Remove abis

* use header coinbase for verification

* remove hashToBool util methods

* use require and remove unneccessary address field from test

* add unit tests

* cleanups

* Update consensus/dummy/consensus.go

Co-authored-by: Darioush Jalali <[email protected]>

* Update contract-examples/contracts/IAllowList.sol

Co-authored-by: Darioush Jalali <[email protected]>

* Update core/stateful_precompile_test.go

Co-authored-by: Darioush Jalali <[email protected]>

* Update core/stateful_precompile_test.go

Co-authored-by: Darioush Jalali <[email protected]>

* Update core/stateful_precompile_test.go

Co-authored-by: Darioush Jalali <[email protected]>

* Update precompile/reward_manager.go

Co-authored-by: Darioush Jalali <[email protected]>

* Update precompile/reward_manager.go

Co-authored-by: Darioush Jalali <[email protected]>

* Update precompile/reward_manager.go

Co-authored-by: Darioush Jalali <[email protected]>

* review fixes

* add e2e tests and example contract

* add initial config to test

* Update contract-examples/tasks.ts

Co-authored-by: Darioush Jalali <[email protected]>

* Update contract-examples/test/ExampleRewardDistributor.ts

Co-authored-by: Darioush Jalali <[email protected]>

* Update contract-examples/test/ExampleRewardDistributor.ts

Co-authored-by: Darioush Jalali <[email protected]>

* review fixes

* Update consensus/dummy/consensus.go

Co-authored-by: aaronbuchwald <[email protected]>

* Update params/config.go

Co-authored-by: aaronbuchwald <[email protected]>

* Update params/config.go

Co-authored-by: aaronbuchwald <[email protected]>

* fixs after review

* add string method to reward manager

* revert coinbase syntactic verification

* review fixes

* review fixes

* add disabled precompile test cases

* use hash value instead of address

* config cleanup

* Update contract-examples/tasks.ts

Co-authored-by: Darioush Jalali <[email protected]>

* Update contract-examples/tasks.ts

Co-authored-by: Darioush Jalali <[email protected]>

* more nit fixes

* fix conflict

* add disabled test case
  • Loading branch information
ceyonur authored Nov 21, 2022
1 parent e59b1be commit 928526a
Show file tree
Hide file tree
Showing 28 changed files with 1,838 additions and 412 deletions.
4 changes: 2 additions & 2 deletions accounts/abi/bind/precompile_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ var (
{{- if not .Original.IsConstant | and $contract.AllowList}}
ErrCannot{{.Normalized.Name}} = errors.New("non-enabled cannot {{.Original.Name}}")
ErrCannot{{.Normalized.Name}} = errors.New("non-enabled cannot call {{.Original.Name}}")
{{- end}}
{{- end}}
{{- if .Contract.Fallback | and $contract.AllowList}}
Err{{.Contract.Type}}CannotFallback = errors.New("non-enabled cannot use fallback function")
Err{{.Contract.Type}}CannotFallback = errors.New("non-enabled cannot call fallback function")
{{- end}}
{{.Contract.Type}}ABI abi.ABI // will be initialized by init function
Expand Down
4 changes: 4 additions & 0 deletions consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ type ChainHeaderReader interface {

// GetFeeConfigAt retrieves the fee config and last changed block number at block header.
GetFeeConfigAt(parent *types.Header) (commontype.FeeConfig, *big.Int, error)

// GetCoinbaseAt retrieves the configured coinbase address at [parent].
// If fee recipients are allowed, returns true in the second return value and a predefined address in the first value.
GetCoinbaseAt(parent *types.Header) (common.Address, bool, error)
}

// ChainReader defines a small collection of methods needed to access the local
Expand Down
29 changes: 29 additions & 0 deletions consensus/dummy/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/params"
"github.com/ava-labs/subnet-evm/trie"
"github.com/ava-labs/subnet-evm/vmerrs"
"github.com/ethereum/go-ethereum/common"
)

Expand Down Expand Up @@ -69,6 +70,26 @@ func NewFullFaker() *DummyEngine {
}
}

// verifyCoinbase checks that the coinbase is valid for the given [header] and [parent].
func (self *DummyEngine) verifyCoinbase(config *params.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error {
// get the coinbase configured at parent
configuredAddressAtParent, isAllowFeeRecipients, err := chain.GetCoinbaseAt(parent)
if err != nil {
return fmt.Errorf("failed to get coinbase at %v: %w", header.Hash(), err)
}

if isAllowFeeRecipients {
// if fee recipients are allowed we don't need to check the coinbase
return nil
}
// we fetch the configured coinbase at the parent's state
// to check against the coinbase in [header].
if configuredAddressAtParent != header.Coinbase {
return fmt.Errorf("%w: %v does not match required coinbase address %v", vmerrs.ErrInvalidCoinbase, header.Coinbase, configuredAddressAtParent)
}
return nil
}

func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error {
timestamp := new(big.Int).SetUint64(header.Time)

Expand Down Expand Up @@ -175,6 +196,14 @@ func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header
if err := self.verifyHeaderGasFields(config, header, parent, chain); err != nil {
return err
}
// Ensure that coinbase is valid if reward manager is enabled
// If reward manager is disabled, this will be handled in syntactic verification
if config.IsRewardManager(timestamp) {
if err := self.verifyCoinbase(config, header, parent, chain); err != nil {
return err
}
}

// Verify the header's timestamp
if header.Time > uint64(self.clock.Time().Add(allowedFutureBlockTime).Unix()) {
return consensus.ErrFutureBlock
Expand Down
2 changes: 1 addition & 1 deletion contract-examples/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Subnet EVM Contracts

CONTRACTS HERE ARE [ALPHA SOFTWARE](https://en.wikipedia.org/wiki/Software_release_life_cycle#Alpha) AND ARE NOT YET AUDITED. USE AT YOUR OWN RISK!
CONTRACTS HERE ARE [ALPHA SOFTWARE](https://en.wikipedia.org/wiki/Software_release_life_cycle#Alpha) AND ARE NOT AUDITED. USE AT YOUR OWN RISK!

## Introduction

Expand Down
2 changes: 0 additions & 2 deletions contract-examples/contracts/ExampleFeeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ contract ExampleFeeManager is AllowList {
address constant FEE_MANAGER_ADDRESS = 0x0200000000000000000000000000000000000003;
IFeeManager feeManager = IFeeManager(FEE_MANAGER_ADDRESS);

bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");

struct FeeConfig {
uint256 gasLimit;
uint256 targetBlockRate;
Expand Down
33 changes: 33 additions & 0 deletions contract-examples/contracts/ExampleRewardManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IRewardManager.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

// ExampleRewardManager is a sample wrapper contract for RewardManager precompile.
contract ExampleRewardManager is Ownable {
address constant REWARD_MANAGER_ADDRESS = 0x0200000000000000000000000000000000000004;
IRewardManager rewardManager = IRewardManager(REWARD_MANAGER_ADDRESS);

constructor() Ownable() {}

function currentRewardAddress() public view returns (address) {
return rewardManager.currentRewardAddress();
}

function setRewardAddress(address addr) public onlyOwner {
rewardManager.setRewardAddress(addr);
}

function allowFeeRecipients() public onlyOwner {
rewardManager.allowFeeRecipients();
}

function disableRewards() public onlyOwner {
rewardManager.disableRewards();
}

function areFeeRecipientsAllowed() public view returns (bool) {
return rewardManager.areFeeRecipientsAllowed();
}
}
10 changes: 5 additions & 5 deletions contract-examples/contracts/IAllowList.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
pragma solidity ^0.8.0;

interface IAllowList {
// Set [addr] to have the admin role over the minter list
// Set [addr] to have the admin role over the precompile contract.
function setAdmin(address addr) external;

// Set [addr] to be enabled on the minter list
// Set [addr] to be enabled on the precompile contract.
function setEnabled(address addr) external;

// Set [addr] to have no role over the minter list
// Set [addr] to have no role for the precompile contract.
function setNone(address addr) external;

// Read the status of [addr]
function readAllowList(address addr) external view returns (uint256);
// Read the status of [addr].
function readAllowList(address addr) external view returns (uint256 role);
}
20 changes: 20 additions & 0 deletions contract-examples/contracts/IRewardManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./IAllowList.sol";

interface IRewardManager is IAllowList {
// setRewardAddress sets the reward address to the given address
function setRewardAddress(address addr) external;

// allowFeeRecipients allows block builders to claim fees
function allowFeeRecipients() external;

// disableRewards disables block rewards and starts burning fees
function disableRewards() external;

// currentRewardAddress returns the current reward address
function currentRewardAddress() external view returns (address rewardAddress);

// areFeeRecipientsAllowed returns true if fee recipients are allowed
function areFeeRecipientsAllowed() external view returns (bool isAllowed);
}
21 changes: 21 additions & 0 deletions contract-examples/scripts/deployExampleRewardManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {
Contract,
ContractFactory
} from "ethers"
import { ethers } from "hardhat"


const main = async (): Promise<any> => {
const Contract: ContractFactory = await ethers.getContractFactory("ExampleRewardDistributor")
const contract: Contract = await Contract.deploy()

await contract.deployed()
console.log(`Contract deployed to: ${contract.address}`)
}

main()
.then(() => process.exit(0))
.catch(error => {
console.error(error)
process.exit(1)
})
Loading

0 comments on commit 928526a

Please sign in to comment.