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

[types] add ChainID to user txn #4969

Closed
wants to merge 3 commits into from

Conversation

christinacelee
Copy link

@christinacelee christinacelee commented Jul 8, 2020

Motivation

Adding ChainId to a user txn, to prevent txns potentially being replayed across networks (chains). The ChainID is represented as a u8

In addition to the regular ChainId, this PR also adds the ReservedChain type to specify chain IDs that are reserved. This enum type is primarily used to improve human readability of chain ID in config files and CLI (is used as an adaptor from string to the actual u8 representation of the chain ID).

Test plan

Checked that CLI and config can consume both human-readable and numeric input of chain ID

@christinacelee
Copy link
Author

some of the changes are uncharted territory for me personally, especially KeyManager/secure/config things, so please take a look and make sure I'm not doing something obviously clowny! @JoshLind

Copy link
Contributor

@gregnazario gregnazario left a comment

Choose a reason for hiding this comment

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

lgtm, let's have someone else look over it


impl Display for ChainId {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "ChainId {}", self.0)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we just print the ChainId number without the word ChainId?

Copy link
Author

Choose a reason for hiding this comment

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

I thought ChainId might make things easier to debug (having just the u64 might be unhelpful?), others can weigh in too

Copy link
Contributor

Choose a reason for hiding this comment

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

if it's an enum we'll get the chainId name directly :)

Copy link
Contributor

@mimoo mimoo Jul 9, 2020

Choose a reason for hiding this comment

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

actually I'm thinking that we might want to go further, it'd be good if we can just have "mainnet" or "testnet" as chainid in CLIs or config files : o wdyt? (it sounds like having a number would make reading config files harder)

Comment on lines 17 to 79
impl Default for ChainId {
fn default() -> Self {
ChainId::new(1)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

If the MOCK_CHAIN_ID and the default are going to be the same ID, should we just use the constant for both?

Copy link
Contributor

Choose a reason for hiding this comment

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

+1 (I'm a big fan of just using constants where applicable to remove ambiguity about what ID is actually being picked)

};

#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ChainId(pub u64);
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 it will still work without the u64 as pub

Copy link
Contributor

Choose a reason for hiding this comment

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

enum is better than a u64 imo

Copy link
Contributor

@mimoo mimoo Jul 9, 2020

Choose a reason for hiding this comment

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

agree, I think we should use an enum here, similar to what we did for NetworkId IIRC

Comment on lines -592 to +599
chain_id.clone(),
chain_id,
Copy link
Contributor

Choose a reason for hiding this comment

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

A nice side effect of it now being a number.

@@ -32,6 +34,7 @@ impl Default for KeyManagerConfig {
secure_backend: SecureBackend::InMemoryStorage,
sleep_period_secs: DEFAULT_SLEEP_PERIOD_SECS,
txn_expiration_secs: DEFAULT_TXN_EXPIRATION_SECS,
chain_id: ChainId::mainnet(),
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd be concerned with a default that connects to the mainnet chain. I'll let @JoshLind make the call on that one though.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have strong feelings, but I think this should probably match whatever default we're using in the actual validator/node configs.

Copy link
Contributor

@zekun000 zekun000 left a comment

Choose a reason for hiding this comment

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

not for this pr, but it's worth discussing whether we want to read the chain id from genesis upon start to eliminate two source of truth (config + genesis)

@sblackshear
Copy link
Contributor

@zekun000 I think we should definitely read it from genesis, otherwise validators with different configs will disagree...

@zekun000
Copy link
Contributor

zekun000 commented Jul 9, 2020

sounds like a good on chain config value :)


#[cfg(any(test, feature = "fuzzing"))]
pub fn mock() -> Self {
ChainId::new(MOCK_CHAIN_ID)
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like we tend to use "mock" for components, and "testing" for values

@mimoo
Copy link
Contributor

mimoo commented Jul 9, 2020

Good job on the PR! This looks good to me (besides the ChainId u64 -> Enum), as this is touching a lot of stuff I feel like someone else than me should approve this :D

not for this pr, but it's worth discussing whether we want to read the chain id from genesis upon start to eliminate two source of truth (config + genesis)

I would prefer that we have it in both the config AND the genesis, this way if my config says "testnet" but I start with the "mainnet" genesis then it won't work. A good way to avoid configuration mistakes and be explicit about what we're doing.

@sblackshear
Copy link
Contributor

Good job on the PR! This looks good to me (besides the ChainId u64 -> Enum), as this is touching a lot of stuff I feel like someone else than me should approve this :D

I also prefer enums to plain numbers in general, but Move does not have enums... So I would vote for sticking with the u64 to avoid Rust -> Move conversion surprises.

I would prefer that we have it in both the config AND the genesis, this way if my config says "testnet" but I start with the "mainnet" genesis then it won't work. A good way to avoid configuration mistakes and be explicit about what we're doing.

Makes sense, but at what point in the code should we compare genesis to the config and fail?

@davidiw
Copy link
Contributor

davidiw commented Jul 9, 2020

Thanks for pushing this PR out! It has been a loooong time coming :D!!!!

How did we land on using a u64 to represent the ChainId versus the string earlier? My guess is so that it could be incorporated into the transaction at low cost?

Couple of discussion thoughts / topics:

  • How do we distinguish against the internal / external / third-party chains?
  • How do we ensure that admins properly update this value in their config?

One idea we had floating around was to make the ChainId deterministic computation of shared data plus some fixed shared counter. E.g., "ledger version:ledger hash:counter", but that would be a lot of fields and in a string, so not that great... Have we considered a hash?

Also one concern about this being within an "on-chain config" is -- would that make it mutable? Is that a property we want to have? Is it possible to have immutable values in on-chain configs?

@moeziniaf moeziniaf self-requested a review July 9, 2020 11:18
@sblackshear
Copy link
Contributor

Also one concern about this being within an "on-chain config" is -- would that make it mutable? Is that a property we want to have? Is it possible to have immutable values in on-chain configs?

Agreed that we probably don't want this to be an on-chain config because it's immutable. It should just be an ordinary constant written at genesis.

@zekun000
Copy link
Contributor

zekun000 commented Jul 9, 2020

I'm a little confused about immutability, if the mainnet forks do we want to change the chain id e.g. a fork enabling more risk smart contracts? from what I heard from @aching we do, or we simply change it by writeset.

@aching aching self-requested a review July 9, 2020 17:42
@aching
Copy link
Contributor

aching commented Jul 9, 2020

I think @sblackshear is right (immutable). Supporting easy forks doesn't seem to be a use case. Writeset seems reasonable to me.

@aching
Copy link
Contributor

aching commented Jul 9, 2020

How did we land on using a u64 to represent the ChainId versus the string earlier? My guess is so that it could be incorporated into the transaction at low cost?

u64, u32, enum, etc. become a single byte if mainnet is 0.

Couple of discussion thoughts / topics:

  • How do we distinguish against the internal / external / third-party chains?

I think the only idea is LA will define a set of chains (i.e. mainnet = 0). Any other chain can pick any id. Perhaps if we want LA could also define ranges for private chains, testnet chains, etc.

  • How do we ensure that admins properly update this value in their config?

One idea we had floating around was to make the ChainId deterministic computation of shared data plus some fixed shared counter. E.g., "ledger version:ledger hash:counter", but that would be a lot of fields and in a string, so not that great... Have we considered a hash?

This is about a tradeoff of debuggability of safety vs size. Hash is bigger than a byte. =)

Also one concern about this being within an "on-chain config" is -- would that make it mutable? Is that a property we want to have? Is it possible to have immutable values in on-chain configs?

Agree on immutable.


const MAINNET_CHAIN_ID: u64 = 0;
#[cfg(any(test, feature = "fuzzing"))]
pub const MOCK_CHAIN_ID: u64 = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

should we just have TESTNET_CHAIN_ID here and for all tests? mock gives impression it is generated number.
also, since 0 is mainnet, 1 wil be testnet?

@@ -33,6 +34,8 @@ pub struct ValidatorConfig {
fullnode_address: NetworkAddress,
#[structopt(flatten)]
backends: SecureBackends,
#[structopt(long)]
chain_id: ChainId,
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is quite right, as it's essentially setting a chainID on a single transaction used during the genesis process (this transaction is submitted by each validator operator). Instead, my feeling is that we'll probably want to have the association determine the ID and set it in genesis once, before bootstrapping the network. However, from reading the comments it seems like there's still a lot of on-going discussions about how this should actually work..

Copy link
Contributor

Choose a reason for hiding this comment

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

(If this is something that the association is going to decide at genesis, we may want to setup a sync with @davidiw, to figure out the best place to do this for the management tool)

@bors-libra
Copy link
Contributor

☔ The latest upstream changes (presumably 2f6a802) made this pull request unmergeable. Please resolve the merge conflicts.

@bors-libra
Copy link
Contributor

☔ The latest upstream changes (presumably 0f911dd) made this pull request unmergeable. Please resolve the merge conflicts.

@christinacelee christinacelee marked this pull request as ready for review July 13, 2020 19:21
@christinacelee christinacelee changed the title [draft] add ChainID to user txn [types] add ChainID to user txn Jul 13, 2020
@thefallentree
Copy link
Contributor

please also add changes to json-rpc/types/view.rs to show chainID in JSONRPC

@thefallentree
Copy link
Contributor

as well as updating "json-rpc-spec.md"

@thefallentree
Copy link
Contributor

LGTM on jsonrpc side.

Copy link
Contributor

@aching aching left a comment

Choose a reason for hiding this comment

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

This is awesome. Looks good to me, I have a few minor comments.

@bors-libra delegate+

@@ -117,6 +121,8 @@ struct TxnParams {
pub sender_address: String,
// Sequence number of this transaction corresponding to sender's account.
pub sequence_number: u64,
// Chain ID of the network this transaction is intended for
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Chain ID of the network this transaction is intended for
// Chain ID of the Libra network this transaction is intended for

</td>
<td>u8
</td>
<td>Chain ID of the network this transaction is intended for
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<td>Chain ID of the network this transaction is intended for
<td>Chain ID of the Libra network this transaction is intended for

@@ -102,6 +103,8 @@ pub struct IndexAndSequence {

/// Proxy handling CLI commands/inputs.
pub struct ClientProxy {
/// chain ID of the network this client is interacting with
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// chain ID of the network this client is interacting with
/// chain ID of the Libra network this client is interacting with

Comment on lines 33 to 37
help = "\
Explicitly specify the chain ID of the network the CLI is connecting to: e.g.,
for mainnet: \"MAINNET\" or 0, pre-mainnet: \"PREMAINNET\" or 1,
testnet: \"TESTNET\" or 2, devnet: \"DEVNET\" or 3
"
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like we might want to print this from the 'ReservedChain' values to ensure it doesn't get out of sync.

Comment on lines 17 to 25
pub enum ReservedChain {
MAINNET,
PREMAINNET,
TESTNET,
DEVNET,
TESTING,
}
Copy link
Contributor

@aching aching Jul 14, 2020

Choose a reason for hiding this comment

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

When it comes to reserved Libra networks, I agree MAINNET = 0.

However, PREMAINNET, TESTNET, DEVNET, TESTING, are non-reserved in my opinion. They could evolve over time. One thing we could do if we want to keep a single enum is something like below - renaming to NamedChain and then clearly laying out what is reserved and what is not:

Suggested change
pub enum ReservedChain {
MAINNET,
PREMAINNET,
TESTNET,
DEVNET,
TESTING,
}
pub enum NamedChain {
/// MAINNET is the Libra mainnet production chain and is reserved for 0.
MAINNET,
// The Libra chain below are non-reserved, non-production, and may change over time. They are listed for convenience here.
PREMAINNET,
TESTNET,
DEVNET,
TESTING,
}

@@ -80,6 +81,9 @@ pub struct RawTransaction {
#[serde(serialize_with = "serialize_duration")]
#[serde(deserialize_with = "deserialize_duration")]
expiration_time: Duration,

// chain ID of the network this transaction is intended for
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// chain ID of the network this transaction is intended for
// chain ID of the Libra network this transaction is intended for

@christinacelee
Copy link
Author

@bors-libra r=aching

@bors-libra
Copy link
Contributor

📌 Commit 76a7c36 has been approved by aching

@bors-libra
Copy link
Contributor

⌛ Testing commit 76a7c36 with merge fbd7352...

bors-libra pushed a commit that referenced this pull request Jul 15, 2020
bors-libra pushed a commit that referenced this pull request Jul 15, 2020
Closes: #4969
Approved by: aching
@github-actions
Copy link

Cluster Test Result

all up : 1040 TPS, 4352 ms latency, 5500 ms p99 latency, no expired txns

Repro cmd:

./scripts/cti --tag land_fbd73524 --run bench

@bors-libra
Copy link
Contributor

☀️ Test successful - checks-actions_land_blocking_test, checks-circle_commit_workflow
Approved by: aching
Pushing fbd7352 to master...

@christinacelee christinacelee deleted the txn-chain-id branch July 15, 2020 01:32
@aching aching added the breaking Any changes that will require restarting the testnet label Jul 15, 2020
@@ -21,6 +22,8 @@ pub struct KeyManagerConfig {
pub secure_backend: SecureBackend,
pub sleep_period_secs: u64,
pub txn_expiration_secs: u64,
#[serde(deserialize_with = "chain_id::deserialize_config_chain_id")]
pub chain_id: ChainId,
Copy link
Author

Choose a reason for hiding this comment

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

chain ID needs to be added to key manager config too @mgorven @sausagee

Copy link
Author

Choose a reason for hiding this comment

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking Any changes that will require restarting the testnet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet