Commit d078e9b8 authored by obscuren's avatar obscuren

Refactoring state transitioning

parent b855e5f7
......@@ -25,16 +25,10 @@ func Disassemble(script []byte) (asm []string) {
pc.Add(pc, ethutil.Big1)
a := int64(op) - int64(PUSH1) + 1
data := script[pc.Int64() : pc.Int64()+a]
val := ethutil.BigD(data)
var b []byte
if val.Int64() == 0 {
b = []byte{0}
} else {
b = val.Bytes()
if len(data) == 0 {
data = []byte{0}
}
asm = append(asm, fmt.Sprintf("0x%x", b))
asm = append(asm, fmt.Sprintf("0x%x", data))
pc.Add(pc, big.NewInt(a-1))
}
......
package ethchain
import (
"bytes"
"fmt"
"github.com/ethereum/eth-go/ethutil"
"math/big"
)
func (sm *StateManager) EvalScript(state *State, script []byte, object *StateObject, tx *Transaction, block *Block) (ret []byte, gas *big.Int, err error) {
account := state.GetAccount(tx.Sender())
err = account.ConvertGas(tx.Gas, tx.GasPrice)
if err != nil {
ethutil.Config.Log.Debugln(err)
return
}
closure := NewClosure(account, object, script, state, tx.Gas, tx.GasPrice)
vm := NewVm(state, sm, RuntimeVars{
Origin: account.Address(),
BlockNumber: block.BlockInfo().Number,
PrevHash: block.PrevHash,
Coinbase: block.Coinbase,
Time: block.Time,
Diff: block.Difficulty,
Value: tx.Value,
//Price: tx.GasPrice,
})
ret, gas, err = closure.Call(vm, tx.Data, nil)
// Update the account (refunds)
state.UpdateStateObject(account)
state.UpdateStateObject(object)
return
}
func (self *StateManager) ProcessTransaction(tx *Transaction, coinbase *StateObject, state *State, toContract bool) (gas *big.Int, err error) {
fmt.Printf("state root before update %x\n", state.Root())
defer func() {
if r := recover(); r != nil {
ethutil.Config.Log.Infoln(r)
err = fmt.Errorf("%v", r)
}
}()
gas = new(big.Int)
addGas := func(g *big.Int) { gas.Add(gas, g) }
addGas(GasTx)
// Get the sender
sender := state.GetAccount(tx.Sender())
if sender.Nonce != tx.Nonce {
err = NonceError(tx.Nonce, sender.Nonce)
return
}
sender.Nonce += 1
defer func() {
//state.UpdateStateObject(sender)
// Notify all subscribers
self.Ethereum.Reactor().Post("newTx:post", tx)
}()
txTotalBytes := big.NewInt(int64(len(tx.Data)))
//fmt.Println("txTotalBytes", txTotalBytes)
//txTotalBytes.Div(txTotalBytes, ethutil.Big32)
addGas(new(big.Int).Mul(txTotalBytes, GasData))
rGas := new(big.Int).Set(gas)
rGas.Mul(gas, tx.GasPrice)
// Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it.
totAmount := new(big.Int).Add(tx.Value, rGas)
if sender.Amount.Cmp(totAmount) < 0 {
state.UpdateStateObject(sender)
err = fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
return
}
coinbase.BuyGas(gas, tx.GasPrice)
state.UpdateStateObject(coinbase)
fmt.Printf("1. root %x\n", state.Root())
// Get the receiver
receiver := state.GetAccount(tx.Recipient)
// Send Tx to self
if bytes.Compare(tx.Recipient, tx.Sender()) == 0 {
// Subtract the fee
sender.SubAmount(rGas)
} else {
// Subtract the amount from the senders account
sender.SubAmount(totAmount)
state.UpdateStateObject(sender)
fmt.Printf("3. root %x\n", state.Root())
// Add the amount to receivers account which should conclude this transaction
receiver.AddAmount(tx.Value)
state.UpdateStateObject(receiver)
fmt.Printf("2. root %x\n", state.Root())
}
ethutil.Config.Log.Infof("[TXPL] Processed Tx %x\n", tx.Hash())
return
}
func (sm *StateManager) ApplyTransaction(coinbase []byte, state *State, block *Block, tx *Transaction) (totalGasUsed *big.Int, err error) {
/*
Applies transactions to the given state and creates new
state objects where needed.
If said objects needs to be created
run the initialization script provided by the transaction and
assume there's a return value. The return value will be set to
the script section of the state object.
*/
var (
addTotalGas = func(gas *big.Int) { totalGasUsed.Add(totalGasUsed, gas) }
gas = new(big.Int)
script []byte
)
totalGasUsed = big.NewInt(0)
snapshot := state.Snapshot()
ca := state.GetAccount(coinbase)
// Apply the transaction to the current state
gas, err = sm.ProcessTransaction(tx, ca, state, false)
addTotalGas(gas)
fmt.Println("gas used by tx", gas)
if tx.CreatesContract() {
if err == nil {
// Create a new state object and the transaction
// as it's data provider.
contract := sm.MakeStateObject(state, tx)
if contract != nil {
fmt.Println(Disassemble(contract.Init()))
// Evaluate the initialization script
// and use the return value as the
// script section for the state object.
script, gas, err = sm.EvalScript(state, contract.Init(), contract, tx, block)
fmt.Println("gas used by eval", gas)
addTotalGas(gas)
fmt.Println("total =", totalGasUsed)
fmt.Println("script len =", len(script))
if err != nil {
err = fmt.Errorf("[STATE] Error during init script run %v", err)
return
}
contract.script = script
state.UpdateStateObject(contract)
} else {
err = fmt.Errorf("[STATE] Unable to create contract")
}
} else {
err = fmt.Errorf("[STATE] contract creation tx: %v for sender %x", err, tx.Sender())
}
} else {
// Find the state object at the "recipient" address. If
// there's an object attempt to run the script.
stateObject := state.GetStateObject(tx.Recipient)
if err == nil && stateObject != nil && len(stateObject.Script()) > 0 {
_, gas, err = sm.EvalScript(state, stateObject.Script(), stateObject, tx, block)
addTotalGas(gas)
}
}
parent := sm.bc.GetBlock(block.PrevHash)
total := new(big.Int).Add(block.GasUsed, totalGasUsed)
limit := block.CalcGasLimit(parent)
if total.Cmp(limit) > 0 {
state.Revert(snapshot)
err = GasLimitError(total, limit)
}
return
}
// Apply transactions uses the transaction passed to it and applies them onto
// the current processing state.
func (sm *StateManager) ApplyTransactions(coinbase []byte, state *State, block *Block, txs []*Transaction) ([]*Receipt, []*Transaction) {
// Process each transaction/contract
var receipts []*Receipt
var validTxs []*Transaction
var ignoredTxs []*Transaction // Transactions which go over the gasLimit
totalUsedGas := big.NewInt(0)
for _, tx := range txs {
usedGas, err := sm.ApplyTransaction(coinbase, state, block, tx)
if err != nil {
if IsNonceErr(err) {
continue
}
if IsGasLimitErr(err) {
ignoredTxs = append(ignoredTxs, tx)
// We need to figure out if we want to do something with thse txes
ethutil.Config.Log.Debugln("Gastlimit:", err)
continue
}
ethutil.Config.Log.Infoln(err)
}
accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, usedGas))
receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative}
receipts = append(receipts, receipt)
validTxs = append(validTxs, tx)
}
fmt.Println("################# MADE\n", receipts, "\n############################")
// Update the total gas used for the block (to be mined)
block.GasUsed = totalUsedGas
return receipts, validTxs
}
......@@ -79,3 +79,20 @@ func IsNonceErr(err error) bool {
return ok
}
type OutOfGasErr struct {
Message string
}
func OutOfGasError() *OutOfGasErr {
return &OutOfGasErr{Message: "Out of gas"}
}
func (self *OutOfGasErr) Error() string {
return self.Message
}
func IsOutOfGasErr(err error) bool {
_, ok := err.(*OutOfGasErr)
return ok
}
......@@ -111,6 +111,12 @@ func (m *Memory) Set(offset, size int64, value []byte) {
copy(m.store[offset:offset+size], value)
}
func (m *Memory) Resize(size uint64) {
if uint64(m.Len()) < size {
m.store = append(m.store, make([]byte, size-uint64(m.Len()))...)
}
}
func (m *Memory) Get(offset, size int64) []byte {
return m.store[offset : offset+size]
}
......
This diff is collapsed.
......@@ -135,7 +135,7 @@ func (c *StateObject) ConvertGas(gas, price *big.Int) error {
func (self *StateObject) BuyGas(gas, price *big.Int) error {
rGas := new(big.Int).Set(gas)
rGas.Mul(gas, price)
rGas.Mul(rGas, price)
self.AddAmount(rGas)
......
......@@ -46,15 +46,18 @@ func NewTransactionFromValue(val *ethutil.Value) *Transaction {
return tx
}
func (self *Transaction) GasValue() *big.Int {
return new(big.Int).Mul(self.Gas, self.GasPrice)
}
func (self *Transaction) TotalValue() *big.Int {
v := self.GasValue()
return v.Add(v, self.Value)
}
func (tx *Transaction) Hash() []byte {
data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data}
/*
if tx.contractCreation {
data = append(data, tx.Init)
}
*/
return ethutil.Sha3Bin(ethutil.NewValue(data).Encode())
}
......@@ -185,6 +188,7 @@ type Receipt struct {
PostState []byte
CumulativeGasUsed *big.Int
}
type Receipts []*Receipt
func NewRecieptFromValue(val *ethutil.Value) *Receipt {
r := &Receipt{}
......
......@@ -220,7 +220,7 @@ out:
// Call blocking version.
pool.addTransaction(tx)
ethutil.Config.Log.Debugf("%x => %x (%v) %x\n", tx.Sender()[:4], tx.Recipient[:4], tx.Value, tx.Hash())
ethutil.Config.Log.Debugf("(t) %x => %x (%v) %x\n", tx.Sender()[:4], tx.Recipient[:4], tx.Value, tx.Hash())
// Notify the subscribers
pool.Ethereum.Reactor().Post("newTx:pre", tx)
......
......@@ -21,8 +21,10 @@ const (
NEG = 0x09
LT = 0x0a
GT = 0x0b
EQ = 0x0c
NOT = 0x0d
SLT = 0x0c
SGT = 0x0d
EQ = 0x0e
NOT = 0x0f
// 0x10 range - bit ops
AND = 0x10
......@@ -128,6 +130,8 @@ var opCodeToString = map[OpCode]string{
NEG: "NEG",
LT: "LT",
GT: "GT",
SLT: "SLT",
SGT: "SGT",
EQ: "EQ",
NOT: "NOT",
......
......@@ -5,6 +5,7 @@ import (
"fmt"
"github.com/ethereum/eth-go/ethutil"
_ "github.com/obscuren/secp256k1-go"
"math"
_ "math"
"math/big"
)
......@@ -18,6 +19,7 @@ var (
GasCreate = big.NewInt(100)
GasCall = big.NewInt(20)
GasMemory = big.NewInt(1)
GasData = big.NewInt(5)
GasTx = big.NewInt(500)
)
......@@ -116,9 +118,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
gas.Add(gas, amount)
}
var newMemSize uint64 = 0
switch op {
case SHA3:
setStepGasUsage(GasSha)
case SLOAD:
setStepGasUsage(GasSLoad)
case SSTORE:
......@@ -135,27 +136,61 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
setStepGasUsage(new(big.Int).Mul(mult, GasSStore))
case BALANCE:
setStepGasUsage(GasBalance)
case CREATE:
case MSTORE:
require(2)
newMemSize = stack.Peek().Uint64() + 32
case MSTORE8:
require(2)
newMemSize = stack.Peek().Uint64() + 1
case RETURN:
require(2)
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64()
case SHA3:
require(2)
setStepGasUsage(GasSha)
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64()
case CALLDATACOPY:
require(3)
args := stack.Get(big.NewInt(3))
initSize := new(big.Int).Add(args[1], args[0])
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64()
case CODECOPY:
require(3)
setStepGasUsage(CalculateTxGas(initSize))
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64()
case CALL:
require(7)
setStepGasUsage(GasCall)
case MLOAD, MSIZE, MSTORE8, MSTORE:
setStepGasUsage(GasMemory)
x := stack.data[stack.Len()-6].Uint64() + stack.data[stack.Len()-7].Uint64()
y := stack.data[stack.Len()-4].Uint64() + stack.data[stack.Len()-5].Uint64()
newMemSize = uint64(math.Max(float64(x), float64(y)))
case CREATE:
require(3)
setStepGasUsage(GasCreate)
newMemSize = stack.data[stack.Len()-2].Uint64() + stack.data[stack.Len()-3].Uint64()
default:
setStepGasUsage(GasStep)
}
newMemSize = (newMemSize + 31) / 32 * 32
if newMemSize > uint64(mem.Len()) {
m := GasMemory.Uint64() * (newMemSize - uint64(mem.Len())) / 32
setStepGasUsage(big.NewInt(int64(m)))
}
if !closure.UseGas(gas) {
ethutil.Config.Log.Debugln("Insufficient gas", closure.Gas, gas)
return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas)
}
mem.Resize(newMemSize)
switch op {
case LOG:
stack.Print()
......@@ -340,6 +375,23 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case CALLDATACOPY:
case CODESIZE:
case CODECOPY:
var (
size = int64(len(closure.Script))
mOff = stack.Pop().Int64()
cOff = stack.Pop().Int64()
l = stack.Pop().Int64()
)
if cOff > size {
cOff = 0
l = 0
} else if cOff+l > size {
l = 0
}
code := closure.Script[cOff : cOff+l]
mem.Set(mOff, l, code)
case GASPRICE:
stack.Push(closure.Price)
......@@ -448,7 +500,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
// Transfer all remaining gas to the new
// contract so it may run the init script
gas := new(big.Int).Set(closure.Gas)
closure.UseGas(gas)
//closure.UseGas(gas)
// Create the closure
c := NewClosure(closure.callee,
......@@ -498,12 +550,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
if contract != nil {
// Prepay for the gas
// If gas is set to 0 use all remaining gas for the next call
if gas.Cmp(big.NewInt(0)) == 0 {
// Copy
gas = new(big.Int).Set(closure.Gas)
}
closure.UseGas(gas)
//closure.UseGas(gas)
// Add the value to the state object
contract.AddAmount(value)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment