-
Notifications
You must be signed in to change notification settings - Fork 74
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
basic chain using tower-abci and penumbra stack #5
Conversation
this steps just moves everything out of `sources/` and places it into `crates/`. `cargo check` passes after adding making the crates workspace members in the root `Cargo.toml`. The crates do not refer to each other by path, and they are also not yet cleaned up.
70ee28a
to
ee39c01
Compare
@SuperFluffy I think these are all addressed; the only thing that may not be fully addressed is adding context for every error, as some errors already had context added lower-down on the stack so I wasn't sure if I should re-add another context. let me know if you want literally every error to have additional context and I can add! |
state_tx.put_block_height(0); | ||
|
||
// call init_chain on all components | ||
AccountsComponent::init_chain(&mut state_tx, &genesis_state).await?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have some sort of Component manager to define modules and the order they will be called in similar to the Cosmos manager?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the cosmos manager seems more needed because it's an SDK, and it needs to figure out what modules exist at runtime, whereas for us it's fine to just write them in and do it at compile time (this is what penumbra does too)
Ord, | ||
Debug, | ||
)] | ||
pub struct Nonce(u32); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use a u64 here? I believe ethereum uses uint64 for nonce, as does cosmos for sequence?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, can update, u64 just seemed a bit large lol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to plan on being successful than not!
} | ||
|
||
#[instrument(skip(self))] | ||
fn put_account_nonce(&mut self, address: &Address, nonce: Nonce) -> Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need a put_account_nonce
or should this have all the increment logic and be increment_account_nonce
? Is there ever an instance in which account nonce should go up by more than one at a time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we have an increment function then it'll have a get and a put, but usually wherever this function is called there's already a get, so it seems a bit redundant to have 2 gets as we want to minimize reads/writes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would we need the get if it was an increment? It seems we are only getting to do the increment.
Ok(()) | ||
} | ||
|
||
async fn execute<S: StateWriteExt>(&self, state: &mut S) -> Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there anything preventing the call of execute
without having first validated the tx? What happens if there are insufficient funds when this is called?
In general I wonder if structurally we should be moving more of the logic of "tx creates these DB level changes" directly into the state implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, this function is only called inside DeliverTx
, which always performs validation (stateless and non-stateless) before executing it. what do you mean by moving it directly into the state implementation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just want to make sure we are creating hardened code here. Should it be possible to call execute before validation? If no we should enforce that it isn't possible not hope someone doesn't forget that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have a write-up about how state gets transformed when moving through these components.
let from_balance = state.get_account_balance(&self.from).await?; | ||
let from_nonce = state.get_account_nonce(&self.from).await?; | ||
let to_balance = state.get_account_balance(&self.to).await?; | ||
state.put_account_balance(&self.from, from_balance - self.amount)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this is just that lower level, but it feels like maybe the logic for state transitions which has race possibilities should be handled by the state itself? If putting the balance succeeds but nonce fails, we have issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, so these writes are all happening on a StateDelta
, which is basically a list of pending writes that happen during a block's execution. the writes are all batched and only actually written to state once Commit
is called. so yeah this effectively is atomic afaik
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we create an issue to add tests for error cases here?
let from_nonce = state.get_account_nonce(&self.from).await?; | ||
let to_balance = state.get_account_balance(&self.to).await?; | ||
state.put_account_balance(&self.from, from_balance - self.amount)?; | ||
state.put_account_nonce(&self.from, from_nonce + Nonce::from(1))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be setting to the nonce of the transaction and validating that it is acceptable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the validation occurs outside this function in check_stateful
(which happens right before execute
is called) so I don't think we need to double validate
* mention that gcc12 is required * make items crate-private by default * add direnv rc to gitignore * remove dead code and unnecessary test the tests were essentially checking if the serde derive was successful * more expressive naming in genesis state * straight u128, not f64 cast * move genesis to its own module * no error handling in tests * define an apphash newtype wrapper like in penumbra * move services to their own module * WIP * add tracing to the tower abci server This commit vendors tracing-tower in the same way as penumbra has done. It then propagates the tracing spans as needed. * format tracing-tower * use penumbra tower trace * remove to_bytes, from_bytes, rely on borsh * provide context for everything * allow configuring listen_addr, genesis state through cli * clippy suggestion * rm forgotten uncomment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Great start for the chain overall.
I don't consider any of my comments blocking on this, but we should create issues and consider resolving separately.
My last commits fixed compilation in CI. The reason is that CI defines a The other commits are about fixing clippy lints. I also increased the number of lints that are used for |
To document for posteriority: I added context to literally every error I could find. |
tonic = "0.9" | ||
tracing = "0.1" | ||
tracing-subscriber = "0.3" | ||
uuid = "1.3.1" | ||
which = "4.4.0" | ||
|
||
[patch."https://github.com/penumbra-zone/penumbra.git"] | ||
penumbra-storage = { git = "https://github.com/astriaorg/penumbra.git", branch = "noot/fix-trace-v0.53.1" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you open PRs against penumbra and open an issue here so that we can track it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving to unblock this.
Opening an issue to fix rocksdb in CI and docker. See also these github issues:
rust-rocksdb/rust-rocksdb#310 (comment)
rust-rocksdb/rust-rocksdb#354 (comment)
Implements a basic ABCI application that communicates with a tendermint process. The application only has account logic at this stage, and only one transaction type
accounts::transaction::Transaction
which represents a value transfer. It usespenumbra_storage::Storage
as the state storage, andpenumbra_component::Component
to define an application component (in this case, there is only theAccountsComponent
).It uses the
tower_abci::Service
as the base layer which talks to tendermint. The application logic is built up around the requests made by tendermint.Currently, there are 3 accounts that have a hard-coded balance of 10e18 at genesis:
alice
,bob
, andcarol
.Usage:
Clone and build https://github.com/astriaorg/cometbft (
make install && make install_abci
)You can use
abci-cli
to query the application, for example, to query Alice's nonce: (note that the0xdeadbeef
is totally arbitrary, abci-cli requires some hex string as an arg :/)You can also curl endpoints directly:
After node has started, submit a tx:
Note: the tx was generated with the test
test_transaction
; will need to make a prettier CLI for thisInside cometbft you'll see the tx was included:
You'll also see a log inside the sequencer app:
Query Alice's nonce again
Nonce should increase after the tx was sent :D (note the value is borsh-serialized, so doesn't correspond to the raw value exactly)
You can set the path to
accounts/balance/(alice, bob, carol)
to check balances also