Op Code | Mnemonic | Parameters | Operation |
---|---|---|---|
A | EVAL8 | An inverse Poland expression | Evaluate the inverse Poland expression (8-bit data) stored the result to the destination. |
B | EVAL16 | An inverse Poland expression | Evaluate the inverse Poland expression (16-bit data) stored the result to the destination. |
C | EVAL32 | An inverse Poland expression | Evaluate the inverse Poland expression (32-bit data) stored the result to the destination. |
D | EVAL64 | An inverse Poland expression | Evaluate the inverse Poland expression (64-bit data) stored the result to the destination. |
E | EVAL256 | An inverse Poland expression | Evaluate the inverse Poland expression (256-bit data) stored the result to the destination. |
F | CONV | {dataType}{addrOperand}{dataType}{addrOperand} | Convert between different types of data |
G | HASH | {addrOperand}{addrOperand}{patOperand} | Sha-256 hash |
H | HASH160 | {addrOperand}{addrOperand}{patOperand}{patOperand}? | Ripemd 160 hash (btcutil style or bare) |
I | SIGCHECK | {addrOperand}{patOperand}{patOperand}{addrOperand} | Signature verification |
K | IF | {patOperand}{patOperand} | Conditional jump |
L | CALL | {patOperand}{patOperand}+ | Function call |
M | EXEC | {addrOperand}{patOperand}{addrOperand}{addrOperand}{patOperand}* | Execute another contract |
N | LOAD | {addrOperand}{patOperand} | Load persistent data |
O | STORE | {patOperand}{regexp.MustCompile(`^[rRBWDQHhkK]|(L[0-9]+)`)}{patOperand} | Store persistent data |
P | DEL | {patOperand} | Delete persistent data |
Q | LIBLOAD | {patOperand}{patOperand} | Load a library |
R | MALLOC | {addrOperand}{patOperand} | Memory allocation in global space |
S | ALLOC | {addrOperand}{patOperand} | Memory allocation in local space |
T | COPY | {addrOperand}{addrOperand}{patOperand} | Data copy |
U | COPYIMM | {addrOperand}{dataType}{patOperand} | Immediate data copy< |
V | SELFDESTRUCT | {patOperand} | Self destruction |
X | REVERT | Fail and stop execution | |
Y | RETURN | Return from function call | |
a | RECEIVED | {addrOperand} | Current outpoint |
b | TXFEE | {addrOperand}{patOperand} | Minum transaction fees |
c | GETCOIN | {addrOperand} | Token received |
d | NULOP | ||
e | SPEND | {patOperand}{patOperand} | Spend the contract's coin (add an input to the transaction) |
f | ADDRIGHTDEF | {addrOperand}{addrOperand} | Add a right definition to the transaction |
g | ADDTXOUT | {addrOperand}{addrOperand} | Add an output to the transaction |
h | GETDEFINITION | {addrOperand}{patOperand}{patOperand} | Get a definition |
i | GETUTXO | {addrOperand}{patOperand}{patOperand} | Get an UTXO |
j | MINT | {addrOperand}{patOperand}{patOperand}{patOperand} | Mint a coin |
k | META | {addrOperand}{patOperand}{patOperand} | Get contract's meta data |
l | TIME | {addrOperand} | Current block timestamp |
m | HEIGHT | {addrOperand} | Current block height |
n | TXIOCOUNT | Current block height | |
z | STOP | Stop execution |
In OVM, memory is organized as stack of heaps (frames). When a contract starts, a memory heap is created and placed in the stack. Each time when a function is called (execution of CALL instruction), a new heap (frame) is created and pushed into the stack. When execution control returns from the function (execution of RETURN instruction), the frame if popped from the stack. By using heap of stacks, the need of garbage collection is reduced.
OVM uses 64-bit address with the higher 32 bits for stack level and lower 32 bits for offset within a frame. OVM code can directly memory locations in local frame (the present top-of-stack frame) and global frame (the bottom-of-stack frame). Any other memory location can only be addressed indirectly. Syntax for direct address is: g?i{NUMBER}, where g is for global frame. This i5 represents the memory location of 5-th byte in the top-of-stack frame, and gi20 represents the memory location of 20-th byte from current lib base in the global frame (frame 0).
For indirect addressing, the syntax is: g?i{2,6}{NUMBER}, with each i for one level of deference. E.g, ii5 represents the memory location reference by the address stored at the 64-bit location begining at 5-th byte in the top-of-stack frame.
An address may be modified by a ' (single quotation) or " (double quotation) modifier as offsets to first and last deferernce when addressing involves multiple deferences.
Execution FlowOVM maintains an instruction counter pointing to the next instruction to be executed. Usually, the instruction counter will increase by 1 after execution of an instruction. CALL, IF, RETURN, LIBLOAD instructions will alter the flow of execution as follows:
IF: IF instruction has two parameters, the first is a byte data, the second is offset to instruction counter. OVM evaluates the first parameter, if it is not zero, OVM adds the second parameter to the instruction counter (relative jump). Otherwise, instruction counter increases by 1 (normal flow).
CALL: CALL instruction has two or more parameters. The first parameter is either 0 or a 20-byte lib address. If it is 0, the second parameter indicates an offset from the current instruction as entry of the function call. If it is not 0, the control flow goes to the entry of the lib indicated by the lib address and the second parameters is passed as a function name.
RETURN: RETURN instruction sets the instruction counter to the instruction following the CALL instruction.
LIBLOAD: LIBLOAD loads a contract indicated by the second parameter (the first parameter is access control flags), appends the instructions to the current program and immediately execute a CALL to the lib with 1 as function name. The lib is expected to perform one-time lib initialization and return. If the lib has already been loaded before, nothing is done.
Passing parameters:Smart contract execution can be trigged by a script in a transaction's output. The script begings with 0x88, followed by 20-byte contract address, and varialble length parameters. OVM loads the contract code indicated by the address, sets instruction counter to 0, create and initialize frame 0. The first 4 bytes of frame 0 contains length of parameters, the next 4 bytes of frame 0 is 0. Starting at byte 8 of frame 0 is the parameter. Thus the address of contract parameter is gi0"8. Conventionally, the first 4 bytes of parameters is function name.
Smart contract execution can also be trigged by a direct RPC call. User may send an RPC request to a node containing a script in the format as described above. The node will execute and sends result back to the user. However, nothing will be recorded in the block chain database.
When contract execution ends normally, the OVM expects the first 4 bytes of frame 0 contains length of result which is followed by the result. The result will be ignore if the smart contract is trigged in a transaction.
With a function call, a new frame is created. Similarly, the first 4 bytes of the frame contains length of parameters, the next 4 bytes of frame is the frame's ID. Starting at byte 8 of the frame is the parameter. Thus the address of a function's parameters is always i0"8. If the function call is a local call (first parameter is 0), starting with parameter 3, each item is considered as a 64-bit data and passed to the function. If the function call is a call to another lib, parameter 2 is considered as 4-byte function name and passed first. Then the rest as 64-bit parameter data.
If a function is to return data back to the caller, the function should take some addresses as parameters. The caller should supply these addresses and the necessary memory. The function will store the results to these addresses. A function should never return an address in the current frame as the frame will be destroyed when the function returns.
A smart contract may triger execution of another smart contract through EXEC instruction. The difference betweeen EXEC and LIBLOAD/CALL instruction is that EXEC will cause execution in the new contract's context while LIBLOAD/CALL will execute in the current contract context. Circular EXEC is not allowed while recurcive calls among loaded libs are permitted.
Inverse Poland Expression:The EVAL instructions (EVAL8, EVAL16, EVAL32, EVAL64, EVAL256) take inverse Poland expression, evaluate it and store the result to destination. The first item in the expression is already an address as destination. The rest items are evaluated as a normal inverse Poland expression. When evaluation is done, the bottom of evaluation stack is considered as the result and store to the destination. Allowed operators are:
Operator | Function | Stack Pops | Stack Pushes | Exception |
---|---|---|---|---|
+,-,*,/,% | Math ops | 2 | 1 | |
[,] | Left, right shift | 2 | 1 | Not for EVAL256 |
|,&,^ | Bitwise OR, AND, XOR | 2 | 1 | |
~ | Bitwise NOT | 1 | 1 | |
? | Select | 3 | 1 | |
>,<,=,!,(,) | Comparison: >, <, ==, !=, <=, >= | 2 | 1 | |
u | Unsigned. Next op is unsiged. | 0 | 0 | Not for EVAL256 |
# | Pow. | 2 | 1 |
SPEND instruction causes an item added to the current transaction's input list.
ADDTXOUT instruction causes an item added to the current transaction's output list.
MINT instruction causes an item added to the current coinbase's output list.
ADDRIGHTDEF instruction causes an item added to the current transaction's definition list.
RECEIVED instruction loads outpoint representing the current transaction output.
GETCOIN instruction loads the token passed to the current transaction output.
TXFEE returns the minimum transaction fees requried for the current transaction.
GETDEFINITION returns the definition indicated by the hash parameter.
GETUTXO returns the definition indicated by the outpoint parameter.
META returns the requested contract meta data.
TIME returns the block timestamp.
HEIGHT returns the block height.
TXIOCOUNT returns current transaction's input and output count.