This SMIP documents the life-cycle of a single transaction from the SVM's perspective.
Goals and motivation
The main motivation is to put on spec the whole process since it contains multiple parts.
Some of the parts are directly related to the Account Unification design. Others parts exist to serve the new Nonce Scheme needs. Since there are many delicate details, having this SMIP should ease raising open-questions and putting everyone on the same page.
High-level design
In this section, we assume we have a transaction we got from the Network which is not yet in the MemPool.
By saying a transaction in this document, we refer to a Call transaction over an existing Account or a Spawn transaction (spawning new Accounts out of a given Template).
Important: The life-cycle of a Deploy Template should be much more simplified and for the 1st Mainnet, each Template will be deployed as part of the Genesis process.
1. Syntactic Validation
Purpose: Validating that the transaction is syntactically valid.
Before/After MemPool: Before
Is Wasm: No
Costs Gas: No
Input: Transaction (the Message part, the Envelope isn't required by SVM)
Output: True/False
Output Processing:
If True Then
Proceed to Next Step
Else
Discard Transaction // Tx will not enter the MemPool
2. Fixed-Gas Threshold
Purpose: Ensuring the value given under the transaction's gas_limit suffices for executing the transaction.
Notes:
This check is valid only when dealing with Fixed-Gas Templates (which is the case for the 1st Mainnet).
Having a large enough gas_limit doesn't guarantee that by the time the transaction will execute, the Principal account will have enough balance to pay for the Gas.
Before/After MemPool: Before
Is Wasm? No
Costs Gas? No
Input:
Transaction (the Message part) and the gas_limit (it's part of the Envelope)
Output: True/False
Output Processing:
If True Then
Proceed to Next Step
Else
Discard Transaction // Tx will **not** enter the MemPool
3. Nonce Threshold
Purpose: Returns a few variables to be used later by the Filters & Validity Predicate stages.
Before/After MemPool: Before
Is Wasm: Yes
Costs Gas: No
Has Execution Gas Capacity: YesThis value is part of the consensus (we'll name it as the MAX_NONCE_THRESHOLD_GAS)
Executing Account: Principal
An executing Wasm program under SVM should always run int the context of some Account.
Having said that, since this step running Wasm code has no access to the current Account's storage, the fact we run as Principal has no real meaning.
Has Access to Account's storage : No
Has Access to Account's counter : No
The Principal's Counter is taking part in the Transaction Filter & Validity Predicate stages.
Has Access to the Account's balance : No
Has Access to the Current Layer : No
How to derive the transaction nonce ?
There is no explicit transaction nonce field. Each Template will derive that value differently.
The output of the function should be independent of the transaction nonce.
Input:
Transaction Id
VerifyData
In the future we'll access to more fields of the Transaction.
Right now the focus is to allow the bare minimum data for the Templates required for the 1st Mainnet
Output:
start_layer - The first layer this transaction can be considered a candidate to entering the MemPool - This value can be a layer in the future.
end_layer - The last layer this transaction can stay in the MemPool
cmax - The maximum value for the Account'scounter
mask - The mask to be used later for validation/filtering.
4. Transaction Filter
Purpose: Running a filter over the transaction. If filters passes, we can proceed to the next step (otherwise we discard the transaction).
Before/After MemPool: Before
Is Wasm? No
Costs Gas? No
Algorithm:
IF current_layer not in the range start_layer...end_layer:
Discard // Tx will not enter the MemPool
ELSE IF (nonce_current & nonce_mask) >= cmax:
Discard // Tx will not enter the MemPool
ELSE
Keep // Tx is still got a chance entering the MemPool
What should be the value assigned to the current_layer ?
Since we run the filter when the transaction is still not in the MemPool we need to assign a speculative current_layer
Important: the nonce_current is the current Account's counter - i.e the Principal's counter.
The value taken isn't the one associated with the speculative current_layer but with the last seen value of the Principal's account.
5. Verify
Purpose: Running the verify method of the Principal
Before/After MemPool: Before
Purpose: Running the verify method of the Principal
If to be more precise - running the verify method of the Template associated with the Principal Account.
The current executing Account will be the Principal
Is Wasm? Yes
Costs Gas? No
Has Execution Gas Capacity: Yes
This value is part of the consensus (we'll name it as the MAX_VERIFY_GAS)
Has Access to Account's storage : Only to the Immutable data of the Account
Has Access to Account's counter : No
Has Access to the Account's balance : No
Has Access to the Current Layer : No
Input:
VerifyData
In the future we might allow access to more bits of information.
For the Templates we need right now, having access to the VerifyData should suffice.
Output: True/False
If verify returns True we insert the transaction into the MemPool, otherwise we reject it.
Important: We'll have immutable storage (initialized during running the ctor of an Account) - that storage
will be accessible to the verify- and only that storage restriction we can manage without implementing a PAY_GAS opcode such as the one Ethereum has
6. Authorize
Purpose: Lets the Target assert whether the transaction is good to go from his side and if yes returns a max transfer
Before/After MemPool: Before
Is Wasm? Yes
Has Execution Gas Capacity: Yes
This value is part of the consensus (we'll name it as the MAX_AUTHORIZE_GAS)
Has Access to Account's storage : Only to the Immutable data of the Account
Has Access to Account's counter : No
Has Access to the Account's balance : No
Has Access to the Current Layer : No
Has Access to the Principal Address: No
Input:
AuthorizeData
In the future we might allow access to more bits of information.
For the Templates we need right now, having access to the AuthorizeData should suffice.
Output: False when transaction isn't authorized by the Target and (True, MAX_WITHDRAW) otherwise.
MAX_WITHDRAW is the maximum allowed balance to be withdrawn from Target's balance as part of the transaction execution.
When transaction has been authorized we insert it into the MemPool.
7. Validity Predicate
Purpose: Running a filter over the transaction. If the filters passes, we can proceed to the next step.
Otherwise, we don't automatically remove the transaction from the MemPool.
In case the current_layer > end_layer we do remove it - if not, we keep it in the MemPool for the next layer.
Before/After MemPool: After
We run the Validity Predicate as part of building the Unified Block.
In that stage we know the current layer's (let's denote it as I) output but not necessarily the output of the Hare of layer I - 1.
Is Wasm? No
Costs Gas? No
Algorithm: The same as 4. Transaction Filter
Important: This time we know the true value of the current_layer(We don't set a speculative value).
8. Verify
Purpose: Running the verify method of the Principal
Before/After MemPool: After
Purpose: Running the verify method of the Principal
If to be more precise - running the verify method of the Template associated with the Principal Account.
The current executing Account will be the Principal
Is Wasm? Yes
Costs Gas? No
Has Execution Gas Capacity: Yes
This value is part of the consensus (we'll name it as the MAX_VERIFY_GAS)
Has Access to Account's storage : No
Has Access to Account's counter : No
Has Access to the Account's balance : No
Has Access to the Current Layer : No
Input:
VerifyData
In the future we might allow access to more bits of information.
For the Templates we need right now, having access to the VerifyData should suffice.
Output: True/False
9. Authorize
As explained at 6.
10. Principal'sCounter Update
Purpose: Setting the new Principal's Account Counter
Is Wasm? Yes
Costs Gas? Yes
Has Access to Account's storage : Only to the Immutable Storage of the Account.
Has Access to Account's counter : No
Has Access to the Account's balance : No (We update the Counter externally)
Has Access to the Current Layer : No
Output: new_account_counter, The next Principal's Account Counter
Output Processing:
If both conditions are satisfied:
**new_account_counter > current_account_counter
(new_account_nonce - 1) & mask < cmask**
Then
Override the new Principal's counter with **`new_account_counter`**
Else
Override the new Principal's counter with **`cmask & mask`**
11. Execution
Purpose: Executing the function of the transaction
Executing Account: Target
Before/After MemPool: After
Is Wasm? Yes
Costs Gas? Yes
Has Access to Account's storage : Yes
Has Access to Account's counter : Only Reads
Has Access to the Account's balance : Yes
Has Access to the Current Layer : Yes
Input:
Calldata
In the future we'll access to more fields of the Transaction.
Right now the focus is to allow the bare minimum data for the Templates required for the 1st Mainnet
Output: Receipt
Questions/concerns
There are many steps required for a full execution a transaction.
Not trivial to implement Verify and Authorize
Being able to reuse implementation of a Nonce Scheme across different Templates is critical.
Clients such as smapp will have to be aware to the Nonce Scheme implemented by the Template.
Each client should be able to get the last counter of an Account and derive it's next value as part of crafting a transaction.
This SMIP might involve some changes to the Hare protocol.
SVM Transaction Life-cycle
Overview
This SMIP documents the life-cycle of a single transaction from the SVM's perspective.
Goals and motivation
The main motivation is to put on spec the whole process since it contains multiple parts. Some of the parts are directly related to the Account Unification design. Others parts exist to serve the new Nonce Scheme needs. Since there are many delicate details, having this SMIP should ease raising open-questions and putting everyone on the same page.
High-level design
In this section, we assume we have a transaction we got from the Network which is not yet in the MemPool.
By saying a transaction in this document, we refer to a
Call
transaction over an existingAccount
or aSpawn
transaction (spawning newAccount
s out of a givenTemplate
).Important: The life-cycle of a
Deploy Template
should be much more simplified and for the 1st Mainnet, each Template will be deployed as part of the Genesis process.Message
part, theEnvelope
isn't required by SVM)2. Fixed-Gas Threshold
Purpose: Ensuring the value given under the transaction's
gas_limit
suffices for executing the transaction.Notes:
gas_limit
doesn't guarantee that by the time the transaction will execute, the Principal account will have enough balance to pay for the Gas.Message
part) and thegas_limit
(it's part of theEnvelope
)3. Nonce Threshold
Purpose: Returns a few variables to be used later by the Filters & Validity Predicate stages.
Before/After MemPool: Before
Is Wasm: Yes
Costs Gas: No
Has Execution Gas Capacity: Yes This value is part of the consensus (we'll name it as the MAX_NONCE_THRESHOLD_GAS)
Executing Account: Principal An executing Wasm program under SVM should always run int the context of some Account. Having said that, since this step running Wasm code has no access to the current Account's storage, the fact we run as Principal has no real meaning.
Has Access to Account's storage : No
Has Access to Account's counter : No
The
Principal's Counter
is taking part in the Transaction Filter & Validity Predicate stages.Has Access to the Account's balance : No
Has Access to the
Current Layer
: NoHow to derive the
transaction nonce
? There is no explicittransaction nonce
field. Each Template will derive that value differently. The output of the function should be independent of thetransaction nonce
.Input:
Transaction Id
VerifyData
Output:
current_layer
? Since we run the filter when the transaction is still not in the MemPool we need to assign a speculativecurrent_layer
nonce_current
is the current Account's counter - i.e the Principal's counter. The value taken isn't the one associated with the speculativecurrent_layer
but with the last seen value of the Principal's account.verify
method of thePrincipal
verify
method of thePrincipal
If to be more precise - running theverify
method of the Template associated with thePrincipal
Account. The current executingAccount
will be thePrincipal
Current Layer
: NoVerifyData
VerifyData
should suffice.verify
returnsTrue
we insert the transaction into the MemPool, otherwise we reject it.ctor
of an Account) - that storage will be accessible to theverify
- and only that storage restriction we can manage without implementing a PAY_GAS opcode such as the one Ethereum hasCurrent Layer
: NoPrincipal Address
: NoAuthorizeData
AuthorizeData
should suffice.current_layer > end_layer
we do remove it - if not, we keep it in the MemPool for the next layer.Validity Predicate
as part of building theUnified Block
. In that stage we know the current layer's (let's denote it asI
) output but not necessarily the output of the Hare of layerI - 1
.4. Transaction Filter
current_layer
(We don't set a speculative value).verify
method of thePrincipal
verify
method of thePrincipal
If to be more precise - running theverify
method of the Template associated with thePrincipal
Account. The current executingAccount
will be thePrincipal
Current Layer
: NoVerifyData
VerifyData
should suffice.Current Layer
: Nonew_account_counter
, The next Principal's Account CounterCurrent Layer
: YesCalldata
Questions/concerns
Verify
andAuthorize
smapp
will have to be aware to the Nonce Scheme implemented by the Template. Each client should be able to get the last counter of an Account and derive it's next value as part of crafting a transaction.Dependencies and interactions
go-svm
smapp
/ other clientsStakeholders and reviewers
@lrettig @avive @neysofu @noamnelke @sudachen