diff --git a/cclient/consensus.go b/cclient/consensus.go index 631178ade..66ca3f63c 100644 --- a/cclient/consensus.go +++ b/cclient/consensus.go @@ -10,6 +10,7 @@ import ( rpcclient "github.com/cometbft/cometbft/rpc/client" coretypes "github.com/cometbft/cometbft/rpc/core/types" tmtypes "github.com/cometbft/cometbft/types" + types "github.com/cosmos/cosmos-sdk/types" ) // TODO(reece): get off cometbft types into internal relayer. @@ -46,6 +47,13 @@ type ConsensusClient interface { data bytes.HexBytes, opts rpcclient.ABCIQueryOptions, ) (*coretypes.ResultABCIQuery, error) + + SimulateTransaction(ctx context.Context, tx []byte, cfg *SimTxConfig) (types.GasInfo, error) +} + +type SimTxConfig struct { + // CometBFT only function (QueryABCI). + QueryABCIFunc func(ctx context.Context, req abci.RequestQuery) (abci.ResponseQuery, error) } type Status struct { @@ -91,3 +99,46 @@ type ResultBroadcastTx struct { Codespace string `json:"codespace"` Hash bytes.HexBytes `json:"hash"` } + +type TxResultResponse struct { + Events []*Event `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` + // bytes resp = 2; // []transaction.Msg + Error string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` + Code uint32 `protobuf:"varint,4,opt,name=code,proto3" json:"code,omitempty"` + Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` + Log string `protobuf:"bytes,6,opt,name=log,proto3" json:"log,omitempty"` + Info string `protobuf:"bytes,7,opt,name=info,proto3" json:"info,omitempty"` + GasWanted uint64 `protobuf:"varint,8,opt,name=gas_wanted,proto3" json:"gas_wanted,omitempty"` + GasUsed uint64 `protobuf:"varint,9,opt,name=gas_used,proto3" json:"gas_used,omitempty"` + Codespace string `protobuf:"bytes,10,opt,name=codespace,proto3" json:"codespace,omitempty"` + TxHash string `protobuf:"bytes,11,opt,name=tx_hash,proto3" json:"tx_hash,omitempty"` +} + +type Event struct { + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Attributes []*EventAttribute `protobuf:"bytes,2,rep,name=attributes,proto3" json:"attributes,omitempty"` +} + +func convertConsensusEvents(e []*Event) []abci.Event { + events := make([]abci.Event, len(e)) + for _, ev := range e { + attributes := make([]abci.EventAttribute, len(ev.Attributes)) + for idx, attr := range ev.Attributes { + attributes[idx] = abci.EventAttribute{ + Key: attr.Key, + Value: attr.Value, + } + } + + events = append(events, abci.Event{ + Type: ev.Type, + Attributes: attributes, + }) + } + return events +} + +type EventAttribute struct { + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} diff --git a/cclient/consensus_cmbft.go b/cclient/consensus_cmbft.go index 5ba50492f..2537c0425 100644 --- a/cclient/consensus_cmbft.go +++ b/cclient/consensus_cmbft.go @@ -5,14 +5,26 @@ import ( "fmt" "time" + "github.com/avast/retry-go/v4" + abci "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/bytes" rpcclient "github.com/cometbft/cometbft/rpc/client" coretypes "github.com/cometbft/cometbft/rpc/core/types" tmtypes "github.com/cometbft/cometbft/types" + sdk "github.com/cosmos/cosmos-sdk/types" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" ) var _ ConsensusClient = (*CometRPCClient)(nil) +var ( + // originally from relayer/chains/cosmos/tx.go + rtyAttNum = uint(5) + rtyAtt = retry.Attempts(rtyAttNum) + rtyDel = retry.Delay(time.Millisecond * 400) + rtyErr = retry.LastErrorOnly(true) +) + // GetBlock implements ConsensusClient. func (r CometRPCClient) GetBlockTime(ctx context.Context, height uint64) (time.Time, error) { h := int64(height) @@ -131,6 +143,40 @@ func (r CometRPCClient) DoBroadcastTxSync(ctx context.Context, tx []byte) (*TxRe }, nil } +// SimulateTransaction implements ConsensusClient. +func (r CometRPCClient) SimulateTransaction(ctx context.Context, tx []byte, cfg *SimTxConfig) (sdk.GasInfo, error) { + simQuery := abci.RequestQuery{ + Path: "/cosmos.tx.v1beta1.Service/Simulate", + Data: tx, + } + + if cfg == nil { + return sdk.GasInfo{}, fmt.Errorf("BUG: SimulateTransaction cfg is nil, cfg.QueryABCIFunc is required for CometRPCClient") + } + + var res abci.ResponseQuery + if err := retry.Do(func() error { + var err error + res, err = cfg.QueryABCIFunc(ctx, simQuery) + if err != nil { + return err + } + return nil + }, retry.Context(ctx), rtyAtt, rtyDel, rtyErr); err != nil { + return sdk.GasInfo{}, err + } + + var simRes txtypes.SimulateResponse + if err := simRes.Unmarshal(res.Value); err != nil { + return sdk.GasInfo{}, err + } + + return sdk.GasInfo{ + GasWanted: simRes.GasInfo.GasWanted, + GasUsed: simRes.GasInfo.GasUsed, + }, nil +} + // GetABCIQueryWithOptions implements ConsensusClient. func (r CometRPCClient) GetABCIQueryWithOptions(ctx context.Context, path string, data bytes.HexBytes, opts rpcclient.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) { q, err := r.ABCIQueryWithOptions(ctx, path, data, opts) diff --git a/cclient/consensus_gordian.go b/cclient/consensus_gordian.go index 10dd0b420..416a08bd3 100644 --- a/cclient/consensus_gordian.go +++ b/cclient/consensus_gordian.go @@ -16,6 +16,7 @@ import ( "github.com/cometbft/cometbft/rpc/client" coretypes "github.com/cometbft/cometbft/rpc/core/types" tmtypes "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/types" ) var _ ConsensusClient = (*GordianConsensus)(nil) @@ -31,48 +32,6 @@ func NewGordianConsensus(addr string) *GordianConsensus { } } -type TxResultResponse struct { - Events []*Event `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` - // bytes resp = 2; // []transaction.Msg - Error string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` - Code uint32 `protobuf:"varint,4,opt,name=code,proto3" json:"code,omitempty"` - Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` - Log string `protobuf:"bytes,6,opt,name=log,proto3" json:"log,omitempty"` - Info string `protobuf:"bytes,7,opt,name=info,proto3" json:"info,omitempty"` - GasWanted uint64 `protobuf:"varint,8,opt,name=gas_wanted,proto3" json:"gas_wanted,omitempty"` - GasUsed uint64 `protobuf:"varint,9,opt,name=gas_used,proto3" json:"gas_used,omitempty"` - Codespace string `protobuf:"bytes,10,opt,name=codespace,proto3" json:"codespace,omitempty"` - TxHash string `protobuf:"bytes,11,opt,name=tx_hash,proto3" json:"tx_hash,omitempty"` -} -type Event struct { - Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` - Attributes []*EventAttribute `protobuf:"bytes,2,rep,name=attributes,proto3" json:"attributes,omitempty"` -} - -func convertConsensusEvents(e []*Event) []abci.Event { - events := make([]abci.Event, len(e)) - for _, ev := range e { - attributes := make([]abci.EventAttribute, len(ev.Attributes)) - for idx, attr := range ev.Attributes { - attributes[idx] = abci.EventAttribute{ - Key: attr.Key, - Value: attr.Value, - } - } - - events = append(events, abci.Event{ - Type: ev.Type, - Attributes: attributes, - }) - } - return events -} - -type EventAttribute struct { - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` -} - // ----- // DoBroadcastTxAsync implements ConsensusClient. @@ -106,8 +65,44 @@ func (g *GordianConsensus) DoBroadcastTxSync(ctx context.Context, tx []byte) (*T return &resp, nil } +// SimulateTransaction implements ConsensusClient. +func (g *GordianConsensus) SimulateTransaction(ctx context.Context, tx []byte, cfg *SimTxConfig) (types.GasInfo, error) { + var body io.Reader + if tx != nil { + body = bytes.NewReader(tx) + } else { + return types.GasInfo{}, fmt.Errorf("SimulateTransaction tx is nil") + } + + res, err := http.Post(fmt.Sprintf("%s/debug/simulate_tx", g.addr), "application/json", body) + if err != nil { + fmt.Printf("error making http request: %s\n", err) + os.Exit(1) + } + + var resp TxResultResponse + if err := json.NewDecoder(res.Body).Decode(&resp); err != nil { + fmt.Printf("error decoding response: %s\n", err) + return types.GasInfo{}, err + } + + return types.GasInfo{ + GasWanted: resp.GasWanted, + GasUsed: resp.GasUsed, + }, nil +} + // GetABCIQuery implements ConsensusClient. func (g *GordianConsensus) GetABCIQuery(ctx context.Context, queryPath string, data cmtbytes.HexBytes) (*ABCIQueryResponse, error) { + // res, err := cc.QueryABCI(ctx, abci.RequestQuery{ + // Path: "store/upgrade/key", + // Height: int64(height - 1), + // Data: key, + // Prove: true, + // }) + // if err != nil { + // return nil, clienttypes.Height{}, err + // } panic("unimplemented") } diff --git a/cclient/gordian_test.go b/cclient/gordian_test.go index 08d1c030c..3230405c3 100644 --- a/cclient/gordian_test.go +++ b/cclient/gordian_test.go @@ -27,6 +27,11 @@ func TestGordian(t *testing.T) { require.NoError(t, err) t.Log(bt) + gasInfo, err := gc.SimulateTransaction(ctx, []byte(tx), nil) + require.NoError(t, err) + require.GreaterOrEqual(t, gasInfo.GasUsed, uint64(1)) + t.Log(gasInfo) + resp, err := gc.DoBroadcastTxSync(ctx, []byte(tx)) fmt.Println("resp", resp) require.NoError(t, err) diff --git a/relayer/chains/cosmos/tx.go b/relayer/chains/cosmos/tx.go index a8aaf833b..87d8ae4a3 100644 --- a/relayer/chains/cosmos/tx.go +++ b/relayer/chains/cosmos/tx.go @@ -1792,30 +1792,17 @@ func (cc *CosmosProvider) CalculateGas(ctx context.Context, txf tx.Factory, sign return txtypes.SimulateResponse{}, 0, err } - simQuery := abci.RequestQuery{ - Path: "/cosmos.tx.v1beta1.Service/Simulate", - Data: txBytes, - } - - var res abci.ResponseQuery - if err := retry.Do(func() error { - var err error - res, err = cc.QueryABCI(ctx, simQuery) - if err != nil { - return err - } - return nil - }, retry.Context(ctx), rtyAtt, rtyDel, rtyErr); err != nil { - return txtypes.SimulateResponse{}, 0, err - } - - var simRes txtypes.SimulateResponse - if err := simRes.Unmarshal(res.Value); err != nil { + gasInfo, err := cc.ConsensusClient.SimulateTransaction(ctx, txBytes, &cclient.SimTxConfig{ + QueryABCIFunc: cc.QueryABCI, + }) + if err != nil { return txtypes.SimulateResponse{}, 0, err } - gas, err := cc.AdjustEstimatedGas(simRes.GasInfo.GasUsed) - return simRes, gas, err + gas, err := cc.AdjustEstimatedGas(gasInfo.GasUsed) + return txtypes.SimulateResponse{ + GasInfo: &gasInfo, + }, gas, err } // TxFactory instantiates a new tx factory with the appropriate configuration settings for this chain.