mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 20:56:42 +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
|
type proofvar uint16
|
||||||
|
|
||||||
const (
|
type buffer struct {
|
||||||
ProofNone proofvar = iota
|
headers [][]byte
|
||||||
ProofHHA
|
bodies [][]byte
|
||||||
ProofRoots
|
receipts [][]byte
|
||||||
ProofCapella
|
proofs [][]byte
|
||||||
ProofDeneb
|
tds [][]byte
|
||||||
)
|
}
|
||||||
|
|
||||||
// Proof bundles variant + compressed bytes.
|
type offsets struct {
|
||||||
type Proof struct {
|
headers []uint64
|
||||||
Variant proofvar
|
bodys []uint64
|
||||||
Data []byte
|
receipts []uint64
|
||||||
|
proofoffsets []uint64
|
||||||
|
tdoff []uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type Builder struct {
|
type Builder struct {
|
||||||
|
|
@ -76,22 +78,12 @@ type Builder struct {
|
||||||
buf *bytes.Buffer
|
buf *bytes.Buffer
|
||||||
sn *snappy.Writer
|
sn *snappy.Writer
|
||||||
|
|
||||||
headers [][]byte
|
buff buffer
|
||||||
bodies [][]byte
|
off offsets
|
||||||
receipts [][]byte
|
|
||||||
proofs [][]byte
|
|
||||||
tds [][]byte
|
|
||||||
tdsint []*big.Int
|
|
||||||
hashes []common.Hash
|
|
||||||
|
|
||||||
headeroffsets []uint64
|
|
||||||
bodyoffsets []uint64
|
|
||||||
receiptoffsets []uint64
|
|
||||||
proofoffsets []uint64
|
|
||||||
tdoff []uint64
|
|
||||||
|
|
||||||
prooftype proofvar
|
prooftype proofvar
|
||||||
|
tdsint []*big.Int
|
||||||
|
hashes []common.Hash
|
||||||
startNum *uint64
|
startNum *uint64
|
||||||
writtenBytes 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.
|
// 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 {
|
func (b *Builder) Add(header types.Header, body types.Body, receipts types.Receipts, td *big.Int, proof Proof) error {
|
||||||
var pv proofvar = ProofNone
|
pv := proofVariantOf(proof) // variant code (or proofNone)
|
||||||
var pData []byte
|
var ep []byte // encoded proof payload
|
||||||
|
|
||||||
if proof != nil {
|
if proof != nil {
|
||||||
pv, pData = proof.Variant, proof.Data
|
var buf bytes.Buffer
|
||||||
if pv == ProofNone || len(pData) == 0 {
|
if err := proof.EncodeRLP(&buf); err != nil {
|
||||||
return fmt.Errorf("invalid proof: variant=%d len=%d", pv, len(pData))
|
return fmt.Errorf("encode proof: %w", err)
|
||||||
}
|
}
|
||||||
|
ep = buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(b.headers) == 0 {
|
if len(b.buff.headers) == 0 {
|
||||||
b.prooftype = pv
|
b.prooftype = pv
|
||||||
} else if pv != b.prooftype {
|
} 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)
|
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)
|
return fmt.Errorf("encode receipts: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ep []byte
|
return b.AddRLP(
|
||||||
if pv != ProofNone {
|
eh, eb, er, ep,
|
||||||
ep, err = rlp.EncodeToBytes([]interface{}{uint16(pv), proof.Data})
|
header.Number.Uint64(),
|
||||||
if err != nil {
|
header.Hash(), td,
|
||||||
return fmt.Errorf("encode proof: %w", err)
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
// 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 {
|
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)
|
return fmt.Errorf("exceeds MaxEraESize %d", MaxEraESize)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.headers = append(b.headers, headerRLP)
|
b.buff.headers = append(b.buff.headers, headerRLP)
|
||||||
b.bodies = append(b.bodies, bodyRLP)
|
b.buff.bodies = append(b.buff.bodies, bodyRLP)
|
||||||
b.receipts = append(b.receipts, receipts)
|
b.buff.receipts = append(b.buff.receipts, receipts)
|
||||||
b.tds = append(b.tds, uint256LE(td))
|
b.buff.tds = append(b.buff.tds, uint256LE(td))
|
||||||
b.tdsint = append(b.tdsint, new(big.Int).Set(td))
|
b.tdsint = append(b.tdsint, new(big.Int).Set(td))
|
||||||
b.hashes = append(b.hashes, blockHash)
|
b.hashes = append(b.hashes, blockHash)
|
||||||
if proof != nil {
|
if proof != nil {
|
||||||
b.proofs = append(b.proofs, proof)
|
b.buff.proofs = append(b.buff.proofs, proof)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Write Era2 version before writing any blocks.
|
//Write Era2 version before writing any blocks.
|
||||||
|
|
@ -181,40 +172,64 @@ func (b *Builder) Finalize() (common.Hash, error) {
|
||||||
if b.startNum == nil {
|
if b.startNum == nil {
|
||||||
return common.Hash{}, errors.New("no blocks added, cannot finalize")
|
return common.Hash{}, errors.New("no blocks added, cannot finalize")
|
||||||
}
|
}
|
||||||
var err error
|
for _, data := range b.buff.headers {
|
||||||
if err := b.flushKind(TypeCompressedHeader, b.headers, true, &b.headeroffsets); err != nil {
|
off, err := b.addEntry(TypeCompressedHeader, data, true)
|
||||||
return common.Hash{}, err
|
if err != nil {
|
||||||
|
return common.Hash{}, fmt.Errorf("headers: %w", err)
|
||||||
}
|
}
|
||||||
if err := b.flushKind(TypeCompressedBody, b.bodies, true, &b.bodyoffsets); err != nil {
|
b.off.headers = append(b.off.headers, off)
|
||||||
return common.Hash{}, err
|
|
||||||
}
|
|
||||||
if err := b.flushKind(TypeCompressedReceipts, b.receipts, true, &b.receiptoffsets); err != nil {
|
|
||||||
return common.Hash{}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(b.tds) > 0 {
|
for _, data := range b.buff.bodies {
|
||||||
if err := b.flushKind(TypeTotalDifficulty, b.tds, false, &b.tdoff); err != nil {
|
off, err := b.addEntry(TypeCompressedBody, data, true)
|
||||||
return common.Hash{}, err
|
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 b.prooftype != ProofNone {
|
||||||
if err := b.flushKind(TypeProof, b.proofs, true, &b.proofoffsets); err != nil {
|
for _, data := range b.buff.proofs {
|
||||||
return common.Hash{}, err
|
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
|
var accRoot common.Hash
|
||||||
if len(b.hashes) > 0 {
|
if len(b.hashes) > 0 {
|
||||||
|
var err error
|
||||||
accRoot, err = ComputeAccumulator(b.hashes, b.tdsint)
|
accRoot, err = ComputeAccumulator(b.hashes, b.tdsint)
|
||||||
if err != nil {
|
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 {
|
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 {
|
} else {
|
||||||
b.writtenBytes += uint64(n)
|
b.writtenBytes += uint64(n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return accRoot, b.writeIndex()
|
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
|
// write index takes all the offset table and writes it to the file
|
||||||
// the index table contains all offsets to specific block entries
|
// the index table contains all offsets to specific block entries
|
||||||
func (b *Builder) writeIndex() error {
|
func (b *Builder) writeIndex() error {
|
||||||
count := uint64(len(b.headeroffsets))
|
count := uint64(len(b.off.headers))
|
||||||
compcount := uint64(3)
|
componentCount := uint64(3)
|
||||||
if len(b.tds) > 0 {
|
if len(b.buff.tds) > 0 {
|
||||||
compcount++
|
componentCount++
|
||||||
}
|
}
|
||||||
if len(b.proofs) > 0 {
|
if len(b.buff.proofs) > 0 {
|
||||||
compcount++
|
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
|
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(idx, *b.startNum)
|
binary.LittleEndian.PutUint64(index, *b.startNum)
|
||||||
base := int64(b.writtenBytes)
|
base := int64(b.writtenBytes)
|
||||||
pos := 8
|
|
||||||
rel := func(abs uint64) uint64 { return uint64(int64(abs) - base) }
|
rel := func(abs uint64) uint64 { return uint64(int64(abs) - base) }
|
||||||
for i := uint64(0); i < count; i++ {
|
for i := uint64(0); i < count; i++ {
|
||||||
binary.LittleEndian.PutUint64(idx[pos:], rel(b.headeroffsets[i]))
|
basePosition := 8 + i*componentCount*8
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
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
|
return err
|
||||||
} else {
|
} else {
|
||||||
b.writtenBytes += uint64(n)
|
b.writtenBytes += uint64(n)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,22 @@ type metadata struct {
|
||||||
length int64 // length of the file in bytes
|
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
|
type BlockProofHistoricalHashesAccumulator [15]common.Hash // 15 * 32 = 480 bytes
|
||||||
|
|
||||||
// BlockProofHistoricalRoots – Altair / Bellatrix historical_roots branch.
|
// BlockProofHistoricalRoots – Altair / Bellatrix historical_roots branch.
|
||||||
|
|
@ -47,6 +63,55 @@ type BlockProofHistoricalSummariesDeneb struct {
|
||||||
Slot uint64 // 8 => 840 bytes
|
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 {
|
type ReadAtSeekCloser interface {
|
||||||
io.ReaderAt
|
io.ReaderAt
|
||||||
io.Seeker
|
io.Seeker
|
||||||
|
|
@ -57,7 +122,6 @@ type Era struct {
|
||||||
f ReadAtSeekCloser
|
f ReadAtSeekCloser
|
||||||
s *e2store.Reader
|
s *e2store.Reader
|
||||||
m metadata // metadata for the Era file
|
m metadata // metadata for the Era file
|
||||||
headeroff, bodyoff, receiptsoff, tdoff, proofsoff []uint64 // offsets for each entry type
|
|
||||||
indstart int64
|
indstart int64
|
||||||
rootheader uint64 // offset of the root header in the file if present
|
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
|
// retrieves the block if present within the era file
|
||||||
func (e *Era) GetBlockByNumber(blockNum uint64) (*types.Block, error) {
|
func (e *Era) GetBlockByNumber(blockNum uint64) (*types.Block, error) {
|
||||||
h, err := e.getHeader(blockNum)
|
h, err := e.GetHeader(blockNum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
b, err := e.getBody(blockNum)
|
b, err := e.GetBody(blockNum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// retrieves header from era file through the cached offset table
|
||||||
func (e *Era) getHeader(blockNum uint64) (*types.Header, error) {
|
func (e *Era) GetHeader(num uint64) (*types.Header, error) {
|
||||||
if blockNum < e.m.start || blockNum >= e.m.start+e.m.count {
|
off, err := e.headerOff(num)
|
||||||
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]))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
var h types.Header
|
||||||
return &h, rlp.Decode(r, &h)
|
return &h, rlp.Decode(r, &h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// retrieves body from era file through cached offset table
|
// retrieves body from era file through cached offset table
|
||||||
func (e *Era) getBody(blockNum uint64) (*types.Body, error) {
|
func (e *Era) GetBody(num uint64) (*types.Body, error) {
|
||||||
if blockNum < e.m.start || blockNum >= e.m.start+e.m.count {
|
off, err := e.bodyOff(num)
|
||||||
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]))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
var b types.Body
|
||||||
return &b, rlp.Decode(r, &b)
|
return &b, rlp.Decode(r, &b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// retrieves td from era file through cached offset table
|
// retrieves td from era file through cached offset table
|
||||||
func (e *Era) getTD(blockNum uint64) (*big.Int, error) {
|
func (e *Era) getTD(blockNum uint64) (*big.Int, error) {
|
||||||
if blockNum < e.m.start || blockNum >= e.m.start+e.m.count {
|
off, err := e.tdOff(blockNum)
|
||||||
return nil, fmt.Errorf("block number %d out of range [%d, %d)",
|
if err != nil {
|
||||||
blockNum, e.m.start, e.m.start+e.m.count)
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(e.tdoff) == 0 {
|
r, _, err := e.s.ReaderAt(TypeTotalDifficulty, int64(off))
|
||||||
return nil, fmt.Errorf("total-difficulty section not present")
|
|
||||||
}
|
|
||||||
|
|
||||||
r, _, err := e.s.ReaderAt(TypeTotalDifficulty, int64(e.tdoff[blockNum-e.m.start]))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// retrieves the raw body frame in bytes of a specific block
|
||||||
func (e *Era) GetRawBodyFrameByNumber(blockNum uint64) ([]byte, error) {
|
func (e *Era) GetRawBodyFrameByNumber(blockNum uint64) ([]byte, error) {
|
||||||
if blockNum < e.m.start || blockNum >= e.m.start+e.m.count {
|
off, err := e.bodyOff(blockNum)
|
||||||
return nil, fmt.Errorf("block number %d out of range [%d, %d)", blockNum, e.m.start, e.m.start+e.m.count)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// retrieves the raw receipts frame in bytes of a specific block
|
||||||
func (e *Era) GetRawReceiptsFrameByNumber(blockNum uint64) ([]byte, error) {
|
func (e *Era) GetRawReceiptsFrameByNumber(blockNum uint64) ([]byte, error) {
|
||||||
if blockNum < e.m.start || blockNum >= e.m.start+e.m.count {
|
off, err := e.rcptOff(blockNum)
|
||||||
return nil, fmt.Errorf("block number %d out of range [%d, %d)", blockNum, e.m.start, e.m.start+e.m.count)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// retrieves the raw proof frame in bytes of a specific block proof
|
||||||
func (e *Era) GetRawProofFrameByNumber(blockNum uint64) ([]byte, error) {
|
func (e *Era) GetRawProofFrameByNumber(blockNum uint64) ([]byte, error) {
|
||||||
if len(e.proofsoff) == 0 {
|
off, err := e.proofOff(blockNum)
|
||||||
return nil, fmt.Errorf("proofs section not present")
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
if blockNum < e.m.start || blockNum >= e.m.start+e.m.count {
|
r, _, err := e.s.ReaderAt(TypeProof, int64(off))
|
||||||
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]))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -232,33 +296,6 @@ func (e *Era) loadIndex() error {
|
||||||
return err
|
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
|
var off int64 = 0 // start at byte-0 of file
|
||||||
|
|
||||||
for off < e.indstart { // never enter the Block-Index TLV
|
for off < e.indstart { // never enter the Block-Index TLV
|
||||||
|
|
@ -275,80 +312,116 @@ func (e *Era) loadIndex() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// retrieves a range of block from any start block to n number of blocks
|
// Getter methods to calculate offset of a specific component in the file.
|
||||||
// the components retrieved can be customized depending on the properties wanted
|
func (e *Era) headerOff(num uint64) (uint64, error) { return e.indexOffset(num, compHeader) }
|
||||||
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) {
|
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 {
|
if count == 0 {
|
||||||
err = fmt.Errorf("count must be > 0")
|
return nil, fmt.Errorf("count must be > 0")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if first < e.m.start || first+count > e.m.start+e.m.count {
|
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 nil, 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out := make([]*types.Header, count)
|
||||||
for i := uint64(0); i < count; i++ {
|
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 {
|
// GetBodies returns RLP-decoded bodies.
|
||||||
r, _, er := e.s.ReaderAt(TypeCompressedHeader, int64(e.headeroff[id]))
|
func (e *Era) GetBodies(first, count uint64) ([]*types.Body, error) {
|
||||||
if er != nil {
|
if count == 0 {
|
||||||
err = er
|
return nil, fmt.Errorf("count must be > 0")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if er = rlp.Decode(snappy.NewReader(r), &hdrs[i]); er != nil {
|
if first < e.m.start || first+count > e.m.start+e.m.count {
|
||||||
err = er
|
return nil, fmt.Errorf("range [%d,%d) out of bounds", first, first+count)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, _, err := e.s.ReaderAt(TypeCompressedBody, int64(off))
|
||||||
r, _, er := e.s.ReaderAt(TypeCompressedBody, int64(e.bodyoff[id]))
|
if err != nil {
|
||||||
if er != nil {
|
return nil, err
|
||||||
err = er
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if er = rlp.Decode(snappy.NewReader(r), &bods[i]); er != nil {
|
var b types.Body
|
||||||
err = er
|
if err := rlp.Decode(snappy.NewReader(r), &b); err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
out[i] = &b
|
||||||
}
|
}
|
||||||
if wantRec {
|
return out, nil
|
||||||
r, _, er := e.s.ReaderAt(TypeCompressedReceipts, int64(e.receiptsoff[id]))
|
|
||||||
if er != nil {
|
|
||||||
err = er
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if er = rlp.Decode(snappy.NewReader(r), &recs[i]); er != nil {
|
|
||||||
err = er
|
// GetReceipts returns RLP-decoded receipts.
|
||||||
return
|
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 {
|
out := make([]types.Receipts, count)
|
||||||
err = fmt.Errorf("proofs section not present")
|
for i := uint64(0); i < count; i++ {
|
||||||
return
|
n := first + i
|
||||||
|
off, err := e.rcptOff(n)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
r, _, er := e.s.ReaderAt(TypeProof, int64(e.proofsoff[id]))
|
r, _, err := e.s.ReaderAt(TypeCompressedReceipts, int64(off))
|
||||||
if er != nil {
|
if err != nil {
|
||||||
err = er
|
return nil, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
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()
|
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++ {
|
for i := 0; i < 128; i++ {
|
||||||
bn := uint64(i)
|
bn := uint64(i)
|
||||||
gotBlock, err := era.GetBlockByNumber(bn)
|
gotBlock, err := era.GetBlockByNumber(bn)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue