mirror of https://github.com/status-im/op-geth.git
Moved the vm code the block manager and added more opcodes
This commit is contained in:
parent
33004d704e
commit
8c4eca2490
|
@ -6,14 +6,14 @@ import (
|
||||||
"github.com/ethereum/ethutil-go"
|
"github.com/ethereum/ethutil-go"
|
||||||
"log"
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type BlockChain struct {
|
type BlockChain struct {
|
||||||
|
// Last block
|
||||||
LastBlock *ethutil.Block
|
LastBlock *ethutil.Block
|
||||||
|
// The famous, the fabulous Mister GENESIIIIIIS (block)
|
||||||
genesisBlock *ethutil.Block
|
genesisBlock *ethutil.Block
|
||||||
|
// Last known total difficulty
|
||||||
TD *big.Int
|
TD *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,11 +22,10 @@ func NewBlockChain() *BlockChain {
|
||||||
bc.genesisBlock = ethutil.NewBlock(ethutil.Encode(ethutil.Genesis))
|
bc.genesisBlock = ethutil.NewBlock(ethutil.Encode(ethutil.Genesis))
|
||||||
|
|
||||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
||||||
bc.TD = new(big.Int)
|
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
|
||||||
bc.TD.SetBytes(ethutil.Config.Db.LastKnownTD())
|
|
||||||
|
|
||||||
// TODO get last block from the database
|
// TODO get last block from the database
|
||||||
//bc.LastBlock = bc.genesisBlock
|
bc.LastBlock = bc.genesisBlock
|
||||||
|
|
||||||
return bc
|
return bc
|
||||||
}
|
}
|
||||||
|
@ -46,6 +45,9 @@ type BlockManager struct {
|
||||||
|
|
||||||
// Stack for processing contracts
|
// Stack for processing contracts
|
||||||
stack *Stack
|
stack *Stack
|
||||||
|
|
||||||
|
// Last known block number
|
||||||
|
LastBlockNumber *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlockManager() *BlockManager {
|
func NewBlockManager() *BlockManager {
|
||||||
|
@ -54,6 +56,10 @@ func NewBlockManager() *BlockManager {
|
||||||
stack: NewStack(),
|
stack: NewStack(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the last known block number based on the blockchains last
|
||||||
|
// block
|
||||||
|
bm.LastBlockNumber = bm.BlockInfo(bm.bc.LastBlock).Number
|
||||||
|
|
||||||
return bm
|
return bm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +105,20 @@ func (bm *BlockManager) ProcessBlock(block *ethutil.Block) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bm *BlockManager) writeBlockInfo(block *ethutil.Block) {
|
||||||
|
bi := ethutil.BlockInfo{Number: bm.LastBlockNumber.Add(bm.LastBlockNumber, big.NewInt(1))}
|
||||||
|
|
||||||
|
ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.MarshalRlp())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bm *BlockManager) BlockInfo(block *ethutil.Block) ethutil.BlockInfo {
|
||||||
|
bi := ethutil.BlockInfo{}
|
||||||
|
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
|
||||||
|
bi.UnmarshalRlp(data)
|
||||||
|
|
||||||
|
return bi
|
||||||
|
}
|
||||||
|
|
||||||
func (bm *BlockManager) CalculateTD(block *ethutil.Block) bool {
|
func (bm *BlockManager) CalculateTD(block *ethutil.Block) bool {
|
||||||
uncleDiff := new(big.Int)
|
uncleDiff := new(big.Int)
|
||||||
for _, uncle := range block.Uncles {
|
for _, uncle := range block.Uncles {
|
||||||
|
@ -204,10 +224,11 @@ func (bm *BlockManager) ProcessContract(tx *ethutil.Transaction, block *ethutil.
|
||||||
lockChan <- true
|
lockChan <- true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bm *BlockManager) ProcContract(tx *ethutil.Transaction,
|
// Contract evaluation is done here.
|
||||||
block *ethutil.Block, cb TxCallback) {
|
func (bm *BlockManager) ProcContract(tx *ethutil.Transaction, block *ethutil.Block, cb TxCallback) {
|
||||||
// Instruction pointer
|
// Instruction pointer
|
||||||
pc := 0
|
pc := 0
|
||||||
|
blockInfo := bm.BlockInfo(block)
|
||||||
|
|
||||||
contract := block.GetContract(tx.Hash())
|
contract := block.GetContract(tx.Hash())
|
||||||
if contract == nil {
|
if contract == nil {
|
||||||
|
@ -238,6 +259,8 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
switch op {
|
switch op {
|
||||||
|
case oSTOP:
|
||||||
|
break out
|
||||||
case oADD:
|
case oADD:
|
||||||
x, y := bm.stack.Popn()
|
x, y := bm.stack.Popn()
|
||||||
// (x + y) % 2 ** 256
|
// (x + y) % 2 ** 256
|
||||||
|
@ -378,7 +401,34 @@ out:
|
||||||
case oBLK_TIMESTAMP:
|
case oBLK_TIMESTAMP:
|
||||||
bm.stack.Push(big.NewInt(block.Time).String())
|
bm.stack.Push(big.NewInt(block.Time).String())
|
||||||
case oBLK_NUMBER:
|
case oBLK_NUMBER:
|
||||||
|
bm.stack.Push(blockInfo.Number.String())
|
||||||
|
case oBLK_DIFFICULTY:
|
||||||
|
bm.stack.Push(block.Difficulty.String())
|
||||||
|
case oBASEFEE:
|
||||||
|
// e = 10^21
|
||||||
|
e := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(21), big.NewInt(0))
|
||||||
|
d := new(big.Rat)
|
||||||
|
d.SetInt(block.Difficulty)
|
||||||
|
c := new(big.Rat)
|
||||||
|
c.SetFloat64(0.5)
|
||||||
|
// d = diff / 0.5
|
||||||
|
d.Quo(d, c)
|
||||||
|
// base = floor(d)
|
||||||
|
base.Div(d.Num(), d.Denom())
|
||||||
|
|
||||||
|
x := new(big.Int)
|
||||||
|
x.Div(e, base)
|
||||||
|
|
||||||
|
// x = floor(10^21 / floor(diff^0.5))
|
||||||
|
bm.stack.Push(x.String())
|
||||||
|
case oSHA256:
|
||||||
|
case oRIPEMD160:
|
||||||
|
case oECMUL:
|
||||||
|
case oECADD:
|
||||||
|
case oECSIGN:
|
||||||
|
case oECRECOVER:
|
||||||
|
case oECVALID:
|
||||||
|
case oSHA3:
|
||||||
case oPUSH:
|
case oPUSH:
|
||||||
// Get the next entry and pushes the value on the stack
|
// Get the next entry and pushes the value on the stack
|
||||||
pc++
|
pc++
|
||||||
|
@ -386,12 +436,25 @@ out:
|
||||||
case oPOP:
|
case oPOP:
|
||||||
// Pop current value of the stack
|
// Pop current value of the stack
|
||||||
bm.stack.Pop()
|
bm.stack.Pop()
|
||||||
|
case oDUP:
|
||||||
|
case oSWAP:
|
||||||
|
case oMLOAD:
|
||||||
|
case oMSTORE:
|
||||||
|
case oSLOAD:
|
||||||
|
case oSSTORE:
|
||||||
|
case oJMP:
|
||||||
|
case oJMPI:
|
||||||
|
case oIND:
|
||||||
|
case oEXTRO:
|
||||||
|
case oBALANCE:
|
||||||
|
case oMKTX:
|
||||||
|
case oSUICIDE:
|
||||||
|
/*
|
||||||
case oLOAD:
|
case oLOAD:
|
||||||
// Load instruction X on the stack
|
// Load instruction X on the stack
|
||||||
i, _ := strconv.Atoi(bm.stack.Pop())
|
i, _ := strconv.Atoi(bm.stack.Pop())
|
||||||
bm.stack.Push(contract.State().Get(string(ethutil.NumberToBytes(uint64(i), 32))))
|
bm.stack.Push(contract.State().Get(string(ethutil.NumberToBytes(uint64(i), 32))))
|
||||||
case oSTOP:
|
*/
|
||||||
break out
|
|
||||||
}
|
}
|
||||||
pc++
|
pc++
|
||||||
}
|
}
|
||||||
|
|
284
vm.go
284
vm.go
|
@ -1,284 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/ethereum/ethutil-go"
|
|
||||||
"math/big"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Op codes
|
|
||||||
const (
|
|
||||||
oSTOP int = 0x00
|
|
||||||
oADD int = 0x01
|
|
||||||
oMUL int = 0x02
|
|
||||||
oSUB int = 0x03
|
|
||||||
oDIV int = 0x04
|
|
||||||
oSDIV int = 0x05
|
|
||||||
oMOD int = 0x06
|
|
||||||
oSMOD int = 0x07
|
|
||||||
oEXP int = 0x08
|
|
||||||
oNEG int = 0x09
|
|
||||||
oLT int = 0x0a
|
|
||||||
oLE int = 0x0b
|
|
||||||
oGT int = 0x0c
|
|
||||||
oGE int = 0x0d
|
|
||||||
oEQ int = 0x0e
|
|
||||||
oNOT int = 0x0f
|
|
||||||
oMYADDRESS int = 0x10
|
|
||||||
oTXSENDER int = 0x11
|
|
||||||
oTXVALUE int = 0x12
|
|
||||||
oTXFEE int = 0x13
|
|
||||||
oTXDATAN int = 0x14
|
|
||||||
oTXDATA int = 0x15
|
|
||||||
oBLK_PREVHASH int = 0x16
|
|
||||||
oBLK_COINBASE int = 0x17
|
|
||||||
oBLK_TIMESTAMP int = 0x18
|
|
||||||
oBLK_NUMBER int = 0x19
|
|
||||||
oBLK_DIFFICULTY int = 0x1a
|
|
||||||
oSHA256 int = 0x20
|
|
||||||
oRIPEMD160 int = 0x21
|
|
||||||
oECMUL int = 0x22
|
|
||||||
oECADD int = 0x23
|
|
||||||
oECSIGN int = 0x24
|
|
||||||
oECRECOVER int = 0x25
|
|
||||||
oECVALID int = 0x26
|
|
||||||
oPUSH int = 0x30
|
|
||||||
oPOP int = 0x31
|
|
||||||
oDUP int = 0x32
|
|
||||||
oDUPN int = 0x33
|
|
||||||
oSWAP int = 0x34
|
|
||||||
oSWAPN int = 0x35
|
|
||||||
oLOAD int = 0x36
|
|
||||||
oSTORE int = 0x37
|
|
||||||
oJMP int = 0x40
|
|
||||||
oJMPI int = 0x41
|
|
||||||
oIND int = 0x42
|
|
||||||
oEXTRO int = 0x50
|
|
||||||
oBALANCE int = 0x51
|
|
||||||
oMKTX int = 0x60
|
|
||||||
oSUICIDE int = 0xff
|
|
||||||
)
|
|
||||||
|
|
||||||
type OpType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
tNorm = iota
|
|
||||||
tData
|
|
||||||
tExtro
|
|
||||||
tCrypto
|
|
||||||
)
|
|
||||||
|
|
||||||
type TxCallback func(opType OpType) bool
|
|
||||||
|
|
||||||
// Simple push/pop stack mechanism
|
|
||||||
type Stack struct {
|
|
||||||
data []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStack() *Stack {
|
|
||||||
return &Stack{}
|
|
||||||
}
|
|
||||||
func (st *Stack) Pop() string {
|
|
||||||
s := len(st.data)
|
|
||||||
|
|
||||||
str := st.data[s-1]
|
|
||||||
st.data = st.data[:s-1]
|
|
||||||
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *Stack) Popn() (*big.Int, *big.Int) {
|
|
||||||
s := len(st.data)
|
|
||||||
|
|
||||||
strs := st.data[s-2:]
|
|
||||||
st.data = st.data[:s-2]
|
|
||||||
|
|
||||||
return ethutil.Big(strs[0]), ethutil.Big(strs[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *Stack) Push(d string) {
|
|
||||||
st.data = append(st.data, d)
|
|
||||||
}
|
|
||||||
func (st *Stack) Print() {
|
|
||||||
fmt.Println(st.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Vm struct {
|
|
||||||
// Stack
|
|
||||||
stack *Stack
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewVm() *Vm {
|
|
||||||
return &Vm{
|
|
||||||
stack: NewStack(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vm *Vm) ProcContract(tx *ethutil.Transaction,
|
|
||||||
block *ethutil.Block, cb TxCallback) {
|
|
||||||
// Instruction pointer
|
|
||||||
pc := 0
|
|
||||||
|
|
||||||
contract := block.GetContract(tx.Hash())
|
|
||||||
if contract == nil {
|
|
||||||
fmt.Println("Contract not found")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Pow256 := ethutil.BigPow(2, 256)
|
|
||||||
|
|
||||||
//fmt.Printf("# op arg\n")
|
|
||||||
out:
|
|
||||||
for {
|
|
||||||
// The base big int for all calculations. Use this for any results.
|
|
||||||
base := new(big.Int)
|
|
||||||
// XXX Should Instr return big int slice instead of string slice?
|
|
||||||
// Get the next instruction from the contract
|
|
||||||
//op, _, _ := Instr(contract.state.Get(string(Encode(uint32(pc)))))
|
|
||||||
nb := ethutil.NumberToBytes(uint64(pc), 32)
|
|
||||||
op, _, _ := ethutil.Instr(contract.State().Get(string(nb)))
|
|
||||||
|
|
||||||
if !cb(0) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if Debug {
|
|
||||||
//fmt.Printf("%-3d %-4d\n", pc, op)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch op {
|
|
||||||
case oADD:
|
|
||||||
x, y := vm.stack.Popn()
|
|
||||||
// (x + y) % 2 ** 256
|
|
||||||
base.Add(x, y)
|
|
||||||
base.Mod(base, Pow256)
|
|
||||||
// Pop result back on the stack
|
|
||||||
vm.stack.Push(base.String())
|
|
||||||
case oSUB:
|
|
||||||
x, y := vm.stack.Popn()
|
|
||||||
// (x - y) % 2 ** 256
|
|
||||||
base.Sub(x, y)
|
|
||||||
base.Mod(base, Pow256)
|
|
||||||
// Pop result back on the stack
|
|
||||||
vm.stack.Push(base.String())
|
|
||||||
case oMUL:
|
|
||||||
x, y := vm.stack.Popn()
|
|
||||||
// (x * y) % 2 ** 256
|
|
||||||
base.Mul(x, y)
|
|
||||||
base.Mod(base, Pow256)
|
|
||||||
// Pop result back on the stack
|
|
||||||
vm.stack.Push(base.String())
|
|
||||||
case oDIV:
|
|
||||||
x, y := vm.stack.Popn()
|
|
||||||
// floor(x / y)
|
|
||||||
base.Div(x, y)
|
|
||||||
// Pop result back on the stack
|
|
||||||
vm.stack.Push(base.String())
|
|
||||||
case oSDIV:
|
|
||||||
x, y := vm.stack.Popn()
|
|
||||||
// n > 2**255
|
|
||||||
if x.Cmp(Pow256) > 0 {
|
|
||||||
x.Sub(Pow256, x)
|
|
||||||
}
|
|
||||||
if y.Cmp(Pow256) > 0 {
|
|
||||||
y.Sub(Pow256, y)
|
|
||||||
}
|
|
||||||
z := new(big.Int)
|
|
||||||
z.Div(x, y)
|
|
||||||
if z.Cmp(Pow256) > 0 {
|
|
||||||
z.Sub(Pow256, z)
|
|
||||||
}
|
|
||||||
// Push result on to the stack
|
|
||||||
vm.stack.Push(z.String())
|
|
||||||
case oMOD:
|
|
||||||
x, y := vm.stack.Popn()
|
|
||||||
base.Mod(x, y)
|
|
||||||
vm.stack.Push(base.String())
|
|
||||||
case oSMOD:
|
|
||||||
x, y := vm.stack.Popn()
|
|
||||||
// n > 2**255
|
|
||||||
if x.Cmp(Pow256) > 0 {
|
|
||||||
x.Sub(Pow256, x)
|
|
||||||
}
|
|
||||||
if y.Cmp(Pow256) > 0 {
|
|
||||||
y.Sub(Pow256, y)
|
|
||||||
}
|
|
||||||
z := new(big.Int)
|
|
||||||
z.Mod(x, y)
|
|
||||||
if z.Cmp(Pow256) > 0 {
|
|
||||||
z.Sub(Pow256, z)
|
|
||||||
}
|
|
||||||
// Push result on to the stack
|
|
||||||
vm.stack.Push(z.String())
|
|
||||||
case oEXP:
|
|
||||||
x, y := vm.stack.Popn()
|
|
||||||
base.Exp(x, y, Pow256)
|
|
||||||
|
|
||||||
vm.stack.Push(base.String())
|
|
||||||
case oNEG:
|
|
||||||
base.Sub(Pow256, ethutil.Big(vm.stack.Pop()))
|
|
||||||
vm.stack.Push(base.String())
|
|
||||||
case oLT:
|
|
||||||
x, y := vm.stack.Popn()
|
|
||||||
// x < y
|
|
||||||
if x.Cmp(y) < 0 {
|
|
||||||
vm.stack.Push("1")
|
|
||||||
} else {
|
|
||||||
vm.stack.Push("0")
|
|
||||||
}
|
|
||||||
case oLE:
|
|
||||||
x, y := vm.stack.Popn()
|
|
||||||
// x <= y
|
|
||||||
if x.Cmp(y) < 1 {
|
|
||||||
vm.stack.Push("1")
|
|
||||||
} else {
|
|
||||||
vm.stack.Push("0")
|
|
||||||
}
|
|
||||||
case oGT:
|
|
||||||
x, y := vm.stack.Popn()
|
|
||||||
// x > y
|
|
||||||
if x.Cmp(y) > 0 {
|
|
||||||
vm.stack.Push("1")
|
|
||||||
} else {
|
|
||||||
vm.stack.Push("0")
|
|
||||||
}
|
|
||||||
case oGE:
|
|
||||||
x, y := vm.stack.Popn()
|
|
||||||
// x >= y
|
|
||||||
if x.Cmp(y) > -1 {
|
|
||||||
vm.stack.Push("1")
|
|
||||||
} else {
|
|
||||||
vm.stack.Push("0")
|
|
||||||
}
|
|
||||||
case oNOT:
|
|
||||||
x, y := vm.stack.Popn()
|
|
||||||
// x != y
|
|
||||||
if x.Cmp(y) != 0 {
|
|
||||||
vm.stack.Push("1")
|
|
||||||
} else {
|
|
||||||
vm.stack.Push("0")
|
|
||||||
}
|
|
||||||
case oMYADDRESS:
|
|
||||||
vm.stack.Push(string(tx.Hash()))
|
|
||||||
case oTXSENDER:
|
|
||||||
vm.stack.Push(string(tx.Sender()))
|
|
||||||
case oPUSH:
|
|
||||||
// Get the next entry and pushes the value on the stack
|
|
||||||
pc++
|
|
||||||
vm.stack.Push(contract.State().Get(string(ethutil.NumberToBytes(uint64(pc), 32))))
|
|
||||||
case oPOP:
|
|
||||||
// Pop current value of the stack
|
|
||||||
vm.stack.Pop()
|
|
||||||
case oLOAD:
|
|
||||||
// Load instruction X on the stack
|
|
||||||
i, _ := strconv.Atoi(vm.stack.Pop())
|
|
||||||
vm.stack.Push(contract.State().Get(string(ethutil.NumberToBytes(uint64(i), 32))))
|
|
||||||
case oSTOP:
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
pc++
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.stack.Print()
|
|
||||||
}
|
|
76
vm_test.go
76
vm_test.go
|
@ -1,76 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
/*
|
|
||||||
import (
|
|
||||||
_"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func TestVm(t *testing.T) {
|
|
||||||
InitFees()
|
|
||||||
|
|
||||||
db, _ := NewMemDatabase()
|
|
||||||
Db = db
|
|
||||||
|
|
||||||
ctrct := NewTransaction("", 200000000, []string{
|
|
||||||
"PUSH", "1a2f2e",
|
|
||||||
"PUSH", "hallo",
|
|
||||||
"POP", // POP hallo
|
|
||||||
"PUSH", "3",
|
|
||||||
"LOAD", // Load hallo back on the stack
|
|
||||||
|
|
||||||
"PUSH", "1",
|
|
||||||
"PUSH", "2",
|
|
||||||
"ADD",
|
|
||||||
|
|
||||||
"PUSH", "2",
|
|
||||||
"PUSH", "1",
|
|
||||||
"SUB",
|
|
||||||
|
|
||||||
"PUSH", "100000000000000000000000",
|
|
||||||
"PUSH", "10000000000000",
|
|
||||||
"SDIV",
|
|
||||||
|
|
||||||
"PUSH", "105",
|
|
||||||
"PUSH", "200",
|
|
||||||
"MOD",
|
|
||||||
|
|
||||||
"PUSH", "100000000000000000000000",
|
|
||||||
"PUSH", "10000000000000",
|
|
||||||
"SMOD",
|
|
||||||
|
|
||||||
"PUSH", "5",
|
|
||||||
"PUSH", "10",
|
|
||||||
"LT",
|
|
||||||
|
|
||||||
"PUSH", "5",
|
|
||||||
"PUSH", "5",
|
|
||||||
"LE",
|
|
||||||
|
|
||||||
"PUSH", "50",
|
|
||||||
"PUSH", "5",
|
|
||||||
"GT",
|
|
||||||
|
|
||||||
"PUSH", "5",
|
|
||||||
"PUSH", "5",
|
|
||||||
"GE",
|
|
||||||
|
|
||||||
"PUSH", "10",
|
|
||||||
"PUSH", "10",
|
|
||||||
"NOT",
|
|
||||||
|
|
||||||
"MYADDRESS",
|
|
||||||
"TXSENDER",
|
|
||||||
|
|
||||||
"STOP",
|
|
||||||
})
|
|
||||||
tx := NewTransaction("1e8a42ea8cce13", 100, []string{})
|
|
||||||
|
|
||||||
block := CreateBlock("", 0, "", "c014ba53", 0, 0, "", []*Transaction{ctrct, tx})
|
|
||||||
db.Put(block.Hash(), block.MarshalRlp())
|
|
||||||
|
|
||||||
bm := NewBlockManager()
|
|
||||||
bm.ProcessBlock( block )
|
|
||||||
}
|
|
||||||
*/
|
|
Loading…
Reference in New Issue