Skip to content
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ jobs:
make contracts
# epoch length is fixed in lorentz HF.
# epoch is 200, 400, 500, 1000
# must wait for 1000 block (1.5sec * 1000)
sleep 1500
# must wait for 1000 block (1.5sec * 400)
sleep 600
make relayer
make test
- name: integration-test
Expand Down
21 changes: 11 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
![CI](https://github.com/datachainlab/ibc-parlia-relay/workflows/CI/badge.svg?branch=main)

## Supported Versions
- [yui-relayer v0.5.3](https://github.com/hyperledger-labs/yui-relayer/releases/tag/v0.5.3)
- [ethereum-ibc-relay-chain v0.3.4](https://github.com/datachainlab/ethereum-ibc-relay-chain/releases/tag/v0.3.4)
- [yui-relayer v0.5.11](https://github.com/hyperledger-labs/yui-relayer/releases/tag/v0.5.11)
- [ethereum-ibc-relay-chain v0.3.16](https://github.com/datachainlab/ethereum-ibc-relay-chain/releases/tag/v0.3.6)

## Setup Relayer

Expand All @@ -29,14 +29,6 @@ func main() {
}
```

## Change blocks per epoch

* You can change blocks per epoch by build arguments.
* This is only for local net.
```
go build -tags dev -ldflags="-X github.com/datachainlab/ibc-parlia-relay/module/constant.blocksPerEpoch=20" -o testrly .
```

## Development

Generate proto buf with protobuf definition of [parlia-elc](https://github.com/datachainlab/parlia-elc).
Expand All @@ -48,3 +40,12 @@ cd ibc-parlia-relay
make proto-import
make proto-gen
```

## About ForkSpec

1. Set HF height as soon as possibile
As soon as the HF height is determined, please modify the timestamp in the ForkSpec to the height as soon as possible.
HF height is calculated from timestamp, but the further away from the HF, the longer it takes to calculate.

2. Limitation of the CreateClient
When the latest HF height is not set it is impossible to create client if the latest finalize header is after latest HF timestamp
3 changes: 0 additions & 3 deletions module/constant/production.go

This file was deleted.

9 changes: 0 additions & 9 deletions module/facade.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,6 @@ import (
)

// Facade for tool modules

func GetPreviousEpoch(v uint64) uint64 {
return getPreviousEpoch(v)
}

func GetCurrentEpoch(v uint64) uint64 {
return getCurrentEpoch(v)
}

func QueryFinalizedHeader(ctx context.Context, fn getHeaderFn, height uint64, limitHeight uint64) ([]*ETHHeader, error) {
return queryFinalizedHeader(ctx, fn, height, limitHeight)
}
Expand Down
215 changes: 212 additions & 3 deletions module/fork_spec.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
package module

import (
"context"
"fmt"
"github.com/hyperledger-labs/yui-relayer/log"
"math"
"os"
"strconv"
)

type Network string

const (
Expand All @@ -8,29 +17,229 @@ const (
Mainnet Network = "mainnet"
)

var localLorentzHF isForkSpec_HeightOrTimestamp = &ForkSpec_Height{Height: 1}

func init() {
localLorentzHFTimestamp := os.Getenv("LOCAL_LORENTZ_HF_TIMESTAMP")
if localLorentzHFTimestamp != "" {
result, err := strconv.Atoi(localLorentzHFTimestamp)
if err != nil {
panic(err)
}
localLorentzHF = &ForkSpec_Timestamp{Timestamp: uint64(result)}
}
}

func GetForkParameters(network Network) []*ForkSpec {
switch network {
case Localnet:
return []*ForkSpec{
// Pascal HF
{
HeightOrTimestamp: &ForkSpec_Timestamp{Timestamp: 0},
// Must Set Milli timestamp
HeightOrTimestamp: &ForkSpec_Height{Height: 0},
AdditionalHeaderItemCount: 1,
EpochLength: 200,
MaxTurnLength: 9,
},
// Lorentz HF
{
// Must Set Milli timestamp
HeightOrTimestamp: localLorentzHF,
AdditionalHeaderItemCount: 1,
EpochLength: 500,
MaxTurnLength: 64,
},
}
case Testnet:
return []*ForkSpec{
{
HeightOrTimestamp: &ForkSpec_Timestamp{Timestamp: 0},
// https://forum.bnbchain.org/t/bnb-chain-upgrades-testnet/934
HeightOrTimestamp: &ForkSpec_Height{Height: 48576786},
AdditionalHeaderItemCount: 1,
EpochLength: 200,
MaxTurnLength: 9,
},
{
HeightOrTimestamp: &ForkSpec_Timestamp{Timestamp: math.MaxUint64},
AdditionalHeaderItemCount: 1,
EpochLength: 500,
MaxTurnLength: 64,
},
}
case Mainnet:
return []*ForkSpec{
{
HeightOrTimestamp: &ForkSpec_Timestamp{Timestamp: 0},
// https://bscscan.com/block/47618307
// https://github.com/bnb-chain/bsc/releases/tag/v1.5.7
HeightOrTimestamp: &ForkSpec_Height{Height: 47618307},
AdditionalHeaderItemCount: 1,
EpochLength: 200,
MaxTurnLength: 9,
},
{
HeightOrTimestamp: &ForkSpec_Timestamp{Timestamp: math.MaxUint64},
AdditionalHeaderItemCount: 1,
EpochLength: 500,
MaxTurnLength: 64,
},
}
}
return nil
}

type BoundaryEpochs struct {
PreviousForkSpec ForkSpec
CurrentForkSpec ForkSpec
BoundaryHeight uint64
PrevLast uint64
CurrentFirst uint64
Intermediates []uint64
}

type BoundaryHeight struct {
Height uint64
CurrentForkSpec ForkSpec
}

func (b BoundaryHeight) GetBoundaryEpochs(prevForkSpec ForkSpec) (*BoundaryEpochs, error) {
boundaryHeight := b.Height
prevLast := boundaryHeight - (boundaryHeight % prevForkSpec.EpochLength)
index := uint64(0)
currentFirst := uint64(0)
for {
candidate := boundaryHeight + index
if candidate%b.CurrentForkSpec.EpochLength == 0 {
currentFirst = candidate
break
}
index++
}
intermediates := make([]uint64, 0)
// starts 0, 200, 400...epoch_length
if prevLast == 0 {
const defaultEpochLength = 200
for mid := prevLast + defaultEpochLength; mid < prevForkSpec.EpochLength; mid += defaultEpochLength {
intermediates = append(intermediates, mid)
}
}
for mid := prevLast + prevForkSpec.EpochLength; mid < currentFirst; mid += prevForkSpec.EpochLength {
intermediates = append(intermediates, mid)
}

return &BoundaryEpochs{
PreviousForkSpec: prevForkSpec,
CurrentForkSpec: b.CurrentForkSpec,
BoundaryHeight: boundaryHeight,
PrevLast: prevLast,
CurrentFirst: currentFirst,
Intermediates: intermediates,
}, nil
}

func (be BoundaryEpochs) CurrentEpochBlockNumber(number uint64) uint64 {
if number >= be.CurrentFirst {
return number - (number % be.CurrentForkSpec.EpochLength)
}

if len(be.Intermediates) > 0 {
for i := len(be.Intermediates) - 1; i >= 0; i-- {
if number >= be.Intermediates[i] {
return be.Intermediates[i]
}
}
}
return number - (number % be.PreviousForkSpec.EpochLength)
}

func (be BoundaryEpochs) PreviousEpochBlockNumber(currentEpochBlockNumber uint64) uint64 {
if currentEpochBlockNumber == 0 {
return 0
}
if currentEpochBlockNumber <= be.PrevLast {
return currentEpochBlockNumber - be.PreviousForkSpec.EpochLength
}

for i, mid := range be.Intermediates {
if currentEpochBlockNumber == mid {
if i == 0 {
return be.PrevLast
}
return be.Intermediates[i-1]
}
}

if currentEpochBlockNumber == be.CurrentFirst {
if len(be.Intermediates) == 0 {
return be.PrevLast
}
return be.Intermediates[len(be.Intermediates)-1]
}

return currentEpochBlockNumber - be.CurrentForkSpec.EpochLength
}

func FindTargetForkSpec(forkSpecs []*ForkSpec, height uint64, timestamp uint64) (*ForkSpec, *ForkSpec, error) {
reversed := make([]*ForkSpec, len(forkSpecs))
for i, spec := range forkSpecs {
reversed[len(forkSpecs)-i-1] = spec
}

getPrev := func(current *ForkSpec, i int) *ForkSpec {
if i == len(reversed)-1 {
return current
}
return reversed[i+1]
}

for i, spec := range reversed {
if x, ok := spec.GetHeightOrTimestamp().(*ForkSpec_Height); ok {
if x.Height <= height {
return spec, getPrev(spec, i), nil
}
} else {
if spec.GetTimestamp() <= timestamp {
return spec, getPrev(spec, i), nil
}
}
}
return nil, nil, fmt.Errorf("no fork spec found height=%d, timestmp=%d", height, timestamp)
}

var boundaryHeightCache = make(map[uint64]uint64)

func GetBoundaryHeight(headerFn getHeaderFn, currentHeight uint64, currentForkSpec ForkSpec) (*BoundaryHeight, error) {
logger := log.GetLogger()
boundaryHeight := uint64(0)
if condition, ok := currentForkSpec.GetHeightOrTimestamp().(*ForkSpec_Height); ok {
boundaryHeight = condition.Height
} else {
ts := currentForkSpec.GetTimestamp()
if v, ok := boundaryHeightCache[ts]; ok {
boundaryHeight = v
} else {
logger.Debug("seek fork height", "currentHeight", currentHeight, "ts", ts)
for i := int64(currentHeight); i >= 0; i-- {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional optimization: Consider estimating heights near hardfork points using ForkSpec's block generation time to reduce header query overhead. This isn't a required fix but could improve performance for post-hardfork requests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be supported after the next version release. There is a grace period until the next Maxwell HF.

h, err := headerFn(context.Background(), uint64(i))
if err != nil {
return nil, err
}
if MilliTimestamp(h) == ts {
boundaryHeight = h.Number.Uint64()
logger.Debug("seek fork height found", "currentHeight", currentHeight, "ts", ts, "boundaryHeight", boundaryHeight)
boundaryHeightCache[ts] = boundaryHeight
break
} else if MilliTimestamp(h) < ts {
boundaryHeight = h.Number.Uint64() + 1
logger.Debug("seek fork height found", "currentHeight", currentHeight, "ts", ts, "boundaryHeight", boundaryHeight)
boundaryHeightCache[ts] = boundaryHeight
break
}
}
}
}
return &BoundaryHeight{
Height: boundaryHeight,
CurrentForkSpec: currentForkSpec,
}, nil
}
Loading