Skip to content

Commit

Permalink
Added ETH1 Block SSZ Hashing to CL (#6549)
Browse files Browse the repository at this point in the history
more of SSZ again.
  • Loading branch information
Giulio2002 committed Jan 11, 2023
1 parent a4751aa commit 4bee05d
Show file tree
Hide file tree
Showing 20 changed files with 397 additions and 643 deletions.
8 changes: 5 additions & 3 deletions cl/cltypes/beacon_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type BeaconBody struct {
// A summary of the current state of the beacon chain
SyncAggregate *SyncAggregate
// Data related to crosslink records and executing operations on the Ethereum 2.0 chain
ExecutionPayload *ExecutionPayload
ExecutionPayload *Eth1Block
// The version of the beacon chain
version clparams.StateVersion
}
Expand Down Expand Up @@ -252,9 +252,11 @@ func (b *SignedBeaconBlock) EncodeForStorage() ([]byte, error) {
Version: uint8(b.Version()),
Eth2BlockRoot: blockRoot,
}

if b.Version() >= clparams.BellatrixVersion {
storageObject.Eth1Number = b.Block.Body.ExecutionPayload.BlockNumber
storageObject.Eth1BlockHash = b.Block.Body.ExecutionPayload.BlockHash
eth1Block := b.Block.Body.ExecutionPayload
storageObject.Eth1Number = eth1Block.NumberU64()
storageObject.Eth1BlockHash = eth1Block.Header.BlockHashCL
}
var buffer bytes.Buffer
if err := cbor.Marshal(&buffer, storageObject); err != nil {
Expand Down
202 changes: 202 additions & 0 deletions cl/cltypes/eth1_block.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package cltypes

import (
"fmt"

"github.com/ledgerwatch/erigon/cl/clparams"
"github.com/ledgerwatch/erigon/cl/cltypes/ssz_utils"
"github.com/ledgerwatch/erigon/cl/merkle_tree"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/core/types"
)

// ETH1Block represents a block structure CL-side.
type Eth1Block struct {
Header *types.Header
// Transactions can be kept in bytes.
Body *types.RawBody
}

func (b *Eth1Block) NumberU64() uint64 {
return b.Header.Number.Uint64()
}

func (b *Eth1Block) Withdrawals() types.Withdrawals {
return types.Withdrawals(b.Body.Withdrawals)
}

func (b *Eth1Block) EncodingSizeSSZ() (size int) {
size = 508

if b.Header == nil {
return
}
// Field (10) 'ExtraData'
size += len(b.Header.Extra)
// Field (13) 'Transactions'
for _, tx := range b.Body.Transactions {
size += 4
size += len(tx)
}

if b.Header.WithdrawalsHash != nil {
size += len(b.Body.Withdrawals) * 44
}

return
}

func (b *Eth1Block) DecodeSSZ(buf []byte, version clparams.StateVersion) error {
if len(buf) < b.EncodingSizeSSZ() {
return ssz_utils.ErrLowBufferSize
}
b.Header = new(types.Header)

pos := b.Header.DecodeHeaderMetadataForSSZ(buf)
// Compute block SSZ offsets.
extraDataOffset := ssz_utils.BaseExtraDataSSZOffsetBlock
transactionsOffset := ssz_utils.DecodeOffset(buf[pos : pos+4])
pos += 4
var withdrawalOffset *uint32
if version >= clparams.CapellaVersion {
withdrawalOffset = new(uint32)
*withdrawalOffset = ssz_utils.DecodeOffset(buf[pos : pos+4])
}
// Compute extra data.
b.Header.Extra = common.CopyBytes(buf[extraDataOffset:transactionsOffset])
if len(b.Header.Extra) > 32 {
return fmt.Errorf("Decode(SSZ): Extra data field length should be less or equal to 32, got %d", len(b.Header.Extra))
}
// Compute transactions
var transactionsBuffer []byte
if withdrawalOffset == nil {
transactionsBuffer = buf[transactionsOffset:]
} else {
transactionsBuffer = buf[transactionsOffset:*withdrawalOffset]
}

length := uint32(0)
transactionsPosition := 4
var txOffset uint32
if len(transactionsBuffer) == 0 {
length = 0
} else {
if len(transactionsBuffer) < 4 {
return ssz_utils.ErrLowBufferSize
}
txOffset = ssz_utils.DecodeOffset(transactionsBuffer)
length = txOffset / 4
// Retrieve tx length
if txOffset%4 != 0 {
return ssz_utils.ErrBadDynamicLength
}
}

b.Body = new(types.RawBody)
b.Body.Transactions = make([][]byte, length)
txIdx := 0
// Loop through each transaction
for length > 0 {
var txEndOffset uint32
if length == 1 {
txEndOffset = uint32(len(transactionsBuffer))
} else {
txEndOffset = ssz_utils.DecodeOffset(transactionsBuffer[transactionsPosition:])
}
transactionsPosition += 4
if txOffset > txEndOffset {
return ssz_utils.ErrBadOffset
}
b.Body.Transactions[txIdx] = transactionsBuffer[txOffset:txEndOffset]
// Decode RLP and put it in the tx list.
// Update parameters for next iteration
txOffset = txEndOffset
txIdx++
length--
}
// Cache transaction ssz root.
var err error
b.Header.TxHashSSZ, err = merkle_tree.TransactionsListRoot(b.Body.Transactions)
if err != nil {
return err
}
// Cache transaction rlp hash.
b.Header.TxHash = types.DeriveSha(types.BinaryTransactions(b.Body.Transactions))
// If withdrawals are enabled, process them.
if withdrawalOffset != nil {
withdrawalsCount := (uint32(len(buf)) - *withdrawalOffset) / 44
if withdrawalsCount > 16 {
return fmt.Errorf("Decode(SSZ): Withdrawals field length should be less or equal to 16, got %d", withdrawalsCount)
}
b.Body.Withdrawals = make([]*types.Withdrawal, withdrawalsCount)
for i := range b.Body.Withdrawals {
b.Body.Withdrawals[i].DecodeSSZ(buf[(*withdrawalOffset)+uint32(i)*44:])
}
// Cache withdrawal root.
b.Header.WithdrawalsHash = new(common.Hash)
withdrawalRoot, err := b.Withdrawals().HashSSZ(16)
if err != nil {
return err
}
*b.Header.WithdrawalsHash = withdrawalRoot
}
return nil
}

func (b *Eth1Block) EncodeSSZ(dst []byte) ([]byte, error) {
buf := dst
var err error
currentOffset := ssz_utils.BaseExtraDataSSZOffsetBlock

if b.Header.WithdrawalsHash != nil {
currentOffset += 4
}
buf, err = b.Header.EncodeHeaderMetadataForSSZ(buf, currentOffset)
if err != nil {
return nil, err
}
currentOffset += len(b.Header.Extra)
// use raw body for encoded txs and offsets.
body := b.Body
// Write transaction offset
buf = append(buf, ssz_utils.OffsetSSZ(uint32(currentOffset))...)

for _, tx := range body.Transactions {
currentOffset += len(tx) + 4
}
// Write withdrawals offset if exist
if b.Header.WithdrawalsHash != nil {
buf = append(buf, ssz_utils.OffsetSSZ(uint32(currentOffset))...)
}
// Sanity check for extra data then write it.
if len(b.Header.Extra) > 32 {
return nil, fmt.Errorf("Encode(SSZ): Extra data field length should be less or equal to 32, got %d", len(b.Header.Extra))
}
buf = append(buf, b.Header.Extra...)
// Write all tx offsets
txOffset := len(body.Transactions) * 4
for _, tx := range body.Transactions {
buf = append(buf, ssz_utils.OffsetSSZ(uint32(txOffset))...)
txOffset += len(tx)
}
// Write all transactions
for _, tx := range body.Transactions {
buf = append(buf, tx...)
}

if b.Header.WithdrawalsHash != nil {
// Append all withdrawals SSZ
for _, withdrawal := range body.Withdrawals {
buf = append(buf, withdrawal.EncodeSSZ()...)
}
}
return buf, nil
}

func (b *Eth1Block) HashSSZ() ([32]byte, error) {
return b.Header.HashSSZ()
}

func (b *Eth1Block) Hash() common.Hash {
return b.Header.Hash()
}
53 changes: 0 additions & 53 deletions cl/cltypes/execution_payload.go

This file was deleted.

9 changes: 9 additions & 0 deletions cl/cltypes/ssz_utils/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ssz_utils

import "errors"

var (
ErrLowBufferSize = errors.New("ssz(DecodeSSZ): bad encoding size")
ErrBadDynamicLength = errors.New("ssz(DecodeSSZ): bad dynamic length")
ErrBadOffset = errors.New("ssz(DecodeSSZ): invalid offset")
)
17 changes: 17 additions & 0 deletions cl/cltypes/ssz_utils/ssz.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import (
ssz "github.com/ferranbt/fastssz"
)

var (
BaseExtraDataSSZOffsetHeader = 536
BaseExtraDataSSZOffsetBlock = 508
)

type ObjectSSZ interface {
ssz.Marshaler
ssz.Unmarshaler
Expand All @@ -31,6 +36,18 @@ func MarshalUint64SSZ(buf []byte, x uint64) {
binary.LittleEndian.PutUint64(buf, x)
}

func Uint64SSZ(x uint64) []byte {
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, x)
return b
}

func OffsetSSZ(x uint32) []byte {
b := make([]byte, 4)
binary.LittleEndian.PutUint32(b, x)
return b
}

// EncodeOffset marshals a little endian uint32 to buf
func EncodeOffset(buf []byte, offset uint32) {
binary.LittleEndian.PutUint32(buf, offset)
Expand Down
20 changes: 1 addition & 19 deletions cl/cltypes/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,6 @@ type AttesterSlashing struct {
Attestation_2 *IndexedAttestation
}

// we will send this to Erigon once validation is done.
type ExecutionPayload struct {
ParentHash [32]byte `ssz-size:"32"`
FeeRecipient [20]byte `ssz-size:"20"`
StateRoot [32]byte `ssz-size:"32"`
ReceiptsRoot [32]byte `ssz-size:"32"`
LogsBloom []byte `ssz-size:"256"`
PrevRandao [32]byte `ssz-size:"32"`
BlockNumber uint64
GasLimit uint64
GasUsed uint64
Timestamp uint64
ExtraData []byte `ssz-max:"32"`
BaseFeePerGas []byte `ssz-size:"32"`
BlockHash [32]byte `ssz-size:"32"`
Transactions [][]byte `ssz-size:"?,?" ssz-max:"1048576,1073741824"`
}

/*
* Block body for Consensus Layer, we only care about its hash and execution payload.
*/
Expand All @@ -52,7 +34,7 @@ type BeaconBodyBellatrix struct {
Deposits []*Deposit `ssz-max:"16"`
VoluntaryExits []*SignedVoluntaryExit `ssz-max:"16"`
SyncAggregate *SyncAggregate
ExecutionPayload *ExecutionPayload
ExecutionPayload *Eth1Block
}

/*
Expand Down
Loading

0 comments on commit 4bee05d

Please sign in to comment.