Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Phase4 #20

Merged
merged 42 commits into from
Dec 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
c78f627
Include commitment, connection and ics23 prost gen files
hu55a1n1 Sep 29, 2021
a8bb61f
Implement IBC Connection Reader/Keeper traits
hu55a1n1 Sep 29, 2021
08f5a6a
Add ibc-proto crate as dependency
hu55a1n1 Sep 29, 2021
fd50149
Implement Connection gRPC query server
hu55a1n1 Sep 30, 2021
6151dc0
Fix memory get impl bug
hu55a1n1 Sep 30, 2021
82ccfd4
Change serde for ConnectionEnd to use protobufs
hu55a1n1 Sep 30, 2021
92215ed
Provide placeholder proofs in ABCI query response
hu55a1n1 Sep 30, 2021
3952aba
Update Cargo.toml
hu55a1n1 Oct 6, 2021
81a76a0
Implement Tx simulate service
hu55a1n1 Oct 6, 2021
0899fd7
Allow modules to return query proofs
hu55a1n1 Oct 11, 2021
e5bc766
Implement proof support for InMemoryStore
hu55a1n1 Oct 11, 2021
bd5613c
Proof support for IBC module
hu55a1n1 Oct 11, 2021
a921346
Modify module query method to add proof arg
hu55a1n1 Oct 12, 2021
e78f7c7
Refine query proof impl for IBC module
hu55a1n1 Oct 12, 2021
75e45e4
Enable indexing for IBC events
hu55a1n1 Oct 12, 2021
3bc037c
Fix missing prefix for store proof
hu55a1n1 Oct 12, 2021
5a7d148
Update ibc deps
hu55a1n1 Oct 18, 2021
5c53a51
Return ConsensusStateNotFound error
hu55a1n1 Oct 25, 2021
9a4de4a
Implement next/prev_consensus_state()
hu55a1n1 Oct 25, 2021
114a79d
Patch IBC deps
hu55a1n1 Oct 25, 2021
25406a1
Placeholder impl for account nonce
hu55a1n1 Oct 25, 2021
50ff86c
Print committed app hash
hu55a1n1 Nov 2, 2021
dec0597
Modify Store::get_proof() to include height
hu55a1n1 Nov 2, 2021
ba775c4
Minor refactoring
hu55a1n1 Nov 2, 2021
80d3d15
Fix AVL store get_proof() for overwritten keys
hu55a1n1 Nov 4, 2021
f011cb3
Add trace log to get_proof()
hu55a1n1 Nov 4, 2021
3d628fc
Add store() and commit() method to Module trait
hu55a1n1 Nov 4, 2021
0c0023d
Implement sub-stores as separate stores with only commitments in main…
hu55a1n1 Nov 4, 2021
a84bc08
Depend on ibc-rs's basecoin/phase-4 branch
hu55a1n1 Nov 4, 2021
8f7716d
Fix clippy warnings
hu55a1n1 Nov 5, 2021
41a4b09
Remove generic prefix from SubStore
hu55a1n1 Nov 5, 2021
f3dc2ad
More cleanup
hu55a1n1 Nov 5, 2021
e7e8975
Change WalStore strategy to apply in-place and reset using log
hu55a1n1 Nov 6, 2021
baf6935
Update substore hash after commit
hu55a1n1 Nov 6, 2021
8ef7fbf
Refactor SubStore and update docs
hu55a1n1 Nov 8, 2021
6e03809
Remove redundant definitions of {next, prev}_consensus_state()
hu55a1n1 Nov 19, 2021
e8bb36b
Update ibc patched deps
hu55a1n1 Nov 19, 2021
5a948c5
Return old value from AVL set()
hu55a1n1 Nov 29, 2021
564fe24
Modify Store::set() to return old value for updates
hu55a1n1 Nov 29, 2021
10faf2d
Fix AVL set_node
hu55a1n1 Nov 29, 2021
c0c13df
Fix WALStore impl
hu55a1n1 Nov 29, 2021
3ea1bcb
Update ibc.md
hu55a1n1 Dec 6, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ description = """
use of tendermint-rs.
"""

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bytes = "1.0.1"
cosmrs = "0.3.0"
flex-error = { version = "0.4.2", features = [ "eyre_tracer" ] }
ibc = "0.8.0"
ics23 = "0.6.0"
ibc = "=0.8.0"
ibc-proto = "=0.12.0"
ics23 = "=0.6.7"
prost = "0.9.0"
prost-types = "0.9.0"
serde = "1.0"
Expand All @@ -34,3 +33,7 @@ tracing = "0.1.26"
tracing-subscriber = "0.2.18"
tonic="0.6"
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }

[patch.crates-io]
ibc = { git = "https://github.com/informalsystems/ibc-rs.git", branch = "basecoin/phase-4-1" }
ibc-proto = { git = "https://github.com/informalsystems/ibc-rs.git", branch = "basecoin/phase-4-1" }
64 changes: 60 additions & 4 deletions docs/modules/ibc.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ This module has been tested with `hermes`.
Edit your `genesis.json` file (default location `~/.tendermint/config/genesis.json`) to update the `chain_id`.
```json
{
"chain_id": "basecoin"
"chain_id": "basecoin-0"
}
```

Edit the `config.toml` file (default location `~/.hermes/config.toml`) for `hermes` and add an entry for the basecoin chain:
```toml
[[chains]]
id = 'basecoin'
id = 'basecoin-0'
rpc_addr = 'http://127.0.0.1:26357'
grpc_addr = 'http://127.0.0.1:9093'
websocket_addr = 'ws://localhost:26357/websocket'
Expand All @@ -29,6 +29,62 @@ store_prefix = 'basecoin'
gas_price = { price = 0.001, denom = 'stake' }
clock_drift = '5s'
trusting_period = '14days'
proof_specs = '''
[
{
"leaf_spec": {
"hash": 1,
"prehash_key": 0,
"prehash_value": 0,
"length": 0,
"prefix": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
},
"inner_spec": {
"child_order": [
0,
1,
2
],
"child_size": 32,
"min_prefix_length": 0,
"max_prefix_length": 64,
"empty_child": [
0,
32
],
"hash": 1
},
"max_depth": 0,
"min_depth": 0
},
{
"leaf_spec": {
"hash": 1,
"prehash_key": 0,
"prehash_value": 0,
"length": 0,
"prefix": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
},
"inner_spec": {
"child_order": [
0,
1,
2
],
"child_size": 32,
"min_prefix_length": 0,
"max_prefix_length": 64,
"empty_child": [
0,
32
],
"hash": 1
},
"max_depth": 0,
"min_depth": 0
}
]
'''
```
**Note:** The above settings must match the corresponding settings in Tendermint's `config.toml`.

Expand All @@ -47,6 +103,6 @@ $ hermes keys add basecoin-0 -f user_seed.json
### Step 4: Create and Update a client
Assuming the `basecoin-0` chain and tendermint are running (see instructions on [README.md#run-the-basecoin-app-and-tendermint](../../README.md#step-4-run-the-basecoin-app-and-tendermint)).
```shell
$ hermes tx raw create-client basecoin ibc-0
$ hermes tx raw update-client basecoin 07-tendermint-0
$ hermes tx raw create-client basecoin-0 ibc-0
$ hermes tx raw update-client basecoin-0 07-tendermint-0
```
155 changes: 125 additions & 30 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

pub(crate) mod modules;
mod response;

pub(crate) mod store;

use crate::app::modules::{prefix, Bank, Error, ErrorDetail, Ibc, Identifiable, Module};
use crate::app::response::ResponseFromErrorExt;
use crate::app::store::{Height, Path, ProvableStore, SharedStore, Store, SubStore, WalStore};
use crate::app::store::{
Height, Identifier, Path, ProvableStore, RevertibleStore, SharedStore, Store, SubStore,
};
use crate::prostgen::cosmos::auth::v1beta1::{
query_server::Query as AuthQuery, BaseAccount, QueryAccountRequest, QueryAccountResponse,
QueryAccountsRequest, QueryAccountsResponse, QueryParamsRequest as AuthQueryParamsRequest,
Expand All @@ -34,6 +35,11 @@ use crate::prostgen::cosmos::staking::v1beta1::{
QueryValidatorUnbondingDelegationsRequest, QueryValidatorUnbondingDelegationsResponse,
QueryValidatorsRequest, QueryValidatorsResponse,
};
use crate::prostgen::cosmos::tx::v1beta1::service_server::Service as TxService;
use crate::prostgen::cosmos::tx::v1beta1::{
BroadcastTxRequest, BroadcastTxResponse, GetTxRequest, GetTxResponse, GetTxsEventRequest,
GetTxsEventResponse, SimulateRequest, SimulateResponse,
};

use std::convert::TryInto;
use std::sync::{Arc, RwLock};
Expand All @@ -47,48 +53,56 @@ use tendermint_proto::abci::{
Event, RequestDeliverTx, RequestInfo, RequestInitChain, RequestQuery, ResponseCommit,
ResponseDeliverTx, ResponseInfo, ResponseInitChain, ResponseQuery,
};
use tendermint_proto::crypto::ProofOp;
use tendermint_proto::crypto::ProofOps;
use tendermint_proto::p2p::DefaultNodeInfo;
use tonic::{Request, Response, Status};
use tracing::{debug, info};

type MainStore<S> = SharedStore<RevertibleStore<S>>;
type ModuleStore<S> = SubStore<MainStore<S>>;
type Shared<T> = Arc<RwLock<T>>;

/// BaseCoin ABCI application.
///
/// Can be safely cloned and sent across threads, but not shared.
#[derive(Clone)]
pub(crate) struct BaseCoinApp<S> {
store: SharedStore<WalStore<S>>,
modules: Arc<RwLock<Vec<Box<dyn Module + Send + Sync>>>>,
store: MainStore<S>,
pub modules: Shared<Vec<Box<dyn Module<ModuleStore<S>> + Send + Sync>>>,
account: Shared<BaseAccount>, // TODO(hu55a1n1): get from user and move to provable store
}

impl<S: ProvableStore + 'static> BaseCoinApp<S> {
impl<S: Default + ProvableStore + 'static> BaseCoinApp<S> {
/// Constructor.
pub(crate) fn new(store: S) -> Self {
let store = SharedStore::new(WalStore::new(store));
pub(crate) fn new(store: S) -> Result<Self, S::Error> {
let store = SharedStore::new(RevertibleStore::new(store));
// `SubStore` guarantees modules exclusive access to all paths in the store key-space.
let modules: Vec<Box<dyn Module + Send + Sync>> = vec![
Box::new(Bank {
store: SubStore::new(store.clone(), prefix::Bank),
}),
Box::new(Ibc {
store: SubStore::new(store.clone(), prefix::Ibc),
client_counter: 0,
}),
let modules: Vec<Box<dyn Module<ModuleStore<S>> + Send + Sync>> = vec![
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For interest's sake, is there any other pattern that would work here that doesn't involve using trait objects?

No need to change this now, I was just wondering.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question! I've been thinking about this myself recently. 🤔 One possible alternative would be to have an enum with one variant per module, but that would imply that we know of all supported modules beforehand and that is not desirable if we think of this code as part of a framework.
Furthermore, ICS25 requires that ->

The module set should be dynamic: chains should be able to add and destroy modules, which can themselves bind to and unbind from ports, at will with a persistent IBC handler.

So I am not aware of a better alternative but happy to discuss other ideas/solutions.

Box::new(Bank::new(SubStore::new(
store.clone(),
prefix::Bank {}.identifier(),
)?)),
Box::new(Ibc::new(SubStore::new(
store.clone(),
prefix::Ibc {}.identifier(),
)?)),
];
Self {
Ok(Self {
store,
modules: Arc::new(RwLock::new(modules)),
}
account: Default::default(),
})
}
}

pub(crate) fn sub_store<I: Identifiable>(
&self,
prefix: I,
) -> SubStore<SharedStore<WalStore<S>>, I> {
SubStore::new(self.store.clone(), prefix)
impl<S: Default + ProvableStore> BaseCoinApp<S> {
pub(crate) fn get_store(&self, prefix: Identifier) -> Option<ModuleStore<S>> {
let modules = self.modules.read().unwrap();
let module = modules.iter().find(|m| m.store().prefix() == prefix);
module.map(|m| m.store())
}
}

impl<S> BaseCoinApp<S> {
// try to deliver the message to all registered modules
// if `module.deliver()` returns `Error::not_handled()`, try next module
// Return:
Expand Down Expand Up @@ -120,7 +134,7 @@ impl<S> BaseCoinApp<S> {
}
}

impl<S: ProvableStore + 'static> Application for BaseCoinApp<S> {
impl<S: Default + ProvableStore + 'static> Application for BaseCoinApp<S> {
fn info(&self, request: RequestInfo) -> ResponseInfo {
let (last_block_height, last_block_app_hash) = {
let state = self.store.read().unwrap();
Expand Down Expand Up @@ -171,16 +185,44 @@ impl<S: ProvableStore + 'static> Application for BaseCoinApp<S> {
&request.data,
path.as_ref(),
Height::from(request.height as u64),
request.prove,
) {
// success - implies query was handled by this module, so return response
Ok(result) => {
let store = self.store.read().unwrap();
let proof_ops = if request.prove {
let proof = store
.get_proof(
Height::from(request.height as u64),
&"ibc".to_owned().try_into().unwrap(),
)
.unwrap();
let mut buffer = Vec::new();
proof.encode(&mut buffer).unwrap(); // safety - cannot fail since buf is a vector

let mut ops = vec![];
if let Some(mut proofs) = result.proof {
ops.append(&mut proofs);
}
ops.push(ProofOp {
r#type: "".to_string(),
// FIXME(hu55a1n1)
key: "ibc".to_string().into_bytes(),
thanethomson marked this conversation as resolved.
Show resolved Hide resolved
data: buffer,
});
Some(ProofOps { ops })
} else {
None
};

return ResponseQuery {
code: 0,
log: "exists".to_string(),
key: request.data,
value: result,
height: self.store.read().unwrap().current_height() as i64,
..ResponseQuery::default()
value: result.data,
proof_ops,
height: store.current_height() as i64,
..Default::default()
};
}
// `Error::not_handled()` - implies query isn't known or was intercepted but not
Expand Down Expand Up @@ -240,9 +282,20 @@ impl<S: ProvableStore + 'static> Application for BaseCoinApp<S> {
}

fn commit(&self) -> ResponseCommit {
let mut modules = self.modules.write().unwrap();
for m in modules.iter_mut() {
m.commit().expect("failed to commit to state");
thanethomson marked this conversation as resolved.
Show resolved Hide resolved
}

let mut state = self.store.write().unwrap();
let data = state.commit().expect("failed to commit to state");
info!("Committed height {}", state.current_height() - 1);
info!(
"Committed height {} with hash({})",
state.current_height() - 1,
data.iter()
.map(|b| format!("{:02X}", b))
.collect::<String>()
);
ResponseCommit {
data,
retain_height: 0,
Expand Down Expand Up @@ -327,9 +380,10 @@ impl<S: ProvableStore + 'static> AuthQuery for BaseCoinApp<S> {
) -> Result<Response<QueryAccountResponse>, Status> {
debug!("Got auth account request");

let account = BaseAccount::default();
let mut account = self.account.write().unwrap();
let mut buf = Vec::new();
account.encode(&mut buf).unwrap(); // safety - cannot fail since buf is a vector
account.sequence += 1;

Ok(Response::new(QueryAccountResponse {
account: Some(Any {
Expand Down Expand Up @@ -457,3 +511,44 @@ impl<S: ProvableStore + 'static> StakingQuery for BaseCoinApp<S> {
}))
}
}

#[tonic::async_trait]
impl<S: ProvableStore + 'static> TxService for BaseCoinApp<S> {
async fn simulate(
&self,
request: Request<SimulateRequest>,
) -> Result<Response<SimulateResponse>, Status> {
// TODO(hu55a1n1): implement tx based simulate
let _: Tx = request
.into_inner()
.tx_bytes
.as_slice()
.try_into()
.map_err(|_| Status::invalid_argument("failed to deserialize tx"))?;
Ok(Response::new(SimulateResponse {
gas_info: None,
result: None,
}))
}

async fn get_tx(
&self,
_request: Request<GetTxRequest>,
) -> Result<Response<GetTxResponse>, Status> {
unimplemented!()
}

async fn broadcast_tx(
&self,
_request: Request<BroadcastTxRequest>,
) -> Result<Response<BroadcastTxResponse>, Status> {
unimplemented!()
}

async fn get_txs_event(
&self,
_request: Request<GetTxsEventRequest>,
) -> Result<Response<GetTxsEventResponse>, Status> {
unimplemented!()
}
}
Loading