Skip to content

Commit

Permalink
SimulateTransaction to reduce ABCI requirement for GasInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
Reecepbcups committed Sep 6, 2024
1 parent d8db9db commit cc84847
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 63 deletions.
51 changes: 51 additions & 0 deletions cclient/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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"`
}
46 changes: 46 additions & 0 deletions cclient/consensus_cmbft.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
79 changes: 37 additions & 42 deletions cclient/consensus_gordian.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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.
Expand Down Expand Up @@ -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")
}

Expand Down
5 changes: 5 additions & 0 deletions cclient/gordian_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
29 changes: 8 additions & 21 deletions relayer/chains/cosmos/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit cc84847

Please sign in to comment.