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

block-version-based protocol evolution #1468

Conversation

cbeck88
Copy link
Contributor

@cbeck88 cbeck88 commented Feb 9, 2022

This makes transaction builder and validation aware of BlockVersion.
It also plumbs this config into consensus.

This commit adds support for the change in fog sample paykit
and mobilecoind

See also: mobilecoinfoundation/mcips#26

Checklist:

  • Rebase on master
  • Do the responder id parts
  • I made the block version be a parameter to things like form_block, but it might be better to do it differently, e.g. block version is announced on enclave startup, and can change after form_block but not in between calls to form block?
  • There is a place where I am taking a max of latest_block.version and configured block version, but perhaps it should only ever use the configured block version.
  • In many tests, I decided it was worth testing all acceptable values of block version, and in other places, I only tested BlockVersion(1) or BlockVersion::MAX because that seemed like the highest value testing. Review these decisions.

@cbeck88 cbeck88 requested review from jcape, remoun, mfaulk, eranrund, sugargoat and a team February 9, 2022 17:58
validate_memos_exist(tx)?;
} else {
validate_no_memos_exist(tx)?;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is one of the main points of the PR

@@ -65,10 +67,11 @@ impl<FPR: FogPubkeyResolver> TransactionBuilder<FPR> {
/// * `memo_builder` - An object which creates memos for the TxOuts in this
/// transaction
pub fn new<MB: MemoBuilder + 'static + Send + Sync>(
block_version: BlockVersion,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is one of the other main points of the PR

@@ -34,15 +38,15 @@ pub trait MemoBuilder: Debug {
value: u64,
recipient: &PublicAddress,
memo_context: MemoContext,
) -> Result<Option<MemoPayload>, NewMemoError>;
) -> Result<MemoPayload, NewMemoError>;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I changed this because in the new world it doesn't make sense for a memo builder to sometimes omit the memo and sometimes not -- the block version controls if the memo is present or not, and the memo builder can just write MemoPayload::default if they don't want to write any content. This also means NoMemoBuildergoes away and is replaced by EmptyMemoBuilder everywhere it was used, since it will do the right thing based on Block version.

let result = self.add_output_with_fog_hint_address(
value,
recipient,
recipient,
|memo_ctxt| mb.make_memo_for_output(value, recipient, memo_ctxt),
|memo_ctxt| {
if block_version.e_memo_feature_is_supported() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is where the transaction builder uses block version to decide whether or not to filter out memos

Copy link
Contributor

@jcape jcape left a comment

Choose a reason for hiding this comment

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

Comments on BlockVersion (namely, keep it private, and use try_from to check whether a version is supported -- this is where the "this version is not supported" check happens, rather than making everyone that's consuming a chain implement their own version).

transaction/core/src/blockchain/block_version.rs Outdated Show resolved Hide resolved
transaction/core/src/blockchain/block_version.rs Outdated Show resolved Hide resolved
transaction/core/src/blockchain/block_version.rs Outdated Show resolved Hide resolved
transaction/core/src/blockchain/block_version.rs Outdated Show resolved Hide resolved
@eranrund
Copy link
Contributor

eranrund commented Feb 9, 2022

Tip for reviewers: consider pulling locally and doing git diff --ignore-space-change master. A lot of the changes here are indentation and result in an enormous diff in GitHub.

@remoun
Copy link
Contributor

remoun commented Feb 9, 2022

Tip for reviewers: consider pulling locally and doing git diff --ignore-space-change master. A lot of the changes here are indentation and result in an enormous diff in GitHub.

Agreed. Fortunately GH also supports ignoring whitespace with ?w=1: https://github.com/mobilecoinfoundation/mobilecoin/pull/1468/files?diff=split&w=1

@eranrund
Copy link
Contributor

eranrund commented Feb 9, 2022

Tip for reviewers: consider pulling locally and doing git diff --ignore-space-change master. A lot of the changes here are indentation and result in an enormous diff in GitHub.

Agreed. Fortunately GH also supports ignoring whitespace with ?w=1: https://github.com/mobilecoinfoundation/mobilecoin/pull/1468/files?diff=split&w=1

TIL! Thanks!

@cbeck88 cbeck88 force-pushed the block-version-aware-tx-builder-validation branch 2 times, most recently from 2e3277b to 86c37a3 Compare February 16, 2022 16:40
@@ -122,7 +123,7 @@ impl Block {
/// * `root_element` - The root element for membership proofs
/// * `block_contents` - Contents of the block.
pub fn new(
version: u32,
version: BlockVersion,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we could consider that Block doesn't use a BlockVersion and uses a u32 instead, since it is using u32 internally. that would make the ledger db test around adding too recent blocks still possible.

Copy link
Contributor

Choose a reason for hiding this comment

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

I personally prefer this version with the transmute hack in the test.

Copy link
Contributor

Choose a reason for hiding this comment

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

+1 for keeping BlockVersion and using the hack in test

let mut ledger = create_ledger();
let n_blocks = 1;
initialize_ledger(&mut ledger, n_blocks, &sender, &mut rng);
initialize_ledger(
adapt_hack(&block_version),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would really like to refactor to kill off this adapt_hack stuff, I think we just have to reorganize the crates a little bit.

Copy link
Contributor

Choose a reason for hiding this comment

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

That would be nice (in a followup PR)

@cbeck88 cbeck88 changed the title proof of concept for block-version-based protocol evolution block-version-based protocol evolution Feb 16, 2022
@cbeck88
Copy link
Contributor Author

cbeck88 commented Feb 16, 2022

I think this may be ready to go, I fixed up all the consensus-service plumbing:

  • enclave_init takes the BlockVersion and it is not an argument to form_block or the tx validation routine, instead it is stored in the enclave and can't be changed after init.
  • That's better because the BlockVersion needs to go into the responder id, and the idea right now is that the network needs to be restarted to change the BlockVersion. (We can investigate protocol version scp statements in a later PR)
  • BlockVersion now has strong invariants and uses #[serde(try_from = "u32")] to prevent skirting them
  • Most of the changes are now in tests. There is some question if Block::new( should actually take a BlockVersion.

let invalid_block = Block::new_with_parent(
last_block.version + 1,
unsafe { core::mem::transmute(last_block.version + 1) },
Copy link
Contributor Author

@cbeck88 cbeck88 Feb 16, 2022

Choose a reason for hiding this comment

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

let me know if you guys have feelings about this, i think this is a valuable test to keep running, and this lets us bypass the invariant enforced by BlockVersion that the version can't exceed BlockVersion::MAX

Copy link
Contributor Author

@cbeck88 cbeck88 Feb 16, 2022

Choose a reason for hiding this comment

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

i guess another option is to make add_block function of ledger db not take a BlockVersion, and just take a u32 and internally convert it to a BlockVersion

Copy link
Contributor

Choose a reason for hiding this comment

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

I think its better to keep using the proper type and doing this hack in the test code.

consensus/enclave/impl/src/lib.rs Outdated Show resolved Hide resolved
crypto/digestible/src/lib.rs Outdated Show resolved Hide resolved
crypto/digestible/src/lib.rs Outdated Show resolved Hide resolved
transaction/core/src/blockchain/block_version.rs Outdated Show resolved Hide resolved
fog/sample-paykit/src/cached_tx_data/mod.rs Outdated Show resolved Hide resolved
@cbeck88
Copy link
Contributor Author

cbeck88 commented Feb 18, 2022

Here's the comparison with deployment of current master to the same network
Screenshot at 2022-02-18 14-28-29
Screenshot at 2022-02-18 14-28-52
Screenshot at 2022-02-18 14-29-00

@cbeck88
Copy link
Contributor Author

cbeck88 commented Feb 18, 2022

it looks maybe a little worse to me towards the end of the slam, but peak is still ~40 tps, not sure if the rest of the graph is statistically significant

@cbeck88
Copy link
Contributor Author

cbeck88 commented Feb 18, 2022

i'm going to try deploying this PR a second time

Copy link
Contributor

@eranrund eranrund left a comment

Choose a reason for hiding this comment

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

LGTM. I think it's okay to merge even if performance is a bit degraded because this is an important piece in many upcoming changes and releases. It would be great if we could find the time to do some profiling of stuff in the enclave and consensus-service. It's been awhile since we went through that exercise..

@cbeck88
Copy link
Contributor Author

cbeck88 commented Feb 18, 2022

Here are the results from the second deploy (of this PR):
Screenshot at 2022-02-18 16-05-06
Screenshot at 2022-02-18 16-05-14
Screenshot at 2022-02-18 16-05-28

@eranrund
Copy link
Contributor

20s tx completion time is a pretty big difference than 15s I am used to seeing...

@cbeck88
Copy link
Contributor Author

cbeck88 commented Feb 18, 2022

@eranrund yeah i'm not sure what could be causing this, maybe its because we are holding the mutex for the fees for a longer period of time?

@cbeck88
Copy link
Contributor Author

cbeck88 commented Feb 18, 2022

maybe we should try replacing the Mutex<Option<...>> for the config with a OnceCell

@eranrund
Copy link
Contributor

@eranrund yeah i'm not sure what could be causing this, maybe its because we are holding the mutex for the fees for a longer period of time?

Interesting, maybe?
You could try moving this https://github.com/garbageslam/mobilecoin/blob/block-version-aware-tx-builder-validation/consensus/enclave/impl/src/lib.rs#L367-L371 into a scoped block and cloning the stuff you need outside of the scope (just the minimum fee and block version)
Should be an easy test and seems like thats a good change regardless.

@cbeck88
Copy link
Contributor Author

cbeck88 commented Feb 19, 2022

I just made a quick patch like this:

cbeck88#4

and pushed it to build, let's see what happens

(I didn't see your suggestion before I started working on it this way)

@cbeck88
Copy link
Contributor Author

cbeck88 commented Feb 19, 2022

That makes the TPS look better, the completion time is still not 15 though... but better than before
Screenshot at 2022-02-18 17-52-56
Screenshot at 2022-02-18 17-53-03
Screenshot at 2022-02-18 17-53-12

Copy link
Contributor

@jcape jcape left a comment

Choose a reason for hiding this comment

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

This generally looks good to me, the only real question is what BlockVersion::default() should actually default to---it feels to me like it should be ONE (or MIN, or whatever), not MAX, but I'm also not sure where it's actually used...

use mc_transaction_core::{tokens::Mob, Token, TokenId};
use serde::{Deserialize, Serialize};

/// A thread-safe object that contains a map of fee value by token id.
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
#[derive(Clone, Debug, Deserialize, Digestible, Eq, Hash, PartialEq, Serialize)]
pub struct FeeMap {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this get reduced to a newtype struct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Probably but lets do it in a subsequent PR, it's kind of orthogonal to this PR, and there's a lot more token config stuff coming down the pipeline

fn default() -> Self {
Self {
fee_map: FeeMap::default(),
block_version: BlockVersion::MAX,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we'd want this to be ::MIN, right?

Copy link
Contributor Author

@cbeck88 cbeck88 Feb 19, 2022

Choose a reason for hiding this comment

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

Remoun had suggested ::MAX in earlier review. (IIRC)
I think the default doesn't actually matter here, the actual default in the enclave is None basically, and the struct_opt default I think is 1 for backwards compat

consensus/enclave/api/src/error.rs Outdated Show resolved Hide resolved
consensus/enclave/impl/src/lib.rs Outdated Show resolved Hide resolved
@@ -564,7 +599,7 @@ impl ConsensusEnclave for SgxConsensusEnclave {

// Form the block.
let block = Block::new_with_parent(
Copy link
Contributor

Choose a reason for hiding this comment

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

If we want to do this, can we make a ticket for it?

consensus/enclave/impl/src/lib.rs Show resolved Hide resolved
consensus/enclave/impl/src/lib.rs Outdated Show resolved Hide resolved
use mc_util_from_random::FromRandom;
use rand::{rngs::StdRng, SeedableRng};
use rand_core::RngCore;
use tempdir::TempDir;
use test::Bencher;

// TODO: Should these tests run over several block versions?
const BLOCK_VERSION: BlockVersion = BlockVersion::ONE;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this used? The tests below appear to all use BlockVersion::ONE directly...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In my search I see a lot of uses of BLOCK_VERSION in this file and no uses of BlockVersion::ONE. If you prefer we can mass replace them

@cbeck88
Copy link
Contributor Author

cbeck88 commented Feb 19, 2022

I ran it through deploy again -- I think it's possible that when I tested the once-cell change i didn't deploy the commit I thought I was deploying. These numbers look better (after merging the once-cell part to the PR)

Screenshot at 2022-02-19 12-06-14
Screenshot at 2022-02-19 12-06-27
Screenshot at 2022-02-19 12-06-37

@cbeck88 cbeck88 merged commit b3e6620 into mobilecoinfoundation:master Feb 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

5 participants