From 095110b8564a8c0d4ee0d4c60bb03d1d28625f81 Mon Sep 17 00:00:00 2001 From: Nina Barbakadze Date: Fri, 29 Nov 2024 13:37:54 +0100 Subject: [PATCH] feat: add light client initialisation --- go.mod | 5 +- go.sum | 13 +- ibc/lightclients/groth16/client_state.go | 8 +- ibc/lightclients/groth16/update.go | 1 + simapp/txclient.go | 639 ++++++++++++++++++++ solidity-ibc-eureka | 2 +- testing/demo/pkg/initialise_light_client.go | 187 ++++++ 7 files changed, 839 insertions(+), 16 deletions(-) create mode 100644 simapp/txclient.go create mode 100644 testing/demo/pkg/initialise_light_client.go diff --git a/go.mod b/go.mod index 69a566f..b55e2e5 100644 --- a/go.mod +++ b/go.mod @@ -279,4 +279,7 @@ require ( sigs.k8s.io/yaml v1.4.0 // indirect ) -replace github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 +replace ( + github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 + github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 +) diff --git a/go.sum b/go.sum index 2dc5fc6..c497a19 100644 --- a/go.sum +++ b/go.sum @@ -570,12 +570,6 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -850,8 +844,6 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= @@ -1118,6 +1110,8 @@ github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtD github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -1591,10 +1585,8 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1746,6 +1738,7 @@ google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200324203455-a04cca1dde73/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= diff --git a/ibc/lightclients/groth16/client_state.go b/ibc/lightclients/groth16/client_state.go index a19c988..01b5a22 100644 --- a/ibc/lightclients/groth16/client_state.go +++ b/ibc/lightclients/groth16/client_state.go @@ -33,7 +33,7 @@ func NewClientState( ) *ClientState { return &ClientState{ LatestHeight: latestHeight, - StateTransitionVerifierKey: stateTransitionVerifierKey, + // StateTransitionVerifierKey: stateTransitionVerifierKey, CodeCommitment: codeCommitment, GenesisStateRoot: genesisStateRoot, } @@ -63,9 +63,9 @@ func (cs ClientState) status( // Validate performs a basic validation of the client state fields. func (cs ClientState) Validate() error { - if cs.StateTransitionVerifierKey == nil { - return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "state transition verifier key is nil") - } + // if cs.StateTransitionVerifierKey == nil { + // return sdkerrors.Wrap(clienttypes.ErrInvalidClient, "state transition verifier key is nil") + // } return nil } diff --git a/ibc/lightclients/groth16/update.go b/ibc/lightclients/groth16/update.go index 07f300c..79b759c 100644 --- a/ibc/lightclients/groth16/update.go +++ b/ibc/lightclients/groth16/update.go @@ -13,6 +13,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" "github.com/cosmos/ibc-go/v9/modules/core/exported" + ) // VerifyClientMessage checks if the clientMessage is of type Header diff --git a/simapp/txclient.go b/simapp/txclient.go new file mode 100644 index 0000000..3173091 --- /dev/null +++ b/simapp/txclient.go @@ -0,0 +1,639 @@ +package simapp + +// import ( +// "bytes" +// "context" +// "errors" +// "fmt" +// "math" +// "strconv" +// "strings" +// "sync" +// "time" + +// "github.com/celestiaorg/go-square/blob" +// "github.com/cosmos/cosmos-sdk/client" +// nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" +// cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" +// "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" +// codectypes "github.com/cosmos/cosmos-sdk/codec/types" +// "github.com/cosmos/cosmos-sdk/crypto/keyring" +// sdktypes "github.com/cosmos/cosmos-sdk/types" +// sdktx "github.com/cosmos/cosmos-sdk/types/tx" +// paramtypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" +// abci "github.com/tendermint/tendermint/abci/types" +// "google.golang.org/grpc" + +// "github.com/celestiaorg/celestia-app/v2/app" +// "github.com/celestiaorg/celestia-app/v2/app/encoding" +// // apperrors "github.com/celestiaorg/celestia-app/v2/app/errors" +// // "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" +// "github.com/celestiaorg/celestia-app/v2/x/blob/types" +// "github.com/celestiaorg/celestia-app/v2/x/minfee" +// ) + +// const ( +// DefaultPollTime = 3 * time.Second +// DefaultGasMultiplier float64 = 1.1 +// ) + +// type Option func(client *TxClient) + +// type Account struct { +// name string +// address types.AccAddress +// pubKey cryptotypes.PubKey +// accountNumber uint64 + +// // the signers local view of the sequence number +// sequence uint64 +// } + +// // Signer is struct for building and signing Celestia transactions +// // It supports multiple accounts wrapping a Keyring. +// // NOTE: All transactions may only have a single signer +// // Signer is not thread-safe. +// type Signer struct { +// keys keyring.Keyring +// enc client.TxConfig +// chainID string +// // FIXME: the signer is currently incapable of detecting an appversion +// // change and could produce incorrect PFBs if it the network is at an +// // appVersion that the signer does not support +// appVersion uint64 + +// // set of accounts that the signer can manage. Should match the keys on the keyring +// accounts map[string]*Account +// addressToAccountMap map[string]string +// } + +// // WithGasMultiplier is a functional option allows to configure the gas multiplier. +// func WithGasMultiplier(multiplier float64) Option { +// return func(c *TxClient) { +// c.gasMultiplier = multiplier +// } +// } + +// // WithDefaultGasPrice sets the gas price. +// func WithDefaultGasPrice(price float64) Option { +// return func(c *TxClient) { +// c.defaultGasPrice = price +// } +// } + +// func WithPollTime(time time.Duration) Option { +// return func(c *TxClient) { +// c.pollTime = time +// } +// } + +// func WithDefaultAddress(address sdktypes.AccAddress) Option { +// return func(c *TxClient) { +// record, err := c.signer.keys.KeyByAddress(address) +// if err != nil { +// panic(err) +// } +// c.defaultAccount = record.Name +// c.defaultAddress = address +// } +// } + +// func WithDefaultAccount(name string) Option { +// return func(c *TxClient) { +// rec, err := c.signer.keys.Key(name) +// if err != nil { +// panic(err) +// } +// addr, err := rec.GetAddress() +// if err != nil { +// panic(err) +// } +// c.defaultAccount = name +// c.defaultAddress = addr +// } +// } + +// // TxClient is an abstraction for building, signing, and broadcasting Celestia transactions +// // It supports multiple accounts. If none is specified, it will +// // try use the default account. +// // TxClient is thread-safe. +// type TxClient struct { +// mtx sync.Mutex +// signer *Signer +// registry codectypes.InterfaceRegistry +// grpc *grpc.ClientConn +// // how often to poll the network for confirmation of a transaction +// pollTime time.Duration +// // gasMultiplier is used to increase gas limit as it is sometimes underestimated +// gasMultiplier float64 +// // defaultGasPrice is the price used if no price is provided +// defaultGasPrice float64 +// defaultAccount string +// defaultAddress sdktypes.AccAddress +// } + +// // NewTxClient returns a new signer using the provided keyring +// func NewTxClient( +// signer *Signer, +// conn *grpc.ClientConn, +// registry codectypes.InterfaceRegistry, +// options ...Option, +// ) (*TxClient, error) { +// records, err := signer.keys.List() +// if err != nil { +// return nil, fmt.Errorf("retrieving keys: %w", err) +// } + +// if len(records) == 0 { +// return nil, errors.New("signer must have at least one key") +// } + +// addr, err := records[0].GetAddress() +// if err != nil { +// return nil, err +// } + +// txClient := &TxClient{ +// signer: signer, +// registry: registry, +// grpc: conn, +// pollTime: DefaultPollTime, +// gasMultiplier: DefaultGasMultiplier, +// defaultGasPrice: 0, +// defaultAccount: records[0].Name, +// defaultAddress: addr, +// } + +// for _, opt := range options { +// opt(txClient) +// } + +// return txClient, nil +// } + +// // SetupTxClient uses the underlying grpc connection to populate the chainID, accountNumber and sequence number of all +// // the accounts in the keyring. +// func SetupTxClient( +// ctx context.Context, +// keys keyring.Keyring, +// conn *grpc.ClientConn, +// encCfg encoding.Config, +// options ...Option, +// ) (*TxClient, error) { +// resp, err := tmservice.NewServiceClient(conn).GetLatestBlock( +// ctx, +// &tmservice.GetLatestBlockRequest{}, +// ) +// if err != nil { +// return nil, err +// } + +// chainID := resp.SdkBlock.Header.ChainID +// appVersion := resp.SdkBlock.Header.Version.App + +// records, err := keys.List() +// if err != nil { +// return nil, err +// } + +// accounts := make([]*Account, 0, len(records)) +// for _, record := range records { +// addr, err := record.GetAddress() +// if err != nil { +// return nil, err +// } +// accNum, seqNum, err := QueryAccount(ctx, conn, encCfg.InterfaceRegistry, addr) +// if err != nil { +// // skip over the accounts that don't exist in state +// continue +// } + +// accounts = append(accounts, NewAccount(record.Name, accNum, seqNum)) +// } + +// // query the min gas price from the chain +// minPrice, err := QueryMinimumGasPrice(ctx, conn) +// if err != nil { +// return nil, fmt.Errorf("querying minimum gas price: %w", err) +// } +// options = append([]Option{WithDefaultGasPrice(minPrice)}, options...) + +// signer, err := NewSigner(keys, encCfg.TxConfig, chainID, appVersion, accounts...) +// if err != nil { +// return nil, fmt.Errorf("failed to create signer: %w", err) +// } + +// return NewTxClient(signer, conn, encCfg.InterfaceRegistry, options...) +// } + +// // SubmitPayForBlob forms a transaction from the provided blobs, signs it, and submits it to the chain. +// // TxOptions may be provided to set the fee and gas limit. +// func (client *TxClient) SubmitPayForBlob(ctx context.Context, blobs []*blob.Blob, opts ...TxOption) (*sdktypes.TxResponse, error) { +// resp, err := client.BroadcastPayForBlob(ctx, blobs, opts...) +// if err != nil { +// return resp, err +// } + +// return client.ConfirmTx(ctx, resp.TxHash) +// } + +// func (client *TxClient) SubmitPayForBlobWithAccount(ctx context.Context, account string, blobs []*blob.Blob, opts ...TxOption) (*sdktypes.TxResponse, error) { +// resp, err := client.BroadcastPayForBlobWithAccount(ctx, account, blobs, opts...) +// if err != nil { +// return resp, err +// } + +// return client.ConfirmTx(ctx, resp.TxHash) +// } + +// // BroadcastPayForBlob signs and broadcasts a transaction to pay for blobs. +// // It does not confirm that the transaction has been committed on chain. +// // If no gas or gas price is set, it will estimate the gas and use +// // the max effective gas price: max(localMinGasPrice, networkMinGasPrice). +// func (client *TxClient) BroadcastPayForBlob(ctx context.Context, blobs []*blob.Blob, opts ...TxOption) (*sdktypes.TxResponse, error) { +// return client.BroadcastPayForBlobWithAccount(ctx, client.defaultAccount, blobs, opts...) +// } + +// func (client *TxClient) BroadcastPayForBlobWithAccount(ctx context.Context, account string, blobs []*blob.Blob, opts ...TxOption) (*sdktypes.TxResponse, error) { +// client.mtx.Lock() +// defer client.mtx.Unlock() +// if err := client.checkAccountLoaded(ctx, account); err != nil { +// return nil, err +// } + +// blobSizes := make([]uint32, len(blobs)) +// for i, blob := range blobs { +// blobSizes[i] = uint32(len(blob.Data)) +// } + +// gasLimit := uint64(float64(types.DefaultEstimateGas(blobSizes)) * client.gasMultiplier) +// fee := uint64(math.Ceil(appconsts.DefaultMinGasPrice * float64(gasLimit))) +// // prepend calculated params, so it can be overwritten in case the user has specified it. +// opts = append([]TxOption{SetGasLimit(gasLimit), SetFee(fee)}, opts...) + +// txBytes, _, err := client.signer.CreatePayForBlobs(account, blobs, opts...) +// if err != nil { +// return nil, err +// } + +// return client.broadcastTx(ctx, txBytes, account) +// } + +// // SubmitTx forms a transaction from the provided messages, signs it, and submits it to the chain. TxOptions +// // may be provided to set the fee and gas limit. +// func (client *TxClient) SubmitTx(ctx context.Context, msgs []sdktypes.Msg, opts ...TxOption) (*sdktypes.TxResponse, error) { +// resp, err := client.BroadcastTx(ctx, msgs, opts...) +// if err != nil { +// return resp, err +// } + +// return client.ConfirmTx(ctx, resp.TxHash) +// } + +// func (client *TxClient) BroadcastTx(ctx context.Context, msgs []sdktypes.Msg, opts ...TxOption) (*sdktypes.TxResponse, error) { +// client.mtx.Lock() +// defer client.mtx.Unlock() +// account, err := client.getAccountNameFromMsgs(msgs) +// if err != nil { +// return nil, err +// } + +// if err := client.checkAccountLoaded(ctx, account); err != nil { +// return nil, err +// } + +// txBuilder, err := client.signer.txBuilder(msgs, opts...) +// if err != nil { +// return nil, err +// } + +// hasUserSetFee := false +// for _, coin := range txBuilder.GetTx().GetFee() { +// if coin.Denom == appconsts.BondDenom { +// hasUserSetFee = true +// break +// } +// } + +// gasLimit := txBuilder.GetTx().GetGas() +// if gasLimit == 0 { +// if !hasUserSetFee { +// // add at least 1utia as fee to builder as it affects gas calculation. +// txBuilder.SetFeeAmount(sdktypes.NewCoins(sdktypes.NewCoin(appconsts.BondDenom, sdktypes.NewInt(1)))) +// } +// gasLimit, err = client.estimateGas(ctx, txBuilder) +// if err != nil { +// return nil, err +// } +// txBuilder.SetGasLimit(gasLimit) +// } + +// if !hasUserSetFee { +// fee := int64(math.Ceil(appconsts.DefaultMinGasPrice * float64(gasLimit))) +// txBuilder.SetFeeAmount(sdktypes.NewCoins(sdktypes.NewCoin(appconsts.BondDenom, sdktypes.NewInt(fee)))) +// } + +// account, _, err = client.signer.signTransaction(txBuilder) +// if err != nil { +// return nil, err +// } + +// txBytes, err := client.signer.EncodeTx(txBuilder.GetTx()) +// if err != nil { +// return nil, err +// } + +// return client.broadcastTx(ctx, txBytes, account) +// } + +// func (client *TxClient) broadcastTx(ctx context.Context, txBytes []byte, signer string) (*sdktypes.TxResponse, error) { +// txClient := sdktx.NewServiceClient(client.grpc) +// resp, err := txClient.BroadcastTx( +// ctx, +// &sdktx.BroadcastTxRequest{ +// Mode: sdktx.BroadcastMode_BROADCAST_MODE_SYNC, +// TxBytes: txBytes, +// }, +// ) +// if err != nil { +// return nil, err +// } +// if resp.TxResponse.Code != abci.CodeTypeOK { +// if apperrors.IsNonceMismatchCode(resp.TxResponse.Code) { +// // query the account to update the sequence number on-chain for the account +// _, seqNum, err := QueryAccount(ctx, client.grpc, client.registry, client.signer.accounts[signer].address) +// if err != nil { +// return nil, fmt.Errorf("querying account for new sequence number: %w\noriginal tx response: %s", err, resp.TxResponse.RawLog) +// } +// if err := client.signer.SetSequence(signer, seqNum); err != nil { +// return nil, fmt.Errorf("setting sequence: %w", err) +// } +// return client.retryBroadcastingTx(ctx, txBytes) +// } +// return resp.TxResponse, fmt.Errorf("tx failed with code %d: %s", resp.TxResponse.Code, resp.TxResponse.RawLog) +// } + +// // after the transaction has been submitted, we can increment the +// // sequence of the signer +// if err := client.signer.IncrementSequence(signer); err != nil { +// return nil, fmt.Errorf("increment sequencing: %w", err) +// } +// return resp.TxResponse, nil +// } + +// // retryBroadcastingTx creates a new transaction by copying over an existing transaction but creates a new signature with the +// // new sequence number. It then calls `broadcastTx` and attempts to submit the transaction +// func (client *TxClient) retryBroadcastingTx(ctx context.Context, txBytes []byte) (*sdktypes.TxResponse, error) { +// blobTx, isBlobTx := blob.UnmarshalBlobTx(txBytes) +// if isBlobTx { +// txBytes = blobTx.Tx +// } +// tx, err := client.signer.DecodeTx(txBytes) +// if err != nil { +// return nil, err +// } + +// opts := make([]TxOption, 0) +// if granter := tx.FeeGranter(); granter != nil { +// opts = append(opts, SetFeeGranter(granter)) +// } +// if payer := tx.FeePayer(); payer != nil { +// opts = append(opts, SetFeePayer(payer)) +// } +// if memo := tx.GetMemo(); memo != "" { +// opts = append(opts, SetMemo(memo)) +// } +// if fee := tx.GetFee(); fee != nil { +// opts = append(opts, SetFee(fee.AmountOf(appconsts.BondDenom).Uint64())) +// } +// if gas := tx.GetGas(); gas > 0 { +// opts = append(opts, SetGasLimit(gas)) +// } + +// txBuilder, err := client.signer.txBuilder(tx.GetMsgs(), opts...) +// if err != nil { +// return nil, err +// } +// signer, _, err := client.signer.signTransaction(txBuilder) +// if err != nil { +// return nil, fmt.Errorf("resigning transaction: %w", err) +// } + +// newTxBytes, err := client.signer.EncodeTx(txBuilder.GetTx()) +// if err != nil { +// return nil, err +// } + +// // rewrap the blob tx if it was originally a blob tx +// if isBlobTx { +// newTxBytes, err = blob.MarshalBlobTx(newTxBytes, blobTx.Blobs...) +// if err != nil { +// return nil, err +// } +// } + +// return client.broadcastTx(ctx, newTxBytes, signer) +// } + +// // ConfirmTx periodically pings the provided node for the commitment of a transaction by its +// // hash. It will continually loop until the context is cancelled, the tx is found or an error +// // is encountered. +// func (client *TxClient) ConfirmTx(ctx context.Context, txHash string) (*sdktypes.TxResponse, error) { +// txClient := sdktx.NewServiceClient(client.grpc) + +// pollTicker := time.NewTicker(client.pollTime) +// defer pollTicker.Stop() + +// for { +// resp, err := txClient.GetTx(ctx, &sdktx.GetTxRequest{Hash: txHash}) +// if err == nil { +// if resp.TxResponse.Code != 0 { +// return resp.TxResponse, fmt.Errorf("tx was included but failed with code %d: %s", resp.TxResponse.Code, resp.TxResponse.RawLog) +// } +// return resp.TxResponse, nil +// } +// // FIXME: this is a relatively brittle of working out whether to retry or not. The tx might be not found for other +// // reasons. It may have been removed from the mempool at a later point. We should build an endpoint that gives the +// // signer more information on the status of their transaction and then update the logic here +// if !strings.Contains(err.Error(), "not found") { +// return &sdktypes.TxResponse{}, err +// } + +// // Wait for the next round. +// select { +// case <-ctx.Done(): +// return &sdktypes.TxResponse{}, ctx.Err() +// case <-pollTicker.C: +// } +// } +// } + +// // EstimateGas simulates the transaction, calculating the amount of gas that was consumed during execution. The final +// // result will be multiplied by gasMultiplier(that is set in TxClient) +// func (client *TxClient) EstimateGas(ctx context.Context, msgs []sdktypes.Msg, opts ...TxOption) (uint64, error) { +// client.mtx.Lock() +// defer client.mtx.Unlock() + +// txBuilder, err := client.signer.txBuilder(msgs, opts...) +// if err != nil { +// return 0, err +// } + +// return client.estimateGas(ctx, txBuilder) +// } + +// func (client *TxClient) estimateGas(ctx context.Context, txBuilder client.TxBuilder) (uint64, error) { +// // add at least 1utia as fee to builder as it affects gas calculation. +// txBuilder.SetFeeAmount(sdktypes.NewCoins(sdktypes.NewCoin(appconsts.BondDenom, sdktypes.NewInt(1)))) + +// _, _, err := client.signer.signTransaction(txBuilder) +// if err != nil { +// return 0, err +// } +// txBytes, err := client.signer.EncodeTx(txBuilder.GetTx()) +// if err != nil { +// return 0, err +// } +// resp, err := sdktx.NewServiceClient(client.grpc).Simulate(ctx, &sdktx.SimulateRequest{ +// TxBytes: txBytes, +// }) +// if err != nil { +// return 0, err +// } + +// gasLimit := uint64(float64(resp.GasInfo.GasUsed) * client.gasMultiplier) +// return gasLimit, nil +// } + +// // Account returns an account of the signer from the key name. Also returns a bool if the +// // account exists. +// // Thread-safe +// func (client *TxClient) Account(name string) *Account { +// client.mtx.Lock() +// defer client.mtx.Unlock() +// acc, exists := client.signer.accounts[name] +// if !exists { +// return nil +// } +// return acc.Copy() +// } + +// func (client *TxClient) AccountByAddress(address sdktypes.AccAddress) *Account { +// client.mtx.Lock() +// defer client.mtx.Unlock() +// return client.signer.AccountByAddress(address) +// } + +// func (client *TxClient) DefaultAddress() sdktypes.AccAddress { +// return client.defaultAddress +// } + +// func (client *TxClient) DefaultAccountName() string { return client.defaultAccount } + +// func (client *TxClient) checkAccountLoaded(ctx context.Context, account string) error { +// if _, exists := client.signer.accounts[account]; exists { +// return nil +// } +// record, err := client.signer.keys.Key(account) +// if err != nil { +// return fmt.Errorf("trying to find account %s on keyring: %w", account, err) +// } +// addr, err := record.GetAddress() +// if err != nil { +// return fmt.Errorf("retrieving address from keyring: %w", err) +// } +// accNum, sequence, err := QueryAccount(ctx, client.grpc, client.registry, addr) +// if err != nil { +// return fmt.Errorf("querying account %s: %w", account, err) +// } +// return client.signer.AddAccount(NewAccount(account, accNum, sequence)) +// } + +// func (client *TxClient) getAccountNameFromMsgs(msgs []sdktypes.Msg) (string, error) { +// var addr sdktypes.AccAddress +// for _, msg := range msgs { +// signers := msg.GetSigners() +// if len(signers) != 1 { +// return "", fmt.Errorf("only one signer per transaction supported, got %d", len(signers)) +// } +// if addr == nil { +// addr = signers[0] +// } +// if !bytes.Equal(addr, signers[0]) { +// return "", errors.New("not supported: got two different signers across multiple messages") +// } +// } +// record, err := client.signer.keys.KeyByAddress(addr) +// if err != nil { +// return "", err +// } +// return record.Name, nil +// } + +// // Signer exposes the tx clients underlying signer +// func (client *TxClient) Signer() *Signer { +// return client.signer +// } + +// func (client *TxClient) SetDefaultGasPrice(price float64) { +// client.mtx.Lock() +// defer client.mtx.Unlock() +// client.defaultGasPrice = price +// } + +// func (client *TxClient) SetGasMultiplier(multiplier float64) { +// client.mtx.Lock() +// defer client.mtx.Unlock() +// client.gasMultiplier = multiplier +// } + +// // QueryMinimumGasPrice queries both the nodes local and network wide +// // minimum gas prices, returning the maximum of the two. +// func QueryMinimumGasPrice(ctx context.Context, grpcConn *grpc.ClientConn) (float64, error) { +// cfgRsp, err := nodeservice.NewServiceClient(grpcConn).Config(ctx, &nodeservice.ConfigRequest{}) +// if err != nil { +// return 0, err +// } + +// localMinCoins, err := sdktypes.ParseDecCoins(cfgRsp.MinimumGasPrice) +// if err != nil { +// return 0, err +// } +// localMinPrice := localMinCoins.AmountOf(app.BondDenom).MustFloat64() + +// networkMinPrice, err := QueryNetworkMinGasPrice(ctx, grpcConn) +// if err != nil { +// // check if the network version supports a global min gas +// // price using a regex check. If not (i.e. v1) use the +// // local price only +// if strings.Contains(err.Error(), "unknown subspace: minfee") { +// return localMinPrice, nil +// } +// return 0, err +// } + +// // return the highest value of the two +// if networkMinPrice > localMinPrice { +// return networkMinPrice, nil +// } +// return localMinPrice, nil +// } + +// func QueryNetworkMinGasPrice(ctx context.Context, grpcConn *grpc.ClientConn) (float64, error) { +// paramsClient := paramtypes.NewQueryClient(grpcConn) +// // NOTE: that we don't prove that this is the correct value +// paramResponse, err := paramsClient.Params(ctx, ¶mtypes.QueryParamsRequest{Subspace: minfee.ModuleName, Key: string(minfee.KeyNetworkMinGasPrice)}) +// if err != nil { +// return 0, fmt.Errorf("querying params module: %w", err) +// } + +// var networkMinPrice float64 +// // Value is empty if network min gas price is not supported i.e. v1 state machine. +// if paramResponse.Param.Value != "" { +// networkMinPrice, err = strconv.ParseFloat(strings.Trim(paramResponse.Param.Value, `"`), 64) +// if err != nil { +// return 0, fmt.Errorf("parsing network min gas price: %w", err) +// } +// } +// return networkMinPrice, nil +// } diff --git a/solidity-ibc-eureka b/solidity-ibc-eureka index d952d31..6f003e5 160000 --- a/solidity-ibc-eureka +++ b/solidity-ibc-eureka @@ -1 +1 @@ -Subproject commit d952d312065f9c3689a9f3100dc98cc0c86c2f77 +Subproject commit 6f003e57e5ee3c909167a187dd4b259ff69b0892 diff --git a/testing/demo/pkg/initialise_light_client.go b/testing/demo/pkg/initialise_light_client.go new file mode 100644 index 0000000..af0b09f --- /dev/null +++ b/testing/demo/pkg/initialise_light_client.go @@ -0,0 +1,187 @@ +package pkg + +import ( + "bytes" + "context" + "fmt" + "math/big" + "time" + + "github.com/celestiaorg/celestia-zkevm-ibc-demo/simapp" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/celestiaorg/celestia-zkevm-ibc-demo/ibc/lightclients/groth16" + clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types" +) + +func InitializeLightClient(ctx context.Context, simapp simapp.SimApp, trustHeight uint64) error { + // ETH SET UP + ethClient, err := ethclient.Dial("http://localhost:8545") + if err != nil { + return fmt.Errorf("failed to connect to ethereum client: %v", err) + } + + // retrieve the genesis block + genesisBlock, err := ethClient.BlockByNumber(ctx, big.NewInt(0)) + if err != nil { + return fmt.Errorf("failed to get genesis block: %v", err) + } + + latestBlock, err := ethClient.BlockByNumber(ctx, nil) + if err != nil { + return fmt.Errorf("failed to get latest block: %v", err) + } + + clientState := groth16.NewClientState(latestBlock.Number().Uint64(), []byte{}, []byte{}, []byte{}, genesisBlock.Root().Bytes()) + clientStateAny, err := cdctypes.NewAnyWithValue(clientState) + if err != nil { + return fmt.Errorf("failed to create client state any: %v", err) + } + latestBlockTime := time.Unix(int64(latestBlock.Time()), 0) + consensusState := groth16.NewConsensusState(latestBlockTime, latestBlock.Root().Bytes()) + consensusStateAny, err := cdctypes.NewAnyWithValue(consensusState) + if err != nil { + return fmt.Errorf("failed to create consensus state any: %v", err) + } + + // relayer address registered in simapp + relayer := "cosmos16p2hfunj38hucvlzx4fp4dj42y6gddcxj60yxn" + + msg := &clienttypes.MsgCreateClient{ + ClientState: clientStateAny, + ConsensusState: consensusStateAny, + Signer: relayer, + } + + // simapp address + clientCtx, err := SetupClientContext(&simapp) + if err != nil { + return fmt.Errorf("failed to setup client context: %v", err) + } + + BroadcastMessages(ctx, clientCtx, simapp, relayer, 200, msg) + return nil +} + +// SetupClientContext initializes a Cosmos SDK ClientContext +func SetupClientContext(simapp *simapp.SimApp) (client.Context, error) { + // Chain-specific configurations + chainID := simapp.ChainID() + nodeURI := "http://localhost:26657" // RPC endpoint + homeDir := "../../files/simapp-validator" // Path to Cosmos configuration directory + + // Keyring setup + kr, err := keyring.New(simapp.Name(), keyring.BackendTest, homeDir, nil, simapp.AppCodec()) + if err != nil { + return client.Context{}, fmt.Errorf("failed to initialize keyring: %v", err) + } + + // Initialize ClientContext + clientCtx := client.Context{}. + WithChainID(chainID). + WithKeyring(kr). + WithNodeURI(nodeURI). + WithHomeDir(homeDir) + + // Create a Tendermint RPC client and set it in ClientContext + rpcClient, err := client.NewClientFromNode(nodeURI) + if err != nil { + return client.Context{}, fmt.Errorf("failed to create RPC client: %v", err) + } + clientCtx = clientCtx.WithClient(rpcClient) + + // Set codec (you need to pass your own codec) + clientCtx = clientCtx.WithCodec(simapp.AppCodec()) + + // Set input/output for CLI commands (if applicable) + clientCtx = clientCtx.WithInput(nil).WithOutput(nil) + + return clientCtx, nil +} + +// GetFactory returns an instance of tx.Factory that is configured with this Broadcaster's CosmosChain +// and the provided user. ConfigureFactoryOptions can be used to specify arbitrary options to configure the returned +// factory. +func GetFactory(clientContext client.Context, user string, factoryOptions tx.Factory) (tx.Factory, error) { + sdkAdd, err := sdk.AccAddressFromBech32(user) + if err != nil { + return tx.Factory{}, err + } + + account, err := clientContext.AccountRetriever.GetAccount(clientContext, sdkAdd) + if err != nil { + return tx.Factory{}, err + } + + return defaultTxFactory(clientContext, account), nil +} + +// defaultTxFactory creates a new Factory with default configuration. +func defaultTxFactory(clientCtx client.Context, account client.Account) tx.Factory { + return tx.Factory{}. + WithAccountNumber(account.GetAccountNumber()). + WithSequence(account.GetSequence()). + WithSignMode(signing.SignMode_SIGN_MODE_DIRECT). + WithGas(flags.DefaultGasLimit). + WithGasPrices("0.002"). + WithMemo("interchaintest"). + WithTxConfig(clientCtx.TxConfig). + WithAccountRetriever(clientCtx.AccountRetriever). + WithKeybase(clientCtx.Keyring). + WithChainID(clientCtx.ChainID). + WithSimulateAndExecute(false) +} + +// BroadcastMessages broadcasts the provided messages to the given chain and signs them on behalf of the provided user. +// Once the broadcast response is returned, we wait for two blocks to be created on chain. +func BroadcastMessages(ctx context.Context, clientContext client.Context, simapp simapp.SimApp, user string, gas uint64, msgs ...interface { + ProtoMessage() + Reset() + String() string +}) (*sdk.TxResponse, error) { + // sdk.GetConfig().SetBech32PrefixForAccount(chain.Config().Bech32Prefix, chain.Config().Bech32Prefix+sdk.PrefixPublic) + // sdk.GetConfig().SetBech32PrefixForValidator( + // chain.Config().Bech32Prefix+sdk.PrefixValidator+sdk.PrefixOperator, + // chain.Config().Bech32Prefix+sdk.PrefixValidator+sdk.PrefixOperator+sdk.PrefixPublic, + // ) + + // Create a tx.Factory instance and apply the factoryOptions function + factory := tx.Factory{} + factoryOptions := factory.WithGas(gas) + + f, err := GetFactory(clientContext, user, factoryOptions) + if err != nil { + return nil, err + } + + buffer := &bytes.Buffer{} + clientContext.Output = buffer + + if err := tx.BroadcastTx(clientContext, f, msgs...); err != nil { + return &sdk.TxResponse{}, err + } + + if buffer == nil || buffer.Len() == 0 { + return nil, fmt.Errorf("empty buffer, transaction has not been executed yet") + } + + // unmarshall buffer into txresponse + var txResp sdk.TxResponse + if err := clientContext.Codec.UnmarshalJSON(buffer.Bytes(), &txResp); err != nil { + return nil, fmt.Errorf("failed to unmarshal tx response: %v", err) + } + + return &txResp, nil +} + +type User interface { + KeyName() string + FormattedAddress() string +}