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
Conversation
01bd851
to
23d5442
Compare
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 |
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, let's have someone else look over it
types/src/chain_id.rs
Outdated
|
||
impl Display for ChainId { | ||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { | ||
write!(f, "ChainId {}", self.0) |
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 just print the ChainId number without the word ChainId
?
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 thought ChainId
might make things easier to debug (having just the u64 might be unhelpful?), others can weigh in too
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 it's an enum we'll get the chainId name directly :)
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.
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)
types/src/chain_id.rs
Outdated
impl Default for ChainId { | ||
fn default() -> Self { | ||
ChainId::new(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.
If the MOCK_CHAIN_ID
and the default
are going to be the same ID, should we just use the constant for both?
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.
+1 (I'm a big fan of just using constants where applicable to remove ambiguity about what ID is actually being picked)
types/src/chain_id.rs
Outdated
}; | ||
|
||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] | ||
pub struct ChainId(pub u64); |
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 think it will still work without the u64
as pub
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.
enum is better than a u64 imo
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.
agree, I think we should use an enum here, similar to what we did for NetworkId IIRC
chain_id.clone(), | ||
chain_id, |
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.
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(), |
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'd be concerned with a default that connects to the mainnet chain. I'll let @JoshLind make the call on that one though.
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 don't have strong feelings, but I think this should probably match whatever default we're using in the actual validator/node configs.
23d5442
to
e1ad7d6
Compare
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.
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)
e1ad7d6
to
4a08edd
Compare
@zekun000 I think we should definitely read it from genesis, otherwise validators with different configs will disagree... |
sounds like a good on chain config value :) |
types/src/chain_id.rs
Outdated
|
||
#[cfg(any(test, feature = "fuzzing"))] | ||
pub fn mock() -> Self { | ||
ChainId::new(MOCK_CHAIN_ID) |
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 feel like we tend to use "mock" for components, and "testing" for values
Good job on the PR! This looks good to me (besides the ChainId
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. |
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.
Makes sense, but at what point in the code should we compare genesis to the config and fail? |
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:
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? |
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. |
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. |
I think @sblackshear is right (immutable). Supporting easy forks doesn't seem to be a use case. Writeset seems reasonable to me. |
u64, u32, enum, etc. become a single byte if mainnet is 0.
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.
This is about a tradeoff of debuggability of safety vs size. Hash is bigger than a byte. =)
Agree on immutable. |
types/src/chain_id.rs
Outdated
|
||
const MAINNET_CHAIN_ID: u64 = 0; | ||
#[cfg(any(test, feature = "fuzzing"))] | ||
pub const MOCK_CHAIN_ID: u64 = 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 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, |
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 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..
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 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)
4a08edd
to
be79449
Compare
☔ The latest upstream changes (presumably 2f6a802) made this pull request unmergeable. Please resolve the merge conflicts. |
be79449
to
8cb5e56
Compare
☔ The latest upstream changes (presumably 0f911dd) made this pull request unmergeable. Please resolve the merge conflicts. |
698b214
to
91f2885
Compare
please also add changes to json-rpc/types/view.rs to show chainID in JSONRPC |
as well as updating "json-rpc-spec.md" |
91f2885
to
4633d27
Compare
LGTM on jsonrpc side. |
ed1b3d9
to
8880386
Compare
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.
This is awesome. Looks good to me, I have a few minor comments.
@bors-libra delegate+
client/swiss-knife/src/main.rs
Outdated
@@ -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 |
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.
// Chain ID of the network this transaction is intended for | |
// Chain ID of the Libra network this transaction is intended for |
json-rpc/json-rpc-spec.md
Outdated
</td> | ||
<td>u8 | ||
</td> | ||
<td>Chain ID of the network this transaction is intended for |
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.
<td>Chain ID of the network this transaction is intended for | |
<td>Chain ID of the Libra network this transaction is intended for |
testsuite/cli/src/client_proxy.rs
Outdated
@@ -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 |
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.
/// chain ID of the network this client is interacting with | |
/// chain ID of the Libra network this client is interacting with |
testsuite/cli/src/main.rs
Outdated
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 | ||
" |
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 feel like we might want to print this from the 'ReservedChain' values to ensure it doesn't get out of sync.
types/src/chain_id.rs
Outdated
pub enum ReservedChain { | ||
MAINNET, | ||
PREMAINNET, | ||
TESTNET, | ||
DEVNET, | ||
TESTING, | ||
} |
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.
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:
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, | |
} |
types/src/transaction/mod.rs
Outdated
@@ -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 |
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.
// chain ID of the network this transaction is intended for | |
// chain ID of the Libra network this transaction is intended for |
8880386
to
76a7c36
Compare
@bors-libra r=aching |
📌 Commit 76a7c36 has been approved by |
Closes: #4969 Approved by: aching
Closes: #4969 Approved by: aching
Cluster Test Result
Repro cmd:
|
☀️ Test successful - checks-actions_land_blocking_test, checks-circle_commit_workflow |
@@ -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, |
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.
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.
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