Skip to content

Commit e593290

Browse files
authored
feat: Add validation for blobTxType (coinbase#114)
* Add validation for blobTxType
1 parent 6ff104c commit e593290

File tree

5 files changed

+39274
-12419
lines changed

5 files changed

+39274
-12419
lines changed

internal/blockchain/parser/ethereum/ethereum_validator.go

Lines changed: 97 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/ethereum/go-ethereum/crypto"
1414
"github.com/ethereum/go-ethereum/rlp"
1515
"github.com/ethereum/go-ethereum/trie"
16+
"github.com/holiman/uint256"
1617
"go.uber.org/zap"
1718

1819
"golang.org/x/xerrors"
@@ -160,6 +161,24 @@ func (v *ethereumValidator) validateBlockHeader(ctx context.Context, header *api
160161
protocolHeader.WithdrawalsHash = &hash
161162
}
162163

164+
// EIP-4788: Beacon block root in the EVM
165+
if header.ParentBeaconBlockRoot != "" {
166+
root := geth.HexToHash(header.ParentBeaconBlockRoot)
167+
protocolHeader.ParentBeaconRoot = &root
168+
}
169+
170+
// EIP-4844: include the add blob gas used and excess blob gas to the block header.
171+
if header.GetBlobGasUsed() != 0 {
172+
bgu := header.GetBlobGasUsed()
173+
protocolHeader.BlobGasUsed = &bgu
174+
}
175+
176+
// EIP-4844: include the add blob gas used and excess blob gas to the block header.
177+
if header.GetExcessBlobGas() != 0 {
178+
ebg := header.GetExcessBlobGas()
179+
protocolHeader.ExcessBlobGas = &ebg
180+
}
181+
163182
// Note that Hash returns the block hash of the header, which is simply the keccak256 hash of its RLP encoding.
164183
// We expect that the block hash recomputed following the protocol should match the one from the payload itself.
165184
expectedHash := protocolHeader.Hash()
@@ -295,11 +314,75 @@ func (v *ethereumValidator) toGethTransaction(transaction *api.EthereumTransacti
295314
// https://github.com/ethereum/go-ethereum/blob/8013a494fe3f0812aa1661aba43898d22a6fc061/internal/ethapi/transaction_args.go#L284
296315
var data types.TxData
297316
switch {
317+
case transaction.GetType() == types.BlobTxType:
318+
//v.logger.Debug(
319+
// "toGethTransaction: BlobTx",
320+
// zap.String("hash", transaction.GetHash()),
321+
//)
322+
323+
value256, overflow := uint256.FromBig(value)
324+
if overflow {
325+
return nil, xerrors.Errorf("failed to convert value %s to uint256.Int", value.String())
326+
}
327+
328+
var chainId *uint256.Int
329+
if transaction.GetOptionalChainId() != nil {
330+
chainId = uint256.NewInt(transaction.GetChainId())
331+
}
332+
333+
var blobFeeCap *uint256.Int
334+
if transaction.GetOptionalMaxFeePerBlobGas() != nil {
335+
bfc, err := uint256.FromDecimal(transaction.GetMaxFeePerBlobGas())
336+
if err != nil {
337+
return nil, xerrors.Errorf("failed to convert blob fee cap %s to uint256.Int", transaction.GetMaxFeePerBlobGas())
338+
}
339+
340+
blobFeeCap = bfc
341+
}
342+
343+
al := types.AccessList{}
344+
if transaction.GetTransactionAccessList() != nil {
345+
al = toGethAccessList(transaction.GetTransactionAccessList())
346+
}
347+
348+
var blobHashes []geth.Hash
349+
blobHashes = make([]geth.Hash, len(transaction.BlobVersionedHashes))
350+
for i, h := range transaction.BlobVersionedHashes {
351+
blobHashes[i] = geth.HexToHash(h)
352+
}
353+
parsedR, err := uint256.FromHex(transaction.GetR())
354+
if err != nil {
355+
return nil, xerrors.Errorf("failed to convert r %s to uint256.Int: %w", transaction.GetR(), err)
356+
}
357+
parsedS, err := uint256.FromHex(transaction.GetS())
358+
if err != nil {
359+
return nil, xerrors.Errorf("failed to convert s %s to uint256.Int: %w", transaction.GetS(), err)
360+
}
361+
parsedV, err := uint256.FromHex(transaction.GetV())
362+
if err != nil {
363+
return nil, xerrors.Errorf("failed to convert v %s to uint256.Int: %w", transaction.GetV(), err)
364+
}
365+
data = &types.BlobTx{
366+
ChainID: chainId,
367+
Nonce: transaction.GetNonce(),
368+
GasTipCap: uint256.NewInt(transaction.GetMaxPriorityFeePerGas()),
369+
GasFeeCap: uint256.NewInt(transaction.GetMaxFeePerGas()),
370+
Gas: transaction.GetGas(),
371+
To: *to,
372+
Value: value256,
373+
Data: input,
374+
AccessList: al,
375+
BlobFeeCap: blobFeeCap,
376+
BlobHashes: blobHashes,
377+
V: parsedV,
378+
R: parsedR,
379+
S: parsedS,
380+
}
298381
case transaction.GetOptionalMaxFeePerGas() != nil:
299-
v.logger.Debug(
300-
"toGethTransaction: DynamicFeeTx",
301-
zap.String("hash", transaction.GetHash()),
302-
)
382+
//v.logger.Debug(
383+
// "toGethTransaction: DynamicFeeTx",
384+
// zap.String("hash", transaction.GetHash()),
385+
//)
303386
al := types.AccessList{}
304387
if transaction.GetTransactionAccessList() != nil {
305388
al = toGethAccessList(transaction.GetTransactionAccessList())
@@ -321,10 +404,10 @@ func (v *ethereumValidator) toGethTransaction(transaction *api.EthereumTransacti
321404
}
322405

323406
case transaction.GetOptionalTransactionAccessList() != nil:
324-
v.logger.Debug(
325-
"toGethTransaction: AccessListTx",
326-
zap.String("hash", transaction.GetHash()),
327-
)
407+
//v.logger.Debug(
408+
// "toGethTransaction: AccessListTx",
409+
// zap.String("hash", transaction.GetHash()),
410+
//)
328411
data = &types.AccessListTx{
329412
To: to,
330413
ChainID: chainId,
@@ -342,12 +425,12 @@ func (v *ethereumValidator) toGethTransaction(transaction *api.EthereumTransacti
342425
case (v.config.Blockchain() == common.Blockchain_BLOCKCHAIN_OPTIMISM ||
343426
v.config.Blockchain() == common.Blockchain_BLOCKCHAIN_BASE) &&
344427
transaction.GetType() == types.DepositTxType:
345-
v.logger.Debug(
346-
"toGethTransaction: DepositTx",
347-
zap.String("hash", transaction.GetHash()),
348-
zap.String("sourceHash", transaction.GetSourceHash()),
349-
zap.Bool("isSystemTx", transaction.GetIsSystemTx()),
350-
)
428+
//v.logger.Debug(
429+
// "toGethTransaction: DepositTx",
430+
// zap.String("hash", transaction.GetHash()),
431+
// zap.String("sourceHash", transaction.GetSourceHash()),
432+
// zap.Bool("isSystemTx", transaction.GetIsSystemTx()),
433+
//)
351434
data = &types.DepositTx{
352435
SourceHash: geth.HexToHash(transaction.GetSourceHash()),
353436
From: geth.HexToAddress(transaction.GetFrom()),

internal/blockchain/parser/ethereum/ethereum_validator_test.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ func TestEthereumValidator_Success(t *testing.T) {
4646
// 6. block 17000000: post merge, pre shanghai.
4747
// 7. block 17034873: post shanghai, no withdrawals in the block, but has withdrawalsRoot.
4848
// 8. block 17300000: post shanghai, has withdrawals in the block.
49+
// 9. block 22119617: post dencun, has blob header and blob transactions.
4950

50-
blocks := []int{1000, 2000000, 4000000, 8000000, 14000000, 17000000, 17034873, 17300000}
51+
blocks := []int{1000, 2000000, 4000000, 8000000, 14000000, 17000000, 17034873, 17300000, 22119617}
5152
for _, b := range blocks {
5253
var block api.NativeBlock
5354
path := fmt.Sprintf("parser/ethereum/native_block_%d.json", b)
@@ -98,6 +99,20 @@ func TestEthereumValidator_Failures(t *testing.T) {
9899
err = parser.ValidateBlock(ctx, corrupt_block)
99100
require.True(xerrors.Is(err, ErrInvalidBlockHash))
100101

102+
// Corrupt the amount of blob gas used
103+
corrupt_block = proto.Clone(&block).(*api.NativeBlock)
104+
corrupt_block.GetEthereum().GetHeader().OptionalBlobGasUsed = &api.EthereumHeader_BlobGasUsed{
105+
BlobGasUsed: 100,
106+
}
107+
err = parser.ValidateBlock(ctx, corrupt_block)
108+
require.True(xerrors.Is(err, ErrInvalidBlockHash))
109+
110+
// Corrupt the beacon block root
111+
corrupt_block = proto.Clone(&block).(*api.NativeBlock)
112+
corrupt_block.GetEthereum().GetHeader().ParentBeaconBlockRoot = "0x0d86349e9ee1647771048bf8905f2fc85bd348ec75abac5e3e2f0b5dec9"
113+
err = parser.ValidateBlock(ctx, corrupt_block)
114+
require.True(xerrors.Is(err, ErrInvalidBlockHash))
115+
101116
// 2. Modify the transactions in different ways and fail the transactions verification.
102117
err = fixtures.UnmarshalPB("parser/ethereum/native_block_14000000.json", &block)
103118
require.NoError(err)
@@ -122,6 +137,23 @@ func TestEthereumValidator_Failures(t *testing.T) {
122137
err = parser.ValidateBlock(ctx, corrupt_block)
123138
require.True(xerrors.Is(err, ErrInvalidTransactionsHash))
124139

140+
// Corrupt the max blob gas fee (from 6000000000 to 10).
141+
var blob_block api.NativeBlock
142+
err = fixtures.UnmarshalPB("parser/ethereum/native_block_70951.json", &blob_block)
143+
require.NoError(err)
144+
corrupt_block = proto.Clone(&blob_block).(*api.NativeBlock)
145+
corrupt_block.GetEthereum().Transactions[0].OptionalMaxFeePerBlobGas = &api.EthereumTransaction_MaxFeePerBlobGas{
146+
MaxFeePerBlobGas: "10",
147+
}
148+
err = parser.ValidateBlock(ctx, corrupt_block)
149+
require.True(xerrors.Is(err, ErrInvalidTransactionsHash))
150+
151+
// Corrupt the blob hashes
152+
corrupt_block = proto.Clone(&blob_block).(*api.NativeBlock)
153+
corrupt_block.GetEthereum().Transactions[0].BlobVersionedHashes = []string{"0x0000"}
154+
err = parser.ValidateBlock(ctx, corrupt_block)
155+
require.True(xerrors.Is(err, ErrInvalidTransactionsHash))
156+
125157
// 3. Modify the receipts in different ways and fail the receipts verification.
126158
err = fixtures.UnmarshalPB("parser/ethereum/native_block_8000000.json", &block)
127159
require.NoError(err)

0 commit comments

Comments
 (0)