fip | title | author | discussions-to | status | type | category | created | spec-sections | requires | replaces | |
---|---|---|---|---|---|---|---|---|---|---|---|
0055 |
Supporting Ethereum Accounts, Addresses, and Transactions |
Raúl Kripalani (@raulk), Steven Allen (@stebalien) |
Accepted |
Technical Core |
Core |
2022-12-02 |
|
N/A |
Table of Contents generated with DocToc
- Simple Summary
- Abstract
- Change Motivation
- Specification
- Design Rationale
- Backwards Compatibility
- Test Cases
- Security Considerations
- Incentive Considerations
- Product Considerations
- Implementation
- Copyright
In order to enable existing Ethereum tools to seamlessly interact with the Filecoin network, it is necessary for Filecoin to:
- Recognize and deal with Ethereum addresses.
- Fulfill Ethereum address expectations, both for accounts (secp256k1 public key derived) and contracts.
- Model Ethereum's Externally-Owned Accounts (EOAs).
- Support and validate native transactions issued by (EOAs) from Ethereum wallets.
This FIP delivers these requirements by leveraging the f4 extensible address class (newly introduced in FIP-0048) to model Ethereum Addresses, managed by the Ethereum Address Manager (EAM) built-in actor introduced herein.
It also introduces the Ethereum Account built-in actor to represent Ethereum Externally Owned accounts, capable of acting as message senders, and a new Delegated signature type, which enables validating native EIP-1559 Ethereum transactions carrying a secp256k1 ECDSA signatures over their RLP encoding.
Both these elements act as extension points for an eventual fully-fledged Account Abstraction framework.
This FIP introduces three technical elements:
-
It formally defines the
f410
Ethereum address space, nested under decimal namespace10
within thef4
address class as defined in FIP-0048. This address space is managed by the Ethereum Address Manager (EAM) built-in actor, which also offers public methods to act as a factory for EVM smart contracts. The subaddress of anf410
address is the original Ethereum address. Ethereum addresses can be cast asf410
addresses, and viceversa. -
The Ethereum Account built-in actor, representing an Externally-Owned Ethereum account, backed by a secp256k1 key, and capable of acting as a sender of native EIP-1559 Ethereum transactions. This actor will become an Abstract Account (AA) actor, if and when such framework is eventually introduced in the protocol.
-
The Delegated signature type, carrying a signature eventually verified by actor code through Account Abstraction (AA). At this time, Delegated signatures are special-cased secp256k1 ECDSA signatures over the RLP encoding of native EIP-1559 Ethereum transactions.
Finally, it extends the existing Account actor (f1/f3) to align it with the Ethereum Account actor and the Placeholder actor by making it accept all methods greater than or equal to the minimum FRC-0042 method number (2^24
).
The FVM is a multi-runtime execution environment, designed to accommodate various runtimes atop a Wasm-based foundation. Supporting foreign addressing schemes, account models, and transaction formats is important to achieve full portability of programs, tools, libraries, and know-how to Filecoin.
In the case of the EVM runtime introduced in FIP-0054, the runtime itself, as well as EVM smart contracts and tools, make various assumptions about addresses. Concretely about their data type (uint160), derivation (from public key for accounts), and/or generation. Fulfilling these assumptions is essential to avoid (a) breakage of original smart contracts and (b) long-ranged codebase forks with patches that would result in maintenance hell for Core Devs. In addition, we'd like to refrain from baking these assumptions into the core protocol to avoid external coupling and unsustainable bespoke specialization.
We reconcile these requirements and constraints by modelling Ethereum addresses over the brand new f4
delegated address class, under a dedicated namespace, managed by the Ethereum Address Manager actor introduced herein.
Furthermore, when porting foreign runtimes to Filecoin, it is highly desirable to retain full tool and ecosystem stack compatibility with Filecoin. This involves supporting transactions issued from existing wallets without modifications, verifying their native signature and authentication schemes. The Delegated signature type and the Ethereum Account actor enable this. They also serve as cornerstones for a future fully-fledged Account Abstraction (AA) framework in Filecoin.
We introduce the Ethereum Address Manager (EAM) built-in actor as a singleton built-in actor, sitting at ID address f010
.
This actor manages the Ethereum address space, anchored at the f4
address namespace equalling its actor ID (10
in decimal).
For more information on the f4 address class, refer to FIP-0048.
The subaddress is the literal Ethereum address in binary form.
#
# Ethereum address in Filecoin as an f4 address
#
0x04 || 0x10 || <binary Ethereum address>
| | |
A B C
# A: address class, also termed "protocol"
# B: leb128-encoded actor ID of address manager (termed "namespace")
# C: subaddress within the namespace
In textual form, this is represented as follows:
#
# Textual form of the above address
#
"f4" || "10" || <eth address || checksum>
| | |
A B C
# A: address class, also termed "protocol"
# B: namespace as a decimal number with no padding
# C: subaddress (in this case the Ethereum address) with checksum appended, base32 encoded
The EAM also acts like an EVM smart contract factory, offering methods that respect the Ethereum address generation rules and semantics for the original CREATE
and CREATE2
mechanisms.
During migration:
- The Wasm bytecode of the EAM (referred by its CodeCID) is linked under the System actor's registry under the key
"eam"
. - A single copy of the EAM is deployed as a singleton actor under ID address
f010
. - A new version of the Init actor that recognized the EAM as an authorized delegated address manager is deployed, by loading its Wasm bytecode to the blockstore, linking its CodeCID in the System actor, and updating
f01
's CodeCID to it in the state tree. - An Ethereum Account actor is created in the state tree with balance zero, nonce zero, and delegated address
f410faaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaonc6iji
(as per FIP-0048), corresponding to the Ethereum Null Address (0x0).
The Init actor statically recognizes the EAM as an authorized delegated address manager, and allows it to call its InitActor#Exec4
method.
Note that address managers like the EAM use the InitActor#Exec4
method to deploy new actors with a delegated f4 address under the namespace matching their actor ID (10
in this case).
The caller provides the subaddress as an argument to such method.
For more details, refer to FIP-0048.
This actor has no state. The actor's Head
field in the state tree is the EMPTY_ARR_CID
static value, defined as the CID of an empty CBOR byte string.
The Constructor has no functional relevance. It is merely a safeguard to prevent accidental instantiation by users by asserting that:
- It is being called by the System actor (f00).
- It has been deployed on address
f10
.
The Constructor takes no input parameters and produces no return value.
It returns the USR_FORBIDDEN
(18) exit code when the above assumptions are unmet.
To act like an EVM smart contract factory, the EAM offers three Filecoin methods:
Create
(method number 2).Create2
(method number 3).CreateExternal
(method number 4).
All these methods create new EVM actors but have slightly different semantics, parameters, and permissions.
In general:
- The contract deployment message is directed to one of the
Create
,Create2
, orCreateExternal
methods of the EAM, containing the EVM init bytecode and further arguments (see below). - The EAM determines the caller's Ethereum address by:
- First attempting to extract it from the caller's
f4
address. - Otherwise, the EAM creates an "embedded ID address" from the caller's ID.
- First attempting to extract it from the caller's
- The EAM computes the would-be Ethereum address, respecting the original Ethereum semantics where applicable, thus retaining tool compatibility.
- The EAM exits with
USR_FORBIDDEN
if said address is a reserved precompile address or an embedded ID address:- Is in the range
0x00-0xff
. - Is in the range
0xfe000...000-0xfe000...0ff
. - Is in the range
0xff000...00000000000000000-0xff000-0ffffffffffffffff
.
- Is in the range
- If an EVM contract has already been deployed to the target address, the EAM attempts to resurrect the contract.
- It calls the
Resurrect
method (see [FIP-0055]) with the given initcode and the caller's address. - If successful, the EAM returns the actor's ID and Ethereum address (see
Return
below). - Otherwise, the EAM propagates the exit code from
Resurrect
.
- It calls the
- Otherwise, the EAM invokes the Init actor's
Exec4
method (see FIP-0048), supplying:- The CodeCID of the EVM runtime actor (introduced in FIP-0054).
- The EVM runtime actor's constructor parameters, concretely the EVM init code and the address of the original creator.
- The computed Ethereum address, which the Init actor uses as the subaddress to form the
f410
address for binding it in its address registry.
- The EAM returns the new actor's ID, f2 address, and the Ethereum address (see
Return
below).
// DAG-CBOR tuple encoded.
pub struct Return {
/// The ID of the EVM runtime actor that was constructed.
pub actor_id: ActorID,
/// Its f2 address.
pub robust_address: Option<Address>,
/// Its Ethereum address, translatable to an `f410` address.
pub eth_address: [u8; 20],
}
The Create
method may only be called by the EVM's CREATE
opcode. It's called with:
// DAG-CBOR tuple encoded.
pub struct CreateParams {
/// EVM init code.
pub initcode: Vec<u8>,
/// Nonce with which to create this smart contract.
pub nonce: u64,
}
After verifying that the caller is an EVM contract, the Create
method computes the address as the last 20 bytes of
Keccak-256(rlp_list(caller_eth_address, nonce))
then follows the general contract deployment logic specified above.
Note that this method differs from Ethereum in that we take a user-provided nonce as a parameter. This is necessary for the EAM to learn the nonce of a contract, when the CREATE
opcode is used and is secure because:
- This method may only be invoked by the EVM runtime (the caller must be an EVM contract, and the
call_actor
precompile (see [FIP-0055]) cannot invoke methods < 1024. - The system itself prevents deploying over existing actors.
The Create2
method may only be called by the EVM's CREATE2
opcode. It's called with:
// DAG-CBOR tuple encoded.
pub struct CreateParams {
/// EVM init code.
pub initcode: Vec<u8>,
/// Salt from which to derive the new contract's address.
pub salt: [u8; 32],
}
After verifying that the caller is an EVM contract, the Create2
method computes the address as the last 20 bytes of
Keccak-256(concat([0xff], caller_eth_addr, salt, Keccak-256(initcode)))
then follows the general contract deployment logic specified above.
The CreateExternal
method may only be called by externally owned accounts (currently EthAccount and native Accounts).
CreateExternal
computes the address according to the same logic as Create
with two key differences:
- The
nonce
comes from the top-level message, not the method parameters. - If the caller is a native account, the new contract's address is not derived from the caller's Ethereum address as the caller's Ethereum address will be an embedded ID address, which isn't reorg stable. Instead, the
caller_eth_addr
input to theCreate
address computation logic is set to the last 20 bytes ofKeccak-256(caller_key_addr)
.
We introduce a new Delegated
signature type for Filecoin messages with serialized value 3
.
Delegated signatures are opaque to the protocol, and are validated by actor logic.
Delegated signatures will be used in combination with abstract account senders, once Account Abstraction (AA) is fully realized.
However, we establish a transitory period where Delegated signatures are special-cased.
We establish a transitory period whereby Delegated
signatures are assumed to be secp256k1 signatures over the RLP representation of a native EIP-1559 Ethereum transaction sent from an Ethereum Account actor.
During this period, all senders of messages with Delegated
signatures must have an f410
address, that is, an address under the EAM's namespace.
Additionally, delegated signatures are restricted to:
- Messages to the Ethereum Address Manager (
f010
), calling itsCreateExternal
method, and carrying a non-empty, validly CBOR-encoded byte array as its parameters (with no extra bytes) - Messages to ID addresses (
f0
) other thanf010
, and messages tof410
addresses (f4
addresses assigned by the EAM) withMethodNum
equal to the EVM built-in actor'sInvokeContract
method number (frc42_hash("InvokeEVM")
), and carrying a validly CBOR-encoded byte array as its parameters (with no extra bytes).
A message carrying a Delegated
signature that does not meet one of these criteria is considered to have an invalid signature.
The RLP representation is reconstituted from the Filecoin message as specified below.
This effectively couples the Delegated
signature type to its only possible use at this stage, but provides an extension point for the future introduction of Account Abstraction (AA).
Such transitory Delegated
signatures must be verified by:
- Reforming and repacking the native RLP-encoded EIP-1559 Ethereum transaction from the Filecoin message (original signature payload).
- Recovering the secp256k1 public key from such payload and the ECDSA signature bytes inside the
Delegated
signature. - Computing the Ethereum address from the secp256k1 public key by hashing the public key using Keccak-256, and retaining the last 20 bytes (i.e. original Ethereum address derivation logic).
- Casting to an
f410
address (see conversion rules EAM rules). - Asserting that the message sender's
f410
address matches the computedf410
address. - Forming the inverse of the reformed RLP-encoded message (converting back into a Filecoin message), and ensuring that it matches the original signature payload.
Should any of these steps fail, the signature must be considered invalid.
The client must perform this verification before chain inclusion, and before handing off the message to the FVM for execution.
To verify the Delegated signature, the unsigned RLP-encoded EIP-1559 Ethereum transaction is reconstituted in the following way from the Filecoin message. The top-level object is an RLP list, and integers are encoded in the RLP way (big endian with no leading zeroes)
Pos | Ethereum Field | Source |
---|---|---|
0 | Chain ID | Static network-specific chain ID, re-encoded as an RLP integer |
1 | Nonce | FilecoinMessage#Nonce , re-encoded as an RLP integer |
2 | Max priority fee gas | FilecoinMessage#GasPremium , re-encoded as an RLP integer |
3 | Max fee per gas | FilecoinMessage#GasFeeCap , re-encoded as an RLP integer |
4 | Gas limit | FilecoinMessage#GasLimit , re-encoded as an RLP integer |
5 | Recipient | Ethereum address extracted from the f410 or f0 address in FilecoinMessage#To , or nil if f010 (EAM), as this denotes a contract deployment, re-encoded as an RLP byte string |
6 | Value | FilecoinMessage#Value , re-encoded as an RLP integer |
7 | Input data | CBOR-deserialization of FilecoinMessage#Params as a CBOR byte string, and re-encoded as an RLP byte string |
8 | Access list | Empty. |
Additionally, this conversion asserts that:
- The sender address is a valid
f410
address with a 20 byte payload. - The recipient is either a valid
f410
address or a validf0
address. - If the recipient is
f010
(EAM),FilecoinMessage#Method
isEAM#CreateExternal
(4
). Otherwise,FilecoinMessage#Method
isEVM#InvokeContract
(3844450837
). - The message version is 0.
- The parameters contain no additional values or bytes beyond the CBOR encoded byte string.
If these conditions are not met, the signature is considered invalid.
To fully guarantee fidelity of the signature over the RLP-encoded payload, we re-calculate the Filecoin message, as an inverse of the payload calculated above. If the CBOR-serialization of the inverse does not precisely match that of the original Filecoin message, the signature is considered invalid. We do so as follows:
Pos | Filecoin Message Field | Source |
---|---|---|
0 | Version | 0, the only supported Message version on Filecoin as of this FIP |
1 | Recipient | Filecoin address converted (f410 or ID address) from the EthereumTransaction#To field, or f010 if nil as this denotes a contract deployment |
2 | Sender | The message's sender (Ethereum address) encoded as an f410 address, extracted from the transaction's signature through EcRecover |
3 | Nonce | EthereumTransaction#Nonce |
4 | Value | EthereumTransaction#Value |
5 | Gas Limit | EthereumTransaction#GasLimit |
6 | Gas Fee Cap | EthereumTransaction#GasFeeCap |
7 | Gas Premium | EthereumTransaction#MaxPriorityFeeGas |
8 | Method | EAM::CreateExternal , if EthereumTransaction#To is nil. EVM::InvokeContract otherwise |
9 | Params | CBOR-serialization of the byte array EthereumTransaction#Input . |
We introduce the Ethereum Account, a non-singleton built-in actor representing an external Ethereum identity backed by a secp256k1 key.
The Ethereum Account must be recognized by the FVM and by clients as a valid message sender of messages with Delegated
signatures, as per the scheme above.
Ethereum Accounts can only submit RLP-encoded EIP-1559 Ethereum transactions.
During the migration where this FIP goes live, the Wasm bytecode of the Ethereum Account (referred to by its CodeCID) is linked under the System actor's registry under the key "ethaccount"
.
Filecoin clients and the FVM recognize the Ethereum Account as an authorized message sender. This includes components validating and executing messages, like the message pool, chain syncer, and executor.
When the FVM executes the first message fulfilling the following characteristics, it promotes the Placeholder actor into an Ethereum Account by updating its CodeCID in-place, prior to executing the message.
- The message carries a valid transitory Delegated signature.
- The message carries nonce zero.
- The message carries a gas limit sufficient to cover chain inclusion.
- The
f410
address resolved from the recovered public secp256k1 key corresponds to a Placeholder actor.
The promotion remains effective even if the message itself results in an exit code other than 0, including out of gas.
Failure to meet chain inclusion gas will prevent the inclusion of the message and, therefore, will not promote the Placeholder into an Ethereum Account, even if we technically ascertained that the Placeholder corresponds to an Externally-Owned Account.
This actor implements one method:
- A
Constructor
that:- Takes no parameters and returns no parameters.
- Asserts that the caller is the system actor (not the init actor). An EthAccount can only be constructed by promoting a Placeholder on first send.
Additionally, this actor returns success (instead of rejecting the call with an "unhandled message" error) when called with any method numbers greater than or equal to the minimum FRC-0042 method number (2^24
).
This actor has no state. The actor's Head
field in the state tree is the EMPTY_ARR_CID
static value, defined as the CID of an empty CBOR byte string.
The account actor is changed to do nothing and return success for all methods greater than or equal to the minimum FRC-0042 method number to match the Ethereum Account actor.
We chose to not implement AuthenticateMessage
for now as there a few troublesome edge-cases and no clear use-cases. Specifically:
AuthenticateMessage
will fail for "placeholders". Specifically, if a user sends to a non-existent f410 address, the placeholder deployed at that address will not implementAuthenticateMessage
(until an EthAccount gets deployed to that address on first send).- How
AuthenticateMessage
should be implemented with respect to theeth_sign
JSON-RPC method is unclear. Specifically, whether or not to internally prepends the "Ethereum Signed Message:" prefix as done byeth_sign
.
We expect that this method will eventually be implemented on EthAccounts once there's a guiding use-case (and once the quirks around placeholders can be worked out).
As of this FIP, both account types now accept calls to all method numbers greater than or equal to 2^24
:
- This means all accounts become "universal acceptors" and will automatically accept any funds deposited via FRC-0042 interfaces in the future. Previously, the built-in account explicitly implemented the FRC-0053 universal receiver hook, but this is no longer necessary.
- Method numbers less than
2^24
still reject calls by default to be conservative and to mirror the private-use behavior implemented in other built-in actors. This restriction will likely be lifted or altered in the future.
We prototyped Account Abstraction solutions extensively before submitting this FIP. This FIP preserves the key extension points that will facilitate a (projected) elegant transition to fully-fledged Account Abstraction (AA).
We forecast these rough steps to conduct the transition:
- Remove Delegated signature validation logic from clients, treating the signature as an opaque blob at the client level.
- Perform AA-specific gas preflight checks in the client before accepting the message for inclusion, or propagating via the mpool.
- Move the Ethereum transaction signature verification logic inside the
ethaccount
actor, so that it runs on-chain. - Generalise the Placeholder actor promotion path so that the relevant address manager gets to indicate the promotion target. Reconcile with (3).
- Trigger the signature validation logic within the FVM when the sender is an Abstract Account.
On a functional level, backwards compatibility is not affected, as this FIP introduces strictly additive features. However, implementing this FIP will require built-in actor changes. We expect the CodeCIDs of all built-in actors to be altered, as a consequence of the new and adjusted logic. The new actor code will need to be linked in the System actor, and the CodeCID of all existing actors in the state tree will need to be patched to their new counterparts.
The reference implementations will include tests.
This FIP may be enhanced with test vectors covering Delegated signature verification scenarios.
This FIP introduces a new signature type and a new transaction format to the protocol. That is, we introduce new pathways to initiate transactions in the system, and extend the kinds of payloads that are entitled to do so.
It is worthy to note that Filecoin already supports secp256k1 ECDSA signatures under signature type 1. Clients are expected to reuse existing signature verification and key recovery logic. However, what differs is the authentication logic. The original signature payload must be reconstructed by repacking the RLP-encoded EIP-1559 Ethereum transaction. Any flaws in this logic is subject to security events.
The current solution only assigns f410
addresses to Ethereum Accounts and EVM smart contracts.
These interactions can benefit from stable f410
addressing:
- Cross-contract calls and value transfers (EVM <> EVM).
- Ethereum account to contract calls and value transfers, and viceversa (Eth Account <> EVM).
These interactions cannot benefit from stable addressing:
- EVM smart contracts calls and transfers to Wasm actors.
- EVM smart contract value transfers to non-Ethereum accounts (f1/f3 addresses).
If an EVM smart contract wishes to interact with a Wasm actor, it must use a masked ID address (0xff0000..<id address>
style addresses).
Unfortunately, contrary to f410
addresses, ID addresses are not reorg stable.
In other words, the ID may be reassigned within the chain's finality window, hence this security notice.
Note that even if the theoretical finality window is 900 epochs, the probability of reorgs decays as the chain progresses from the ID assignment.
The implications of this limitation are as follows:
- Not a problem for singleton actors (e.g. power actor, storage market actor, etc.), as their ID addresses are fixed, but may be problematic for non-singleton actors.
- As per FIP-0050, the only non-singleton actor callable by EVM smart contracts is the Miner actor. Interactions with recently-created miner actors are subject to reorg sensitivity.
- Tranferring value to an f1 or f3 account can only be done via its masked ID address and is thus subject to reorg sensitivity.
- Transferring value to Wasm non-singleton non-account actors (e.g. payment channels or multisigs) is also subject to reorg sensitivity.
- Transferring value to an inexistent f1 and f3 address from a smart contract is not possible as no ID address exists yet.
Unfortunately the protocol does not offer a built-in way to fetch the creation epoch of an actor, so implementing timing guards to prevent transfers or calls to reorgable ID addresses within contracts is impossible.
Note that we deliberately discarded assigning f410
addresses to non-Ethereum related actors at this stage to favor design simplicity in an already complex feature set, but we may revisit this decision in the future.
No incentive considerations apply.
This FIP is essential to enable the product aspirations of FEVM (Filecoin EVM), concretely the seamless compatibility with existing Ethereum wallets, libraries, and tools.
Explorers will need to adapt to display f410
addresses bound to actors and their respective Ethereum addresses, on actor pages, message pages, and more.
Explorers will need to recognize Ethereum Account actors and Delegated signatures.
Exchanges must support receiving funds from EVM smart contracts.
Exchange may choose to enable sending to f410
addresses, and may support taking in Ethereum addresses and converting them to f410
addresses.
As specified above, we place several restrictions on messages that carry a delegated signature, limiting them to certain specific Ethereum-related cases. This is because this FIP introduces a transitory period where Delegated signatures are special-cased to meet the product goals of the FEVM. Placing these restrictions allows us to avoid compromising on the longer-term goals of Account Abstraction, while still immediately enabling compatibility with the existing Ethereum ecosystem.
These restrictions will be removed as we upgrade towards Account Abstraction.
- The actor implementation lives in
filecoin-project/builtin-actors
, concretely in thenext
branch at the time of writing. - The reference FVM implementation lives in
filecoin-project/ref-fvm
. - The reference client implementation lives in
filecoin-project/lotus
, concretely in thenv18-fevm
branch at the time of writing.
Copyright and related rights waived via CC0.