Vite VM Vite VM (Virtual Machine) retains the semantics of most EVM instructions, but is not a clone of EVM. Therefore, new instructions such as synccall
and callbackdest
need to be implemented from scratch, and some EVM instructions such as return
, returndatasize
, returndatacopy
need to be reimplemented to adapt to the Vite protocol.
Transaction Types Three new transaction types are added in Vite VM:
SendSyncCall : A send transaction initiated by a synchronous call, SendCallback : A send transaction initiated by a callback that successfully executes and returns a result. SendFailureCallback : A send transaction initiated by a callback that fails to execute and returns an error. Execution Context Each transaction with one of the three new types contains an execution context data stored in the database.
The execution context is defined as follows:
Copy
ExecutionContext {
referrer types. Hash
callback big. Int
stack [ ] big. Int
memory [ ] byte
}
For a SendSyncCall
transaction, its execution context includes:
referrer
holds the send transaction hash of the upstream (origin) sync call. callback
holds the 4-bytes method id of the callback function entry. stack
holds a snapshot of the contract stack at the time synccall
is executed. memory
holds a snapshot of the contract memory at the time synccall
is executed. For a SendCallback
or a SendFailureCallback
transaction, its execution context only includes a referrer
field:
referrer
holds the send transaction hash of the latest sync call. Note: referrer
could look a bit confusing, see the example below:
SyncCall Instruction The synccall
instruction is implemented in pseudocode as follows:
Copy
func opSynccall ( ) {
callback, toAddress, tokenID, amount, inOffset, inSize := vm. stack. pop ( 6 )
calldata := vm. mem. get ( inOffset, inSize)
if vm. Context. originSendBlock == nil {
origin := this. sendBlock
} else {
origin := vm. Context. Origin
}
tx := ViteRequestTransaction {
from: this. address,
to: toAddress,
type : SendSyncCall,
value: amount,
token: tokenID,
data: calldata
executionContext: ExecutionContext {
referrer: origin. Hash,
callback: callback,
stack: vm. stack,
memory: vm. memory,
}
}
vite. trigger ( tx)
}
Return Instruction The return
instruction is implemented in pseudocode as follows:
Copy
func opReturn ( ) {
offset, size := vm. stack. pop ( 2 )
ret := vm. mem. get ( offset, size)
sendType := this. sendBlock. BlockType
if sendType == SendSyncCall || sendType == SendCallback || sendType == SendFailureCallback {
if vm. Context. Origin == nil {
origin := this. sendBlock
} else {
origin := vm. Context. Origin
}
if origin. BlockType == SendSyncCall {
callback := origin. executionContext. callback
data := concat ( callback, ret)
tx := ViteRequestTransaction {
from: this. address,
to: origin. address,
type : SendCallback,
data: data,
executionContext: ExecutionContext {
referrer: origin. Hash,
}
}
vite. trigger ( tx)
}
}
}
CallbackDest Instruction The callbackdest
instruction is implemented in pseudocode as follows:
Copy
func opCallbackDest ( ) {
sendType := this. sendBlock. BlockType
if sendType == SendCallback || sendType == SendFailureCallback {
referrer := this. sendBlock. executionContext. referrer
if referrer. address != this. address || referrer. ToAddress != this. sendBlock. address {
return nil , error
}
origin := referrer. executionContext. referrer
if origin. ToAddress != this. address {
return nil , error
}
vm. Context. Origin = origin
vm. stack = referrer. executionContext. stack
if sendType == SendCallback {
stack. push ( 1 )
} else {
stack. push ( 0 )
}
vm. memory = referrer. executionContext. memory
}
}
Advanced example Show Objectives
Solidity Compiler and EVM Pseudo assembly code for contract A
:
Copy
function_selector:
method_id := calldata(0, 4)
if method_id == sig_hash(add)
jump(tag_function_add)
else
fallback()
stop
tag_function_add:
a, b := abi_decode(calldata)
tmp := checked_add(a, b)
ret := abi_encode(tmp)
return(ret)
Pseudo assembly code for contract B
:
Copy
function_selector:
method_id := calldata(0, 4)
if method_id == sig_hash(sum)
jump(tag_function_sum)
else
fallback()
stop
tag_function_sum:
a, b, c := abi_decode(calldata)
contract_address := contractA
method_id := sig_hash(add)
params := abi_encode(a, b)
call(contract_address, method_id, params)
partialResult := abi_decode(returndata)
params := abi_encode(partialResult, c)
call(contract_address, method_id, params)
ret := abi_decode(returndata)
return(ret)
Pseudo assembly code for contract C
:
Copy
function_selector:
method_id := calldata(0, 4)
if method_id == sig_hash(test)
jump(tag_function_test)
else
fallback()
stop
tag_function_test:
contract_address := contractB
method_id := sig_hash(sum)
params := abi_encode(1, 2, 3)
call(contract_address, method_id, params)
ret := abi_decode(returndata)
data = ret
return
Solidity++ Compiler Pseudo assembly code for contract A
:
Copy
function_selector:
method_id := calldata(0, 4)
if method_id == sig_hash(add)
jump(tag_function_add)
else
fallback()
stop
tag_function_add:
a, b := abi_decode(calldata)
tmp := checked_add(a, b)
ret := abi_encode(tmp)
return(ret)
Pseudo assembly code for contract B
:
Copy
function_selector:
method_id := calldata(0, 4)
if method_id == sig_hash(sum)
jump(tag_function_sum)
else if method_id == 0x00000001
jump(tag_callback_01)
else if method_id == 0x00000002
jump(tag_callback_02)
else
fallback()
stop
tag_function_sum:
a, b, c := abi_decode(calldata)
contract_address := contractA
method_id := sig_hash(add)
params := abi_encode(a, b)
callback_id := 0x00000001
synccall(contract_address, method_id, params, callback_id)
stop
tag_callback_01:
callbackdest
p := abi_decode(calldata)
contract_address := contractA
method_id := sig_hash(add)
params := abi_encode(p, c)
callback_id := 0x00000002
synccall(contract_address, method_id, params, callback_id)
stop
tag_callback_02:
callbackdest
ret := abi_decode(calldata)
return(ret)
Pseudo assembly code for contract C
:
Copy
function_selector:
method_id := calldata(0, 4)
if method_id == sig_hash(test)
jump(tag_function_test)
else if method_id == 0x00000001
jump(tag_callback_01)
else
fallback()
stop
tag_function_test:
contract_address := contractB
method_id := sig_hash(sum)
params := abi_encode(1, 2, 3)
callback_id := 0x00000001
synccall(contract_address, method_id, params, callback_id)
stop
tag_callback_01:
callbackdest
ret := abi_decode(calldata)
return(ret)
Vite VM