Skip to content

Commit 9f2a0c9

Browse files
authored
Merge pull request #6 from bane-labs/policy-txpool
apply policy check to txpool and block verification
2 parents c896e52 + e876faf commit 9f2a0c9

File tree

6 files changed

+82
-1
lines changed

6 files changed

+82
-1
lines changed

core/error.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,11 @@ var (
110110

111111
// ErrBlobTxCreate is returned if a blob transaction has no explicit to field.
112112
ErrBlobTxCreate = errors.New("blob transaction of type create")
113+
114+
// ErrBlockedSender is returned if the transaction sender is blocked by policy.
115+
ErrBlockedSender = errors.New("blocked sender")
116+
117+
// ErrUnderpriced is returned if a transaction's gas price is below the minimum
118+
// configured for the transaction pool.
119+
ErrUnderpriced = errors.New("transaction underpriced")
113120
)

core/state_transition.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
"github.com/ethereum/go-ethereum/common"
2525
cmath "github.com/ethereum/go-ethereum/common/math"
26+
"github.com/ethereum/go-ethereum/core/systemcontracts"
2627
"github.com/ethereum/go-ethereum/core/types"
2728
"github.com/ethereum/go-ethereum/core/vm"
2829
"github.com/ethereum/go-ethereum/crypto/kzg4844"
@@ -293,6 +294,19 @@ func (st *StateTransition) preCheck() error {
293294
return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA,
294295
msg.From.Hex(), codeHash)
295296
}
297+
// Ensure the transaction is allowed by policy
298+
// Apply policy minimum gas tip cap
299+
var minGasTipCap = st.state.GetState(systemcontracts.PolicyProxyHash, systemcontracts.GetMinGasTipCapStateHash())
300+
if msg.GasTipCap.Cmp(minGasTipCap.Big()) < 0 {
301+
return fmt.Errorf("%w: address %v, gastipcap %v", ErrUnderpriced,
302+
msg.From.Hex(), msg.GasTipCap)
303+
}
304+
// Apply policy blacklist
305+
var blocked = st.state.GetState(systemcontracts.PolicyProxyHash, systemcontracts.GetBlackListStateHash(msg.From))
306+
if blocked != (common.Hash{}) {
307+
return fmt.Errorf("%w: address %v", ErrBlockedSender,
308+
msg.From.Hex())
309+
}
296310
}
297311
// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
298312
if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {

core/systemcontracts/contracts.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,30 @@ import (
66

77
"github.com/ethereum/go-ethereum/accounts/abi"
88
"github.com/ethereum/go-ethereum/common"
9+
"github.com/ethereum/go-ethereum/crypto"
910
)
1011

1112
// governanceABI is an ABI of system governing contract.
1213
const governanceABI = `[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"candidate","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CandidateWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"candidate","type":"address"}],"name":"Exit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"validators","type":"address[]"}],"name":"Persist","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"candidate","type":"address"}],"name":"Register","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Revoke","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Vote","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"VoterClaim","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"candidateBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"candidateGasPerVote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"consensusSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"currentConsensus","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentEpochStartHeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"epochStartGasPerVote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exitCandidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"exitHeightOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCandidates","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentConsensus","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"govReward","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minVoteAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"onPersist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"receivedVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shareRate","type":"uint256"}],"name":"registerCandidate","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"registerFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"revokeVote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"scaleFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"shareRateOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"candidateTo","type":"address"}],"name":"vote","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"voteHeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"votedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"votedTo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"voterGasPerVote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawRegisterFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]`
1314

15+
const policyABI = `[{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"AddBlackList","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"RemoveBlackList","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"baseFee","type":"uint256"}],"name":"SetBaseFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"gasTipCap","type":"uint256"}],"name":"SetMinGasTipCap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"bytes32","name":"methodKey","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"paramKey","type":"bytes32"}],"name":"Vote","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"methodKey","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"paramKey","type":"bytes32"}],"name":"VotePass","type":"event"},{"inputs":[],"name":"GOV_ADMIN","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SELF","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"addBlackList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"baseFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"govReward","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isBlackListed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isMiner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minGasTipCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"removeBlackList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_baseFee","type":"uint256"}],"name":"setBaseFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasTipCap","type":"uint256"}],"name":"setMinGasTipCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"}]`
16+
17+
// some storage slot indexes of policy contract.
18+
const blackListSlotIndex = 1
19+
const minGasTipCapSlotIndex = 2
20+
const baseFeeSlotIndex = 3
21+
1422
// A set of genesis contracts.
1523
var (
1624
// GovernanceProxyHash is a governing contract hash that manages validators
1725
// voting and rewards.
1826
GovernanceProxyHash = common.HexToAddress("0x1212000000000000000000000000000000000001")
1927
// GovernanceABI is a compiled ABI of Governance contract.
2028
GovernanceABI abi.ABI
29+
// PolicyProxyHash is a management contract hash for system policies.
30+
PolicyProxyHash = common.HexToAddress("0x1212000000000000000000000000000000000002")
31+
// PolicyABI is a compiled ABI of Policy contract.
32+
PolicyABI abi.ABI
2133
)
2234

2335
func init() {
@@ -26,4 +38,26 @@ func init() {
2638
if err != nil {
2739
panic(fmt.Errorf("failed to decode Governance contract ABI: %w", err))
2840
}
41+
PolicyABI, err = abi.JSON(strings.NewReader(policyABI))
42+
if err != nil {
43+
panic(fmt.Errorf("failed to decode Policy contract ABI: %w", err))
44+
}
45+
}
46+
47+
// GetMinGasTipCapStateHash computes and returns the storage key of minGasTipCap
48+
// in policy contract, for reading corresponding values from statedb.
49+
func GetMinGasTipCapStateHash() common.Hash {
50+
return common.BytesToHash([]byte{minGasTipCapSlotIndex})
51+
}
52+
53+
// GetBaseFeeStateHash computes and returns the storage key of baseFee
54+
// in policy contract, for reading corresponding values from statedb.
55+
func GetBaseFeeStateHash() common.Hash {
56+
return common.BytesToHash([]byte{baseFeeSlotIndex})
57+
}
58+
59+
// GetBlackListStateHash computes and returns the storage key of blockList
60+
// in policy contract with an address, for reading corresponding values from statedb.
61+
func GetBlackListStateHash(addr common.Address) common.Hash {
62+
return crypto.Keccak256Hash(common.LeftPadBytes(addr.Bytes(), 32), common.LeftPadBytes([]byte{blackListSlotIndex}, 32))
2963
}

core/txpool/errors.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ var (
2626
// ErrInvalidSender is returned if the transaction contains an invalid signature.
2727
ErrInvalidSender = errors.New("invalid sender")
2828

29+
// ErrBlockedSender is returned if the transaction sender is blocked by policy.
30+
ErrBlockedSender = errors.New("blocked sender")
31+
2932
// ErrUnderpriced is returned if a transaction's gas price is below the minimum
3033
// configured for the transaction pool.
3134
ErrUnderpriced = errors.New("transaction underpriced")

core/txpool/validation.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/ethereum/go-ethereum/common"
2525
"github.com/ethereum/go-ethereum/core"
2626
"github.com/ethereum/go-ethereum/core/state"
27+
"github.com/ethereum/go-ethereum/core/systemcontracts"
2728
"github.com/ethereum/go-ethereum/core/types"
2829
"github.com/ethereum/go-ethereum/crypto/kzg4844"
2930
"github.com/ethereum/go-ethereum/log"
@@ -200,6 +201,17 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op
200201
if next > tx.Nonce() {
201202
return fmt.Errorf("%w: next nonce %v, tx nonce %v", core.ErrNonceTooLow, next, tx.Nonce())
202203
}
204+
// Ensure the transaction is allowed by policy
205+
// Apply policy minimum gas tip cap
206+
var minGasTipCap = opts.State.GetState(systemcontracts.PolicyProxyHash, systemcontracts.GetMinGasTipCapStateHash())
207+
if tx.GasTipCap().Cmp(minGasTipCap.Big()) < 0 {
208+
return fmt.Errorf("%w: policy needed %v, tip permitted %v", ErrUnderpriced, minGasTipCap.Big(), tx.GasTipCap())
209+
}
210+
// Apply policy blacklist
211+
var blocked = opts.State.GetState(systemcontracts.PolicyProxyHash, systemcontracts.GetBlackListStateHash(from))
212+
if blocked != (common.Hash{}) {
213+
return fmt.Errorf("%w: sender %s", ErrBlockedSender, from.Hex())
214+
}
203215
// Ensure the transaction doesn't produce a nonce gap in pools that do not
204216
// support arbitrary orderings
205217
if opts.FirstNonceGap != nil {

eth/api_backend.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ import (
2525
"github.com/ethereum/go-ethereum"
2626
"github.com/ethereum/go-ethereum/accounts"
2727
"github.com/ethereum/go-ethereum/common"
28+
cmath "github.com/ethereum/go-ethereum/common/math"
2829
"github.com/ethereum/go-ethereum/consensus"
2930
"github.com/ethereum/go-ethereum/core"
3031
"github.com/ethereum/go-ethereum/core/bloombits"
3132
"github.com/ethereum/go-ethereum/core/rawdb"
3233
"github.com/ethereum/go-ethereum/core/state"
34+
"github.com/ethereum/go-ethereum/core/systemcontracts"
3335
"github.com/ethereum/go-ethereum/core/txpool"
3436
"github.com/ethereum/go-ethereum/core/types"
3537
"github.com/ethereum/go-ethereum/core/vm"
@@ -363,7 +365,16 @@ func (b *EthAPIBackend) SyncProgress() ethereum.SyncProgress {
363365
}
364366

365367
func (b *EthAPIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
366-
return b.gpo.SuggestTipCap(ctx)
368+
suggestTipCap, err := b.gpo.SuggestTipCap(ctx)
369+
if err != nil {
370+
return nil, err
371+
}
372+
stateDb, _, err := b.StateAndHeaderByNumber(ctx, rpc.LatestBlockNumber)
373+
if err != nil {
374+
return nil, err
375+
}
376+
minGasTipCap := stateDb.GetState(systemcontracts.PolicyProxyHash, systemcontracts.GetMinGasTipCapStateHash()).Big()
377+
return cmath.BigMax(suggestTipCap, minGasTipCap), nil
367378
}
368379

369380
func (b *EthAPIBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) {

0 commit comments

Comments
 (0)