Proper tests
This commit is contained in:
parent
b29c1eecd1
commit
88a9c62fcc
@ -6,11 +6,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ethereum/eth-go/ethutil"
|
"github.com/ethereum/eth-go/ethutil"
|
||||||
_ "github.com/ethereum/eth-go/ethwire"
|
_ "github.com/ethereum/eth-go/ethwire"
|
||||||
"github.com/obscuren/secp256k1-go"
|
|
||||||
"log"
|
"log"
|
||||||
"math"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -113,11 +110,14 @@ func (bm *BlockManager) ApplyTransactions(block *Block, txs []*Transaction) {
|
|||||||
// If there's no recipient, it's a contract
|
// If there's no recipient, it's a contract
|
||||||
if tx.IsContract() {
|
if tx.IsContract() {
|
||||||
block.MakeContract(tx)
|
block.MakeContract(tx)
|
||||||
bm.ProcessContract(tx, block)
|
} else {
|
||||||
|
if contract := block.GetContract(tx.Recipient); contract != nil {
|
||||||
|
bm.ProcessContract(contract, tx, block)
|
||||||
} else {
|
} else {
|
||||||
bm.TransactionPool.ProcessTransaction(tx, block)
|
bm.TransactionPool.ProcessTransaction(tx, block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block processing and validating with a given (temporarily) state
|
// Block processing and validating with a given (temporarily) state
|
||||||
@ -300,7 +300,7 @@ func (bm *BlockManager) Stop() {
|
|||||||
bm.bc.Stop()
|
bm.bc.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bm *BlockManager) ProcessContract(tx *Transaction, block *Block) {
|
func (bm *BlockManager) ProcessContract(contract *Contract, tx *Transaction, block *Block) {
|
||||||
// Recovering function in case the VM had any errors
|
// Recovering function in case the VM had any errors
|
||||||
/*
|
/*
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -310,402 +310,16 @@ func (bm *BlockManager) ProcessContract(tx *Transaction, block *Block) {
|
|||||||
}()
|
}()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Process contract
|
vm := &Vm{}
|
||||||
bm.ProcContract(tx, block, func(opType OpType) bool {
|
vm.Process(contract, NewState(block.state), RuntimeVars{
|
||||||
// TODO turn on once big ints are in place
|
address: tx.Hash()[12:],
|
||||||
//if !block.PayFee(tx.Hash(), StepFee.Uint64()) {
|
blockNumber: block.BlockInfo().Number,
|
||||||
// return false
|
sender: tx.Sender(),
|
||||||
//}
|
prevHash: block.PrevHash,
|
||||||
|
coinbase: block.Coinbase,
|
||||||
return true // Continue
|
time: block.Time,
|
||||||
|
diff: block.Difficulty,
|
||||||
|
txValue: tx.Value,
|
||||||
|
txData: tx.Data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contract evaluation is done here.
|
|
||||||
func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallback) {
|
|
||||||
addr := tx.Hash()[12:]
|
|
||||||
// Instruction pointer
|
|
||||||
pc := 0
|
|
||||||
blockInfo := bm.bc.BlockInfo(block)
|
|
||||||
|
|
||||||
contract := block.GetContract(addr)
|
|
||||||
|
|
||||||
if contract == nil {
|
|
||||||
fmt.Println("Contract not found")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Pow256 := ethutil.BigPow(2, 256)
|
|
||||||
|
|
||||||
if ethutil.Config.Debug {
|
|
||||||
fmt.Printf("# op\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
stepcount := 0
|
|
||||||
totalFee := new(big.Int)
|
|
||||||
|
|
||||||
// helper function for getting a contract's memory address
|
|
||||||
getMem := func(num int) *ethutil.Value {
|
|
||||||
nb := ethutil.BigToBytes(big.NewInt(int64(num)), 256)
|
|
||||||
return contract.Addr(nb)
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
for {
|
|
||||||
stepcount++
|
|
||||||
// The base big int for all calculations. Use this for any results.
|
|
||||||
base := new(big.Int)
|
|
||||||
val := getMem(pc)
|
|
||||||
//fmt.Printf("%x = %d, %v %x\n", r, len(r), v, nb)
|
|
||||||
op := OpCode(val.Uint())
|
|
||||||
|
|
||||||
var fee *big.Int = new(big.Int)
|
|
||||||
var fee2 *big.Int = new(big.Int)
|
|
||||||
if stepcount > 16 {
|
|
||||||
fee.Add(fee, StepFee)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the fees
|
|
||||||
switch op {
|
|
||||||
case oSSTORE:
|
|
||||||
y, x := bm.stack.Peekn()
|
|
||||||
val := contract.Addr(ethutil.BigToBytes(x, 256))
|
|
||||||
if val.IsEmpty() && len(y.Bytes()) > 0 {
|
|
||||||
fee2.Add(DataFee, StoreFee)
|
|
||||||
} else {
|
|
||||||
fee2.Sub(DataFee, StoreFee)
|
|
||||||
}
|
|
||||||
case oSLOAD:
|
|
||||||
fee.Add(fee, StoreFee)
|
|
||||||
case oEXTRO, oBALANCE:
|
|
||||||
fee.Add(fee, ExtroFee)
|
|
||||||
case oSHA256, oRIPEMD160, oECMUL, oECADD, oECSIGN, oECRECOVER, oECVALID:
|
|
||||||
fee.Add(fee, CryptoFee)
|
|
||||||
case oMKTX:
|
|
||||||
fee.Add(fee, ContractFee)
|
|
||||||
}
|
|
||||||
|
|
||||||
tf := new(big.Int).Add(fee, fee2)
|
|
||||||
if contract.Amount.Cmp(tf) < 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Add the fee to the total fee. It's subtracted when we're done looping
|
|
||||||
totalFee.Add(totalFee, tf)
|
|
||||||
|
|
||||||
if !cb(0) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if ethutil.Config.Debug {
|
|
||||||
fmt.Printf("%-3d %-4s", pc, op.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
switch op {
|
|
||||||
case oSTOP:
|
|
||||||
fmt.Println("")
|
|
||||||
break out
|
|
||||||
case oADD:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// (x + y) % 2 ** 256
|
|
||||||
base.Add(x, y)
|
|
||||||
base.Mod(base, Pow256)
|
|
||||||
// Pop result back on the stack
|
|
||||||
bm.stack.Push(base)
|
|
||||||
case oSUB:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// (x - y) % 2 ** 256
|
|
||||||
base.Sub(x, y)
|
|
||||||
base.Mod(base, Pow256)
|
|
||||||
// Pop result back on the stack
|
|
||||||
bm.stack.Push(base)
|
|
||||||
case oMUL:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// (x * y) % 2 ** 256
|
|
||||||
base.Mul(x, y)
|
|
||||||
base.Mod(base, Pow256)
|
|
||||||
// Pop result back on the stack
|
|
||||||
bm.stack.Push(base)
|
|
||||||
case oDIV:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// floor(x / y)
|
|
||||||
base.Div(x, y)
|
|
||||||
// Pop result back on the stack
|
|
||||||
bm.stack.Push(base)
|
|
||||||
case oSDIV:
|
|
||||||
x, y := bm.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
|
|
||||||
bm.stack.Push(z)
|
|
||||||
case oMOD:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
base.Mod(x, y)
|
|
||||||
bm.stack.Push(base)
|
|
||||||
case oSMOD:
|
|
||||||
x, y := bm.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
|
|
||||||
bm.stack.Push(z)
|
|
||||||
case oEXP:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
base.Exp(x, y, Pow256)
|
|
||||||
|
|
||||||
bm.stack.Push(base)
|
|
||||||
case oNEG:
|
|
||||||
base.Sub(Pow256, bm.stack.Pop())
|
|
||||||
bm.stack.Push(base)
|
|
||||||
case oLT:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// x < y
|
|
||||||
if x.Cmp(y) < 0 {
|
|
||||||
bm.stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
case oLE:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// x <= y
|
|
||||||
if x.Cmp(y) < 1 {
|
|
||||||
bm.stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
case oGT:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// x > y
|
|
||||||
if x.Cmp(y) > 0 {
|
|
||||||
bm.stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
case oGE:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// x >= y
|
|
||||||
if x.Cmp(y) > -1 {
|
|
||||||
bm.stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
case oNOT:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// x != y
|
|
||||||
if x.Cmp(y) != 0 {
|
|
||||||
bm.stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
case oMYADDRESS:
|
|
||||||
bm.stack.Push(ethutil.BigD(tx.Hash()))
|
|
||||||
case oTXSENDER:
|
|
||||||
bm.stack.Push(ethutil.BigD(tx.Sender()))
|
|
||||||
case oTXVALUE:
|
|
||||||
bm.stack.Push(tx.Value)
|
|
||||||
case oTXDATAN:
|
|
||||||
bm.stack.Push(big.NewInt(int64(len(tx.Data))))
|
|
||||||
case oTXDATA:
|
|
||||||
v := bm.stack.Pop()
|
|
||||||
// v >= len(data)
|
|
||||||
if v.Cmp(big.NewInt(int64(len(tx.Data)))) >= 0 {
|
|
||||||
bm.stack.Push(ethutil.Big("0"))
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(ethutil.Big(tx.Data[v.Uint64()]))
|
|
||||||
}
|
|
||||||
case oBLK_PREVHASH:
|
|
||||||
bm.stack.Push(ethutil.BigD(block.PrevHash))
|
|
||||||
case oBLK_COINBASE:
|
|
||||||
bm.stack.Push(ethutil.BigD(block.Coinbase))
|
|
||||||
case oBLK_TIMESTAMP:
|
|
||||||
bm.stack.Push(big.NewInt(block.Time))
|
|
||||||
case oBLK_NUMBER:
|
|
||||||
bm.stack.Push(big.NewInt(int64(blockInfo.Number)))
|
|
||||||
case oBLK_DIFFICULTY:
|
|
||||||
bm.stack.Push(block.Difficulty)
|
|
||||||
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)
|
|
||||||
case oSHA256, oSHA3, oRIPEMD160:
|
|
||||||
// This is probably save
|
|
||||||
// ceil(pop / 32)
|
|
||||||
length := int(math.Ceil(float64(bm.stack.Pop().Uint64()) / 32.0))
|
|
||||||
// New buffer which will contain the concatenated popped items
|
|
||||||
data := new(bytes.Buffer)
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
// Encode the number to bytes and have it 32bytes long
|
|
||||||
num := ethutil.NumberToBytes(bm.stack.Pop().Bytes(), 256)
|
|
||||||
data.WriteString(string(num))
|
|
||||||
}
|
|
||||||
|
|
||||||
if op == oSHA256 {
|
|
||||||
bm.stack.Push(base.SetBytes(ethutil.Sha256Bin(data.Bytes())))
|
|
||||||
} else if op == oSHA3 {
|
|
||||||
bm.stack.Push(base.SetBytes(ethutil.Sha3Bin(data.Bytes())))
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(base.SetBytes(ethutil.Ripemd160(data.Bytes())))
|
|
||||||
}
|
|
||||||
case oECMUL:
|
|
||||||
y := bm.stack.Pop()
|
|
||||||
x := bm.stack.Pop()
|
|
||||||
//n := bm.stack.Pop()
|
|
||||||
|
|
||||||
//if ethutil.Big(x).Cmp(ethutil.Big(y)) {
|
|
||||||
data := new(bytes.Buffer)
|
|
||||||
data.WriteString(x.String())
|
|
||||||
data.WriteString(y.String())
|
|
||||||
if secp256k1.VerifyPubkeyValidity(data.Bytes()) == 1 {
|
|
||||||
// TODO
|
|
||||||
} else {
|
|
||||||
// Invalid, push infinity
|
|
||||||
bm.stack.Push(ethutil.Big("0"))
|
|
||||||
bm.stack.Push(ethutil.Big("0"))
|
|
||||||
}
|
|
||||||
//} else {
|
|
||||||
// // Invalid, push infinity
|
|
||||||
// bm.stack.Push("0")
|
|
||||||
// bm.stack.Push("0")
|
|
||||||
//}
|
|
||||||
|
|
||||||
case oECADD:
|
|
||||||
case oECSIGN:
|
|
||||||
case oECRECOVER:
|
|
||||||
case oECVALID:
|
|
||||||
case oPUSH:
|
|
||||||
pc++
|
|
||||||
bm.stack.Push(getMem(pc).BigInt())
|
|
||||||
case oPOP:
|
|
||||||
// Pop current value of the stack
|
|
||||||
bm.stack.Pop()
|
|
||||||
case oDUP:
|
|
||||||
// Dup top stack
|
|
||||||
x := bm.stack.Pop()
|
|
||||||
bm.stack.Push(x)
|
|
||||||
bm.stack.Push(x)
|
|
||||||
case oSWAP:
|
|
||||||
// Swap two top most values
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
bm.stack.Push(y)
|
|
||||||
bm.stack.Push(x)
|
|
||||||
case oMLOAD:
|
|
||||||
x := bm.stack.Pop()
|
|
||||||
bm.stack.Push(bm.mem[x.String()])
|
|
||||||
case oMSTORE:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
bm.mem[x.String()] = y
|
|
||||||
case oSLOAD:
|
|
||||||
// Load the value in storage and push it on the stack
|
|
||||||
x := bm.stack.Pop()
|
|
||||||
// decode the object as a big integer
|
|
||||||
decoder := ethutil.NewValueFromBytes([]byte(contract.State().Get(x.String())))
|
|
||||||
if !decoder.IsNil() {
|
|
||||||
bm.stack.Push(decoder.BigInt())
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
case oSSTORE:
|
|
||||||
// Store Y at index X
|
|
||||||
y, x := bm.stack.Popn()
|
|
||||||
addr := ethutil.BigToBytes(x, 256)
|
|
||||||
fmt.Printf(" => %x (%v) @ %v", y.Bytes(), y, ethutil.BigD(addr))
|
|
||||||
contract.SetAddr(addr, y)
|
|
||||||
//contract.State().Update(string(idx), string(y))
|
|
||||||
case oJMP:
|
|
||||||
x := int(bm.stack.Pop().Uint64())
|
|
||||||
// Set pc to x - 1 (minus one so the incrementing at the end won't effect it)
|
|
||||||
pc = x
|
|
||||||
pc--
|
|
||||||
case oJMPI:
|
|
||||||
x := bm.stack.Pop()
|
|
||||||
// Set pc to x if it's non zero
|
|
||||||
if x.Cmp(ethutil.BigFalse) != 0 {
|
|
||||||
pc = int(x.Uint64())
|
|
||||||
pc--
|
|
||||||
}
|
|
||||||
case oIND:
|
|
||||||
bm.stack.Push(big.NewInt(int64(pc)))
|
|
||||||
case oEXTRO:
|
|
||||||
memAddr := bm.stack.Pop()
|
|
||||||
contractAddr := bm.stack.Pop().Bytes()
|
|
||||||
|
|
||||||
// Push the contract's memory on to the stack
|
|
||||||
bm.stack.Push(getContractMemory(block, contractAddr, memAddr))
|
|
||||||
case oBALANCE:
|
|
||||||
// Pushes the balance of the popped value on to the stack
|
|
||||||
d := block.State().Get(bm.stack.Pop().String())
|
|
||||||
ether := NewAddressFromData([]byte(d))
|
|
||||||
bm.stack.Push(ether.Amount)
|
|
||||||
case oMKTX:
|
|
||||||
value, addr := bm.stack.Popn()
|
|
||||||
from, length := bm.stack.Popn()
|
|
||||||
|
|
||||||
j := 0
|
|
||||||
dataItems := make([]string, int(length.Uint64()))
|
|
||||||
for i := from.Uint64(); i < length.Uint64(); i++ {
|
|
||||||
dataItems[j] = string(bm.mem[strconv.Itoa(int(i))].Bytes())
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
// TODO sign it?
|
|
||||||
tx := NewTransaction(addr.Bytes(), value, dataItems)
|
|
||||||
// Add the transaction to the tx pool
|
|
||||||
bm.TransactionPool.QueueTransaction(tx)
|
|
||||||
case oSUICIDE:
|
|
||||||
//addr := bm.stack.Pop()
|
|
||||||
default:
|
|
||||||
fmt.Println("Invalid OPCODE", op)
|
|
||||||
}
|
|
||||||
fmt.Println("")
|
|
||||||
bm.stack.Print()
|
|
||||||
pc++
|
|
||||||
}
|
|
||||||
|
|
||||||
block.UpdateContract(addr, contract)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns an address from the specified contract's address
|
|
||||||
func getContractMemory(block *Block, contractAddr []byte, memAddr *big.Int) *big.Int {
|
|
||||||
contract := block.GetContract(contractAddr)
|
|
||||||
if contract == nil {
|
|
||||||
log.Panicf("invalid contract addr %x", contractAddr)
|
|
||||||
}
|
|
||||||
val := contract.State().Get(memAddr.String())
|
|
||||||
|
|
||||||
// decode the object as a big integer
|
|
||||||
decoder := ethutil.NewValueFromBytes([]byte(val))
|
|
||||||
if decoder.IsNil() {
|
|
||||||
return ethutil.BigFalse
|
|
||||||
}
|
|
||||||
|
|
||||||
return decoder.BigInt()
|
|
||||||
}
|
|
||||||
|
@ -22,8 +22,12 @@ func TestVm(t *testing.T) {
|
|||||||
"1",
|
"1",
|
||||||
"PUSH",
|
"PUSH",
|
||||||
"2",
|
"2",
|
||||||
"STOP",
|
|
||||||
})
|
})
|
||||||
ctrct := NewTransaction(ContractAddr, big.NewInt(200000000), script)
|
tx := NewTransaction(ContractAddr, big.NewInt(200000000), script)
|
||||||
bm.ApplyTransactions(block, []*Transaction{ctrct})
|
addr := tx.Hash()[12:]
|
||||||
|
bm.ApplyTransactions(block, []*Transaction{tx})
|
||||||
|
|
||||||
|
tx2 := NewTransaction(addr, big.NewInt(1e17), nil)
|
||||||
|
tx2.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
|
||||||
|
bm.ApplyTransactions(block, []*Transaction{tx2})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user