mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-21 20:26:41 +00:00
Implemented the proof interface, refactored code, and implemented comments
This commit is contained in:
parent
02b439cdea
commit
ff0837ab29
3 changed files with 352 additions and 222 deletions
|
|
@ -57,18 +57,20 @@ const (
|
|||
|
||||
type proofvar uint16
|
||||
|
||||
const (
|
||||
ProofNone proofvar = iota
|
||||
ProofHHA
|
||||
ProofRoots
|
||||
ProofCapella
|
||||
ProofDeneb
|
||||
)
|
||||
type buffer struct {
|
||||
headers [][]byte
|
||||
bodies [][]byte
|
||||
receipts [][]byte
|
||||
proofs [][]byte
|
||||
tds [][]byte
|
||||
}
|
||||
|
||||
// Proof bundles variant + compressed bytes.
|
||||
type Proof struct {
|
||||
Variant proofvar
|
||||
Data []byte
|
||||
type offsets struct {
|
||||
headers []uint64
|
||||
bodys []uint64
|
||||
receipts []uint64
|
||||
proofoffsets []uint64
|
||||
tdoff []uint64
|
||||
}
|
||||
|
||||
type Builder struct {
|
||||
|
|
@ -76,22 +78,12 @@ type Builder struct {
|
|||
buf *bytes.Buffer
|
||||
sn *snappy.Writer
|
||||
|
||||
headers [][]byte
|
||||
bodies [][]byte
|
||||
receipts [][]byte
|
||||
proofs [][]byte
|
||||
tds [][]byte
|
||||
tdsint []*big.Int
|
||||
hashes []common.Hash
|
||||
|
||||
headeroffsets []uint64
|
||||
bodyoffsets []uint64
|
||||
receiptoffsets []uint64
|
||||
proofoffsets []uint64
|
||||
tdoff []uint64
|
||||
buff buffer
|
||||
off offsets
|
||||
|
||||
prooftype proofvar
|
||||
|
||||
tdsint []*big.Int
|
||||
hashes []common.Hash
|
||||
startNum *uint64
|
||||
writtenBytes uint64
|
||||
}
|
||||
|
|
@ -107,20 +99,23 @@ func NewBuilder(w io.Writer) *Builder {
|
|||
}
|
||||
|
||||
// Add writes a block entry, its reciepts, and optionally its proofs as well into the e2store file.
|
||||
func (b *Builder) Add(header types.Header, body types.Body, receipts types.Receipts, td *big.Int, proof *Proof) error {
|
||||
var pv proofvar = ProofNone
|
||||
var pData []byte
|
||||
func (b *Builder) Add(header types.Header, body types.Body, receipts types.Receipts, td *big.Int, proof Proof) error {
|
||||
pv := proofVariantOf(proof) // variant code (or proofNone)
|
||||
var ep []byte // encoded proof payload
|
||||
|
||||
if proof != nil {
|
||||
pv, pData = proof.Variant, proof.Data
|
||||
if pv == ProofNone || len(pData) == 0 {
|
||||
return fmt.Errorf("invalid proof: variant=%d len=%d", pv, len(pData))
|
||||
var buf bytes.Buffer
|
||||
if err := proof.EncodeRLP(&buf); err != nil {
|
||||
return fmt.Errorf("encode proof: %w", err)
|
||||
}
|
||||
ep = buf.Bytes()
|
||||
}
|
||||
|
||||
if len(b.headers) == 0 {
|
||||
if len(b.buff.headers) == 0 {
|
||||
b.prooftype = pv
|
||||
} else if pv != b.prooftype {
|
||||
return fmt.Errorf("cannot mix proof variants: first=%d now=%d", b.prooftype, pv)
|
||||
return fmt.Errorf("cannot mix proof variants: first=%d now=%d",
|
||||
b.prooftype, pv)
|
||||
}
|
||||
|
||||
eh, err := rlp.EncodeToBytes(&header)
|
||||
|
|
@ -136,31 +131,27 @@ func (b *Builder) Add(header types.Header, body types.Body, receipts types.Recei
|
|||
return fmt.Errorf("encode receipts: %w", err)
|
||||
}
|
||||
|
||||
var ep []byte
|
||||
if pv != ProofNone {
|
||||
ep, err = rlp.EncodeToBytes([]interface{}{uint16(pv), proof.Data})
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode proof: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return b.AddRLP(eh, eb, er, ep, header.Number.Uint64(), header.Hash(), td)
|
||||
return b.AddRLP(
|
||||
eh, eb, er, ep,
|
||||
header.Number.Uint64(),
|
||||
header.Hash(), td,
|
||||
)
|
||||
}
|
||||
|
||||
// AddRLP takes the RLP encoded block components and writes them to the underlying e2store file
|
||||
func (b *Builder) AddRLP(headerRLP []byte, bodyRLP []byte, receipts []byte, proof []byte, blockNum uint64, blockHash common.Hash, td *big.Int) error {
|
||||
if len(b.headers) >= MaxEraESize {
|
||||
if len(b.buff.headers) >= MaxEraESize {
|
||||
return fmt.Errorf("exceeds MaxEraESize %d", MaxEraESize)
|
||||
}
|
||||
|
||||
b.headers = append(b.headers, headerRLP)
|
||||
b.bodies = append(b.bodies, bodyRLP)
|
||||
b.receipts = append(b.receipts, receipts)
|
||||
b.tds = append(b.tds, uint256LE(td))
|
||||
b.buff.headers = append(b.buff.headers, headerRLP)
|
||||
b.buff.bodies = append(b.buff.bodies, bodyRLP)
|
||||
b.buff.receipts = append(b.buff.receipts, receipts)
|
||||
b.buff.tds = append(b.buff.tds, uint256LE(td))
|
||||
b.tdsint = append(b.tdsint, new(big.Int).Set(td))
|
||||
b.hashes = append(b.hashes, blockHash)
|
||||
if proof != nil {
|
||||
b.proofs = append(b.proofs, proof)
|
||||
b.buff.proofs = append(b.buff.proofs, proof)
|
||||
}
|
||||
|
||||
//Write Era2 version before writing any blocks.
|
||||
|
|
@ -181,40 +172,64 @@ func (b *Builder) Finalize() (common.Hash, error) {
|
|||
if b.startNum == nil {
|
||||
return common.Hash{}, errors.New("no blocks added, cannot finalize")
|
||||
}
|
||||
var err error
|
||||
if err := b.flushKind(TypeCompressedHeader, b.headers, true, &b.headeroffsets); err != nil {
|
||||
return common.Hash{}, err
|
||||
for _, data := range b.buff.headers {
|
||||
off, err := b.addEntry(TypeCompressedHeader, data, true)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("headers: %w", err)
|
||||
}
|
||||
if err := b.flushKind(TypeCompressedBody, b.bodies, true, &b.bodyoffsets); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
if err := b.flushKind(TypeCompressedReceipts, b.receipts, true, &b.receiptoffsets); err != nil {
|
||||
return common.Hash{}, err
|
||||
b.off.headers = append(b.off.headers, off)
|
||||
}
|
||||
|
||||
if len(b.tds) > 0 {
|
||||
if err := b.flushKind(TypeTotalDifficulty, b.tds, false, &b.tdoff); err != nil {
|
||||
return common.Hash{}, err
|
||||
for _, data := range b.buff.bodies {
|
||||
off, err := b.addEntry(TypeCompressedBody, data, true)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("bodies: %w", err)
|
||||
}
|
||||
b.off.bodys = append(b.off.bodys, off)
|
||||
}
|
||||
|
||||
for _, data := range b.buff.receipts {
|
||||
off, err := b.addEntry(TypeCompressedReceipts, data, true)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("receipts: %w", err)
|
||||
}
|
||||
b.off.receipts = append(b.off.receipts, off)
|
||||
}
|
||||
|
||||
if len(b.buff.tds) > 0 {
|
||||
for _, data := range b.buff.tds {
|
||||
off, err := b.addEntry(TypeTotalDifficulty, data, false)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("total-difficulty: %w", err)
|
||||
}
|
||||
b.off.tdoff = append(b.off.tdoff, off)
|
||||
}
|
||||
}
|
||||
|
||||
if b.prooftype != ProofNone {
|
||||
if err := b.flushKind(TypeProof, b.proofs, true, &b.proofoffsets); err != nil {
|
||||
return common.Hash{}, err
|
||||
for _, data := range b.buff.proofs {
|
||||
off, err := b.addEntry(TypeProof, data, true)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("proofs: %w", err)
|
||||
}
|
||||
b.off.proofoffsets = append(b.off.proofoffsets, off)
|
||||
}
|
||||
}
|
||||
|
||||
var accRoot common.Hash
|
||||
if len(b.hashes) > 0 {
|
||||
var err error
|
||||
accRoot, err = ComputeAccumulator(b.hashes, b.tdsint)
|
||||
if err != nil {
|
||||
return common.Hash{}, fmt.Errorf("error calculating accumulator root: %w", err)
|
||||
return common.Hash{}, fmt.Errorf("compute accumulator: %w", err)
|
||||
}
|
||||
if n, err := b.w.Write(TypeAccumulatorRoot, accRoot[:]); err != nil {
|
||||
return common.Hash{}, fmt.Errorf("error writing accumulator root: %w", err)
|
||||
return common.Hash{}, fmt.Errorf("write accumulator: %w", err)
|
||||
} else {
|
||||
b.writtenBytes += uint64(n)
|
||||
}
|
||||
}
|
||||
|
||||
return accRoot, b.writeIndex()
|
||||
}
|
||||
|
||||
|
|
@ -282,41 +297,40 @@ func (b *Builder) flushKind(typ uint16, list [][]byte, useSnappy bool, dst *[]ui
|
|||
// write index takes all the offset table and writes it to the file
|
||||
// the index table contains all offsets to specific block entries
|
||||
func (b *Builder) writeIndex() error {
|
||||
count := uint64(len(b.headeroffsets))
|
||||
compcount := uint64(3)
|
||||
if len(b.tds) > 0 {
|
||||
compcount++
|
||||
count := uint64(len(b.off.headers))
|
||||
componentCount := uint64(3)
|
||||
if len(b.buff.tds) > 0 {
|
||||
componentCount++
|
||||
}
|
||||
if len(b.proofs) > 0 {
|
||||
compcount++
|
||||
if len(b.buff.proofs) > 0 {
|
||||
componentCount++
|
||||
}
|
||||
|
||||
idx := make([]byte, 8+count*8*compcount+16) //8 for start block, 8 per property per block, 16 for the number of properties and the number of blocks
|
||||
binary.LittleEndian.PutUint64(idx, *b.startNum)
|
||||
index := make([]byte, 8+count*8*componentCount+16) //8 for start block, 8 per property per block, 16 for the number of properties and the number of blocks
|
||||
binary.LittleEndian.PutUint64(index, *b.startNum)
|
||||
base := int64(b.writtenBytes)
|
||||
pos := 8
|
||||
rel := func(abs uint64) uint64 { return uint64(int64(abs) - base) }
|
||||
for i := uint64(0); i < count; i++ {
|
||||
binary.LittleEndian.PutUint64(idx[pos:], rel(b.headeroffsets[i]))
|
||||
pos += 8
|
||||
binary.LittleEndian.PutUint64(idx[pos:], rel(b.bodyoffsets[i]))
|
||||
pos += 8
|
||||
binary.LittleEndian.PutUint64(idx[pos:], rel(b.receiptoffsets[i]))
|
||||
pos += 8
|
||||
if len(b.tds) > 0 {
|
||||
binary.LittleEndian.PutUint64(idx[pos:], rel(b.tdoff[i]))
|
||||
pos += 8
|
||||
}
|
||||
if len(b.proofs) > 0 {
|
||||
binary.LittleEndian.PutUint64(idx[pos:], rel(b.proofoffsets[i]))
|
||||
pos += 8
|
||||
}
|
||||
}
|
||||
basePosition := 8 + i*componentCount*8
|
||||
|
||||
binary.LittleEndian.PutUint64(idx[pos:], compcount)
|
||||
binary.LittleEndian.PutUint64(index[basePosition:], rel(b.off.headers[i]))
|
||||
binary.LittleEndian.PutUint64(index[basePosition+8:], rel(b.off.bodys[i]))
|
||||
binary.LittleEndian.PutUint64(index[basePosition+16:], rel(b.off.receipts[i]))
|
||||
|
||||
pos := uint64(24)
|
||||
if len(b.buff.tds) > 0 {
|
||||
binary.LittleEndian.PutUint64(index[basePosition+pos:], rel(b.off.tdoff[i]))
|
||||
pos += 8
|
||||
binary.LittleEndian.PutUint64(idx[pos:], count)
|
||||
if n, err := b.w.Write(TypeBlockIndex, idx); err != nil {
|
||||
}
|
||||
if len(b.buff.proofs) > 0 {
|
||||
binary.LittleEndian.PutUint64(index[basePosition+pos:], rel(b.off.proofoffsets[i]))
|
||||
}
|
||||
}
|
||||
indexEnd := 8 + count*componentCount*8
|
||||
|
||||
binary.LittleEndian.PutUint64(index[indexEnd+0:], componentCount)
|
||||
binary.LittleEndian.PutUint64(index[indexEnd+8:], count)
|
||||
if n, err := b.w.Write(TypeBlockIndex, index); err != nil {
|
||||
return err
|
||||
} else {
|
||||
b.writtenBytes += uint64(n)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,22 @@ type metadata struct {
|
|||
length int64 // length of the file in bytes
|
||||
}
|
||||
|
||||
const (
|
||||
ProofNone proofvar = iota
|
||||
proofHHA
|
||||
proofRoots
|
||||
proofCapella
|
||||
proofDeneb
|
||||
)
|
||||
|
||||
const (
|
||||
compHeader = 0
|
||||
compBody = 1
|
||||
compReceipts = 2
|
||||
compTD = 3
|
||||
compProof = 4
|
||||
)
|
||||
|
||||
type BlockProofHistoricalHashesAccumulator [15]common.Hash // 15 * 32 = 480 bytes
|
||||
|
||||
// BlockProofHistoricalRoots – Altair / Bellatrix historical_roots branch.
|
||||
|
|
@ -47,6 +63,55 @@ type BlockProofHistoricalSummariesDeneb struct {
|
|||
Slot uint64 // 8 => 840 bytes
|
||||
}
|
||||
|
||||
type Proof interface {
|
||||
EncodeRLP(w io.Writer) error
|
||||
DecodeRlP(s *rlp.Stream) error
|
||||
Variant() proofvar
|
||||
}
|
||||
|
||||
type hhaAlias BlockProofHistoricalHashesAccumulator // alias ⇒ no EncodeRLP method
|
||||
|
||||
func (p *BlockProofHistoricalHashesAccumulator) EncodeRLP(w io.Writer) error {
|
||||
payload := []interface{}{uint16(proofHHA), hhaAlias(*p)}
|
||||
return rlp.Encode(w, payload)
|
||||
}
|
||||
|
||||
func (p *BlockProofHistoricalHashesAccumulator) Variant() proofvar { return proofHHA }
|
||||
|
||||
type rootsAlias BlockProofHistoricalRoots
|
||||
|
||||
func (p *BlockProofHistoricalRoots) EncodeRLP(w io.Writer) error {
|
||||
payload := []interface{}{uint16(proofRoots), rootsAlias(*p)}
|
||||
return rlp.Encode(w, payload)
|
||||
}
|
||||
|
||||
func (*BlockProofHistoricalRoots) Variant() proofvar { return proofRoots }
|
||||
|
||||
type capellaAlias BlockProofHistoricalSummariesCapella
|
||||
|
||||
func (p *BlockProofHistoricalSummariesCapella) EncodeRLP(w io.Writer) error {
|
||||
payload := []interface{}{uint16(proofCapella), capellaAlias(*p)}
|
||||
return rlp.Encode(w, payload)
|
||||
}
|
||||
|
||||
func (*BlockProofHistoricalSummariesCapella) Variant() proofvar { return proofCapella }
|
||||
|
||||
type denebAlias BlockProofHistoricalSummariesDeneb
|
||||
|
||||
func (p *BlockProofHistoricalSummariesDeneb) EncodeRLP(w io.Writer) error {
|
||||
payload := []interface{}{uint16(proofDeneb), denebAlias(*p)}
|
||||
return rlp.Encode(w, payload)
|
||||
}
|
||||
|
||||
func (*BlockProofHistoricalSummariesDeneb) Variant() proofvar { return proofDeneb }
|
||||
|
||||
func proofVariantOf(p Proof) proofvar {
|
||||
if p == nil {
|
||||
return ProofNone
|
||||
}
|
||||
return p.Variant()
|
||||
}
|
||||
|
||||
type ReadAtSeekCloser interface {
|
||||
io.ReaderAt
|
||||
io.Seeker
|
||||
|
|
@ -57,7 +122,6 @@ type Era struct {
|
|||
f ReadAtSeekCloser
|
||||
s *e2store.Reader
|
||||
m metadata // metadata for the Era file
|
||||
headeroff, bodyoff, receiptsoff, tdoff, proofsoff []uint64 // offsets for each entry type
|
||||
indstart int64
|
||||
rootheader uint64 // offset of the root header in the file if present
|
||||
}
|
||||
|
|
@ -98,11 +162,11 @@ func (e *Era) Count() uint64 {
|
|||
|
||||
// retrieves the block if present within the era file
|
||||
func (e *Era) GetBlockByNumber(blockNum uint64) (*types.Block, error) {
|
||||
h, err := e.getHeader(blockNum)
|
||||
h, err := e.GetHeader(blockNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := e.getBody(blockNum)
|
||||
b, err := e.GetBody(blockNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -110,46 +174,46 @@ func (e *Era) GetBlockByNumber(blockNum uint64) (*types.Block, error) {
|
|||
}
|
||||
|
||||
// retrieves header from era file through the cached offset table
|
||||
func (e *Era) getHeader(blockNum uint64) (*types.Header, error) {
|
||||
if blockNum < e.m.start || blockNum >= e.m.start+e.m.count {
|
||||
return nil, fmt.Errorf("block number %d out of range [%d, %d)", blockNum, e.m.start, e.m.start+e.m.count)
|
||||
}
|
||||
r, _, err := e.s.ReaderAt(TypeCompressedHeader, int64(e.headeroff[blockNum-e.m.start]))
|
||||
func (e *Era) GetHeader(num uint64) (*types.Header, error) {
|
||||
off, err := e.headerOff(num)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r = snappy.NewReader(r)
|
||||
|
||||
r, _, err := e.s.ReaderAt(TypeCompressedHeader, int64(off))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r = snappy.NewReader(r)
|
||||
var h types.Header
|
||||
return &h, rlp.Decode(r, &h)
|
||||
}
|
||||
|
||||
// retrieves body from era file through cached offset table
|
||||
func (e *Era) getBody(blockNum uint64) (*types.Body, error) {
|
||||
if blockNum < e.m.start || blockNum >= e.m.start+e.m.count {
|
||||
return nil, fmt.Errorf("block number %d out of range [%d, %d)", blockNum, e.m.start, e.m.start+e.m.count)
|
||||
}
|
||||
r, _, err := e.s.ReaderAt(TypeCompressedBody, int64(e.bodyoff[blockNum-e.m.start]))
|
||||
func (e *Era) GetBody(num uint64) (*types.Body, error) {
|
||||
off, err := e.bodyOff(num)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r = snappy.NewReader(r)
|
||||
|
||||
r, _, err := e.s.ReaderAt(TypeCompressedBody, int64(off))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r = snappy.NewReader(r)
|
||||
var b types.Body
|
||||
return &b, rlp.Decode(r, &b)
|
||||
}
|
||||
|
||||
// retrieves td from era file through cached offset table
|
||||
func (e *Era) getTD(blockNum uint64) (*big.Int, error) {
|
||||
if blockNum < e.m.start || blockNum >= e.m.start+e.m.count {
|
||||
return nil, fmt.Errorf("block number %d out of range [%d, %d)",
|
||||
blockNum, e.m.start, e.m.start+e.m.count)
|
||||
off, err := e.tdOff(blockNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(e.tdoff) == 0 {
|
||||
return nil, fmt.Errorf("total-difficulty section not present")
|
||||
}
|
||||
|
||||
r, _, err := e.s.ReaderAt(TypeTotalDifficulty, int64(e.tdoff[blockNum-e.m.start]))
|
||||
r, _, err := e.s.ReaderAt(TypeTotalDifficulty, int64(off))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -160,10 +224,11 @@ func (e *Era) getTD(blockNum uint64) (*big.Int, error) {
|
|||
|
||||
// retrieves the raw body frame in bytes of a specific block
|
||||
func (e *Era) GetRawBodyFrameByNumber(blockNum uint64) ([]byte, error) {
|
||||
if blockNum < e.m.start || blockNum >= e.m.start+e.m.count {
|
||||
return nil, fmt.Errorf("block number %d out of range [%d, %d)", blockNum, e.m.start, e.m.start+e.m.count)
|
||||
off, err := e.bodyOff(blockNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, _, err := e.s.ReaderAt(TypeCompressedBody, int64(e.bodyoff[blockNum-e.m.start]))
|
||||
r, _, err := e.s.ReaderAt(TypeCompressedBody, int64(off))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -172,10 +237,11 @@ func (e *Era) GetRawBodyFrameByNumber(blockNum uint64) ([]byte, error) {
|
|||
|
||||
// retrieves the raw receipts frame in bytes of a specific block
|
||||
func (e *Era) GetRawReceiptsFrameByNumber(blockNum uint64) ([]byte, error) {
|
||||
if blockNum < e.m.start || blockNum >= e.m.start+e.m.count {
|
||||
return nil, fmt.Errorf("block number %d out of range [%d, %d)", blockNum, e.m.start, e.m.start+e.m.count)
|
||||
off, err := e.rcptOff(blockNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, _, err := e.s.ReaderAt(TypeCompressedReceipts, int64(e.receiptsoff[blockNum-e.m.start]))
|
||||
r, _, err := e.s.ReaderAt(TypeCompressedReceipts, int64(off))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -184,13 +250,11 @@ func (e *Era) GetRawReceiptsFrameByNumber(blockNum uint64) ([]byte, error) {
|
|||
|
||||
// retrieves the raw proof frame in bytes of a specific block proof
|
||||
func (e *Era) GetRawProofFrameByNumber(blockNum uint64) ([]byte, error) {
|
||||
if len(e.proofsoff) == 0 {
|
||||
return nil, fmt.Errorf("proofs section not present")
|
||||
off, err := e.proofOff(blockNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if blockNum < e.m.start || blockNum >= e.m.start+e.m.count {
|
||||
return nil, fmt.Errorf("block number %d out of range [%d, %d)", blockNum, e.m.start, e.m.start+e.m.count)
|
||||
}
|
||||
r, _, err := e.s.ReaderAt(TypeProof, int64(e.proofsoff[blockNum-e.m.start]))
|
||||
r, _, err := e.s.ReaderAt(TypeProof, int64(off))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -232,33 +296,6 @@ func (e *Era) loadIndex() error {
|
|||
return err
|
||||
}
|
||||
|
||||
toAbs := func(i int) uint64 {
|
||||
rel := binary.LittleEndian.Uint64(offBytes[i*8 : i*8+8])
|
||||
return uint64(int64(rel) + e.indstart)
|
||||
}
|
||||
|
||||
e.headeroff = make([]uint64, e.m.count)
|
||||
e.bodyoff = make([]uint64, e.m.count)
|
||||
e.receiptsoff = make([]uint64, e.m.count)
|
||||
if num > 3 {
|
||||
e.tdoff = make([]uint64, e.m.count)
|
||||
}
|
||||
if num > 4 {
|
||||
e.proofsoff = make([]uint64, e.m.count)
|
||||
}
|
||||
for i := uint64(0); i < e.m.count; i++ {
|
||||
base := int(i * uint64(num))
|
||||
e.headeroff[i] = toAbs(base)
|
||||
e.bodyoff[i] = toAbs(base + 1)
|
||||
e.receiptsoff[i] = toAbs(base + 2)
|
||||
if num > 3 {
|
||||
e.tdoff[i] = toAbs(base + 3)
|
||||
}
|
||||
if num > 4 {
|
||||
e.proofsoff[i] = toAbs(base + 4)
|
||||
}
|
||||
}
|
||||
|
||||
var off int64 = 0 // start at byte-0 of file
|
||||
|
||||
for off < e.indstart { // never enter the Block-Index TLV
|
||||
|
|
@ -275,80 +312,116 @@ func (e *Era) loadIndex() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// retrieves a range of block from any start block to n number of blocks
|
||||
// the components retrieved can be customized depending on the properties wanted
|
||||
func (e *Era) BatchRange(first, count uint64, wantHdr, wantBody, wantRec, wantPrf bool) (hdrs []*types.Header, bods []*types.Body, recs []types.Receipts, prfs [][]byte, err error) {
|
||||
// Getter methods to calculate offset of a specific component in the file.
|
||||
func (e *Era) headerOff(num uint64) (uint64, error) { return e.indexOffset(num, compHeader) }
|
||||
func (e *Era) bodyOff(num uint64) (uint64, error) { return e.indexOffset(num, compBody) }
|
||||
func (e *Era) rcptOff(num uint64) (uint64, error) { return e.indexOffset(num, compReceipts) }
|
||||
func (e *Era) tdOff(num uint64) (uint64, error) { return e.indexOffset(num, compTD) }
|
||||
func (e *Era) proofOff(num uint64) (uint64, error) { return e.indexOffset(num, compProof) }
|
||||
|
||||
// calculates offset to a certain component for a block number within a file.
|
||||
func (e *Era) indexOffset(n uint64, comp int) (uint64, error) {
|
||||
if n < e.m.start || n >= e.m.start+e.m.count {
|
||||
return 0, fmt.Errorf("block %d out of range [%d,%d)", n, e.m.start, e.m.start+e.m.count)
|
||||
}
|
||||
if comp >= int(e.m.components) {
|
||||
return 0, fmt.Errorf("component %d not present", comp)
|
||||
}
|
||||
|
||||
rec := (n-e.m.start)*e.m.components + uint64(comp)
|
||||
pos := e.indstart + 8 + 8 + int64(rec*8)
|
||||
|
||||
var buf [8]byte
|
||||
if _, err := e.f.ReadAt(buf[:], pos); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
rel := binary.LittleEndian.Uint64(buf[:])
|
||||
return uint64(int64(rel) + e.indstart), nil
|
||||
}
|
||||
|
||||
// GetHeaders returns RLP-decoded headers.
|
||||
func (e *Era) GetHeaders(first, count uint64) ([]*types.Header, error) {
|
||||
if count == 0 {
|
||||
err = fmt.Errorf("count must be > 0")
|
||||
return
|
||||
return nil, fmt.Errorf("count must be > 0")
|
||||
}
|
||||
if first < e.m.start || first+count > e.m.start+e.m.count {
|
||||
err = fmt.Errorf("range [%d,%d) out of bounds", first, first+count)
|
||||
return
|
||||
}
|
||||
|
||||
idx := first - e.m.start
|
||||
if wantHdr {
|
||||
hdrs = make([]*types.Header, count)
|
||||
}
|
||||
if wantBody {
|
||||
bods = make([]*types.Body, count)
|
||||
}
|
||||
if wantRec {
|
||||
recs = make([]types.Receipts, count)
|
||||
}
|
||||
if wantPrf {
|
||||
prfs = make([][]byte, count)
|
||||
return nil, fmt.Errorf("range [%d,%d) out of bounds", first, first+count)
|
||||
}
|
||||
|
||||
out := make([]*types.Header, count)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
id := idx + i
|
||||
n := first + i
|
||||
off, err := e.headerOff(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, _, err := e.s.ReaderAt(TypeCompressedHeader, int64(off))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var h types.Header
|
||||
if err := rlp.Decode(snappy.NewReader(r), &h); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[i] = &h
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
if wantHdr {
|
||||
r, _, er := e.s.ReaderAt(TypeCompressedHeader, int64(e.headeroff[id]))
|
||||
if er != nil {
|
||||
err = er
|
||||
return
|
||||
// GetBodies returns RLP-decoded bodies.
|
||||
func (e *Era) GetBodies(first, count uint64) ([]*types.Body, error) {
|
||||
if count == 0 {
|
||||
return nil, fmt.Errorf("count must be > 0")
|
||||
}
|
||||
if er = rlp.Decode(snappy.NewReader(r), &hdrs[i]); er != nil {
|
||||
err = er
|
||||
return
|
||||
if first < e.m.start || first+count > e.m.start+e.m.count {
|
||||
return nil, fmt.Errorf("range [%d,%d) out of bounds", first, first+count)
|
||||
}
|
||||
|
||||
out := make([]*types.Body, count)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
n := first + i
|
||||
off, err := e.bodyOff(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if wantBody {
|
||||
r, _, er := e.s.ReaderAt(TypeCompressedBody, int64(e.bodyoff[id]))
|
||||
if er != nil {
|
||||
err = er
|
||||
return
|
||||
r, _, err := e.s.ReaderAt(TypeCompressedBody, int64(off))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if er = rlp.Decode(snappy.NewReader(r), &bods[i]); er != nil {
|
||||
err = er
|
||||
return
|
||||
var b types.Body
|
||||
if err := rlp.Decode(snappy.NewReader(r), &b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[i] = &b
|
||||
}
|
||||
if wantRec {
|
||||
r, _, er := e.s.ReaderAt(TypeCompressedReceipts, int64(e.receiptsoff[id]))
|
||||
if er != nil {
|
||||
err = er
|
||||
return
|
||||
return out, nil
|
||||
}
|
||||
if er = rlp.Decode(snappy.NewReader(r), &recs[i]); er != nil {
|
||||
err = er
|
||||
return
|
||||
|
||||
// GetReceipts returns RLP-decoded receipts.
|
||||
func (e *Era) GetReceipts(first, count uint64) ([]types.Receipts, error) {
|
||||
if count == 0 {
|
||||
return nil, fmt.Errorf("count must be > 0")
|
||||
}
|
||||
if first < e.m.start || first+count > e.m.start+e.m.count {
|
||||
return nil, fmt.Errorf("range [%d,%d) out of bounds", first, first+count)
|
||||
}
|
||||
if wantPrf {
|
||||
if len(e.proofsoff) == 0 {
|
||||
err = fmt.Errorf("proofs section not present")
|
||||
return
|
||||
|
||||
out := make([]types.Receipts, count)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
n := first + i
|
||||
off, err := e.rcptOff(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, _, er := e.s.ReaderAt(TypeProof, int64(e.proofsoff[id]))
|
||||
if er != nil {
|
||||
err = er
|
||||
return
|
||||
r, _, err := e.s.ReaderAt(TypeCompressedReceipts, int64(off))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prfs[i], _ = io.ReadAll(r)
|
||||
var rc types.Receipts
|
||||
if err := rlp.Decode(snappy.NewReader(r), &rc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[i] = rc
|
||||
}
|
||||
return
|
||||
return out, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,49 @@ func TestEra2Builder(t *testing.T) {
|
|||
}
|
||||
defer era.Close()
|
||||
|
||||
hdrs, err := era.GetHeaders(0, 128)
|
||||
if err != nil {
|
||||
t.Fatalf("BatchHeaders full: %v", err)
|
||||
}
|
||||
bods, err := era.GetBodies(0, 128)
|
||||
if err != nil {
|
||||
t.Fatalf("BatchBodies full: %v", err)
|
||||
}
|
||||
recs, err := era.GetReceipts(0, 128)
|
||||
if err != nil {
|
||||
t.Fatalf("BatchReceipts full: %v", err)
|
||||
}
|
||||
|
||||
for i := 0; i < 128; i++ {
|
||||
if hdrs[i].Hash() != chain.headers[i].Hash() {
|
||||
t.Fatalf("batch header %d mismatch", i)
|
||||
}
|
||||
if !bytes.Equal(
|
||||
mustEncode(chain.bodies[i]),
|
||||
mustEncode(bods[i]),
|
||||
) {
|
||||
t.Fatalf("batch body %d mismatch", i)
|
||||
}
|
||||
if !bytes.Equal(
|
||||
mustEncode(chain.receipts[i]),
|
||||
mustEncode(recs[i]),
|
||||
) {
|
||||
t.Fatalf("batch receipts %d mismatch", i)
|
||||
}
|
||||
}
|
||||
|
||||
const start, cnt = 50, 10
|
||||
winHdrs, err := era.GetHeaders(start, cnt)
|
||||
if err != nil {
|
||||
t.Fatalf("BatchHeaders window: %v", err)
|
||||
}
|
||||
for j := 0; j < cnt; j++ {
|
||||
idx := start + j
|
||||
if winHdrs[j].Hash() != chain.headers[idx].Hash() {
|
||||
t.Fatalf("window header %d mismatch", idx)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < 128; i++ {
|
||||
bn := uint64(i)
|
||||
gotBlock, err := era.GetBlockByNumber(bn)
|
||||
|
|
|
|||
Loading…
Reference in a new issue