-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Add NetworkID field to transactions to help prevent replay attacks on and from side-chains #4370
Conversation
This is worth considering; however, I believe the recommendation is to use different accounts/keys on side-chains. The X-address format recommends using the |
This wouldn't prevent replay attacks, it's just a client side syntactic sugar over the top of existing AccountIDs and destination tags. The resulting serialized transactions are the same and can be replayed in the same way on other chains. Realistically people will absolutely use the same keys on multiple chains. This is an exceedingly common practice on Ethereum chains. There are even legitimate use-cases for it... such as swapping from your own account on one chain to your own account on another over a bridge without attracting a lot of unwanted/unnecessary AML legislation. |
Yes, you are correct. It is an issue if people use the same keys on multiple chains. It may be common practice, but I'm loathe to recommend it, partly because that does open up the risk of replay attacks. Anyway, I'm fine with requiring NetworkID on new chains (as you said - Mainnet would remain unchanged). Let's get community feedback and get this reviewed. |
Ultimately the code doesn’t affect mainnet at all (and thus doesn’t require an amendment). It’s here to automatically apply to future side chains (which I assume there will be quite a few of given the resources going into XChain amendment). If the PR is rejected we should still ensure UINT32 field code 1 is reserved for NetworkID. As long as everyone agrees on that then dealing with multiple chains in the future will be a lot easier. |
Agreed - that's why I didn't put the |
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.
Good change. Just curious, where did you get the 1024 cutoff? Personally I think I'd rather make it so that every non-Mainnet network requires a NetworkID, but I guess it would be disruptive to reset all the test networks just for this.
if (getSingleSection(secConfig, SECTION_NETWORK_ID, strTemp, j_)) | ||
{ | ||
if (strTemp == "main") | ||
NETWORK_ID = 0; | ||
else if (strTemp == "testnet") | ||
NETWORK_ID = 1; | ||
else if (strTemp == "devnet") | ||
NETWORK_ID = 2; | ||
else | ||
NETWORK_ID = beast::lexicalCastThrow<uint32_t>(strTemp); | ||
} |
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 noticed this section is similar to the OverlayImpl.cpp
version; is that something that can be updated to be consistent or shared in some way so this switch doesn't have to be in multiple places?
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 also saw this but didn't want to touch OverlayImpl. Feel free to suggest an additional commit with appropriate tests to remove this redundant code.
It comes from the restriction on port numbers in Linux. We feel that new networks should pick a peering port that is = to their network ID, and this should always be > 1024. :) |
Co-authored-by: Rome Reginelli <mduo13@gmail.com>
should this be "help prevent replay attacks" since network IDs might not be unique? |
@@ -104,6 +104,7 @@ CONSTRUCT_TYPED_SFIELD(sfHookExecutionIndex, "HookExecutionIndex", UINT16, | |||
CONSTRUCT_TYPED_SFIELD(sfHookApiVersion, "HookApiVersion", UINT16, 20); | |||
|
|||
// 32-bit integers (common) | |||
CONSTRUCT_TYPED_SFIELD(sfNetworkID, "NetworkID", UINT32, 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.
I would not resue the unused value unless you are confidente that its not used elsewhere. Also why use the ''common'' value? To save a one byte in the encoding version?
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’ve never seen UINT32 field code 1 used anywhere. Not even in old old ledgers. Maybe someone can shed light on why it was free?
It seems appropriate to use it for an important task such as preventing replay attacks.
Requires at least 2 full code reviews from maintainers or well-known C++ engineers |
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 👍
Suggested commit message:
|
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 have a couple minor comments, but looks good!
@@ -163,7 +163,10 @@ Env::lookup(AccountID const& id) const | |||
{ | |||
auto const iter = map_.find(id); | |||
if (iter == map_.end()) | |||
{ | |||
std::cout << "Unknown account: " << id << "\n"; |
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.
Please consider to remove.
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.
How?
|
||
BEAST_EXPECT(error == ""); | ||
BEAST_EXPECT(c.NETWORK_ID == 10000); | ||
} |
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.
Please consider adding a negative case, e.g.
[network_id]
abcd
explanation: @pwang200 approved the PR. The suggestions could be incorporated (by @pwang200) in a commit (to be cherry picked); or if this PR has been merged, they can be suggested in a new PR. @RichardAH confirmed this is good to go as-is. |
For mainnet/testnet/devnet, do we have to upgrade js sdk or add networkId when we construct the transaction? Is the new networkID mechanism already on testnet/devnet so we could test? |
This change does not affect mainnet/testnet/devnet.
The NetworkID field must not be set in transactions on these networks. Only sidechains and test networks with IDs of 1025 or higher need to have the NetworkID set. |
The Network ID logic should not be applied to pseudo-transactions. This allows amendments to enable on a network with an ID > 1024. Context: - NetworkID: #4370 - Pseudo-transactions: https://xrpl.org/pseudo-transaction-types.html Fix #4736 --------- Co-authored-by: RichardAH <richard.holland@starstone.co.nz>
The Network ID logic should not be applied to pseudo-transactions. This allows amendments to enable on a network with an ID > 1024. Context: - NetworkID: XRPLF#4370 - Pseudo-transactions: https://xrpl.org/pseudo-transaction-types.html Fix XRPLF#4736 --------- Co-authored-by: RichardAH <richard.holland@starstone.co.nz>
High Level Overview of Change
Help to prevent replay attacks by allowing side-chains to require their users specify an accurate
NetworkID
field in every transaction to that chain.This change introduces a new field:
CONSTRUCT_TYPED_SFIELD(sfNetworkID, "NetworkID", UINT32, 1);
And three new local error codes:
telNETWORK_ID_MAKES_TX_NON_CANONICAL
telREQUIRES_NETWORK_ID
telWRONG_NETWORK
.To preserve legacy behaviour (and avoid the need for an amendment) all chains with a network ID less than 1025 retain the existing behaviour. This includes main, testnet, devnet and hooks-testnet. If
sfNetworkID
is present in any txn submitted to any of the nodes on this chain then atelNETWORK_ID_MAKES_TX_NON_CANONICAL
is returned.All other chains now require their users to include an appropriate
sfNetworkID
in every transaction to that chain.Local error codes were chosen because a transaction is not necessarily malformed if it is submitted to a node running on the incorrect chain. This is a local error specific to that node and could be corrected by switching to a different node or by changing the
network_id
on that node.Test Plan
A new unit test suite has been added:
Config unit test was also updated to include some network_id tests:
All tests: