forked from strangelove-ventures/escrow-checker
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
2,806 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,266 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"reflect" | ||
"strconv" | ||
"time" | ||
|
||
sdkerrors "cosmossdk.io/errors" | ||
abci "github.com/cometbft/cometbft/abci/types" | ||
rpcclient "github.com/cometbft/cometbft/rpc/client" | ||
rpchttp "github.com/cometbft/cometbft/rpc/client/http" | ||
libclient "github.com/cometbft/cometbft/rpc/jsonrpc/client" | ||
"github.com/cosmos/cosmos-sdk/codec/types" | ||
legacyerrors "github.com/cosmos/cosmos-sdk/types/errors" | ||
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" | ||
"github.com/cosmos/cosmos-sdk/types/tx" | ||
gogogrpc "github.com/cosmos/gogoproto/grpc" | ||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/codes" | ||
"google.golang.org/grpc/encoding" | ||
"google.golang.org/grpc/encoding/proto" | ||
"google.golang.org/grpc/metadata" | ||
"google.golang.org/grpc/status" | ||
) | ||
|
||
var _ gogogrpc.ClientConn = &Client{} | ||
|
||
var protoCodec = encoding.GetCodec(proto.Name) | ||
|
||
type Client struct { | ||
ChainID string | ||
Address string | ||
RPCClient rpcclient.Client | ||
AccountPrefix string | ||
Cdc Codec | ||
Timeout time.Duration | ||
} | ||
|
||
type Clients []*Client | ||
|
||
func (c Clients) clientByChainID(chainID string) (*Client, error) { | ||
for _, client := range c { | ||
if client.ChainID == chainID { | ||
return client, nil | ||
} | ||
} | ||
|
||
return nil, fmt.Errorf("client with chain ID %s is not configured, check config and re-run the program", chainID) | ||
} | ||
|
||
func NewClient(chainID, rpcAddr, accountPrefix string, timeout time.Duration) *Client { | ||
rpcClient, err := NewRPCClient(rpcAddr, timeout) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
return &Client{ | ||
ChainID: chainID, | ||
Address: rpcAddr, | ||
RPCClient: rpcClient, | ||
AccountPrefix: accountPrefix, | ||
Cdc: MakeCodec(ModuleBasics, accountPrefix, accountPrefix+"valoper"), | ||
} | ||
} | ||
|
||
// Invoke implements the grpc ClientConn.Invoke method | ||
func (c *Client) Invoke(ctx context.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err error) { | ||
// Two things can happen here: | ||
// 1. either we're broadcasting a Tx, in which call we call Tendermint's broadcast endpoint directly, | ||
// 2. or we are querying for state, in which case we call ABCI's Querier. | ||
|
||
// In both cases, we don't allow empty request req (it will panic unexpectedly). | ||
if reflect.ValueOf(req).IsNil() { | ||
return sdkerrors.Wrap(legacyerrors.ErrInvalidRequest, "request cannot be nil") | ||
} | ||
|
||
// Case 1. Broadcasting a Tx. | ||
if reqProto, ok := req.(*tx.BroadcastTxRequest); ok { | ||
if !ok { | ||
return sdkerrors.Wrapf(legacyerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxRequest)(nil), req) | ||
} | ||
resProto, ok := reply.(*tx.BroadcastTxResponse) | ||
if !ok { | ||
return sdkerrors.Wrapf(legacyerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxResponse)(nil), req) | ||
} | ||
|
||
broadcastRes, err := c.TxServiceBroadcast(ctx, reqProto) | ||
if err != nil { | ||
return err | ||
} | ||
*resProto = *broadcastRes | ||
return err | ||
} | ||
|
||
// Case 2. Querying state. | ||
inMd, _ := metadata.FromOutgoingContext(ctx) | ||
abciRes, outMd, err := c.RunGRPCQuery(ctx, method, req, inMd) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if err = protoCodec.Unmarshal(abciRes.Value, reply); err != nil { | ||
return err | ||
} | ||
|
||
for _, callOpt := range opts { | ||
header, ok := callOpt.(grpc.HeaderCallOption) | ||
if !ok { | ||
continue | ||
} | ||
|
||
*header.HeaderAddr = outMd | ||
} | ||
|
||
if c.Cdc.InterfaceRegistry != nil { | ||
return types.UnpackInterfaces(reply, c.Cdc.Marshaler) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// NewStream implements the grpc ClientConn.NewStream method | ||
func (c *Client) NewStream(context.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { | ||
return nil, fmt.Errorf("streaming rpc not supported") | ||
} | ||
|
||
// RunGRPCQuery runs a gRPC query from the clientCtx, given all necessary | ||
// arguments for the gRPC method, and returns the ABCI response. It is used | ||
// to factorize code between client (Invoke) and server (RegisterGRPCServer) | ||
// gRPC handlers. | ||
func (c *Client) RunGRPCQuery(ctx context.Context, method string, req interface{}, md metadata.MD) (abci.ResponseQuery, metadata.MD, error) { | ||
reqBz, err := protoCodec.Marshal(req) | ||
if err != nil { | ||
return abci.ResponseQuery{}, nil, err | ||
} | ||
|
||
// parse height header | ||
if heights := md.Get(grpctypes.GRPCBlockHeightHeader); len(heights) > 0 { | ||
height, err := strconv.ParseInt(heights[0], 10, 64) | ||
if err != nil { | ||
return abci.ResponseQuery{}, nil, err | ||
} | ||
if height < 0 { | ||
return abci.ResponseQuery{}, nil, sdkerrors.Wrapf( | ||
legacyerrors.ErrInvalidRequest, | ||
"client.Context.Invoke: height (%d) from %q must be >= 0", height, grpctypes.GRPCBlockHeightHeader) | ||
} | ||
|
||
} | ||
|
||
height, err := GetHeightFromMetadata(md) | ||
if err != nil { | ||
return abci.ResponseQuery{}, nil, err | ||
} | ||
|
||
prove, err := GetProveFromMetadata(md) | ||
if err != nil { | ||
return abci.ResponseQuery{}, nil, err | ||
} | ||
|
||
abciReq := abci.RequestQuery{ | ||
Path: method, | ||
Data: reqBz, | ||
Height: height, | ||
Prove: prove, | ||
} | ||
|
||
abciRes, err := c.QueryABCI(ctx, abciReq) | ||
if err != nil { | ||
return abci.ResponseQuery{}, nil, err | ||
} | ||
|
||
// Create header metadata. For now the headers contain: | ||
// - block height | ||
// We then parse all the call options, if the call option is a | ||
// HeaderCallOption, then we manually set the value of that header to the | ||
// metadata. | ||
md = metadata.Pairs(grpctypes.GRPCBlockHeightHeader, strconv.FormatInt(abciRes.Height, 10)) | ||
|
||
return abciRes, md, nil | ||
} | ||
|
||
// TxServiceBroadcast is a helper function to broadcast a Tx with the correct gRPC types | ||
// from the tx service. Calls `clientCtx.BroadcastTx` under the hood. | ||
func (c *Client) TxServiceBroadcast(ctx context.Context, req *tx.BroadcastTxRequest) (*tx.BroadcastTxResponse, error) { | ||
if req == nil || req.TxBytes == nil { | ||
return nil, status.Error(codes.InvalidArgument, "invalid empty tx") | ||
} | ||
|
||
//var ( | ||
// blockTimeout = defaultBroadcastWaitTimeout | ||
// err error | ||
// rlyResp *provider.RelayerTxResponse | ||
// callbackErr error | ||
// wg sync.WaitGroup | ||
//) | ||
// | ||
//if cc.PCfg.BlockTimeout != "" { | ||
// blockTimeout, err = time.ParseDuration(cc.PCfg.BlockTimeout) | ||
// if err != nil { | ||
// // Did you call Validate() method on CosmosProviderConfig struct | ||
// // before coming here? | ||
// return nil, err | ||
// } | ||
//} | ||
// | ||
//callback := func(rtr *provider.RelayerTxResponse, err error) { | ||
// rlyResp = rtr | ||
// callbackErr = err | ||
// wg.Done() | ||
//} | ||
// | ||
//wg.Add(1) | ||
// | ||
//if err := cc.broadcastTx(ctx, req.TxBytes, nil, nil, ctx, blockTimeout, []func(*provider.RelayerTxResponse, error){callback}); err != nil { | ||
// return nil, err | ||
//} | ||
// | ||
//wg.Wait() | ||
|
||
//if callbackErr != nil { | ||
// return nil, callbackErr | ||
//} | ||
// | ||
//return &tx.BroadcastTxResponse{ | ||
// TxResponse: &sdk.TxResponse{ | ||
// Height: rlyResp.Height, | ||
// TxHash: rlyResp.TxHash, | ||
// Codespace: rlyResp.Codespace, | ||
// Code: rlyResp.Code, | ||
// Data: rlyResp.Data, | ||
// }, | ||
//}, nil | ||
return nil, nil | ||
} | ||
|
||
func GetHeightFromMetadata(md metadata.MD) (int64, error) { | ||
height := md.Get(grpctypes.GRPCBlockHeightHeader) | ||
if len(height) == 1 { | ||
return strconv.ParseInt(height[0], 10, 64) | ||
} | ||
return 0, nil | ||
} | ||
|
||
func GetProveFromMetadata(md metadata.MD) (bool, error) { | ||
prove := md.Get("x-cosmos-query-prove") | ||
if len(prove) == 1 { | ||
return strconv.ParseBool(prove[0]) | ||
} | ||
return false, nil | ||
} | ||
|
||
func NewRPCClient(addr string, timeout time.Duration) (*rpchttp.HTTP, error) { | ||
httpClient, err := libclient.DefaultHTTPClient(addr) | ||
if err != nil { | ||
return nil, err | ||
} | ||
httpClient.Timeout = timeout | ||
rpcClient, err := rpchttp.NewWithClient(addr, "/websocket", httpClient) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return rpcClient, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package main | ||
|
||
import ( | ||
feegrant "cosmossdk.io/x/feegrant/module" | ||
"cosmossdk.io/x/tx/signing" | ||
"cosmossdk.io/x/upgrade" | ||
"github.com/cometbft/cometbft/libs/sync" | ||
"github.com/cosmos/cosmos-sdk/client" | ||
"github.com/cosmos/cosmos-sdk/codec" | ||
"github.com/cosmos/cosmos-sdk/codec/address" | ||
"github.com/cosmos/cosmos-sdk/codec/types" | ||
"github.com/cosmos/cosmos-sdk/std" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/types/module" | ||
"github.com/cosmos/cosmos-sdk/x/auth" | ||
"github.com/cosmos/cosmos-sdk/x/auth/tx" | ||
authz "github.com/cosmos/cosmos-sdk/x/authz/module" | ||
"github.com/cosmos/cosmos-sdk/x/bank" | ||
"github.com/cosmos/cosmos-sdk/x/crisis" | ||
"github.com/cosmos/cosmos-sdk/x/distribution" | ||
"github.com/cosmos/cosmos-sdk/x/gov" | ||
govclient "github.com/cosmos/cosmos-sdk/x/gov/client" | ||
"github.com/cosmos/cosmos-sdk/x/mint" | ||
"github.com/cosmos/cosmos-sdk/x/params" | ||
paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" | ||
"github.com/cosmos/cosmos-sdk/x/slashing" | ||
"github.com/cosmos/cosmos-sdk/x/staking" | ||
"github.com/cosmos/gogoproto/proto" | ||
"github.com/cosmos/ibc-go/modules/capability" | ||
ibcfee "github.com/cosmos/ibc-go/v8/modules/apps/29-fee" | ||
"github.com/cosmos/ibc-go/v8/modules/apps/transfer" | ||
ibc "github.com/cosmos/ibc-go/v8/modules/core" | ||
) | ||
|
||
var ModuleBasics = []module.AppModuleBasic{ | ||
auth.AppModuleBasic{}, | ||
authz.AppModuleBasic{}, | ||
bank.AppModuleBasic{}, | ||
capability.AppModuleBasic{}, | ||
gov.NewAppModuleBasic( | ||
[]govclient.ProposalHandler{ | ||
paramsclient.ProposalHandler, | ||
}, | ||
), | ||
crisis.AppModuleBasic{}, | ||
distribution.AppModuleBasic{}, | ||
feegrant.AppModuleBasic{}, | ||
mint.AppModuleBasic{}, | ||
params.AppModuleBasic{}, | ||
slashing.AppModuleBasic{}, | ||
staking.AppModuleBasic{}, | ||
upgrade.AppModuleBasic{}, | ||
transfer.AppModuleBasic{}, | ||
ibc.AppModuleBasic{}, | ||
ibcfee.AppModuleBasic{}, | ||
AppModuleBasic{}, | ||
} | ||
|
||
type Codec struct { | ||
InterfaceRegistry types.InterfaceRegistry | ||
Marshaler codec.Codec | ||
TxConfig client.TxConfig | ||
Amino *codec.LegacyAmino | ||
} | ||
|
||
func MakeCodec(moduleBasics []module.AppModuleBasic, accBech32Prefix, valBech32Prefix string) Codec { | ||
modBasic := module.NewBasicManager(moduleBasics...) | ||
encodingConfig := MakeCodecConfig(accBech32Prefix, valBech32Prefix) | ||
std.RegisterLegacyAminoCodec(encodingConfig.Amino) | ||
std.RegisterInterfaces(encodingConfig.InterfaceRegistry) | ||
modBasic.RegisterLegacyAminoCodec(encodingConfig.Amino) | ||
modBasic.RegisterInterfaces(encodingConfig.InterfaceRegistry) | ||
|
||
return encodingConfig | ||
} | ||
|
||
func MakeCodecConfig(accBech32Prefix, valBech32Prefix string) Codec { | ||
interfaceRegistry, err := types.NewInterfaceRegistryWithOptions(types.InterfaceRegistryOptions{ | ||
ProtoFiles: proto.HybridResolver, | ||
SigningOptions: signing.Options{ | ||
AddressCodec: address.NewBech32Codec(accBech32Prefix), | ||
ValidatorAddressCodec: address.NewBech32Codec(valBech32Prefix), | ||
}, | ||
}) | ||
if err != nil { | ||
panic(err) | ||
} | ||
marshaler := codec.NewProtoCodec(interfaceRegistry) | ||
|
||
done := SetSDKConfigContext(accBech32Prefix) | ||
defer done() | ||
|
||
return Codec{ | ||
InterfaceRegistry: interfaceRegistry, | ||
Marshaler: marshaler, | ||
TxConfig: tx.NewTxConfig(marshaler, tx.DefaultSignModes), | ||
Amino: codec.NewLegacyAmino(), | ||
} | ||
} | ||
|
||
// This file is cursed and this mutex is too, you don't want none of this dewey cox. | ||
var sdkConfigMutex sync.Mutex | ||
|
||
// SetSDKContext sets the SDK config to the proper bech32 prefixes. | ||
// Don't use this unless you know what you're doing. | ||
func (c *Client) SetSDKContext() func() { | ||
return SetSDKConfigContext(c.AccountPrefix) | ||
} | ||
|
||
// SetSDKConfigContext sets the SDK config to the given bech32 prefixes. | ||
func SetSDKConfigContext(prefix string) func() { | ||
sdkConfigMutex.Lock() | ||
sdkConf := sdk.GetConfig() | ||
sdkConf.SetBech32PrefixForAccount(prefix, prefix+"pub") | ||
sdkConf.SetBech32PrefixForValidator(prefix+"valoper", prefix+"valoperpub") | ||
sdkConf.SetBech32PrefixForConsensusNode(prefix+"valcons", prefix+"valconspub") | ||
return sdkConfigMutex.Unlock | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package main | ||
|
||
type Config struct { | ||
Chains []ChainConfig `yaml:"chains" json:"chains"` | ||
} | ||
type ChainConfig struct { | ||
Name string `yaml:"name" json:"name"` | ||
ChainID string `yaml:"chain-id" json:"chain-id"` | ||
RPCAddress string `yaml:"rpc-address" json:"rpc-address"` | ||
AccountPrefix string `yaml:"account-prefix" json:"account-prefix"` | ||
Timeout string `yaml:"timeout" json:"timeout"` | ||
} |
Oops, something went wrong.