Predictable actor address generation for wallet contracts, state channels (aka CREATE2) #379
Replies: 7 comments 11 replies
-
This is likely required (or at least very useful) for most use-cases. Basically, it allows one to very easily assert that some address could only contain a specific actor (e.g., a multisig owned by a specific user). Technically, this isn't absolutely required as long as the "origin" actor (the one creating new actors) includes enough information in the "salt". However, that:
NOTE: the EVM effectively includes the constructor parameters in CREATE2, simply because it doesn't have constructor parameters.
Your scheme already does this, right? |
Beta Was this translation helpful? Give feedback.
-
We'll need to remove or change the DeleteActor syscall to avoid resetting the account nonce, else this mechanism will open up tricky issues around replay protection and assurances over the code and possible state at an address (no monotonicity guarantee would be possible). https://github.com/filecoin-project/fvm-specs/issues/100 |
Beta Was this translation helpful? Give feedback.
-
So, I've been thinking about this a bit and went down a rabbit hole of potential constraints:
Etc... Then I remembered: this kind of complexity explosion is why smart contracts exist. ProposalMake it possible to provide CREATE2-like guarantees in userspace (with user-defined actors). Specifically, add an Out of the box, a user will only be able to prove that an actor at address X must have been deployed by some actor at address Y. However, a well-known actor can be deployed to address Y to provide additional constraints. For example, a CREATE2 actor could be created that:
However, unlike CREATE2, this allows us to provide arbitrary constraints on counterfactual contracts:
The main downsides to this approach are:
|
Beta Was this translation helpful? Give feedback.
-
Just an FYI - There was recently a Create op code related hack worth $27M on Optimism. The attacker was able to deploy multisig to an unowned address because the recipient, Wintermute, had created their multisig in 2020 using an out-of-date Create opcode and not Create2. Source: https://rekt.news/wintermute-rekt/ |
Beta Was this translation helpful? Give feedback.
-
I thinking about token APIs, I've realised that predictable addresses on Filecoin will differ from Ethereum in an important way: it's impossible to send native FIL tokens to an Actor address before an actor is deployed there. Unlike pubkey addresses, the system can't automatically deploy an account actor there. This limitation carries over to any reasonably simple implementation of a token API – such token would record balances by canonical ID address, but there's no ID address until an actor is deployed. This is a significant limitation for wallet platforms, where a motivating use case is to delay code deployment until after receiving value. This limitation could be worked around if we can somehow trigger provision of the ID address for an actor address, i.e. init the state tree entry, without knowing the code that's to be deployed there. There are already other cases where, in an attempt to resolve an address to an ID, the built-in actors make a zero-value send to an address in order to init the actor. This only works for pubkey addresses, for which we can deploy the built-in account actor. Maybe the zero-value send could install a sentinel code CID which marks an actor as awaiting deployment via Maybe (this would be cool) the zero-value send installs actor code which implements the |
Beta Was this translation helpful? Give feedback.
-
See #459 for a proposal that may subsume this one. |
Beta Was this translation helpful? Give feedback.
-
F4 address supports this likely - @anorth should we retired this in favour of f4 discussion and FIP? |
Beta Was this translation helpful? Give feedback.
-
At present, the only way to confidently know what address an actor will have once created is to actually create it. The “stable” actor address computed by the NewActorAddress VM syscall remains fixed over chain re-orgs but depends on the history of the account actor originating the transaction where it’s called (it varies with call sequence number) and so can only be trusted by that party (until finality).
I propose adding a method to the Init actor which creates an actor at an address that is predictable ahead of time, controlled by the actor deployer, and remains independent of chain events (state and call sequence). This is the equivalent of Ethereum’s CREATE2 opcode.
I advocate implementing this prior to the FVM M2 release, so that we can better support user-friendly smart-contract wallets from the beginning.
Motivation
Predictable actor addresses allow generation of an address to which an actor can be deployed in the future, controlled by a specific creator. Funds can be sent to such an address prior to an actor being deployed there, and only the nominated creator can later deploy a contract to control them. The resulting address is independent of future events–it does not depend on any blockchain state.
One big use case for this facility is smart-contract wallets. With predictable addresses, users can receive funds into a wallet address prior to deploying any wallet actor there, but confident they will be able to do so. A “user” here could include smart-contract wallet platforms, which can generate addresses for unsophisticated end users prior to those users ever generating a private key, and then deploy a wallet actor only if and when that address receives funds. They could then hand control of the wallet actor over to one or more account actors.
Another use case is state-channel applications that wish to perform off-chain interactions, with the reliable possibility of reverting to on-chain transactions to settle disputes. Predictable contract addresses allow parties to exchange signed messages to such an actor prior to it being deployed. The parties can trust that only a specific piece of code could ever be deployed to that address.
Background
Current state
The existing process for actor address generation and creation is:
Exec
message to the Init actor specifying code CID and constructor paramsNewActorAddress
syscall to obtain a stable address. This is calculated ashash(originator address, originator callseqnum, count of NewActorAddress during this txn)
. The originator address is the SECKP/BLS public-key-derived address.CreateActor
syscall with computed actor ID and Exec code CIDThe computed actor address is stable in the sense that the originator can rely on it being constant even if a reorg causes the allocated actor ID (computed next) to change. It does not depend on state. However, it is not predictable far in advance or trustable by other parties because it depends on the originator callseqnum.
Prior art - CREATE2
The Ethereum CREATE2 opcode (EIP-104) deploys a contract to an address that is computed as
hash(0xFF, sender, salt, bytecode)
. This guarantees that ifsender
ever deploysbytecode
usingCREATE2
and the providedsalt
, it will be stored in the resulting address. Note that thebytecode
here is “initcode”, which is evaluated to generate the code to be deployed, and introduces some hard-to-think-about security challenges.CREATE2
can only be used by contracts (factories) because EOA transaction don’t have salt–this is by design.More background on the EVM contract deployment process, but Filecoin is different (simpler) due to coupling of the VM with the Init actor.
Proposal
Add a new method
Exec2
(name TBC) to the Init actor.Exec2 behaves similarly to
Exec
, but rather than usingNewActorAddress
to compute the address, it computes it ashash(senderID, params.AddressSalt, params.CodeCid)
. This address should have type tagActor
(=2), the same as those generated byNewActorAddress
. These two methods of generating an address will never collide (up to hash security) because the first hashed itemsSenderID
andOriginatorAddress
have no overlap.The hash function shall be blake2b, following the existing actor address scheme.
With this scheme, someone can compute off-chain what address would be generated when a specified sender deploys a specific code CID with a known salt at any point in the future.
Implementation notes
Note that thanks to VM/Init actor coupling, no new syscall is needed here. The only change is to the Init actor.
VM implementations would need to change the behaviour when a message is sent to an actor-type address that is not yet initialised. At present, such sends are rejected (
SysErrInvalidReceiver
). Instead, the destination actor must be created with a placeholder code CID representing code yet to be deployed. The deployment capability must be restricted to the Init actor, as is currently the case.Open questions
Dependency on constructor parameters
This scheme leaves the constructor parameters out of the address inputs. This means that the sender does not commit to them. Future FVM code delegation mechanisms could mean that different constructor parameters result in very different actor behaviour, which is a risk to any third party relying on the code to be deployed to some address.
We could consider including the parameters CID in the address hash inputs. This would provide complete clarity over the code and state to be deployed, but also remove flexibility. E.g, a smart contract wallet platform would be unable to specify different end-users’ private keys, or any other parameter, to different actors at construction (but could invoke a subsequent method to authorize them).
Unification with NewActorAddress
This scheme raises the question of whether we could unify the address generation approach with that used for the existing actor addresses. The use case for
NewActorAddress
is generating a address without an externally provided salt value, stable, but not necessarily predictable. If you squint, the inputs toNewActorAddress
could form the salt to an address created with the proposed scheme–they take the place of an externally-provided unique value.If we added syscalls to expose the originator address and originator nonce, the Init actor could read these value to construct most of the salt, and then compute the address. The remaining salt item is to distinguish multiple new actor creations in a single top-level transaction. Ethereum does this by incrementing the nonce on the caller contract (which for us is always Init), but this makes the address unstable across re-orgs.
Exec
be good enough for all practical needs?If we do this, we could remove the
NewActorAddress
syscall, because the Init actor would compute the address.Compatibility with future address schemes
The FVM team have proposed a new universal address scheme in https://github.com/filecoin-project/fvm-specs/blob/505fa1ed0daabfcf2033e8fe79d9b64019a36ca0/04-evm-mapping.md. Since predictable addresses created by this proposal are in the same address space as existing type 2 actor addresses, they will be similarly compatible with this scheme. Alternatively, a 256-bit hash function could be used to natively create a class-4 address from the beginning.
Beta Was this translation helpful? Give feedback.
All reactions