Skip to content

Commit

Permalink
Merge pull request #29 from datachainlab/remove_non_neighboring_epoch…
Browse files Browse the repository at this point in the history
…_verify

Remove non neighboring epoch verification
  • Loading branch information
yoshidan authored Aug 14, 2024
2 parents d1408e8 + ec8d4c6 commit 87d980d
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 445 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,20 @@ on:
push:
branches:
- main
- develop
pull_request:
types:
- opened
- reopened
- synchronize
- ready_for_review

jobs:
unit-test:
name: unit-test
timeout-minutes: 10
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- name: checkout
uses: actions/checkout@v4
Expand All @@ -32,6 +39,7 @@ jobs:
name: e2e
timeout-minutes: 45
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- name: checkout
uses: actions/checkout@v4
Expand Down
19 changes: 0 additions & 19 deletions module/header_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,25 +70,6 @@ func queryFinalizedHeader(fn getHeaderFn, height uint64, limitHeight uint64) ([]
return nil, nil
}

func queryFinalizedHeaderAfterCheckpoint(fn getHeaderFn, height uint64, limitHeight uint64, checkpoint uint64) ([]*ETHHeader, error) {
var ethHeaders []*ETHHeader
for i := height; i < checkpoint; i++ {
_, h, _, err := queryETHHeader(fn, i)
if err != nil {
return nil, err
}
ethHeaders = append(ethHeaders, h)
}
afterCheckpoint, err := queryFinalizedHeader(fn, checkpoint, limitHeight)
if err != nil {
return nil, err
}
if afterCheckpoint == nil {
return nil, nil
}
return append(ethHeaders, afterCheckpoint...), nil
}

func queryETHHeader(fn getHeaderFn, height uint64) (*types.Header, *ETHHeader, *VoteAttestation, error) {
block, err := fn(context.TODO(), height)
if err != nil {
Expand Down
30 changes: 30 additions & 0 deletions module/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,33 @@ func verifyAccount(target *types.Header, accountProof []byte, path common.Addres
}
return &account, nil
}

type getAccountProof = func(height int64) ([]byte, common.Hash, error)

func withProofAndValidators(headerFn getHeaderFn, accountProofFn getAccountProof, height uint64, ethHeaders []*ETHHeader) (core.Header, error) {

// get RLP-encoded account proof
rlpAccountProof, _, err := accountProofFn(int64(height))
if err != nil {
return nil, fmt.Errorf("failed to get account proof : height = %d, %+v", height, err)
}

header := &Header{
AccountProof: rlpAccountProof,
Headers: ethHeaders,
}

// Get validator set for verify headers
previousEpoch := getPreviousEpoch(height)
header.PreviousValidators, err = queryValidatorSet(headerFn, previousEpoch)
if err != nil {
return nil, fmt.Errorf("ValidatorSet was not found in previous epoch : number = %d : %+v", previousEpoch, err)
}
currentEpoch := getCurrentEpoch(height)
header.CurrentValidators, err = queryValidatorSet(headerFn, currentEpoch)
if err != nil {
return nil, fmt.Errorf("ValidatorSet was not found in current epoch : number= %d : %+v", currentEpoch, err)
}

return header, nil
}
38 changes: 1 addition & 37 deletions module/prover.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,24 +124,12 @@ func (pr *Prover) SetupHeadersForUpdateByLatestHeight(clientStateLatestHeight ex
}
return pr.withProofAndValidators(height, ethHeaders)
}
queryVerifiableNonNeighboringEpochHeader := func(height uint64, limitHeight uint64, checkpoint uint64) (core.Header, error) {
ethHeaders, err := queryFinalizedHeaderAfterCheckpoint(pr.chain.Header, height, limitHeight, checkpoint)
if err != nil {
return nil, err
}
// No finalized header found
if ethHeaders == nil {
return nil, nil
}
return pr.withProofAndValidators(height, ethHeaders)
}
latestHeight, err := pr.chain.LatestHeight()
if err != nil {
return nil, err
}
return setupHeadersForUpdate(
queryVerifiableNeighboringEpochHeader,
queryVerifiableNonNeighboringEpochHeader,
pr.chain.Header,
clientStateLatestHeight,
latestFinalizedHeader,
Expand Down Expand Up @@ -210,31 +198,7 @@ func (pr *Prover) CheckRefreshRequired(counterparty core.ChainInfoICS02Querier)
}

func (pr *Prover) withProofAndValidators(height uint64, ethHeaders []*ETHHeader) (core.Header, error) {

// get RLP-encoded account proof
rlpAccountProof, _, err := pr.getAccountProof(int64(height))
if err != nil {
return nil, fmt.Errorf("failed to get account proof : height = %d, %+v", height, err)
}

header := &Header{
AccountProof: rlpAccountProof,
Headers: ethHeaders,
}

// Get validator set for verify headers
previousEpoch := getPreviousEpoch(height)
header.PreviousValidators, err = queryValidatorSet(pr.chain.Header, previousEpoch)
if err != nil {
return nil, fmt.Errorf("ValidatorSet was not found in previous epoch : number = %d : %+v", previousEpoch, err)
}
currentEpoch := getCurrentEpoch(height)
header.CurrentValidators, err = queryValidatorSet(pr.chain.Header, currentEpoch)
if err != nil {
return nil, fmt.Errorf("ValidatorSet was not found in current epoch : number= %d : %+v", currentEpoch, err)
}

return header, nil
return withProofAndValidators(pr.chain.Header, pr.getAccountProof, height, ethHeaders)
}

func (pr *Prover) buildInitialState(dstHeader core.Header) (exported.ClientState, exported.ConsensusState, error) {
Expand Down
95 changes: 10 additions & 85 deletions module/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@ import (
)

type queryVerifiableNeighboringEpochHeaderFn = func(uint64, uint64) (core.Header, error)
type queryVerifiableNonNeighboringEpochHeaderFn = func(uint64, uint64, uint64) (core.Header, error)

func setupHeadersForUpdate(
queryVerifiableNeighboringEpochHeader queryVerifiableNeighboringEpochHeaderFn,
queryVerifiableNonNeighboringEpochHeader queryVerifiableNonNeighboringEpochHeaderFn,
getHeader getHeaderFn,
clientStateLatestHeight exported.Height,
latestFinalizedHeader *Header,
Expand All @@ -34,51 +32,21 @@ func setupHeadersForUpdate(
}

trustedEpochHeight := toEpoch(savedLatestHeight)
lastUnsavedEpoch := toEpoch(latestFinalizedHeight)
// Check if last epoch can be directly verified by trusted height to reduce request for LCP
if lastUnsavedEpoch > trustedEpochHeight+constant.BlocksPerEpoch {
lastVerifiableEpoch, _ := setupNonNeighboringEpochHeader(getHeader, queryVerifiableNonNeighboringEpochHeader, lastUnsavedEpoch, trustedEpochHeight, latestHeight)
if lastVerifiableEpoch != nil {
log.GetLogger().Debug("Use direct non-neighboring epoch verification", "trusted", trustedEpochHeight, "latestFinalized", latestFinalizedHeight)
if lastUnsavedEpoch == latestFinalizedHeight {
return withTrustedHeight(append(targetHeaders, lastVerifiableEpoch), clientStateLatestHeight), nil
} else {
return withTrustedHeight(append(targetHeaders, lastVerifiableEpoch, latestFinalizedHeader), clientStateLatestHeight), nil
}
}
}

// Append insufficient epoch blocks
for epochHeight := firstUnsavedEpoch; epochHeight <= latestFinalizedHeight; epochHeight += constant.BlocksPerEpoch {
if epochHeight == trustedEpochHeight+constant.BlocksPerEpoch {
verifiableEpoch, err := setupNeighboringEpochHeader(getHeader, queryVerifiableNeighboringEpochHeader, epochHeight, trustedEpochHeight, latestHeight)
if err != nil {
return nil, err
}
if verifiableEpoch == nil {
// not found -> non-neighboring epoch
continue
}
targetHeaders = append(targetHeaders, verifiableEpoch)
} else {
verifiableEpoch, err := setupNonNeighboringEpochHeader(getHeader, queryVerifiableNonNeighboringEpochHeader, epochHeight, trustedEpochHeight, latestHeight)
if err != nil {
return nil, err
}
if verifiableEpoch == nil {
// not found -> next non-neighboring epoch
continue
}
targetHeaders = append(targetHeaders, verifiableEpoch)
for epochHeight := firstUnsavedEpoch; epochHeight < latestFinalizedHeight; epochHeight += constant.BlocksPerEpoch {
verifiableEpoch, err := setupNeighboringEpochHeader(getHeader, queryVerifiableNeighboringEpochHeader, epochHeight, trustedEpochHeight, latestHeight)
if err != nil {
return nil, err
}
if verifiableEpoch == nil {
err = fmt.Errorf("insufficient vote attestation: epochHeight=%d, trustedEpochHeight=%d", epochHeight, trustedEpochHeight)
log.GetLogger().Error("[FastFinalityError]", err)
return withTrustedHeight(targetHeaders, clientStateLatestHeight), err
}
targetHeaders = append(targetHeaders, verifiableEpoch)
trustedEpochHeight = epochHeight
}

if isEpoch(latestFinalizedHeight) ||
// ex) trusted = 200, latest 401, not append latest because it can not be verified
trustedEpochHeight < toEpoch(latestFinalizedHeight) {
return withTrustedHeight(targetHeaders, clientStateLatestHeight), nil
}
return withTrustedHeight(append(targetHeaders, latestFinalizedHeader), clientStateLatestHeight), nil
}

Expand Down Expand Up @@ -111,49 +79,6 @@ func setupNeighboringEpochHeader(
}
}

func setupNonNeighboringEpochHeader(
getHeader getHeaderFn,
queryVerifiableHeader queryVerifiableNonNeighboringEpochHeaderFn,
epochHeight uint64,
trustedEpochHeight uint64,
latestHeight exported.Height,
) (core.Header, error) {
currentValidatorSet, err := queryValidatorSet(getHeader, epochHeight)
if err != nil {
return nil, fmt.Errorf("setupNonNeighboringEpochHeader: failed to get curent validator set: trustedEpochHeight=%d : %+v", trustedEpochHeight, err)
}
trustedValidatorSet, err := queryValidatorSet(getHeader, trustedEpochHeight)
if err != nil {
return nil, fmt.Errorf("setupNonNeighboringEpochHeader: failed to get trusted validator set: trustedEpochHeight=%d : %+v", trustedEpochHeight, err)
}
if !trustedValidatorSet.Contains(currentValidatorSet) {
// It is recommended to recreate the Client.
return nil, fmt.Errorf("setupNonNeighboringEpochHeader: invalid untrusted validator set: epochHeight=%d, trustedEpochHeight=%d", epochHeight, trustedEpochHeight)
}

// ex) trusted(prevSaved = 200), epochHeight = 600 must be finalized from 611 to min(810,latest)
nextCheckpoint := currentValidatorSet.Checkpoint(epochHeight + constant.BlocksPerEpoch)
limit := minUint64(nextCheckpoint-1, latestHeight.GetRevisionHeight())

// Headers after checkpoint are required to verify
previousValidatorSet, err := queryValidatorSet(getHeader, epochHeight-constant.BlocksPerEpoch)
if err != nil {
return nil, fmt.Errorf("setupNonNeighboringEpochHeader: failed to get previous validator set: epochHeight=%d : %+v", epochHeight-constant.BlocksPerEpoch, err)
}
checkpoint := previousValidatorSet.Checkpoint(epochHeight)
if checkpoint+2 > limit {
// Must wait more header
return nil, nil
}
h, err := queryVerifiableHeader(epochHeight, limit, checkpoint)
if h != nil {
// Override CurrentValidator to trusted validator set
// https://github.com/datachainlab/parlia-elc/blob/ce3611c3278508c97d41afed1be38b889602a1b1/light-client/src/header/mod.rs#L132
h.(*Header).CurrentValidators = trustedValidatorSet
}
return h, err
}

func withTrustedHeight(targetHeaders []core.Header, clientStateLatestHeight exported.Height) []core.Header {
logger := log.GetLogger()
for i, h := range targetHeaders {
Expand Down
Loading

0 comments on commit 87d980d

Please sign in to comment.