Skip to content

Commit 8a63640

Browse files
committed
contracts: move encrypted tx gas limit check to consensus level
This commit solves two problems: 1. By the moment of GovReward contract execution we must be sure that encrypted transaction gas limit satisfies minimum required value. 2. By the same moment we must be sure that Envelope transaction has enough gas limit to pay for execution of decrypted transaction. If these two constrains are violated, contract itself can't do anything about it, the Envelope execution will be aborted, but still, Envelope will be accepted to the chain since we don't filter out failed Envelopes (and we shouldn't do that, ref. #422 (comment)). However, the check is still needed because otherwise it's easy for the user to accidently send an invalid Envelope and it will be accepted to the chain spending user's funds for nothing. I won't say that it's a vector of attack because small Envelope fee will lead to the fact that encrypted tx execution itself will be restricted by this value and hence it's not critical for the system and doesn't bring any profit to the user. This commit moves these two checks to mempool and state transition level so that it's impossible to submit Envelope that is improperly constructed. Signed-off-by: Anna Shaleva <[email protected]>
1 parent 603d6ea commit 8a63640

File tree

5 files changed

+35
-4
lines changed

5 files changed

+35
-4
lines changed

antimev/envelope.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ import (
99
"github.com/ethereum/go-ethereum/core/systemcontracts"
1010
"github.com/ethereum/go-ethereum/core/types"
1111
"github.com/ethereum/go-ethereum/crypto/tpke"
12+
"github.com/ethereum/go-ethereum/params"
1213
)
1314

15+
// MinEncryptedGasLimit is the minimum required gas limit for encrypted transaction.
16+
// It is set to be equal to a simple transfer execution cost since it is assumed that
17+
// minimum valid encrypted transaction structure is a simple transfer.
18+
const MinEncryptedGasLimit = uint32(params.TxGas)
19+
1420
var (
1521
// EncryptedDataPrefix is the prefix of Envelope transaction's data. It's used to
1622
// distinguish simple transactions that have GovernanceRewardProxy contract as a
@@ -75,7 +81,7 @@ func GetEncryptedHash(envelope *types.Transaction) common.Hash {
7581

7682
// GetEncryptedGas returns the gas limit of inner encrypted transaction specified in an
7783
// unencrypted part of Envelope data. Passing non-Envelope as an argument is a no-op.
78-
func GetEncryptedGas(envelope *types.Transaction) uint32 {
84+
func GetEncryptedGas(envelopeData []byte) uint32 {
7985
gasOffset := EncryptedDataPrefixLen + EncryptedDataRoundLen
80-
return binary.BigEndian.Uint32(envelope.Data()[gasOffset : gasOffset+EncryptedDataGasLen])
86+
return binary.BigEndian.Uint32(envelopeData[gasOffset : gasOffset+EncryptedDataGasLen])
8187
}

consensus/dbft/dbft.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1263,7 +1263,7 @@ func (c *DBFT) validateDecryptedTx(head *types.Header, decryptedTx *types.Transa
12631263
return fmt.Errorf("decryptedTx hash mismatch: expected %s, got %s", expectedH, decryptedTx.Hash())
12641264
}
12651265
// Ensure decrypted gas limit is the same as the envelope declared
1266-
expectedG := antimev.GetEncryptedGas(envelope)
1266+
expectedG := antimev.GetEncryptedGas(envelope.Data())
12671267
if decryptedTx.Gas() != uint64(expectedG) {
12681268
return fmt.Errorf("decryptedTx gas limit mismatch: expected %v, got %v", expectedG, decryptedTx.Gas())
12691269
}

core/state_transition.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,16 @@ func (st *StateTransition) preCheck() error {
335335
return fmt.Errorf("%w: address %v, gasLimit %v, policy maxEnvelopeGasLimit %v, ", ErrGasLimitReached, msg.From.Hex(), msg.GasLimit,
336336
envelopeGasLimit)
337337
}
338+
// Check that encrypted transaction gas limit satisfies the minimum required gas limit.
339+
encryptedGasLimit := antimev.GetEncryptedGas(msg.Data)
340+
if encryptedGasLimit < antimev.MinEncryptedGasLimit {
341+
return fmt.Errorf("invalid encrypted transaction gas limit: required at least %v, got %v", antimev.MinEncryptedGasLimit, encryptedGasLimit)
342+
}
343+
// The gas limit of Envelope transaction should be enough to cover encrypted transaction
344+
// execution.
345+
if msg.GasLimit < uint64(encryptedGasLimit) {
346+
return fmt.Errorf("invalid envelope gas limit: at least %d is required for encrypted transaction execution, have %d", encryptedGasLimit, msg.GasLimit)
347+
}
338348
var envelopeFee = st.state.GetState(systemcontracts.PolicyProxyHash, systemcontracts.GetEnvelopeFeeStateHash()).Big()
339349
minGasTipCap.Add(minGasTipCap, envelopeFee)
340350
}

core/txpool/errors.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ var (
4545
// maximum allowance of the current block.
4646
ErrGasLimit = errors.New("exceeds block gas limit")
4747

48+
// ErrEnvelopeGasLimit is returned if an Envelope transaction's requested gas limit
49+
// doesn't satisfy verification criteria.
50+
ErrEnvelopeGasLimit = errors.New("invalid Envelope gas limit")
51+
4852
// ErrNegativeValue is a sanity error to ensure no one is able to specify a
4953
// transaction with a negative value.
5054
ErrNegativeValue = errors.New("negative value")

core/txpool/validation.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,18 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op
220220
if antimev.IsEnvelope(tx) {
221221
var envelopeGasLimit = opts.State.GetState(systemcontracts.PolicyProxyHash, systemcontracts.GetMaxEnvelopeGasLimitStateHash()).Big()
222222
if new(big.Int).SetUint64(tx.Gas()).Cmp(envelopeGasLimit) > 0 {
223-
return fmt.Errorf("%w: policy maxEnvelopeGasLimit allowed %v, gas %v", ErrGasLimit, envelopeGasLimit, tx.Gas())
223+
return fmt.Errorf("%w: policy maxEnvelopeGasLimit allowed %v, gas %v", ErrEnvelopeGasLimit, envelopeGasLimit, tx.Gas())
224+
}
225+
// Assuming that encrypted transaction is at least a simple transfer, its gas limit
226+
// must be enough to cover simple transfer execution.
227+
encryptedGasLimit := antimev.GetEncryptedGas(tx.Data())
228+
if encryptedGasLimit < antimev.MinEncryptedGasLimit {
229+
return fmt.Errorf("invalid encrypted transaction gas limit: required at least %v, got %v", antimev.MinEncryptedGasLimit, encryptedGasLimit)
230+
}
231+
// The gas limit of Envelope transaction should be enough to cover encrypted transaction
232+
// execution.
233+
if tx.Gas() < uint64(encryptedGasLimit) {
234+
return fmt.Errorf("%w: at least %d is required for encrypted transaction execution, have %d", ErrEnvelopeGasLimit, encryptedGasLimit, tx.Gas())
224235
}
225236
// Add envelope fee on top of minGasTipCap check
226237
var envelopeFee = opts.State.GetState(systemcontracts.PolicyProxyHash, systemcontracts.GetEnvelopeFeeStateHash()).Big()

0 commit comments

Comments
 (0)