Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Create and Update ELC commands #20

Merged
merged 3 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
66 changes: 64 additions & 2 deletions relay/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import (
)

const (
flagSrc = "src"
flagHeight = "height"
flagSrc = "src"
flagHeight = "height"
flagELCClientID = "elc_client_id"
)

func LCPCmd(ctx *config.Context) *cobra.Command {
Expand All @@ -21,6 +22,8 @@ func LCPCmd(ctx *config.Context) *cobra.Command {
}

cmd.AddCommand(
createELCCmd(ctx),
updateELCCmd(ctx),
updateEnclaveKeyCmd(ctx),
activateClientCmd(ctx),
restoreELCStateCmd(ctx),
Expand Down Expand Up @@ -90,6 +93,57 @@ func activateClientCmd(ctx *config.Context) *cobra.Command {
return srcFlag(cmd)
}

func createELCCmd(ctx *config.Context) *cobra.Command {
cmd := &cobra.Command{
Use: "create-elc [path]",
Short: "Create ELC client",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
c, src, dst, err := ctx.Config.ChainsFromPath(args[0])
if err != nil {
return err
}
var target *core.ProvableChain
if viper.GetBool(flagSrc) {
target = c[src]
} else {
target = c[dst]
}
prover := target.Prover.(*Prover)
return prover.doCreateELC(viper.GetUint64(flagHeight))
},
}
return heightFlag(srcFlag(cmd))
}

func updateELCCmd(ctx *config.Context) *cobra.Command {
cmd := &cobra.Command{
Use: "update-elc [path]",
Short: "Update ELC client",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
c, src, dst, err := ctx.Config.ChainsFromPath(args[0])
if err != nil {
return err
}
var (
target *core.ProvableChain
counterparty *core.ProvableChain
)
if viper.GetBool(flagSrc) {
target = c[src]
counterparty = c[dst]
} else {
target = c[dst]
counterparty = c[src]
}
prover := target.Prover.(*Prover)
return prover.doUpdateELC(viper.GetString(flagELCClientID), counterparty)
},
}
return elcClientIDFlag(srcFlag(cmd))
}

func restoreELCStateCmd(ctx *config.Context) *cobra.Command {
cmd := &cobra.Command{
Use: "restore-elc-state [path]",
Expand Down Expand Up @@ -162,3 +216,11 @@ func heightFlag(cmd *cobra.Command) *cobra.Command {
}
return cmd
}

func elcClientIDFlag(cmd *cobra.Command) *cobra.Command {
cmd.Flags().StringP("elc_client_id", "", "", "a client ID of the ELC client")
if err := viper.BindPFlag(flagELCClientID, cmd.Flags().Lookup(flagELCClientID)); err != nil {
panic(err)
}
return cmd
}
100 changes: 94 additions & 6 deletions relay/lcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types"
"github.com/cosmos/ibc-go/v8/modules/core/exported"
ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported"
mapset "github.com/deckarep/golang-set/v2"
"github.com/hyperledger-labs/yui-relayer/core"
Expand Down Expand Up @@ -260,11 +261,11 @@ func (pr *Prover) validateAdvisoryIDs(ids []string) bool {
return targetSet.Difference(allowedSet).Cardinality() == 0
}

func (pr *Prover) syncUpstreamHeader(includeState bool) ([]*elc.MsgUpdateClientResponse, error) {
func (pr *Prover) updateELC(elcClientID string, includeState bool) ([]*elc.MsgUpdateClientResponse, error) {

// 1. check if the latest height of the client is less than the given height

res, err := pr.lcpServiceClient.Client(context.TODO(), &elc.QueryClientRequest{ClientId: pr.config.ElcClientId})
res, err := pr.lcpServiceClient.Client(context.TODO(), &elc.QueryClientRequest{ClientId: elcClientID})
if err != nil {
return nil, err
}
Expand All @@ -281,11 +282,11 @@ func (pr *Prover) syncUpstreamHeader(includeState bool) ([]*elc.MsgUpdateClientR
return nil, nil
}

log.Printf("syncUpstreamHeader try to update the client in ELC: latest=%v got=%v", clientState.GetLatestHeight(), latestHeader.GetHeight())
log.Printf("try to setup headers: elc_client_id=%v current=%v latest=%v", elcClientID, clientState.GetLatestHeight(), latestHeader.GetHeight())

// 2. query the header from the upstream chain

headers, err := pr.originProver.SetupHeadersForUpdate(NewLCPQuerier(pr.lcpServiceClient, pr.config.ElcClientId), latestHeader)
headers, err := pr.originProver.SetupHeadersForUpdate(NewLCPQuerier(pr.lcpServiceClient, elcClientID), latestHeader)
if err != nil {
return nil, err
}
Expand All @@ -301,7 +302,7 @@ func (pr *Prover) syncUpstreamHeader(includeState bool) ([]*elc.MsgUpdateClientR
return nil, err
}
res, err := pr.lcpServiceClient.UpdateClient(context.TODO(), &elc.MsgUpdateClient{
ClientId: pr.config.ElcClientId,
ClientId: elcClientID,
Header: anyHeader,
IncludeState: includeState,
Signer: pr.activeEnclaveKey.EnclaveKeyAddress,
Expand Down Expand Up @@ -345,14 +346,101 @@ func (pr *Prover) registerEnclaveKey(verifier core.Chain, eki *enclave.EnclaveKe
return ids[0], nil
}

// height: 0 means the latest height
func (pr *Prover) doCreateELC(height uint64) error {
header, err := pr.originProver.GetLatestFinalizedHeader()
if err != nil {
return err
}
latestHeight := header.GetHeight()
if height == 0 {
height = latestHeight.GetRevisionHeight()
} else if height >= latestHeight.GetRevisionHeight() {
return fmt.Errorf("height %v is greater than the latest height %v", height, latestHeight.GetRevisionHeight())
}
h := clienttypes.NewHeight(latestHeight.GetRevisionNumber(), height)
log.Printf("try to create ELC client: height=%v", h)
res, err := pr.createELC(h)
if err != nil {
return err
}
log.Printf("created ELC client: %v", res.ClientId)
// ensure the message is valid
msg, err := lcptypes.EthABIDecodeHeaderedProxyMessage(res.Message)
if err != nil {
return err
}
m, err := msg.GetUpdateStateProxyMessage()
if err != nil {
return err
}
log.Printf("created state: post_height=%v post_state_id=0x%x timestamp=%v", m.PostHeight, m.PostStateID, m.Timestamp.String())
return err
}

func (pr *Prover) doUpdateELC(elcClientID string, counterparty core.FinalityAwareChain) error {
if err := pr.UpdateEKIfNeeded(context.TODO(), counterparty); err != nil {
return err
}
if elcClientID == "" {
elcClientID = pr.config.ElcClientId
}
log.Printf("try to update the ELC client: elc_client_id=%v", elcClientID)
updates, err := pr.updateELC(elcClientID, false)
if err != nil {
return err
}
if len(updates) == 0 {
log.Println("no update is needed")
return nil
}
for _, update := range updates {
commitment, err := lcptypes.EthABIDecodeHeaderedProxyMessage(update.Message)
if err != nil {
return err
}
usm, err := commitment.GetUpdateStateProxyMessage()
if err != nil {
return err
}
log.Printf("updated state: prev_height=%v prev_state_id=0x%x post_height=%v post_state_id=0x%x timestamp=%v", usm.PrevHeight, *usm.PrevStateID, usm.PostHeight, usm.PostStateID, usm.Timestamp.String())
}
return nil
}

func (pr *Prover) createELC(height exported.Height) (*elc.MsgCreateClientResponse, error) {
// NOTE: Query the LCP for available keys, but no need to register it into on-chain here
tmpEKI, err := pr.selectNewEnclaveKey(context.TODO())
if err != nil {
return nil, err
}
originClientState, originConsensusState, err := pr.originProver.CreateInitialLightClientState(height)
if err != nil {
return nil, err
}
anyOriginClientState, err := clienttypes.PackClientState(originClientState)
if err != nil {
return nil, err
}
anyOriginConsensusState, err := clienttypes.PackConsensusState(originConsensusState)
if err != nil {
return nil, err
}
return pr.lcpServiceClient.CreateClient(context.TODO(), &elc.MsgCreateClient{
ClientState: anyOriginClientState,
ConsensusState: anyOriginConsensusState,
Signer: tmpEKI.EnclaveKeyAddress,
})
}

func activateClient(pathEnd *core.PathEnd, src, dst *core.ProvableChain) error {
srcProver := src.Prover.(*Prover)
if err := srcProver.UpdateEKIfNeeded(context.TODO(), dst); err != nil {
return err
}

// 1. LCP synchronises with the latest header of the upstream chain
updates, err := srcProver.syncUpstreamHeader(true)
updates, err := srcProver.updateELC(srcProver.config.ElcClientId, true)
if err != nil {
return err
}
Expand Down
23 changes: 1 addition & 22 deletions relay/prover.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,28 +99,7 @@ func (pr *Prover) GetChainID() string {
// These states will be submitted to the counterparty chain as MsgCreateClient.
// If `height` is nil, the latest finalized height is selected automatically.
func (pr *Prover) CreateInitialLightClientState(height exported.Height) (exported.ClientState, exported.ConsensusState, error) {
// NOTE: Query the LCP for available keys, but no need to register it into on-chain here
tmpEKI, err := pr.selectNewEnclaveKey(context.TODO())
if err != nil {
return nil, nil, err
}
originClientState, originConsensusState, err := pr.originProver.CreateInitialLightClientState(height)
if err != nil {
return nil, nil, err
}
anyOriginClientState, err := clienttypes.PackClientState(originClientState)
if err != nil {
return nil, nil, err
}
anyOriginConsensusState, err := clienttypes.PackConsensusState(originConsensusState)
if err != nil {
return nil, nil, err
}
res, err := pr.lcpServiceClient.CreateClient(context.TODO(), &elc.MsgCreateClient{
ClientState: anyOriginClientState,
ConsensusState: anyOriginConsensusState,
Signer: tmpEKI.EnclaveKeyAddress,
})
res, err := pr.createELC(height)
if err != nil {
return nil, nil, err
}
Expand Down
3 changes: 2 additions & 1 deletion scripts/run_e2e_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ ${LCP_BIN} --log_level=info service start --enclave=${ENCLAVE_PATH} --address=12
LCP_PID=$!
make -C tests/e2e/cases/tm2tm restore

make -C tests/e2e/cases/tm2tm test
make -C tests/e2e/cases/tm2tm test-relay
make -C tests/e2e/cases/tm2tm test-elc-update
make -C tests/e2e/cases/tm2tm network-down
kill $LCP_PID
10 changes: 7 additions & 3 deletions tests/e2e/cases/tm2tm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ setup:
handshake:
RLY_BIN=$(RLY_BIN) ./scripts/handshake

.PHONY: test
test:
RLY_BIN=$(RLY_BIN) ./scripts/test-tx
.PHONY: test-relay
test-relay:
RLY_BIN=$(RLY_BIN) ./scripts/test-relay

.PHONY: restore
restore:
RLY_BIN=$(RLY_BIN) ./scripts/restore

.PHONY: test-elc-update
test-elc-update:
RLY_BIN=$(RLY_BIN) ./scripts/test-elc-update
22 changes: 22 additions & 0 deletions tests/e2e/cases/tm2tm/scripts/test-elc-update
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash

set -eu

RLY="${RLY_BIN} --debug"

output=$(${RLY} lcp create-elc ibc01 --src 2>&1)
if [[ ! $output =~ "created ELC client: 07-tendermint-2" ]]; then
echo "unexpected output: $output"
exit 1
fi
output=$(${RLY} lcp create-elc ibc01 --src=false 2>&1)
if [[ ! $output =~ "created ELC client: 07-tendermint-3" ]]; then
echo "unexpected output: $output"
exit 1
fi

echo "sleeping for 5 seconds"
sleep 5

${RLY} lcp update-elc ibc01 --src --elc_client_id=07-tendermint-2
${RLY} lcp update-elc ibc01 --src=false --elc_client_id=07-tendermint-3
Loading