437 lines
13 KiB
Go
437 lines
13 KiB
Go
package core
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"math/big"
|
|
"testing"
|
|
|
|
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
|
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
|
"github.com/ethereum/go-ethereum/params"
|
|
"github.com/holiman/uint256"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var (
|
|
emptyBlob = kzg4844.Blob{}
|
|
emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
|
|
emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit)
|
|
)
|
|
|
|
func TestIsDataAvailable(t *testing.T) {
|
|
hr := NewMockDAHeaderReader(params.ParliaTestChainConfig)
|
|
tests := []struct {
|
|
block *types.Block
|
|
chasingHead uint64
|
|
withSidecar bool
|
|
err bool
|
|
}{
|
|
{
|
|
block: types.NewBlockWithHeader(&types.Header{
|
|
Number: big.NewInt(1),
|
|
}).WithBody(types.Transactions{
|
|
createMockDATx(hr.Config(), nil),
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof},
|
|
}),
|
|
}, nil),
|
|
chasingHead: 1,
|
|
withSidecar: true,
|
|
err: false,
|
|
},
|
|
{
|
|
block: types.NewBlockWithHeader(&types.Header{
|
|
Number: big.NewInt(1),
|
|
}).WithBody(types.Transactions{
|
|
createMockDATx(hr.Config(), nil),
|
|
createMockDATx(hr.Config(), nil),
|
|
}, nil),
|
|
chasingHead: 1,
|
|
withSidecar: true,
|
|
err: false,
|
|
},
|
|
{
|
|
block: types.NewBlockWithHeader(&types.Header{
|
|
Number: big.NewInt(1),
|
|
}).WithBody(types.Transactions{
|
|
createMockDATx(hr.Config(), nil),
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof},
|
|
}),
|
|
}, nil),
|
|
chasingHead: 1,
|
|
withSidecar: false,
|
|
err: true,
|
|
},
|
|
{
|
|
block: types.NewBlockWithHeader(&types.Header{
|
|
Number: big.NewInt(1),
|
|
}).WithBody(types.Transactions{
|
|
createMockDATx(hr.Config(), nil),
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof},
|
|
}),
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof},
|
|
}),
|
|
}, nil),
|
|
chasingHead: 1,
|
|
withSidecar: true,
|
|
err: false,
|
|
},
|
|
|
|
{
|
|
block: types.NewBlockWithHeader(&types.Header{
|
|
Number: big.NewInt(1),
|
|
}).WithBody(types.Transactions{
|
|
createMockDATx(hr.Config(), nil),
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob, emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof, emptyBlobProof, emptyBlobProof},
|
|
}),
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob, emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof, emptyBlobProof, emptyBlobProof},
|
|
}),
|
|
}, nil),
|
|
chasingHead: params.MinBlocksForBlobRequests + 1,
|
|
withSidecar: true,
|
|
err: true,
|
|
},
|
|
{
|
|
block: types.NewBlockWithHeader(&types.Header{
|
|
Number: big.NewInt(0),
|
|
}).WithBody(types.Transactions{
|
|
createMockDATx(hr.Config(), nil),
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof},
|
|
}),
|
|
}, nil),
|
|
chasingHead: params.MinBlocksForBlobRequests + 1,
|
|
withSidecar: false,
|
|
err: false,
|
|
},
|
|
}
|
|
|
|
for i, item := range tests {
|
|
if item.withSidecar {
|
|
item.block = item.block.WithSidecars(collectBlobsFromTxs(item.block.Header(), item.block.Transactions()))
|
|
}
|
|
hr.setChasingHead(item.chasingHead)
|
|
err := IsDataAvailable(hr, item.block)
|
|
if item.err {
|
|
require.Error(t, err, i)
|
|
t.Log(err)
|
|
continue
|
|
}
|
|
require.NoError(t, err, i)
|
|
}
|
|
}
|
|
|
|
func TestCheckDataAvailableInBatch(t *testing.T) {
|
|
hr := NewMockDAHeaderReader(params.ParliaTestChainConfig)
|
|
tests := []struct {
|
|
chain types.Blocks
|
|
err bool
|
|
index int
|
|
}{
|
|
{
|
|
chain: types.Blocks{
|
|
types.NewBlockWithHeader(&types.Header{
|
|
Number: big.NewInt(1),
|
|
}).WithBody(types.Transactions{
|
|
createMockDATx(hr.Config(), nil),
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof},
|
|
}),
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof},
|
|
}),
|
|
}, nil),
|
|
types.NewBlockWithHeader(&types.Header{
|
|
Number: big.NewInt(2),
|
|
}).WithBody(types.Transactions{
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof},
|
|
}),
|
|
}, nil),
|
|
},
|
|
err: false,
|
|
},
|
|
{
|
|
chain: types.Blocks{
|
|
types.NewBlockWithHeader(&types.Header{
|
|
Number: big.NewInt(1),
|
|
}).WithBody(types.Transactions{
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof},
|
|
}),
|
|
}, nil),
|
|
types.NewBlockWithHeader(&types.Header{
|
|
Number: big.NewInt(2),
|
|
}).WithBody(types.Transactions{
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof},
|
|
}),
|
|
}, nil),
|
|
types.NewBlockWithHeader(&types.Header{
|
|
Number: big.NewInt(3),
|
|
}).WithBody(types.Transactions{
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof},
|
|
}),
|
|
}, nil),
|
|
},
|
|
err: true,
|
|
index: 1,
|
|
},
|
|
{
|
|
chain: types.Blocks{
|
|
types.NewBlockWithHeader(&types.Header{
|
|
Number: big.NewInt(1),
|
|
}).WithBody(types.Transactions{
|
|
createMockDATx(hr.Config(), nil),
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob, emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof, emptyBlobProof, emptyBlobProof},
|
|
}),
|
|
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob, emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof, emptyBlobProof, emptyBlobProof},
|
|
}),
|
|
}, nil),
|
|
},
|
|
err: true,
|
|
index: 0,
|
|
},
|
|
}
|
|
|
|
for i, item := range tests {
|
|
for j, block := range item.chain {
|
|
item.chain[j] = block.WithSidecars(collectBlobsFromTxs(block.Header(), block.Transactions()))
|
|
}
|
|
index, err := CheckDataAvailableInBatch(hr, item.chain)
|
|
if item.err {
|
|
t.Log(index, err)
|
|
require.Error(t, err, i)
|
|
require.Equal(t, item.index, index, i)
|
|
continue
|
|
}
|
|
require.NoError(t, err, i)
|
|
}
|
|
}
|
|
|
|
func BenchmarkEmptySidecarDAChecking(b *testing.B) {
|
|
hr := NewMockDAHeaderReader(params.ParliaTestChainConfig)
|
|
block := types.NewBlockWithHeader(&types.Header{
|
|
Number: big.NewInt(1),
|
|
}).WithBody(types.Transactions{
|
|
createMockDATx(hr.Config(), emptySidecar()),
|
|
createMockDATx(hr.Config(), emptySidecar()),
|
|
createMockDATx(hr.Config(), emptySidecar()),
|
|
createMockDATx(hr.Config(), emptySidecar()),
|
|
createMockDATx(hr.Config(), emptySidecar()),
|
|
createMockDATx(hr.Config(), emptySidecar()),
|
|
}, nil)
|
|
block = block.WithSidecars(collectBlobsFromTxs(block.Header(), block.Transactions()))
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
IsDataAvailable(hr, block)
|
|
}
|
|
}
|
|
|
|
func BenchmarkRandomSidecarDAChecking(b *testing.B) {
|
|
hr := NewMockDAHeaderReader(params.ParliaTestChainConfig)
|
|
const count = 10
|
|
blocks := make([]*types.Block, count)
|
|
for i := 0; i < len(blocks); i++ {
|
|
block := types.NewBlockWithHeader(&types.Header{
|
|
Number: big.NewInt(1),
|
|
}).WithBody(types.Transactions{
|
|
createMockDATx(hr.Config(), randomSidecar()),
|
|
createMockDATx(hr.Config(), randomSidecar()),
|
|
createMockDATx(hr.Config(), randomSidecar()),
|
|
createMockDATx(hr.Config(), randomSidecar()),
|
|
createMockDATx(hr.Config(), randomSidecar()),
|
|
createMockDATx(hr.Config(), randomSidecar()),
|
|
}, nil)
|
|
block = block.WithSidecars(collectBlobsFromTxs(block.Header(), block.Transactions()))
|
|
blocks[i] = block
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
IsDataAvailable(hr, blocks[i%count])
|
|
}
|
|
}
|
|
|
|
func collectBlobsFromTxs(header *types.Header, txs types.Transactions) types.BlobSidecars {
|
|
sidecars := make(types.BlobSidecars, 0, len(txs))
|
|
for i, tx := range txs {
|
|
sidecar := types.NewBlobSidecarFromTx(tx)
|
|
if sidecar == nil {
|
|
continue
|
|
}
|
|
sidecar.TxIndex = uint64(i)
|
|
sidecar.TxHash = tx.Hash()
|
|
sidecar.BlockNumber = header.Number
|
|
sidecar.BlockHash = header.Hash()
|
|
sidecars = append(sidecars, sidecar)
|
|
}
|
|
return sidecars
|
|
}
|
|
|
|
type mockDAHeaderReader struct {
|
|
config *params.ChainConfig
|
|
chasingHead uint64
|
|
}
|
|
|
|
func NewMockDAHeaderReader(config *params.ChainConfig) *mockDAHeaderReader {
|
|
return &mockDAHeaderReader{
|
|
config: config,
|
|
chasingHead: 0,
|
|
}
|
|
}
|
|
|
|
func (r *mockDAHeaderReader) setChasingHead(h uint64) {
|
|
r.chasingHead = h
|
|
}
|
|
|
|
func (r *mockDAHeaderReader) Config() *params.ChainConfig {
|
|
return r.config
|
|
}
|
|
|
|
func (r *mockDAHeaderReader) CurrentHeader() *types.Header {
|
|
return &types.Header{
|
|
Number: new(big.Int).SetUint64(r.chasingHead),
|
|
}
|
|
}
|
|
|
|
func (r *mockDAHeaderReader) ChasingHead() *types.Header {
|
|
return &types.Header{
|
|
Number: new(big.Int).SetUint64(r.chasingHead),
|
|
}
|
|
}
|
|
|
|
func (r *mockDAHeaderReader) GenesisHeader() *types.Header {
|
|
panic("not supported")
|
|
}
|
|
|
|
func (r *mockDAHeaderReader) GetHeader(hash common.Hash, number uint64) *types.Header {
|
|
panic("not supported")
|
|
}
|
|
|
|
func (r *mockDAHeaderReader) GetHeaderByNumber(number uint64) *types.Header {
|
|
panic("not supported")
|
|
}
|
|
|
|
func (r *mockDAHeaderReader) GetHeaderByHash(hash common.Hash) *types.Header {
|
|
panic("not supported")
|
|
}
|
|
|
|
func (r *mockDAHeaderReader) GetTd(hash common.Hash, number uint64) *big.Int {
|
|
panic("not supported")
|
|
}
|
|
|
|
func (r *mockDAHeaderReader) GetHighestVerifiedHeader() *types.Header {
|
|
panic("not supported")
|
|
}
|
|
|
|
func createMockDATx(config *params.ChainConfig, sidecar *types.BlobTxSidecar) *types.Transaction {
|
|
if sidecar == nil {
|
|
tx := &types.DynamicFeeTx{
|
|
ChainID: config.ChainID,
|
|
Nonce: 0,
|
|
GasTipCap: big.NewInt(22),
|
|
GasFeeCap: big.NewInt(5),
|
|
Gas: 25000,
|
|
To: &common.Address{0x03, 0x04, 0x05},
|
|
Value: big.NewInt(99),
|
|
Data: make([]byte, 50),
|
|
}
|
|
return types.NewTx(tx)
|
|
}
|
|
tx := &types.BlobTx{
|
|
ChainID: uint256.MustFromBig(config.ChainID),
|
|
Nonce: 5,
|
|
GasTipCap: uint256.NewInt(22),
|
|
GasFeeCap: uint256.NewInt(5),
|
|
Gas: 25000,
|
|
To: common.Address{0x03, 0x04, 0x05},
|
|
Value: uint256.NewInt(99),
|
|
Data: make([]byte, 50),
|
|
BlobFeeCap: uint256.NewInt(15),
|
|
BlobHashes: sidecar.BlobHashes(),
|
|
Sidecar: sidecar,
|
|
}
|
|
return types.NewTx(tx)
|
|
}
|
|
|
|
func randFieldElement() [32]byte {
|
|
bytes := make([]byte, 32)
|
|
_, err := rand.Read(bytes)
|
|
if err != nil {
|
|
panic("failed to get random field element")
|
|
}
|
|
var r fr.Element
|
|
r.SetBytes(bytes)
|
|
|
|
return gokzg4844.SerializeScalar(r)
|
|
}
|
|
|
|
func randBlob() kzg4844.Blob {
|
|
var blob kzg4844.Blob
|
|
for i := 0; i < len(blob); i += gokzg4844.SerializedScalarSize {
|
|
fieldElementBytes := randFieldElement()
|
|
copy(blob[i:i+gokzg4844.SerializedScalarSize], fieldElementBytes[:])
|
|
}
|
|
return blob
|
|
}
|
|
|
|
func randomSidecar() *types.BlobTxSidecar {
|
|
blob := randBlob()
|
|
commitment, _ := kzg4844.BlobToCommitment(blob)
|
|
proof, _ := kzg4844.ComputeBlobProof(blob, commitment)
|
|
return &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{blob},
|
|
Commitments: []kzg4844.Commitment{commitment},
|
|
Proofs: []kzg4844.Proof{proof},
|
|
}
|
|
}
|
|
|
|
func emptySidecar() *types.BlobTxSidecar {
|
|
return &types.BlobTxSidecar{
|
|
Blobs: []kzg4844.Blob{emptyBlob},
|
|
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
|
Proofs: []kzg4844.Proof{emptyBlobProof},
|
|
}
|
|
}
|