Skip to content

Commit

Permalink
Implemented GRPC client to query account sequence (#361)
Browse files Browse the repository at this point in the history
* Added logic to generate GRPC client from cosmos.auth proto (#337)

* Adding logic to use GRPC client (#337)

* Grpc client connection retrieves account sequence (#337)

* Removed the account sequence flag from the tx raw commands (#337)

* Removed instructions to query and specify account sequence from tx raw command (#337)

* Logic to fetch GRPC endpoint address from config (#337)

* Fixing tests (#361)

* Logic to use the address from the key seed (#337)

* Update readme

* Fix the tx output strings

* Fix update client

Co-authored-by: Anca Zamfir <zamfiranca@gmail.com>
  • Loading branch information
andynog and ancazamfir committed Nov 6, 2020
1 parent 5c3dbe2 commit 6c933e9
Show file tree
Hide file tree
Showing 23 changed files with 245 additions and 95 deletions.
4 changes: 4 additions & 0 deletions proto-compiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ prost-build = "0.6"
tempdir = "0.3.7"
walkdir = "2.3"
argh = "0.1.3"
tonic = "0.3.1"

#[build-dependencies]
tonic-build = "0.3.1"
34 changes: 34 additions & 0 deletions proto-compiler/src/cmd/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ impl CompileCmd {

Self::output_sdk_version(&self.sdk, tmp.as_ref());
Self::compile_protos(&self.sdk, tmp.as_ref());
Self::compile_proto_services(&self.sdk, &tmp.as_ref());
Self::copy_generated_files(tmp.as_ref(), &self.out);
}

Expand Down Expand Up @@ -87,6 +88,39 @@ impl CompileCmd {
config.compile_protos(&protos, &includes).unwrap();
}

fn compile_proto_services(sdk_dir: impl AsRef<Path>, out_dir: impl AsRef<Path>) {

let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let sdk_dir = sdk_dir.as_ref().to_owned();

let proto_includes_paths = [
root.join("../proto"),
sdk_dir.join("proto"),
sdk_dir.join("third_party/proto"),
];

// List available paths for dependencies
let includes = proto_includes_paths.iter().map(|p| p.as_os_str().to_os_string()).collect::<Vec<_>>();

let proto_services_path = [
sdk_dir.join("proto/cosmos/auth/v1beta1/query.proto")
];

// List available paths for dependencies
let services = proto_services_path.iter().map(|p| p.as_os_str().to_os_string()).collect::<Vec<_>>();

// Compile all proto client for GRPC services
println!("[info ] Compiling proto clients for GRPC services!");
tonic_build::configure()
.build_client(true)
.build_server(false)
.format(false)
.out_dir(out_dir)
.compile(&services, &includes).unwrap();

println!("[info ] => Done!");
}

fn copy_generated_files(from_dir: &Path, to_dir: &Path) {
println!(
"[info ] Copying generated files into '{}'...",
Expand Down
2 changes: 2 additions & 0 deletions proto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ prost-types = { version = "0.6" }
anomaly = "0.2"
bytes = "0.6"
thiserror = "1.0"
tonic = "0.3.1"

[dependencies.tendermint-proto]
version = "0.17.0-rc1"
git = "https://github.com/informalsystems/tendermint-rs"
branch = "greg/research-json"


5 changes: 5 additions & 0 deletions proto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
pub const COSMOS_SDK_VERSION: &str = include_str!("prost/COSMOS_SDK_COMMIT");

pub mod cosmos {
pub mod auth {
pub mod v1beta1 {
include!("prost/cosmos.auth.v1beta1.rs");
}
}
pub mod base {
pub mod abci {
pub mod v1beta1 {
Expand Down
2 changes: 1 addition & 1 deletion proto/src/prost/COSMOS_SDK_COMMIT
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<unknown>
15324920548c2629e51d837bcefc1cbc40797c5d
64 changes: 64 additions & 0 deletions proto/src/prost/cosmos.auth.v1beta1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/// BaseAccount defines a base account type. It contains all the necessary fields
/// for basic account functionality. Any custom account type should extend this
/// type for additional functionality (e.g. vesting).
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BaseAccount {
#[prost(string, tag="1")]
pub address: std::string::String,
#[prost(message, optional, tag="2")]
pub pub_key: ::std::option::Option<::prost_types::Any>,
#[prost(uint64, tag="3")]
pub account_number: u64,
#[prost(uint64, tag="4")]
pub sequence: u64,
}
/// ModuleAccount defines an account for modules that holds coins on a pool.
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ModuleAccount {
#[prost(message, optional, tag="1")]
pub base_account: ::std::option::Option<BaseAccount>,
#[prost(string, tag="2")]
pub name: std::string::String,
#[prost(string, repeated, tag="3")]
pub permissions: ::std::vec::Vec<std::string::String>,
}
/// Params defines the parameters for the auth module.
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Params {
#[prost(uint64, tag="1")]
pub max_memo_characters: u64,
#[prost(uint64, tag="2")]
pub tx_sig_limit: u64,
#[prost(uint64, tag="3")]
pub tx_size_cost_per_byte: u64,
#[prost(uint64, tag="4")]
pub sig_verify_cost_ed25519: u64,
#[prost(uint64, tag="5")]
pub sig_verify_cost_secp256k1: u64,
}
/// QueryAccountRequest is the request type for the Query/Account RPC method.
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct QueryAccountRequest {
/// address defines the address to query for.
#[prost(string, tag="1")]
pub address: std::string::String,
}
/// QueryAccountResponse is the response type for the Query/Account RPC method.
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct QueryAccountResponse {
/// account defines the account of the corresponding address.
#[prost(message, optional, tag="1")]
pub account: ::std::option::Option<::prost_types::Any>,
}
/// QueryParamsRequest is the request type for the Query/Params RPC method.
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct QueryParamsRequest {
}
/// QueryParamsResponse is the response type for the Query/Params RPC method.
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct QueryParamsResponse {
/// params defines the parameters of the module.
#[prost(message, optional, tag="1")]
pub params: ::std::option::Option<Params>,
}
# [doc = r" Generated client implementations."] pub mod query_client { # ! [allow (unused_variables , dead_code , missing_docs)] use tonic :: codegen :: * ; # [doc = " Query defines the gRPC querier service."] pub struct QueryClient < T > { inner : tonic :: client :: Grpc < T > , } impl QueryClient < tonic :: transport :: Channel > { # [doc = r" Attempt to create a new client by connecting to a given endpoint."] pub async fn connect < D > (dst : D) -> Result < Self , tonic :: transport :: Error > where D : std :: convert :: TryInto < tonic :: transport :: Endpoint > , D :: Error : Into < StdError > , { let conn = tonic :: transport :: Endpoint :: new (dst) ? . connect () . await ? ; Ok (Self :: new (conn)) } } impl < T > QueryClient < T > where T : tonic :: client :: GrpcService < tonic :: body :: BoxBody > , T :: ResponseBody : Body + HttpBody + Send + 'static , T :: Error : Into < StdError > , < T :: ResponseBody as HttpBody > :: Error : Into < StdError > + Send , { pub fn new (inner : T) -> Self { let inner = tonic :: client :: Grpc :: new (inner) ; Self { inner } } pub fn with_interceptor (inner : T , interceptor : impl Into < tonic :: Interceptor >) -> Self { let inner = tonic :: client :: Grpc :: with_interceptor (inner , interceptor) ; Self { inner } } # [doc = " Account returns account details based on address."] pub async fn account (& mut self , request : impl tonic :: IntoRequest < super :: QueryAccountRequest > ,) -> Result < tonic :: Response < super :: QueryAccountResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.auth.v1beta1.Query/Account") ; self . inner . unary (request . into_request () , path , codec) . await } # [doc = " Params queries all parameters."] pub async fn params (& mut self , request : impl tonic :: IntoRequest < super :: QueryParamsRequest > ,) -> Result < tonic :: Response < super :: QueryParamsResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/cosmos.auth.v1beta1.Query/Params") ; self . inner . unary (request . into_request () , path , codec) . await } } impl < T : Clone > Clone for QueryClient < T > { fn clone (& self) -> Self { Self { inner : self . inner . clone () , } } } impl < T > std :: fmt :: Debug for QueryClient < T > { fn fmt (& self , f : & mut std :: fmt :: Formatter < '_ >) -> std :: fmt :: Result { write ! (f , "QueryClient {{ ... }}") } } }
Empty file added proto/src/prost/cosmos_proto.rs
Empty file.
20 changes: 10 additions & 10 deletions proto/src/prost/ibc.applications.transfer.v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ pub struct Params {
#[prost(bool, tag="2")]
pub receive_enabled: bool,
}
/// GenesisState defines the ibc-transfer genesis state
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GenesisState {
#[prost(string, tag="1")]
pub port_id: std::string::String,
#[prost(message, repeated, tag="2")]
pub denom_traces: ::std::vec::Vec<DenomTrace>,
#[prost(message, optional, tag="3")]
pub params: ::std::option::Option<Params>,
}
/// QueryDenomTraceRequest is the request type for the Query/DenomTrace RPC
/// method
#[derive(Clone, PartialEq, ::prost::Message)]
Expand Down Expand Up @@ -118,13 +128,3 @@ pub struct QueryParamsResponse {
#[prost(message, optional, tag="1")]
pub params: ::std::option::Option<Params>,
}
/// GenesisState defines the ibc-transfer genesis state
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GenesisState {
#[prost(string, tag="1")]
pub port_id: std::string::String,
#[prost(message, repeated, tag="2")]
pub denom_traces: ::std::vec::Vec<DenomTrace>,
#[prost(message, optional, tag="3")]
pub params: ::std::option::Option<Params>,
}
2 changes: 0 additions & 2 deletions proto/src/prost/ibc.lightclients.solomachine.v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ pub struct SignatureAndData {
pub data_type: i32,
#[prost(bytes, tag="3")]
pub data: std::vec::Vec<u8>,
#[prost(uint64, tag="4")]
pub timestamp: u64,
}
/// TimestampedSignatureData contains the signature data and the timestamp of the
/// signature.
Expand Down
55 changes: 20 additions & 35 deletions relayer-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,23 @@ In order to run the Relayer please ensure you have [Rust installed on your machi

### Submitting an IBC transaction

The `tx raw conn-init` command works now. Signing the message is working and the gaia chain (stargate-4) accepts the transaction.
There are a few `tx raw` commands that build IBC datagrams and submit them to the chains (`dest_chain_id` below).

The command accepts two parameters that allows you to send a transaction:
```shell script
relayer-cli -c config.toml tx raw create-client dest_chain_id src_chain_id dest_client_id -k seed_file.json

* **signer-key** (-k) -> specify a key file (name and location) that will be used by the signer. This key seed file has a mnemonic (seed phrase) that can be used to retrieve the private key (BIP-39) used to sign the transaction.
* **account_sequence** (-s) -> this is the account sequence value, basically every time a tx is committed by the account this number increases.
relayer-cli -c config.toml tx raw create-client dest_chain_id src_chain_id dest_client_id -k seed_file.json

#### Steps to testing the transaction:
relayer-cli -c config.toml tx raw conn-init dest_chain_id src_chain_id dest_client_id src_client_id dest_connection_id -d src_connection_id
-k seed_file.json
```

Note: This is work in progress, more commands will be implemented and tested with gaia stargate-4 chains.
As shown above the tx commands currently require specifying a seed file:

* **seed_file** (-k) -> specify a key file (name and location) that will be used by the signer. This key seed file must include a mnemonic (seed phrase) that can be used to retrieve the private key (BIP-39) used to sign the transaction.

#### Steps to testing the transactions:

* Start two chains using the `dev-env` script from the [ovrclk/relayer](https://github.com/ovrclk/relayer) (make sure to checkout stargate-4 version)
* After you run the script, the Go relayer will create a `data` folder for the chains. Open the key seed file `./data/ibc1/key_seed.json` for chain `ibc-1` and look for the account value
Expand All @@ -31,42 +40,23 @@ The command accepts two parameters that allows you to send a transaction:
"mnemonic":"[MNEMONIC WORDS"}
}

* Run the transaction command. In this example, it will try to initialize an `ibczeroconn2` connection on chain `ibc1`

* In order to find the account sequence run the command below:

For the address value after `gaiad query account` use the `address` from the step above.

`$ gaiad query account cosmos1tqzwwr5hrnq2ceg5fg52m720m50xpfy08at7l9 --home ./data/ibc1 --chain-id ibc1 --node tcp://localhost:26557`

This will return a JSON with a sequence number at the end. Make a note of that, you will need this number as an argument to the transaction command.


'@type': /cosmos.auth.v1beta1.BaseAccount
account_number: "0"
address: cosmos1tqzwwr5hrnq2ceg5fg52m720m50xpfy08at7l9
pub_key:
'@type': /cosmos.crypto.secp256k1.PubKey
key: A87prNmWP9pdpYwWjjXPVdbQ9KzlOqbA5rM2gdhjC78t
sequence: "12"


* Run the transaction command. This will try to initialize an `ibczeroconn2` connection on chain `ibc1`

`$ cargo run --bin relayer -- -c ./relayer-cli/tests/fixtures/two_chains.toml tx raw conn-init ibc0 ibc1 ibczeroclient ibconeclient ibczeroconn2 ibconeconn -s 12 -k key_seed.json`
`$ cargo run --bin relayer -- -c ./relayer-cli/tests/fixtures/two_chains.toml tx raw conn-init ibc1 ibc0 ibczeroclient ibconeclient ibczeroconn2 -d ibconeconn -k key_seed.json`

If you get an empty response it means the tx worked

`conn init, result: []`

* Check if the connection was created on `ibc-1` using the Golang relayer
* Check if the connection was created on `ibc-1`:

`$ rly query connections ibc1 | jq .`
`$ cargo run --bin relayer -- -c ./relayer-cli/tests/fixtures/two_chains.toml query connection end ibc1 ibczeroconn2 | jq .`

If you see an entry in the JSON file that points to the `ibczeroconn2` connection with state `STATE_INIT` it confirms that the transaction worked:


{
"id": "ibczeroconn21",
"id": "ibczeroconn2",
"client_id": "ibczeroclient",
"versions": [
"\n\u00011\u0012\rORDER_ORDERED\u0012\u000fORDER_UNORDERED"
Expand All @@ -80,9 +70,4 @@ The command accepts two parameters that allows you to send a transaction:
}
}
},





4 changes: 2 additions & 2 deletions relayer-cli/src/commands/tx/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ impl Runnable for TxCreateClientCmd {
create_client(opts).map_err(|e| Kind::Tx.context(e).into());

match res {
Ok(receipt) => status_ok!("Success", "client updated: {:?}", receipt),
Err(e) => status_err!("client update failed: {}", e),
Ok(receipt) => status_ok!("Success", "client created: {:?}", receipt),
Err(e) => status_err!("client create failed: {}", e),
}
}
}
Expand Down
9 changes: 2 additions & 7 deletions relayer-cli/src/commands/tx/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ pub struct TxRawConnInitCmd {
#[options(help = "identifier of the source connection", short = "d")]
src_connection_id: Option<ConnectionId>,

#[options(help = "account sequence of the signer", short = "s")]
account_sequence: u64,

#[options(
help = "json key file for the signer, must include mnemonic",
short = "k"
Expand All @@ -46,7 +43,6 @@ impl TxRawConnInitCmd {
.iter()
.find(|c| c.id == self.dest_chain_id.parse().unwrap())
.ok_or_else(|| "missing destination chain configuration".to_string())?;

let src_chain_config = config
.chains
.iter()
Expand All @@ -65,7 +61,6 @@ impl TxRawConnInitCmd {
dest_chain_config: dest_chain_config.clone(),
src_chain_config: src_chain_config.clone(),
signer_seed,
account_sequence: self.account_sequence,
};

Ok(opts)
Expand All @@ -88,8 +83,8 @@ impl Runnable for TxRawConnInitCmd {
let res: Result<Vec<u8>, Error> = conn_init(&opts).map_err(|e| Kind::Tx.context(e).into());

match res {
Ok(receipt) => status_ok!("Success", "client updated: {:?}", receipt),
Err(e) => status_err!("client update failed: {}", e),
Ok(receipt) => status_info!("conn init, result: ", "{:?}", receipt),
Err(e) => status_info!("conn init failed, error: ", "{}", e),
}
}
}
Loading

0 comments on commit 6c933e9

Please sign in to comment.