-
Notifications
You must be signed in to change notification settings - Fork 1
SVM Raw Transaction Ranges #69
Description
SVM Raw Transaction Ranges
Overview
This SMIP proposes a mechanism for exposing the bytes of the raw transaction while staying compliant with the Fixed-Gas Wasm restrictions.
Goals and Motivation
This SMIP is a prerequisite for applying crypto-related computations on parts of a transaction.
For example, to implement a non-trivial verify function, the Template needs access to the raw transaction data.
Aside from that, other functions should be able to run similar computations.
High-level design
Reconstruct Transaction Raw Data
The way things are designed today is that the Full-Node (go-spacemesh) is in charge of peeling off the Envelope part of a raw transaction (a.k.a, the one sent over the network) decoding it.
The Full-Node must be able to reason about the Envelope, and it can't treat it as an opaque piece of data.
Later, the go-spacemesh (written in Golang) communicates (using the go-svm library) against SVM (written in Rust).
The same Envelope is passed in a binary format between go-svm and SVM due to FFI limitations.
The format used for the Envelope in the FFI boundary isn't the same as the on-the-wire one.
However, since we want SVM to reconstruct the original Envelope, it's advised that the two encodings will converge.
Otherwise, it'll require more coding effort, which seems excessive.
If the Envelope encodings unite into a single one, the interface between go-svm and SVM can remain today.
Right now, the Envelope and Message are passed as two different parameters; it's an implementation detail whether to stay with that explicit separation.
In any case, SVM needs to know how to transform the encoded Envelope passed to it to the one sent over the network.
(If the two Envelope encodings used will be the same, we can view the transformation as the Identity function).
Once SVM will hold both raw pieces, namely Envelope and Message. It could concatenate both and have the original raw transaction.
Host Functions Exposing Raw Transaction Ranges
The Runtime component should be extended with multiple host functions that will expose the bytes range of the raw transaction data.
Each host function will return a tuple representing a memory range.
This list might not be exhaustive, but it should be very close to the final one:
-
svm_tx_range() -> (i32, i32)
Should return a tuple containing the memory range holding the transaction. -
svm_tx_principal_range() -> (i32, i32)
Should return a tuple containing the memory range holding the transaction'sPrincipaladdress.
We probably can omit the tuple second part since we know that an Address is exactly 20 bytes long.
However, it might be better to stay consistent with the convention used for the other similar methods.
(and be compatible if additional addresses length will be supported someday). -
svm_tx_calldata_range() -> (i32, i32)
Should return a tuple containing the memory range holding thecalldata. -
svm_tx_verifydata_range() -> (i32, i32)
Should return a tuple containing the memory range holding theverifydata. -
svm_tx_func_range() -> (i32, i32)
Should return a tuple containing the memory range holding thefunction nameof the transaction.
Even though Wasm has support for Multiple Return-Values, the Rust support might not be production-ready yet.
In that case, each host function described here will have to be split into two separate functions:
One is returning the starting memory address and the other the ending one. So, for example, instead of having svm_tx_range we'll have both svm_tx_offset and svm_tx_len.
See also the existing svm_calldata_offset and svm_calldata_len as a good reference.
To have the raw transaction in memory, the Runtime will have to copy it into the Wasm instance memory in a very similar manner as done today to the verifydata/calldata.
The call for svm_alloc will take the transaction raw data size into account, and the Function Environment managed by "the host" will keep the ranges of the transaction pieces. It means that the Function Environment will hold pointers to different parts of the raw transaction.
One way to compute these pointers (a.ka offsets) will be by extending the behavior of the Codec not only to return a decoded transaction but also to return metadata containing these offsets.
From there, deriving the memory pointers should be easy.
For example, if we know that the raw transaction starts at memory address 1000 and that the function name field is at offset 50, we can infer that the function name in memory begins at memory address 1000 + 50.
Dependencies and interactions
go-svm- SVM FFI
- SVM Runtime
- SVM Codec