Skip to content

Commit

Permalink
Add cdk rpc (#163)
Browse files Browse the repository at this point in the history
* update

* update

* update

* Add cdk rpc api (#164)

* update

* update

* update

* update

* update
  • Loading branch information
zjg555543 authored Apr 3, 2024
1 parent 3e852e1 commit c11446e
Show file tree
Hide file tree
Showing 14 changed files with 439 additions and 211 deletions.
110 changes: 67 additions & 43 deletions dataavailability/dataavailability.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ import (

"github.com/0xPolygonHermez/zkevm-node/etherman/types"
"github.com/0xPolygonHermez/zkevm-node/log"
"github.com/0xPolygonHermez/zkevm-node/state"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)

const unexpectedHashTemplate = "missmatch on transaction data for batch num %d. Expected hash %s, actual hash: %s"
const unexpectedHashTemplate = "mismatch on transaction data for batch num %d. Expected hash %s, actual hash: %s"

// DataAvailability implements an abstract data availability integration
type DataAvailability struct {
Expand Down Expand Up @@ -60,57 +59,82 @@ func (d *DataAvailability) PostSequence(ctx context.Context, sequences []types.S
// 1. From local DB
// 2. From Sequencer
// 3. From DA backend
func (d *DataAvailability) GetBatchL2Data(batchNum uint64, expectedTransactionsHash common.Hash) ([]byte, error) {
found := true
transactionsData, err := d.state.GetBatchL2DataByNumber(d.ctx, batchNum, nil)
if err != nil {
if err == state.ErrNotFound {
found = false
func (d *DataAvailability) GetBatchL2Data(batchNums []uint64, batchHashes []common.Hash, dataAvailabilityMessage []byte) ([][]byte, error) {
if len(batchNums) != len(batchHashes) {
return nil, fmt.Errorf("invalid L2 batch data retrieval arguments, %d != %d", len(batchNums), len(batchHashes))
}

data, err := d.localData(batchNums, batchHashes)
if err == nil {
return data, nil
}

if !d.isTrustedSequencer {
log.Infof("Try to get data from trusted sequencer for batches %v", batchNums)
data, err = d.trustedSequencerData(batchNums, batchHashes)
if err != nil {
log.Warnf("trusted sequencer failed to return data for batches %v: %s", batchNums, err.Error())
} else {
return nil, fmt.Errorf("failed to get batch data from state for batch num %d: %w", batchNum, err)
return data, nil
}
}
actualTransactionsHash := crypto.Keccak256Hash(transactionsData)
if !found || expectedTransactionsHash != actualTransactionsHash {
if found {
log.Warnf(unexpectedHashTemplate, batchNum, expectedTransactionsHash, actualTransactionsHash)
}
log.Infof("Try to get data from DA backend for batches %v", batchNums)
return d.backend.GetSequence(d.ctx, batchHashes, dataAvailabilityMessage)
}

if !d.isTrustedSequencer {
log.Info("trying to get data from trusted sequencer")
data, err := d.getDataFromTrustedSequencer(batchNum, expectedTransactionsHash)
if err != nil {
log.Warn("failed to get data from trusted sequencer: %w", err)
} else {
return data, nil
}
// localData retrieves batches from local database and returns an error unless all are found
func (d *DataAvailability) localData(numbers []uint64, hashes []common.Hash) ([][]byte, error) {
data, err := d.state.GetBatchL2DataByNumbers(d.ctx, numbers, nil)
if err != nil {
return nil, err
}
var batches [][]byte
for i := 0; i < len(numbers); i++ {
batchNumber := numbers[i]
expectedHash := hashes[i]
batchData, ok := data[batchNumber]
if !ok {
return nil, fmt.Errorf("missing batch %v", batchNumber)
}

log.Info("trying to get data from the data availability backend")
data, err := d.backend.GetBatchL2Data(batchNum, expectedTransactionsHash)
if err != nil {
log.Error("failed to get data from the data availability backend: %w", err)
if d.isTrustedSequencer {
return nil, fmt.Errorf("data not found on the local DB nor on any data committee member")
} else {
return nil, fmt.Errorf("data not found on the local DB, nor from the trusted sequencer nor on any data committee member")
}
actualHash := crypto.Keccak256Hash(batchData)
if actualHash != expectedHash {
err = fmt.Errorf(unexpectedHashTemplate, batchNumber, expectedHash, actualHash)
log.Warnf("wrong local data for hash: %s", err.Error())
return nil, err
} else {
batches = append(batches, batchData)
}
return data, nil
}
return transactionsData, nil
return batches, nil
}

func (d *DataAvailability) getDataFromTrustedSequencer(batchNum uint64, expectedTransactionsHash common.Hash) ([]byte, error) {
b, err := d.zkEVMClient.BatchByNumber(d.ctx, new(big.Int).SetUint64(batchNum))
// trustedSequencerData retrieved batch data from the trusted sequencer and returns an error unless all are found
func (d *DataAvailability) trustedSequencerData(batchNums []uint64, expectedHashes []common.Hash) ([][]byte, error) {
if len(batchNums) != len(expectedHashes) {
return nil, fmt.Errorf("invalid arguments, len of batch numbers does not equal length of expected hashes: %d != %d",
len(batchNums), len(expectedHashes))
}
var nums []*big.Int
for _, n := range batchNums {
nums = append(nums, new(big.Int).SetUint64(n))
}
batchData, err := d.zkEVMClient.BatchesByNumbers(d.ctx, nums)
if err != nil {
return nil, fmt.Errorf("failed to get batch num %d from trusted sequencer: %w", batchNum, err)
return nil, err
}
actualTransactionsHash := crypto.Keccak256Hash(b.BatchL2Data)
if expectedTransactionsHash != actualTransactionsHash {
return nil, fmt.Errorf(
unexpectedHashTemplate, batchNum, expectedTransactionsHash, actualTransactionsHash,
)
if len(batchData) != len(batchNums) {
return nil, fmt.Errorf("missing batch data, expected %d, got %d", len(batchNums), len(batchData))
}
var result [][]byte
for i := 0; i < len(batchNums); i++ {
number := batchNums[i]
batch := batchData[i]
expectedTransactionsHash := expectedHashes[i]
actualTransactionsHash := crypto.Keccak256Hash(batch.BatchL2Data)
if expectedTransactionsHash != actualTransactionsHash {
return nil, fmt.Errorf(unexpectedHashTemplate, number, expectedTransactionsHash, actualTransactionsHash)
}
result = append(result, batch.BatchL2Data)
}
return b.BatchL2Data, nil
return result, nil
}
20 changes: 17 additions & 3 deletions dataavailability/datacommittee/datacommittee.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"golang.org/x/net/context"
)

const unexpectedHashTemplate = "missmatch on transaction data for batch num %d. Expected hash %s, actual hash: %s"
const unexpectedHashTemplate = "missmatch on transaction data. Expected hash %s, actual hash: %s"

// DataCommitteeMember represents a member of the Data Committee
type DataCommitteeMember struct {
Expand Down Expand Up @@ -87,8 +87,22 @@ func (d *DataCommitteeBackend) Init() error {
return nil
}

// GetSequence gets backend data one hash at a time. This should be optimized on the DAC side to get them all at once.
func (d *DataCommitteeBackend) GetSequence(ctx context.Context, hashes []common.Hash, dataAvailabilityMessage []byte) ([][]byte, error) {
// TODO: optimize this on the DAC side by implementing a multi batch retrieve api
var batchData [][]byte
for _, h := range hashes {
data, err := d.GetBatchL2Data(h)
if err != nil {
return nil, err
}
batchData = append(batchData, data)
}
return batchData, nil
}

// GetBatchL2Data returns the data from the DAC. It checks that it matches with the expected hash
func (d *DataCommitteeBackend) GetBatchL2Data(batchNum uint64, hash common.Hash) ([]byte, error) {
func (d *DataCommitteeBackend) GetBatchL2Data(hash common.Hash) ([]byte, error) {
intialMember := d.selectedCommitteeMember
found := false
for !found && intialMember != -1 {
Expand All @@ -110,7 +124,7 @@ func (d *DataCommitteeBackend) GetBatchL2Data(batchNum uint64, hash common.Hash)
actualTransactionsHash := crypto.Keccak256Hash(data)
if actualTransactionsHash != hash {
unexpectedHash := fmt.Errorf(
unexpectedHashTemplate, batchNum, hash, actualTransactionsHash,
unexpectedHashTemplate, hash, actualTransactionsHash,
)
log.Warnf(
"error getting data from DAC node %s at %s: %s",
Expand Down
41 changes: 28 additions & 13 deletions dataavailability/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,49 @@ import (
"github.com/jackc/pgx/v4"
)

// DABackender is an interface for components that store and retrieve batch data
type DABackender interface {
SequenceRetriever
SequenceSender
// Init initializes the DABackend
Init() error
}

// SequenceSender is used to send provided sequence of batches
type SequenceSender interface {
// PostSequence sends the sequence data to the data availability backend, and returns the dataAvailabilityMessage
// as expected by the contract
PostSequence(ctx context.Context, batchesData [][]byte) ([]byte, error)
}

// SequenceRetriever is used to retrieve batch data
type SequenceRetriever interface {
// GetSequence retrieves the sequence data from the data availability backend
GetSequence(ctx context.Context, batchHashes []common.Hash, dataAvailabilityMessage []byte) ([][]byte, error)
}

// === Internal interfaces ===

type stateInterface interface {
GetBatchL2DataByNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) ([]byte, error)
GetBatchL2DataByNumbers(ctx context.Context, batchNumbers []uint64, dbTx pgx.Tx) (map[uint64][]byte, error)
GetBatchByNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.Batch, error)
}

// BatchDataProvider is used to retrieve batch data
type BatchDataProvider interface {
// GetBatchL2Data retrieve the data of a batch from the DA backend. The returned data must be the pre-image of the hash
GetBatchL2Data(batchNum uint64, hash common.Hash) ([]byte, error)
GetBatchL2Data(batchNum []uint64, batchHashes []common.Hash, dataAvailabilityMessage []byte) ([][]byte, error)
}

// SequenceSender is used to send provided sequence of batches
type SequenceSender interface {
// PostSequence sends the sequence data to the data availability backend, and returns the dataAvailabilityMessage
// as expected by the contract
PostSequence(ctx context.Context, batchesData [][]byte) ([]byte, error)
}

// DABackender is the interface needed to implement in order to
// integrate a DA service
type DABackender interface {
// DataManager is an interface for components that send and retrieve batch data
type DataManager interface {
BatchDataProvider
SequenceSender
// Init initializes the DABackend
Init() error
}

// ZKEVMClientTrustedBatchesGetter contains the methods required to interact with zkEVM-RPC
type ZKEVMClientTrustedBatchesGetter interface {
BatchByNumber(ctx context.Context, number *big.Int) (*types.Batch, error)
BatchesByNumbers(ctx context.Context, numbers []*big.Int) ([]*types.BatchData, error)
}
Loading

0 comments on commit c11446e

Please sign in to comment.