Refactoring state transitioning

This commit is contained in:
obscuren 2014-06-13 12:45:11 +02:00
parent b855e5f7df
commit d078e9b8c9
10 changed files with 517 additions and 199 deletions

View File

@ -25,16 +25,10 @@ func Disassemble(script []byte) (asm []string) {
pc.Add(pc, ethutil.Big1) pc.Add(pc, ethutil.Big1)
a := int64(op) - int64(PUSH1) + 1 a := int64(op) - int64(PUSH1) + 1
data := script[pc.Int64() : pc.Int64()+a] data := script[pc.Int64() : pc.Int64()+a]
val := ethutil.BigD(data) if len(data) == 0 {
data = []byte{0}
var b []byte
if val.Int64() == 0 {
b = []byte{0}
} else {
b = val.Bytes()
} }
asm = append(asm, fmt.Sprintf("0x%x", data))
asm = append(asm, fmt.Sprintf("0x%x", b))
pc.Add(pc, big.NewInt(a-1)) pc.Add(pc, big.NewInt(a-1))
} }

225
ethchain/deprecated.go Normal file
View File

@ -0,0 +1,225 @@
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
}

View File

@ -79,3 +79,20 @@ func IsNonceErr(err error) bool {
return ok 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
}

View File

@ -111,6 +111,12 @@ func (m *Memory) Set(offset, size int64, value []byte) {
copy(m.store[offset:offset+size], value) 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 { func (m *Memory) Get(offset, size int64) []byte {
return m.store[offset : offset+size] return m.store[offset : offset+size]
} }

View File

@ -108,8 +108,86 @@ func (sm *StateManager) MakeStateObject(state *State, tx *Transaction) *StateObj
return nil return nil
} }
func (self *StateManager) ProcessTransaction(tx *Transaction, coinbase *StateObject, state *State, toContract bool) (gas *big.Int, err error) { type StateTransition struct {
fmt.Printf("state root before update %x\n", state.Root()) coinbase []byte
tx *Transaction
gas *big.Int
state *State
block *Block
cb, rec, sen *StateObject
}
func NewStateTransition(coinbase []byte, gas *big.Int, tx *Transaction, state *State, block *Block) *StateTransition {
return &StateTransition{coinbase, tx, new(big.Int), state, block, nil, nil, nil}
}
func (self *StateTransition) Coinbase() *StateObject {
if self.cb != nil {
return self.cb
}
self.cb = self.state.GetAccount(self.coinbase)
return self.cb
}
func (self *StateTransition) Sender() *StateObject {
if self.sen != nil {
return self.sen
}
self.sen = self.state.GetAccount(self.tx.Sender())
return self.sen
}
func (self *StateTransition) Receiver() *StateObject {
if self.tx.CreatesContract() {
return nil
}
if self.rec != nil {
return self.rec
}
self.rec = self.state.GetAccount(self.tx.Recipient)
return self.rec
}
func (self *StateTransition) UseGas(amount *big.Int) error {
if self.gas.Cmp(amount) < 0 {
return OutOfGasError()
}
self.gas.Sub(self.gas, amount)
return nil
}
func (self *StateTransition) AddGas(amount *big.Int) {
self.gas.Add(self.gas, amount)
}
func (self *StateTransition) BuyGas() error {
var err error
sender := self.Sender()
if sender.Amount.Cmp(self.tx.GasValue()) < 0 {
return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), self.tx.Value)
}
coinbase := self.Coinbase()
err = coinbase.BuyGas(self.tx.Gas, self.tx.GasPrice)
if err != nil {
return err
}
self.state.UpdateStateObject(coinbase)
self.AddGas(self.tx.Gas)
sender.SubAmount(self.tx.GasValue())
return nil
}
func (self *StateManager) TransitionState(st *StateTransition) (err error) {
//snapshot := st.state.Snapshot()
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ethutil.Config.Log.Infoln(r) ethutil.Config.Log.Infoln(r)
@ -117,173 +195,144 @@ func (self *StateManager) ProcessTransaction(tx *Transaction, coinbase *StateObj
} }
}() }()
gas = new(big.Int) var (
addGas := func(g *big.Int) { gas.Add(gas, g) } tx = st.tx
addGas(GasTx) sender = st.Sender()
receiver *StateObject
// Get the sender )
sender := state.GetAccount(tx.Sender())
if sender.Nonce != tx.Nonce { if sender.Nonce != tx.Nonce {
err = NonceError(tx.Nonce, sender.Nonce) return NonceError(tx.Nonce, sender.Nonce)
return
} }
sender.Nonce += 1 sender.Nonce += 1
defer func() { defer func() {
//state.UpdateStateObject(sender)
// Notify all subscribers // Notify all subscribers
self.Ethereum.Reactor().Post("newTx:post", tx) self.Ethereum.Reactor().Post("newTx:post", tx)
}() }()
txTotalBytes := big.NewInt(int64(len(tx.Data))) if err = st.BuyGas(); err != nil {
txTotalBytes.Div(txTotalBytes, ethutil.Big32) return err
addGas(new(big.Int).Mul(txTotalBytes, GasSStore))
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) receiver = st.Receiver()
state.UpdateStateObject(coinbase)
// Get the receiver if err = st.UseGas(GasTx); err != nil {
receiver := state.GetAccount(tx.Recipient) return err
// 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)
// Add the amount to receivers account which should conclude this transaction
receiver.AddAmount(tx.Value)
state.UpdateStateObject(receiver)
} }
state.UpdateStateObject(sender) dataPrice := big.NewInt(int64(len(tx.Data)))
dataPrice.Mul(dataPrice, GasData)
if err = st.UseGas(dataPrice); err != nil {
return err
}
ethutil.Config.Log.Infof("[TXPL] Processed Tx %x\n", tx.Hash()) if receiver == nil { // Contract
receiver = self.MakeStateObject(st.state, tx)
if receiver == nil {
return fmt.Errorf("ERR. Unable to create contract with transaction %v", tx)
}
}
return if err = self.transferValue(st, sender, receiver); err != nil {
return err
}
if tx.CreatesContract() {
fmt.Println(Disassemble(receiver.Init()))
// Evaluate the initialization script
// and use the return value as the
// script section for the state object.
//script, gas, err = sm.Eval(state, contract.Init(), contract, tx, block)
code, err := self.Eval(st, receiver.Init(), receiver)
if err != nil {
return fmt.Errorf("Error during init script run %v", err)
}
receiver.script = code
}
st.state.UpdateStateObject(sender)
st.state.UpdateStateObject(receiver)
return nil
} }
// Apply transactions uses the transaction passed to it and applies them onto func (self *StateManager) transferValue(st *StateTransition, sender, receiver *StateObject) error {
// the current processing state. if sender.Amount.Cmp(st.tx.Value) < 0 {
func (sm *StateManager) ApplyTransactions(coinbase []byte, state *State, block *Block, txs []*Transaction) ([]*Receipt, []*Transaction) { return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", st.tx.Value, sender.Amount)
// Process each transaction/contract }
var receipts []*Receipt
var validTxs []*Transaction
var ignoredTxs []*Transaction // Transactions which go over the gasLimit
totalUsedGas := big.NewInt(0) // Subtract the amount from the senders account
sender.SubAmount(st.tx.Value)
// Add the amount to receivers account which should conclude this transaction
receiver.AddAmount(st.tx.Value)
for _, tx := range txs { ethutil.Config.Log.Debugf("%x => %x (%v) %x\n", sender.Address()[:4], receiver.Address()[:4], st.tx.Value, st.tx.Hash())
usedGas, err := sm.ApplyTransaction(coinbase, state, block, tx)
return nil
}
func (self *StateManager) ProcessTransactions(coinbase []byte, state *State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) {
var (
receipts Receipts
handled, unhandled Transactions
totalUsedGas = big.NewInt(0)
err error
)
done:
for i, tx := range txs {
txGas := new(big.Int).Set(tx.Gas)
st := NewStateTransition(coinbase, tx.Gas, tx, state, block)
err = self.TransitionState(st)
if err != nil { if err != nil {
if IsNonceErr(err) { switch {
case IsNonceErr(err):
err = nil // ignore error
continue continue
} case IsGasLimitErr(err):
if IsGasLimitErr(err) { unhandled = txs[i:]
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
}
break done
default:
ethutil.Config.Log.Infoln(err) ethutil.Config.Log.Infoln(err)
} }
}
accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, usedGas)) txGas.Sub(txGas, st.gas)
accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, txGas))
receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative} receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative}
receipts = append(receipts, receipt) receipts = append(receipts, receipt)
validTxs = append(validTxs, tx) handled = append(handled, tx)
} }
fmt.Println("################# MADE\n", receipts, "\n############################") fmt.Println("################# MADE\n", receipts, "\n############################")
// Update the total gas used for the block (to be mined) parent.GasUsed = totalUsedGas
block.GasUsed = totalUsedGas
return receipts, validTxs return receipts, handled, unhandled, err
} }
func (sm *StateManager) ApplyTransaction(coinbase []byte, state *State, block *Block, tx *Transaction) (totalGasUsed *big.Int, err error) { func (self *StateManager) Eval(st *StateTransition, script []byte, context *StateObject) (ret []byte, 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 ( var (
addTotalGas = func(gas *big.Int) { totalGasUsed.Add(totalGasUsed, gas) } tx = st.tx
gas = new(big.Int) block = st.block
script []byte initiator = st.Sender()
) )
totalGasUsed = big.NewInt(0)
snapshot := state.Snapshot()
ca := state.GetAccount(coinbase) closure := NewClosure(initiator, context, script, st.state, st.gas, tx.GasPrice)
// Apply the transaction to the current state vm := NewVm(st.state, self, RuntimeVars{
gas, err = sm.ProcessTransaction(tx, ca, state, false) Origin: initiator.Address(),
addTotalGas(gas) BlockNumber: block.BlockInfo().Number,
PrevHash: block.PrevHash,
if tx.CreatesContract() { Coinbase: block.Coinbase,
if err == nil { Time: block.Time,
// Create a new state object and the transaction Diff: block.Difficulty,
// as it's data provider. Value: tx.Value,
contract := sm.MakeStateObject(state, tx) })
if contract != nil { ret, _, err = closure.Call(vm, tx.Data, 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)
addTotalGas(gas)
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 return
} }
@ -325,7 +374,8 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea
fmt.Println(block.Receipts()) fmt.Println(block.Receipts())
// Process the transactions on to current block // Process the transactions on to current block
sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions()) //sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions())
sm.ProcessTransactions(block.Coinbase, state, block, parent, block.Transactions())
// Block validation // Block validation
if err := sm.ValidateBlock(block); err != nil { if err := sm.ValidateBlock(block); err != nil {
@ -464,35 +514,6 @@ func (sm *StateManager) Stop() {
sm.bc.Stop() sm.bc.Stop()
} }
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 (sm *StateManager) notifyChanges(state *State) { func (sm *StateManager) notifyChanges(state *State) {
for addr, stateObject := range state.manifest.objectChanges { for addr, stateObject := range state.manifest.objectChanges {
sm.Ethereum.Reactor().Post("object:"+addr, stateObject) sm.Ethereum.Reactor().Post("object:"+addr, stateObject)

View File

@ -135,7 +135,7 @@ func (c *StateObject) ConvertGas(gas, price *big.Int) error {
func (self *StateObject) BuyGas(gas, price *big.Int) error { func (self *StateObject) BuyGas(gas, price *big.Int) error {
rGas := new(big.Int).Set(gas) rGas := new(big.Int).Set(gas)
rGas.Mul(gas, price) rGas.Mul(rGas, price)
self.AddAmount(rGas) self.AddAmount(rGas)

View File

@ -46,15 +46,18 @@ func NewTransactionFromValue(val *ethutil.Value) *Transaction {
return tx 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 { func (tx *Transaction) Hash() []byte {
data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data} 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()) return ethutil.Sha3Bin(ethutil.NewValue(data).Encode())
} }
@ -185,6 +188,7 @@ type Receipt struct {
PostState []byte PostState []byte
CumulativeGasUsed *big.Int CumulativeGasUsed *big.Int
} }
type Receipts []*Receipt
func NewRecieptFromValue(val *ethutil.Value) *Receipt { func NewRecieptFromValue(val *ethutil.Value) *Receipt {
r := &Receipt{} r := &Receipt{}

View File

@ -220,7 +220,7 @@ out:
// Call blocking version. // Call blocking version.
pool.addTransaction(tx) 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 // Notify the subscribers
pool.Ethereum.Reactor().Post("newTx:pre", tx) pool.Ethereum.Reactor().Post("newTx:pre", tx)

View File

@ -21,8 +21,10 @@ const (
NEG = 0x09 NEG = 0x09
LT = 0x0a LT = 0x0a
GT = 0x0b GT = 0x0b
EQ = 0x0c SLT = 0x0c
NOT = 0x0d SGT = 0x0d
EQ = 0x0e
NOT = 0x0f
// 0x10 range - bit ops // 0x10 range - bit ops
AND = 0x10 AND = 0x10
@ -128,6 +130,8 @@ var opCodeToString = map[OpCode]string{
NEG: "NEG", NEG: "NEG",
LT: "LT", LT: "LT",
GT: "GT", GT: "GT",
SLT: "SLT",
SGT: "SGT",
EQ: "EQ", EQ: "EQ",
NOT: "NOT", NOT: "NOT",

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
_ "github.com/obscuren/secp256k1-go" _ "github.com/obscuren/secp256k1-go"
"math"
_ "math" _ "math"
"math/big" "math/big"
) )
@ -18,6 +19,7 @@ var (
GasCreate = big.NewInt(100) GasCreate = big.NewInt(100)
GasCall = big.NewInt(20) GasCall = big.NewInt(20)
GasMemory = big.NewInt(1) GasMemory = big.NewInt(1)
GasData = big.NewInt(5)
GasTx = big.NewInt(500) GasTx = big.NewInt(500)
) )
@ -116,9 +118,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
gas.Add(gas, amount) gas.Add(gas, amount)
} }
var newMemSize uint64 = 0
switch op { switch op {
case SHA3:
setStepGasUsage(GasSha)
case SLOAD: case SLOAD:
setStepGasUsage(GasSLoad) setStepGasUsage(GasSLoad)
case SSTORE: 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)) setStepGasUsage(new(big.Int).Mul(mult, GasSStore))
case BALANCE: case BALANCE:
setStepGasUsage(GasBalance) 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) require(3)
args := stack.Get(big.NewInt(3)) newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64()
initSize := new(big.Int).Add(args[1], args[0]) case CODECOPY:
require(3)
setStepGasUsage(CalculateTxGas(initSize)) newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64()
case CALL: case CALL:
require(7)
setStepGasUsage(GasCall) 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: default:
setStepGasUsage(GasStep) 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) { if !closure.UseGas(gas) {
ethutil.Config.Log.Debugln("Insufficient gas", closure.Gas, gas) ethutil.Config.Log.Debugln("Insufficient gas", closure.Gas, gas)
return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas) return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas)
} }
mem.Resize(newMemSize)
switch op { switch op {
case LOG: case LOG:
stack.Print() stack.Print()
@ -340,6 +375,23 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case CALLDATACOPY: case CALLDATACOPY:
case CODESIZE: case CODESIZE:
case CODECOPY: 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: case GASPRICE:
stack.Push(closure.Price) 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 // Transfer all remaining gas to the new
// contract so it may run the init script // contract so it may run the init script
gas := new(big.Int).Set(closure.Gas) gas := new(big.Int).Set(closure.Gas)
closure.UseGas(gas) //closure.UseGas(gas)
// Create the closure // Create the closure
c := NewClosure(closure.callee, c := NewClosure(closure.callee,
@ -498,12 +550,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
if contract != nil { if contract != nil {
// Prepay for the gas // Prepay for the gas
// If gas is set to 0 use all remaining gas for the next call //closure.UseGas(gas)
if gas.Cmp(big.NewInt(0)) == 0 {
// Copy
gas = new(big.Int).Set(closure.Gas)
}
closure.UseGas(gas)
// Add the value to the state object // Add the value to the state object
contract.AddAmount(value) contract.AddAmount(value)