aboutsummaryrefslogtreecommitdiffstats
path: root/ethchain
diff options
context:
space:
mode:
Diffstat (limited to 'ethchain')
-rw-r--r--ethchain/keypair.go3
-rw-r--r--ethchain/stack.go22
-rw-r--r--ethchain/state_manager.go34
-rw-r--r--ethchain/transaction.go81
-rw-r--r--ethchain/transaction_pool.go8
-rw-r--r--ethchain/vm.go73
-rw-r--r--ethchain/vm_test.go157
7 files changed, 199 insertions, 179 deletions
diff --git a/ethchain/keypair.go b/ethchain/keypair.go
index 9fdc95972..9daaedbee 100644
--- a/ethchain/keypair.go
+++ b/ethchain/keypair.go
@@ -34,6 +34,7 @@ func (k *KeyPair) Account() *Account {
// Create transaction, creates a new and signed transaction, ready for processing
func (k *KeyPair) CreateTx(receiver []byte, value *big.Int, data []string) *Transaction {
+ /* TODO
tx := NewTransaction(receiver, value, data)
tx.Nonce = k.account.Nonce
@@ -41,6 +42,8 @@ func (k *KeyPair) CreateTx(receiver []byte, value *big.Int, data []string) *Tran
tx.Sign(k.PrivateKey)
return tx
+ */
+ return nil
}
func (k *KeyPair) RlpEncode() []byte {
diff --git a/ethchain/stack.go b/ethchain/stack.go
index 3c2899e62..e3fc4b684 100644
--- a/ethchain/stack.go
+++ b/ethchain/stack.go
@@ -173,21 +173,25 @@ func NewStack() *Stack {
}
func (st *Stack) Pop() *big.Int {
- str := st.data[0]
- st.data = st.data[1:]
+ str := st.data[len(st.data)-1]
+
+ copy(st.data[:len(st.data)-1], st.data[:len(st.data)-1])
+ st.data = st.data[:len(st.data)-1]
return str
}
func (st *Stack) Popn() (*big.Int, *big.Int) {
- ints := st.data[:2]
- st.data = st.data[2:]
+ ints := st.data[len(st.data)-2:]
+
+ copy(st.data[:len(st.data)-2], st.data[:len(st.data)-2])
+ st.data = st.data[:len(st.data)-2]
return ints[0], ints[1]
}
func (st *Stack) Peek() *big.Int {
- str := st.data[0]
+ str := st.data[len(st.data)-1]
return str
}
@@ -202,7 +206,7 @@ func (st *Stack) Push(d *big.Int) {
st.data = append(st.data, d)
}
func (st *Stack) Print() {
- fmt.Println("### STACK ###")
+ fmt.Println("### stack ###")
if len(st.data) > 0 {
for i, val := range st.data {
fmt.Printf("%-3d %v\n", i, val)
@@ -242,15 +246,15 @@ func (m *Memory) Len() int {
}
func (m *Memory) Print() {
- fmt.Println("### MEM ###")
+ fmt.Printf("### mem %d bytes ###\n", len(m.store))
if len(m.store) > 0 {
addr := 0
- for i := 0; i+32 < len(m.store); i += 32 {
+ for i := 0; i+32 <= len(m.store); i += 32 {
fmt.Printf("%03d %v\n", addr, m.store[i:i+32])
addr++
}
} else {
fmt.Println("-- empty --")
}
- fmt.Println("###########")
+ fmt.Println("####################")
}
diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go
index 140b0efd0..78fcb2c2f 100644
--- a/ethchain/state_manager.go
+++ b/ethchain/state_manager.go
@@ -80,7 +80,7 @@ func (sm *StateManager) WatchAddr(addr []byte) *AccountState {
func (sm *StateManager) GetAddrState(addr []byte) *AccountState {
account := sm.addrStateStore.Get(addr)
if account == nil {
- a := sm.bc.CurrentBlock.state.GetAccount(addr)
+ a := sm.procState.GetAccount(addr)
account = &AccountState{Nonce: a.Nonce, Account: a}
}
@@ -98,16 +98,21 @@ func (sm *StateManager) MakeContract(tx *Transaction) {
}
}
+// Apply transactions uses the transaction passed to it and applies them onto
+// the current processing state.
func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) {
// Process each transaction/contract
for _, tx := range txs {
// If there's no recipient, it's a contract
+ // Check if this is a contract creation traction and if so
+ // create a contract of this tx.
if tx.IsContract() {
sm.MakeContract(tx)
- //XXX block.MakeContract(tx)
} else {
+ // Figure out if the address this transaction was sent to is a
+ // contract or an actual account. In case of a contract, we process that
+ // contract instead of moving funds between accounts.
if contract := sm.procState.GetContract(tx.Recipient); contract != nil {
- //XXX if contract := block.state.GetContract(tx.Recipient); contract != nil {
sm.ProcessContract(contract, tx, block)
} else {
err := sm.Ethereum.TxPool().ProcessTransaction(tx, block)
@@ -121,9 +126,9 @@ func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) {
// The prepare function, prepares the state manager for the next
// "ProcessBlock" action.
-func (sm *StateManager) Prepare(processer *State, comparative *State) {
+func (sm *StateManager) Prepare(processor *State, comparative *State) {
sm.compState = comparative
- sm.procState = processer
+ sm.procState = processor
}
// Default prepare function
@@ -172,14 +177,12 @@ func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error {
// if !sm.compState.Cmp(sm.procState)
if !sm.compState.Cmp(sm.procState) {
- //XXX return fmt.Errorf("Invalid merkle root. Expected %x, got %x", block.State().trie.Root, sm.bc.CurrentBlock.State().trie.Root)
return fmt.Errorf("Invalid merkle root. Expected %x, got %x", sm.compState.trie.Root, sm.procState.trie.Root)
}
// Calculate the new total difficulty and sync back to the db
if sm.CalculateTD(block) {
// Sync the current block's state to the database and cancelling out the deferred Undo
- //XXX sm.bc.CurrentBlock.Sync()
sm.procState.Sync()
// Broadcast the valid block back to the wire
@@ -276,14 +279,14 @@ func CalculateUncleReward(block *Block) *big.Int {
func (sm *StateManager) AccumelateRewards(block *Block) error {
// Get the coinbase rlp data
- //XXX addr := processor.state.GetAccount(block.Coinbase)
addr := sm.procState.GetAccount(block.Coinbase)
// Reward amount of ether to the coinbase address
addr.AddFee(CalculateBlockReward(block, len(block.Uncles)))
- //XXX processor.state.UpdateAccount(block.Coinbase, addr)
+
var acc []byte
copy(acc, block.Coinbase)
sm.procState.UpdateAccount(acc, addr)
+
for _, uncle := range block.Uncles {
uncleAddr := sm.procState.GetAccount(uncle.Coinbase)
uncleAddr.AddFee(CalculateUncleReward(uncle))
@@ -301,13 +304,12 @@ func (sm *StateManager) Stop() {
func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, block *Block) {
// Recovering function in case the VM had any errors
- /*
- defer func() {
- if r := recover(); r != nil {
- fmt.Println("Recovered from VM execution with err =", r)
- }
- }()
- */
+ defer func() {
+ if r := recover(); r != nil {
+ fmt.Println("Recovered from VM execution with err =", r)
+ }
+ }()
+
caller := sm.procState.GetAccount(tx.Sender())
closure := NewClosure(caller, contract, sm.procState, tx.Gas, tx.Value)
vm := NewVm(sm.procState, RuntimeVars{
diff --git a/ethchain/transaction.go b/ethchain/transaction.go
index 3b07c81d4..506e3c159 100644
--- a/ethchain/transaction.go
+++ b/ethchain/transaction.go
@@ -1,7 +1,6 @@
package ethchain
import (
- "bytes"
"github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/secp256k1-go"
"math/big"
@@ -18,31 +17,19 @@ type Transaction struct {
Data []string
v byte
r, s []byte
-}
-
-func NewTransaction(to []byte, value *big.Int, data []string) *Transaction {
- tx := Transaction{Recipient: to, Value: value, Nonce: 0, Data: data}
- return &tx
+ // Indicates whether this tx is a contract creation transaction
+ contractCreation bool
}
func NewContractCreationTx(value, gasprice *big.Int, data []string) *Transaction {
- return &Transaction{Value: value, Gasprice: gasprice, Data: data}
+ return &Transaction{Value: value, Gasprice: gasprice, Data: data, contractCreation: true}
}
-func NewContractMessageTx(to []byte, value, gasprice, gas *big.Int, data []string) *Transaction {
+func NewTransactionMessage(to []byte, value, gasprice, gas *big.Int, data []string) *Transaction {
return &Transaction{Recipient: to, Value: value, Gasprice: gasprice, Gas: gas, Data: data}
}
-func NewTx(to []byte, value *big.Int, data []string) *Transaction {
- return &Transaction{Recipient: to, Value: value, Gasprice: big.NewInt(0), Gas: big.NewInt(0), Nonce: 0, Data: data}
-}
-
-// XXX Deprecated
-func NewTransactionFromData(data []byte) *Transaction {
- return NewTransactionFromBytes(data)
-}
-
func NewTransactionFromBytes(data []byte) *Transaction {
tx := &Transaction{}
tx.RlpDecode(data)
@@ -74,7 +61,7 @@ func (tx *Transaction) Hash() []byte {
}
func (tx *Transaction) IsContract() bool {
- return bytes.Compare(tx.Recipient, ContractAddr) == 0
+ return tx.contractCreation
}
func (tx *Transaction) Signature(key []byte) []byte {
@@ -124,16 +111,14 @@ func (tx *Transaction) Sign(privk []byte) error {
}
func (tx *Transaction) RlpData() interface{} {
- // Prepare the transaction for serialization
- return []interface{}{
- tx.Nonce,
- tx.Recipient,
- tx.Value,
- ethutil.NewSliceValue(tx.Data).Slice(),
- tx.v,
- tx.r,
- tx.s,
+ data := []interface{}{tx.Nonce, tx.Value, tx.Gasprice}
+
+ if !tx.contractCreation {
+ data = append(data, tx.Recipient, tx.Gas)
}
+ d := ethutil.NewSliceValue(tx.Data).Slice()
+
+ return append(data, d, tx.v, tx.r, tx.s)
}
func (tx *Transaction) RlpValue() *ethutil.Value {
@@ -150,17 +135,35 @@ func (tx *Transaction) RlpDecode(data []byte) {
func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
tx.Nonce = decoder.Get(0).Uint()
- tx.Recipient = decoder.Get(1).Bytes()
- tx.Value = decoder.Get(2).BigInt()
-
- d := decoder.Get(3)
- tx.Data = make([]string, d.Len())
- for i := 0; i < d.Len(); i++ {
- tx.Data[i] = d.Get(i).Str()
+ tx.Value = decoder.Get(1).BigInt()
+ tx.Gasprice = decoder.Get(2).BigInt()
+
+ // If the 4th item is a list(slice) this tx
+ // is a contract creation tx
+ if decoder.Get(3).IsList() {
+ d := decoder.Get(3)
+ tx.Data = make([]string, d.Len())
+ for i := 0; i < d.Len(); i++ {
+ tx.Data[i] = d.Get(i).Str()
+ }
+
+ tx.v = byte(decoder.Get(4).Uint())
+ tx.r = decoder.Get(5).Bytes()
+ tx.s = decoder.Get(6).Bytes()
+
+ tx.contractCreation = true
+ } else {
+ tx.Recipient = decoder.Get(3).Bytes()
+ tx.Gas = decoder.Get(4).BigInt()
+
+ d := decoder.Get(5)
+ tx.Data = make([]string, d.Len())
+ for i := 0; i < d.Len(); i++ {
+ tx.Data[i] = d.Get(i).Str()
+ }
+
+ tx.v = byte(decoder.Get(6).Uint())
+ tx.r = decoder.Get(7).Bytes()
+ tx.s = decoder.Get(8).Bytes()
}
-
- // TODO something going wrong here
- tx.v = byte(decoder.Get(4).Uint())
- tx.r = decoder.Get(5).Bytes()
- tx.s = decoder.Get(6).Bytes()
}
diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go
index 26827c289..9acd39e31 100644
--- a/ethchain/transaction_pool.go
+++ b/ethchain/transaction_pool.go
@@ -208,7 +208,7 @@ func (pool *TxPool) QueueTransaction(tx *Transaction) {
pool.queueChan <- tx
}
-func (pool *TxPool) Flush() []*Transaction {
+func (pool *TxPool) CurrentTransactions() []*Transaction {
pool.mutex.Lock()
defer pool.mutex.Unlock()
@@ -222,6 +222,12 @@ func (pool *TxPool) Flush() []*Transaction {
i++
}
+ return txList
+}
+
+func (pool *TxPool) Flush() []*Transaction {
+ txList := pool.CurrentTransactions()
+
// Recreate a new list all together
// XXX Is this the fastest way?
pool.pool = list.New()
diff --git a/ethchain/vm.go b/ethchain/vm.go
index 126592b25..98aaa603a 100644
--- a/ethchain/vm.go
+++ b/ethchain/vm.go
@@ -2,14 +2,24 @@ package ethchain
import (
_ "bytes"
- _ "fmt"
+ "fmt"
"github.com/ethereum/eth-go/ethutil"
_ "github.com/obscuren/secp256k1-go"
- "log"
_ "math"
"math/big"
)
+var (
+ GasStep = big.NewInt(1)
+ GasSha = big.NewInt(20)
+ GasSLoad = big.NewInt(20)
+ GasSStore = big.NewInt(100)
+ GasBalance = big.NewInt(20)
+ GasCreate = big.NewInt(100)
+ GasCall = big.NewInt(20)
+ GasMemory = big.NewInt(1)
+)
+
type Vm struct {
txPool *TxPool
// Stack for processing contracts
@@ -70,10 +80,43 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
}
// TODO Get each instruction cost properly
- fee := new(big.Int)
- fee.Add(fee, big.NewInt(1000))
+ gas := new(big.Int)
+ useGas := func(amount *big.Int) {
+ gas.Add(gas, amount)
+ }
+
+ switch op {
+ case oSHA3:
+ useGas(GasSha)
+ case oSLOAD:
+ useGas(GasSLoad)
+ case oSSTORE:
+ var mult *big.Int
+ y, x := stack.Peekn()
+ val := closure.GetMem(x)
+ if val.IsEmpty() && len(y.Bytes()) > 0 {
+ mult = ethutil.Big2
+ } else if !val.IsEmpty() && len(y.Bytes()) == 0 {
+ mult = ethutil.Big0
+ } else {
+ mult = ethutil.Big1
+ }
+ useGas(base.Mul(mult, GasSStore))
+ case oBALANCE:
+ useGas(GasBalance)
+ case oCREATE:
+ useGas(GasCreate)
+ case oCALL:
+ useGas(GasCall)
+ case oMLOAD, oMSIZE, oMSTORE8, oMSTORE:
+ useGas(GasMemory)
+ default:
+ useGas(GasStep)
+ }
+
+ if closure.Gas.Cmp(gas) < 0 {
+ ethutil.Config.Log.Debugln("Insufficient gas", closure.Gas, gas)
- if closure.Gas.Cmp(fee) < 0 {
return closure.Return(nil)
}
@@ -172,10 +215,17 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
} else {
stack.Push(ethutil.BigFalse)
}
- case oNOT:
+ case oEQ:
x, y := stack.Popn()
- // x != y
- if x.Cmp(y) != 0 {
+ // x == y
+ if x.Cmp(y) == 0 {
+ stack.Push(ethutil.BigTrue)
+ } else {
+ stack.Push(ethutil.BigFalse)
+ }
+ case oNOT:
+ x := stack.Pop()
+ if x.Cmp(ethutil.BigFalse) == 0 {
stack.Push(ethutil.BigTrue)
} else {
stack.Push(ethutil.BigFalse)
@@ -259,8 +309,8 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
case oJUMP:
pc = stack.Pop()
case oJUMPI:
- pos, cond := stack.Popn()
- if cond.Cmp(big.NewInt(0)) > 0 {
+ cond, pos := stack.Popn()
+ if cond.Cmp(ethutil.BigTrue) == 0 {
pc = pos
}
case oPC:
@@ -273,6 +323,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
retSize, retOffset := stack.Popn()
// Pop input size and offset
inSize, inOffset := stack.Popn()
+ fmt.Println(inSize, inOffset)
// Get the arguments from the memory
args := mem.Get(inOffset.Int64(), inSize.Int64())
// Pop gas and value of the stack.
@@ -317,6 +368,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte {
}
}
+/*
func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) {
ethutil.Config.Log.Debugf(" => creating inline tx %x %v %v %v", addr, value, from, length)
j := int64(0)
@@ -353,3 +405,4 @@ func contractMemory(state *State, contractAddr []byte, memAddr *big.Int) *big.In
return decoder.BigInt()
}
+*/
diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go
index 047531e09..838f12f56 100644
--- a/ethchain/vm_test.go
+++ b/ethchain/vm_test.go
@@ -1,114 +1,17 @@
package ethchain
import (
- "bytes"
+ _ "bytes"
+ "fmt"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethutil"
+ "github.com/obscuren/mutan"
"math/big"
+ "strings"
"testing"
)
/*
-
-func TestRun(t *testing.T) {
- InitFees()
-
- ethutil.ReadConfig("")
-
- db, _ := ethdb.NewMemDatabase()
- state := NewState(ethutil.NewTrie(db, ""))
-
- script := Compile([]string{
- "TXSENDER",
- "SUICIDE",
- })
-
- tx := NewTransaction(ContractAddr, big.NewInt(1e17), script)
- fmt.Printf("contract addr %x\n", tx.Hash()[12:])
- contract := MakeContract(tx, state)
- vm := &Vm{}
-
- vm.Process(contract, state, RuntimeVars{
- address: tx.Hash()[12:],
- blockNumber: 1,
- sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
- prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
- coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
- time: 1,
- diff: big.NewInt(256),
- txValue: tx.Value,
- txData: tx.Data,
- })
-}
-
-func TestRun1(t *testing.T) {
- ethutil.ReadConfig("")
-
- db, _ := ethdb.NewMemDatabase()
- state := NewState(ethutil.NewTrie(db, ""))
-
- script := Compile([]string{
- "PUSH", "0",
- "PUSH", "0",
- "TXSENDER",
- "PUSH", "10000000",
- "MKTX",
- })
- fmt.Println(ethutil.NewValue(script))
-
- tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
- fmt.Printf("contract addr %x\n", tx.Hash()[12:])
- contract := MakeContract(tx, state)
- vm := &Vm{}
-
- vm.Process(contract, state, RuntimeVars{
- address: tx.Hash()[12:],
- blockNumber: 1,
- sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
- prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
- coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
- time: 1,
- diff: big.NewInt(256),
- txValue: tx.Value,
- txData: tx.Data,
- })
-}
-
-func TestRun2(t *testing.T) {
- ethutil.ReadConfig("")
-
- db, _ := ethdb.NewMemDatabase()
- state := NewState(ethutil.NewTrie(db, ""))
-
- script := Compile([]string{
- "PUSH", "0",
- "PUSH", "0",
- "TXSENDER",
- "PUSH", "10000000",
- "MKTX",
- })
- fmt.Println(ethutil.NewValue(script))
-
- tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
- fmt.Printf("contract addr %x\n", tx.Hash()[12:])
- contract := MakeContract(tx, state)
- vm := &Vm{}
-
- vm.Process(contract, state, RuntimeVars{
- address: tx.Hash()[12:],
- blockNumber: 1,
- sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
- prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
- coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
- time: 1,
- diff: big.NewInt(256),
- txValue: tx.Value,
- txData: tx.Data,
- })
-}
-*/
-
-// XXX Full stack test
func TestRun3(t *testing.T) {
ethutil.ReadConfig("")
@@ -127,12 +30,12 @@ func TestRun3(t *testing.T) {
"PUSH", "0",
"RETURN",
})
- tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
+ tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script)
addr := tx.Hash()[12:]
contract := MakeContract(tx, state)
state.UpdateContract(contract)
- callerScript := ethutil.Compile(
+ callerScript := ethutil.Assemble(
"PUSH", 1337, // Argument
"PUSH", 65, // argument mem offset
"MSTORE",
@@ -149,7 +52,7 @@ func TestRun3(t *testing.T) {
"PUSH", 0,
"RETURN",
)
- callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript)
+ callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript)
// Contract addr as test address
account := NewAccount(ContractAddr, big.NewInt(10000000))
@@ -171,4 +74,50 @@ func TestRun3(t *testing.T) {
if bytes.Compare(ret, exp) != 0 {
t.Errorf("expected return value to be %v, got %v", exp, ret)
}
+}*/
+
+func TestRun4(t *testing.T) {
+ ethutil.ReadConfig("")
+
+ db, _ := ethdb.NewMemDatabase()
+ state := NewState(ethutil.NewTrie(db, ""))
+
+ asm, err := mutan.Compile(strings.NewReader(`
+ a = 10
+ b = 10
+ if a == b {
+ c = 10
+ if c == 10 {
+ d = 1000
+ e = 10
+ }
+ }
+
+ store[0] = 20
+ store[a] = 20
+ `), false)
+ if err != nil {
+ fmt.Println(err)
+ }
+ //asm = append(asm, "LOG")
+ fmt.Println(asm)
+
+ callerScript := ethutil.Assemble(asm...)
+ callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript)
+
+ // Contract addr as test address
+ account := NewAccount(ContractAddr, big.NewInt(10000000))
+ callerClosure := NewClosure(account, MakeContract(callerTx, state), state, big.NewInt(1000000000), new(big.Int))
+
+ vm := NewVm(state, RuntimeVars{
+ origin: account.Address(),
+ blockNumber: 1,
+ prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
+ coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
+ time: 1,
+ diff: big.NewInt(256),
+ // XXX Tx data? Could be just an argument to the closure instead
+ txData: nil,
+ })
+ callerClosure.Call(vm, nil)
}