-
Notifications
You must be signed in to change notification settings - Fork 84
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
Refactor: Unify DomainAddress and account conversions #1967
Conversation
af9bc6c
to
f2f9db5
Compare
Current implementation is done as: pub enum DomainAddress {
Local([u8; 32]),
Evm(EVMChainId, [u8; 20]),
} But I'm thinking of changing this into: pub enum DomainAddress {
Centrifuge(AccountId32),
Evm(EVMChainId, H160),
} I do not like that we expose the EDIT: after playing with the second approach, I feel like it's harder to deal with in a lot of scenarios, so I will keep the first model. |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1967 +/- ##
=======================================
Coverage 47.76% 47.76%
=======================================
Files 183 183
Lines 12929 12887 -42
=======================================
- Hits 6176 6156 -20
+ Misses 6753 6731 -22 ☔ View full report in Codecov by Sentry. |
279d48e
to
84c03a8
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.
I wish we did this earlier because this would have saved me quite some time! Overall, looks really good to me. I just have a few nits and couple of questions.
One change request on which I cannot comment: Please revert the submodule change because you are going back one commit there: This PR wants to change 6e8f1a29dff0d7cf5ff74285cfffadae8a8b303f
which is origin/main to 4301885b9a3b8ec36f3bda4b789daa5b115c006a
} | ||
} | ||
|
||
pub fn from_local(address: impl Into<LocalAddress>) -> DomainAddress { |
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.
Nit: I am not sure I am fully happy with the naming here because the returned domain address implicitly local even though it could also be EVM from a 32 byte array input.
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 user uses a 32-byte array (that represents an eth address expanded to 32 bytes) and sets this as local, then they are using the method wrongly. Or if not wrongly, they are creating an account from an array with X data, where X is a eth address in the array.
Not sure if I got what you want to say.
pub type LocalAddress = [u8; 32]; | ||
pub type EthAddress = [u8; 20]; | ||
|
||
pub fn local_to_eth_address(address: LocalAddress) -> EthAddress { |
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.
Nit: Let's stick to one namespace for Eth, Evm, Solidity
pub fn local_to_eth_address(address: LocalAddress) -> EthAddress { | |
pub fn local_to_evm_address(address: LocalAddress) -> EthAddress { |
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 wanted to distinguish for the name:
- eth: just the address
- evm: and ethereum address + chain information
I say this because not sure for example if [u8; 20]
is an evm address, I think not because it does not have chain information 🤔
But not sure if this association makes sense TBH. It seems not 😅
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 definitely get what you are saying but to me, having two namespaces just causes confusion.
However, since he address format is referred to as the Ethereum address format (as least from Metamask), it appears to me your distinction is correct. So fine with keeping as is.
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.
Will add a comment regarding this then to explain the why. Thanks for the ref.
I think the key sentence is: in a EVM there are ETH address, so evm
makes sense for domains, and eth
for addresses
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.
Frontier's approach is pretty straightforward when it comes to eth/evm addresses, they name these AccountId20
- here.
As someone who didn't have to deal much with Domain
/DomainAddress
, I find that local/eth/evm are confusing as well tbh.
Why not go for something simpler like:
CentrifugeAccount
-AccountId32
.EvmAccount
-AccountId20
/[u8;20]
.CentrifugeEvmAccount
-AccountId32
that's basicallyEvmAccount
+chain_id
+tag
.
Then we could have implementations for:
CentrifugeAccount
->EvmAccount
- truncate;EvmAccount
->CentrifugeAccount
- pad with 0s;CentrifugeAccount
<->CentrifugeEvmAccount
- no need;EvmAccount
->CentrifugeEvmAccount
- appendchain_id
+tag
;CentrifugeEvmAccount
->EvmAccount
- truncate;
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.
Thanks for sharing this!
And these CentrifugeAccount,
etc, should be types on their own, right? So you would directly remove the DomainAddress
type? Which type will you use to track all of them generically?
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.
No, I would add them to the DomainAddress
:
pub enum DomainAddress {
/// A standard Centrifuge account.
Centrifuge(CentrifugeAccount),
/// A Centrifuge account derived from an `EvmAccount`.
CentrifugeEvm(CentrifugeEvmAccount),
/// An EVM-compatible account.
Evm(EvmAccount),
}
This way we can keep the above mentioned conversions, and just have From
implementations for DomainAddress
, and for each of these account types.
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.
Ok, I like the way you're taking it.
Just a problem with this. The variant Evm(EvmAccount)
will not have a Domain
information associated with it. A DomainAddress requires it to later extract this.
For sure, I would not have time to do such a refactor for Monday 😅
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 could add a struct for the evm account that has the chain and the actual account, or keep it as it is right now? I dunno, just thinking out loud at this point ^^
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.
But that would be what we have right now, right? 🤔
I'm going to sketch a new draft with some ideas from this, but reusing the current DomainAddress
, let's see. Thanks for your thoughts!
let decoded = Domain::decode(&mut encoded.as_slice()).unwrap(); | ||
|
||
assert_eq!(domain, decoded); | ||
pub fn as_eth<Address: From<EthAddress>>(&self) -> Address { |
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.
Nit: Namespace
pub fn as_eth<Address: From<EthAddress>>(&self) -> Address { | |
pub fn as_evm<Address: From<EthAddress>>(&self) -> Address { |
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.
Thinking now about just eth/evm
, WDYT?
|
||
test_encode_decode_identity( | ||
Message::TransferTrancheTokens { | ||
pool_id: 1, | ||
tranche_id: default_tranche_id(), | ||
domain: domain_address.domain().into(), | ||
receiver: domain_address.address(), | ||
receiver: domain_address.as_local(), |
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 am skeptical about the changed serialization here. Probably should be
receiver: domain_address.as_local(), | |
receiver: domain_address.as_eth(), |
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.
address()
returned a [u8; 32]
, the receiver
has type [u8; 32]
but as_eth()
returns an [u8;20]
.
Basically we use address
as 32-bytes in the message for compatibility reasons, but later the 12 remaining bytes are discarded by solidity, see this thread: https://kflabs.slack.com/archives/C06FRHCRQL9/p1723627567780339
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.
Thanks for breaking this down. Seems like the message serialization is now correct and was poorly mocked beforehand, i.e. by using the 20 byte account address which in reality, should never occur.
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, previously it was a chaos of different versions of [u8; 20] -> [u8; 32]
conversions
AccountId::new(arr) | ||
}; | ||
pub const CENTRIFUGE_DOMAIN_ADDRESS: DomainAddress = DomainAddress::Centrifuge(ALICE_32); | ||
pub const ALICE_EVM_DOMAIN_ADDRESS: DomainAddress = DomainAddress::Evm(42, ALICE_ETH); |
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.
You re-inserted the incorrect chain ID which was fixed in #1964
pub const ALICE_EVM_DOMAIN_ADDRESS: DomainAddress = DomainAddress::Evm(42, ALICE_ETH); | |
pub const ALICE_EVM_DOMAIN_ADDRESS: DomainAddress = DomainAddress::Evm(CHAIN_ID, ALICE_ETH); |
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 should do something wrong in the rebase, although I do not know how it passes then, I will inspect
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.
Ok, this has changed because ALICE_EVM_LOCAL_ACCOUNT
no longer exists, and now we're using ALICE_EVM_DOMAIN_ADDRESS
, which has chain 42, not sure why TBH...
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.
Ah ok, never mind... fixing 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.
Thanks for being aware of this!
/// Returns the Ethereum address. | ||
/// The H160 retrived is NOT the local representation of that account in our | ||
/// chain, it's the real address in ethereum. | ||
pub fn in_eth(self) -> H160 { |
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.
Nit: Namespace
pub fn in_eth(self) -> H160 { | |
pub fn in_evm(self) -> H160 { |
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.
Will change if you confirm the above makes no sense, that probably not 😆
Good point! It was during the rebase... |
Sounds good to me! |
Well, I need to think a bit more because what's returned is the 32-bit array, so the account name may not make sense in some places where the array is expected (as when we construct a message with an eth address expanded to 32 bytes). Such a dilemma! |
Closed in favor of: #1969 |
Description
Convert
traits. NotDomainAddress
know about to convert accounts.[u8;32]
. See this slack threadBefore this PR you can transform a
DomainAddress
into[u8;32]
in two ways:T::DomainAddressIntoAccount::convert()
which put 20 bytes from Eth + chain + "EVM" number.DomainAddress.address()
which put 20 bytes following by 0s.This is pretty confusing because is easy to use one or the other in different places, ending with different arrays values. From this PR, the second option is removed. We always add the extra information each time we extract the array from a
DomainAddress
DomainAddress
to easy work with them safelyCentrifuge
toLocal
(or maybe I revert this)EVM
toEvm
From/Into
traits fromKeyring
toH160
and[u8;20]
and use instead an explicit method calledin_eth()
(open to name proposals). This method represents the account in the Ethereum network. Note that this differs fromKeyring::Alice.into()[0..20]
, which is used in other places where an ethereum account is stored as anAccountId
and needs to be recovered. Because this is highly confusing, I've removed theinto/from
methods to be more explicit about when the first case happens.