Merge branch 'release/0.5.13'

This commit is contained in:
obscuren 2014-06-16 18:25:24 +02:00
commit 5a0e751736
27 changed files with 1277 additions and 358 deletions

View File

@ -6,7 +6,7 @@ Ethereum
Ethereum Go Development package (C) Jeffrey Wilcke Ethereum Go Development package (C) Jeffrey Wilcke
Ethereum is currently in its testing phase. The current state is "Proof Ethereum is currently in its testing phase. The current state is "Proof
of Concept 5.0 RC12". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)). of Concept 5.0 RC13". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)).
Ethereum Go is split up in several sub packages Please refer to each Ethereum Go is split up in several sub packages Please refer to each
individual package for more information. individual package for more information.

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("%#x", data))
asm = append(asm, fmt.Sprintf("0x%x", b))
pc.Add(pc, big.NewInt(a-1)) pc.Add(pc, big.NewInt(a-1))
} }

View File

@ -154,6 +154,36 @@ func (block *Block) PayFee(addr []byte, fee *big.Int) bool {
return true return true
} }
func (block *Block) CalcGasLimit(parent *Block) *big.Int {
if block.Number.Cmp(big.NewInt(0)) == 0 {
return ethutil.BigPow(10, 6)
}
previous := new(big.Int).Mul(big.NewInt(1023), parent.GasLimit)
current := new(big.Rat).Mul(new(big.Rat).SetInt(block.GasUsed), big.NewRat(6, 5))
curInt := new(big.Int).Div(current.Num(), current.Denom())
result := new(big.Int).Add(previous, curInt)
result.Div(result, big.NewInt(1024))
min := ethutil.BigPow(10, 4)
return ethutil.BigMax(min, result)
/*
base := new(big.Int)
base2 := new(big.Int)
parentGL := bc.CurrentBlock.GasLimit
parentUsed := bc.CurrentBlock.GasUsed
base.Mul(parentGL, big.NewInt(1024-1))
base2.Mul(parentUsed, big.NewInt(6))
base2.Div(base2, big.NewInt(5))
base.Add(base, base2)
base.Div(base, big.NewInt(1024))
*/
}
func (block *Block) BlockInfo() BlockInfo { func (block *Block) BlockInfo() BlockInfo {
bi := BlockInfo{} bi := BlockInfo{}
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...)) data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))

View File

@ -55,6 +55,8 @@ func (bc *BlockChain) NewBlock(coinbase []byte) *Block {
nil, nil,
"") "")
block.MinGasPrice = big.NewInt(10000000000000)
if bc.CurrentBlock != nil { if bc.CurrentBlock != nil {
var mul *big.Int var mul *big.Int
if block.Time < lastBlockTime+42 { if block.Time < lastBlockTime+42 {
@ -72,19 +74,7 @@ func (bc *BlockChain) NewBlock(coinbase []byte) *Block {
block.Number = new(big.Int).Add(bc.CurrentBlock.Number, ethutil.Big1) block.Number = new(big.Int).Add(bc.CurrentBlock.Number, ethutil.Big1)
// max(10000, (parent gas limit * (1024 - 1) + (parent gas used * 6 / 5)) / 1024) block.GasLimit = block.CalcGasLimit(bc.CurrentBlock)
base := new(big.Int)
base2 := new(big.Int)
parentGL := bc.CurrentBlock.GasLimit
parentUsed := bc.CurrentBlock.GasUsed
base.Mul(parentGL, big.NewInt(1024-1))
base2.Mul(parentUsed, big.NewInt(6))
base2.Div(base2, big.NewInt(5))
base.Add(base, base2)
base.Div(base, big.NewInt(1024))
block.GasLimit = ethutil.BigMax(big.NewInt(10000), base)
} }
return block return block
@ -271,7 +261,7 @@ func (bc *BlockChain) GetChain(hash []byte, amount int) []*Block {
func AddTestNetFunds(block *Block) { func AddTestNetFunds(block *Block) {
for _, addr := range []string{ for _, addr := range []string{
"8a40bfaa73256b60764c1bf40675a99083efb075", "51ba59315b3a95761d0863b05ccc7a7f54703d99",
"e4157b34ea9615cfbde6b4fda419828124b70c78", "e4157b34ea9615cfbde6b4fda419828124b70c78",
"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", "1e12515ce3e0f817a4ddef9ca55788a1d66bd2df",
"6c386a4b26f73c802f34673f7248bb118f97424a", "6c386a4b26f73c802f34673f7248bb118f97424a",
@ -285,7 +275,6 @@ func AddTestNetFunds(block *Block) {
account.Amount = ethutil.Big("1606938044258990275541962092341162602522202993782792835301376") //ethutil.BigPow(2, 200) account.Amount = ethutil.Big("1606938044258990275541962092341162602522202993782792835301376") //ethutil.BigPow(2, 200)
block.state.UpdateStateObject(account) block.state.UpdateStateObject(account)
} }
log.Printf("%x\n", block.RlpEncode())
} }
func (bc *BlockChain) setLastBlock() { func (bc *BlockChain) setLastBlock() {

View File

@ -17,7 +17,7 @@ type ClosureRef interface {
// Basic inline closure object which implement the 'closure' interface // Basic inline closure object which implement the 'closure' interface
type Closure struct { type Closure struct {
callee ClosureRef caller ClosureRef
object *StateObject object *StateObject
Script []byte Script []byte
State *State State *State
@ -28,12 +28,14 @@ type Closure struct {
} }
// Create a new closure for the given data items // Create a new closure for the given data items
func NewClosure(callee ClosureRef, object *StateObject, script []byte, state *State, gas, price *big.Int) *Closure { func NewClosure(caller ClosureRef, object *StateObject, script []byte, state *State, gas, price *big.Int) *Closure {
c := &Closure{callee: callee, object: object, Script: script, State: state, Args: nil} c := &Closure{caller: caller, object: object, Script: script, State: state, Args: nil}
// In most cases gas, price and value are pointers to transaction objects // Gas should be a pointer so it can safely be reduced through the run
// This pointer will be off the state transition
c.Gas = gas //new(big.Int).Set(gas)
// In most cases price and value are pointers to transaction objects
// and we don't want the transaction's values to change. // and we don't want the transaction's values to change.
c.Gas = new(big.Int).Set(gas)
c.Price = new(big.Int).Set(price) c.Price = new(big.Int).Set(price)
c.UsedGas = new(big.Int) c.UsedGas = new(big.Int)
@ -83,11 +85,11 @@ func (c *Closure) Call(vm *Vm, args []byte, hook DebugHook) ([]byte, *big.Int, e
} }
func (c *Closure) Return(ret []byte) []byte { func (c *Closure) Return(ret []byte) []byte {
// Return the remaining gas to the callee // Return the remaining gas to the caller
// If no callee is present return it to // If no caller is present return it to
// the origin (i.e. contract or tx) // the origin (i.e. contract or tx)
if c.callee != nil { if c.caller != nil {
c.callee.ReturnGas(c.Gas, c.Price, c.State) c.caller.ReturnGas(c.Gas, c.Price, c.State)
} else { } else {
c.object.ReturnGas(c.Gas, c.Price, c.State) c.object.ReturnGas(c.Gas, c.Price, c.State)
} }
@ -107,7 +109,7 @@ func (c *Closure) UseGas(gas *big.Int) bool {
return true return true
} }
// Implement the Callee interface // Implement the caller interface
func (c *Closure) ReturnGas(gas, price *big.Int, state *State) { func (c *Closure) ReturnGas(gas, price *big.Int, state *State) {
// Return the gas to the closure // Return the gas to the closure
c.Gas.Add(c.Gas, gas) c.Gas.Add(c.Gas, gas)
@ -118,8 +120,8 @@ func (c *Closure) Object() *StateObject {
return c.object return c.object
} }
func (c *Closure) Callee() ClosureRef { func (c *Closure) Caller() ClosureRef {
return c.callee return c.caller
} }
func (c *Closure) N() *big.Int { func (c *Closure) N() *big.Int {

236
ethchain/deprecated.go Normal file
View File

@ -0,0 +1,236 @@
package ethchain
import (
"bytes"
"fmt"
"github.com/ethereum/eth-go/ethutil"
"math/big"
)
func (sm *StateManager) MakeStateObject(state *State, tx *Transaction) *StateObject {
contract := MakeContract(tx, state)
if contract != nil {
state.states[string(tx.CreationAddress())] = contract.state
return contract
}
return nil
}
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

@ -2,6 +2,7 @@ package ethchain
import ( import (
"fmt" "fmt"
"math/big"
) )
// Parent error. In case a parent is unknown this error will be thrown // Parent error. In case a parent is unknown this error will be thrown
@ -43,6 +44,23 @@ func IsValidationErr(err error) bool {
return ok return ok
} }
type GasLimitErr struct {
Message string
Is, Max *big.Int
}
func IsGasLimitErr(err error) bool {
_, ok := err.(*GasLimitErr)
return ok
}
func (err *GasLimitErr) Error() string {
return err.Message
}
func GasLimitError(is, max *big.Int) *GasLimitErr {
return &GasLimitErr{Message: fmt.Sprintf("GasLimit error. Max %s, transaction would take it to %s", max, is), Is: is, Max: max}
}
type NonceErr struct { type NonceErr struct {
Message string Message string
Is, Exp uint64 Is, Exp uint64
@ -61,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

@ -4,30 +4,6 @@ import (
"math/big" "math/big"
) )
var TxFeeRat *big.Int = big.NewInt(100000000000000)
var TxFee *big.Int = big.NewInt(100)
var StepFee *big.Int = big.NewInt(1)
var StoreFee *big.Int = big.NewInt(5)
var DataFee *big.Int = big.NewInt(20)
var ExtroFee *big.Int = big.NewInt(40)
var CryptoFee *big.Int = big.NewInt(20)
var ContractFee *big.Int = big.NewInt(100)
var BlockReward *big.Int = big.NewInt(1.5e+18) var BlockReward *big.Int = big.NewInt(1.5e+18)
var UncleReward *big.Int = big.NewInt(1.125e+18) var UncleReward *big.Int = big.NewInt(1.125e+18)
var UncleInclusionReward *big.Int = big.NewInt(1.875e+17) var UncleInclusionReward *big.Int = big.NewInt(1.875e+17)
var Period1Reward *big.Int = new(big.Int)
var Period2Reward *big.Int = new(big.Int)
var Period3Reward *big.Int = new(big.Int)
var Period4Reward *big.Int = new(big.Int)
func InitFees() {
StepFee.Mul(StepFee, TxFeeRat)
StoreFee.Mul(StoreFee, TxFeeRat)
DataFee.Mul(DataFee, TxFeeRat)
ExtroFee.Mul(ExtroFee, TxFeeRat)
CryptoFee.Mul(CryptoFee, TxFeeRat)
ContractFee.Mul(ContractFee, TxFeeRat)
}

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

@ -16,12 +16,14 @@ type State struct {
// Nested states // Nested states
states map[string]*State states map[string]*State
stateObjects map[string]*StateObject
manifest *Manifest manifest *Manifest
} }
// Create a new state from a given trie // Create a new state from a given trie
func NewState(trie *ethutil.Trie) *State { func NewState(trie *ethutil.Trie) *State {
return &State{trie: trie, states: make(map[string]*State), manifest: NewManifest()} return &State{trie: trie, states: make(map[string]*State), stateObjects: make(map[string]*StateObject), manifest: NewManifest()}
} }
// Resets the trie and all siblings // Resets the trie and all siblings
@ -29,16 +31,24 @@ func (s *State) Reset() {
s.trie.Undo() s.trie.Undo()
// Reset all nested states // Reset all nested states
for _, state := range s.states { for _, stateObject := range s.stateObjects {
state.Reset() if stateObject.state == nil {
continue
}
stateObject.state.Reset()
} }
} }
// Syncs the trie and all siblings // Syncs the trie and all siblings
func (s *State) Sync() { func (s *State) Sync() {
// Sync all nested states // Sync all nested states
for _, state := range s.states { for _, stateObject := range s.stateObjects {
state.Sync() if stateObject.state == nil {
continue
}
stateObject.state.Sync()
} }
s.trie.Sync() s.trie.Sync()
@ -54,62 +64,66 @@ func (s *State) EachStorage(cb ethutil.EachCallback) {
it.Each(cb) it.Each(cb)
} }
func (s *State) GetStateObject(addr []byte) *StateObject { func (self *State) UpdateStateObject(stateObject *StateObject) {
data := s.trie.Get(string(addr)) addr := stateObject.Address()
if data == "" {
if self.stateObjects[string(addr)] == nil {
self.stateObjects[string(addr)] = stateObject
}
ethutil.Config.Db.Put(ethutil.Sha3Bin(stateObject.Script()), stateObject.Script())
self.trie.Update(string(addr), string(stateObject.RlpEncode()))
self.manifest.AddObjectChange(stateObject)
}
func (self *State) GetStateObject(addr []byte) *StateObject {
stateObject := self.stateObjects[string(addr)]
if stateObject != nil {
return stateObject
}
data := self.trie.Get(string(addr))
if len(data) == 0 {
return nil return nil
} }
stateObject := NewStateObjectFromBytes(addr, []byte(data)) stateObject = NewStateObjectFromBytes(addr, []byte(data))
self.stateObjects[string(addr)] = stateObject
// Check if there's a cached state for this contract return stateObject
cachedStateObject := s.states[string(addr)] }
if cachedStateObject != nil {
//fmt.Printf("get cached #%d %x addr: %x\n", cachedStateObject.trie.Cache().Len(), cachedStateObject.Root(), addr[0:4]) func (self *State) GetOrNewStateObject(addr []byte) *StateObject {
stateObject.state = cachedStateObject stateObject := self.GetStateObject(addr)
if stateObject == nil {
stateObject = NewStateObject(addr)
self.stateObjects[string(addr)] = stateObject
} }
return stateObject return stateObject
} }
// Updates any given state object func (self *State) GetAccount(addr []byte) *StateObject {
func (s *State) UpdateStateObject(object *StateObject) { return self.GetOrNewStateObject(addr)
addr := object.Address()
if object.state != nil && s.states[string(addr)] == nil {
s.states[string(addr)] = object.state
//fmt.Printf("update cached #%d %x addr: %x\n", object.state.trie.Cache().Len(), object.state.Root(), addr[0:4])
}
ethutil.Config.Db.Put(ethutil.Sha3Bin(object.Script()), object.Script())
s.trie.Update(string(addr), string(object.RlpEncode()))
s.manifest.AddObjectChange(object)
}
func (s *State) GetAccount(addr []byte) (account *StateObject) {
data := s.trie.Get(string(addr))
if data == "" {
account = NewAccount(addr, big.NewInt(0))
} else {
account = NewStateObjectFromBytes(addr, []byte(data))
}
return
} }
func (s *State) Cmp(other *State) bool { func (s *State) Cmp(other *State) bool {
return s.trie.Cmp(other.trie) return s.trie.Cmp(other.trie)
} }
func (s *State) Copy() *State { func (self *State) Copy() *State {
state := NewState(s.trie.Copy()) if self.trie != nil {
for k, subState := range s.states { state := NewState(self.trie.Copy())
state.states[k] = subState.Copy() for k, stateObject := range self.stateObjects {
state.stateObjects[k] = stateObject.Copy()
}
return state
} }
return state return nil
} }
func (s *State) Snapshot() *State { func (s *State) Snapshot() *State {
@ -165,3 +179,84 @@ func (m *Manifest) AddStorageChange(stateObject *StateObject, storageAddr []byte
m.storageChanges[string(stateObject.Address())][string(storageAddr)] = storage m.storageChanges[string(stateObject.Address())][string(storageAddr)] = storage
} }
/*
// Resets the trie and all siblings
func (s *State) Reset() {
s.trie.Undo()
// Reset all nested states
for _, state := range s.states {
state.Reset()
}
}
// Syncs the trie and all siblings
func (s *State) Sync() {
// Sync all nested states
for _, state := range s.states {
state.Sync()
}
s.trie.Sync()
}
func (s *State) GetStateObject(addr []byte) *StateObject {
data := s.trie.Get(string(addr))
if data == "" {
return nil
}
stateObject := NewStateObjectFromBytes(addr, []byte(data))
// Check if there's a cached state for this contract
cachedStateObject := s.states[string(addr)]
if cachedStateObject != nil {
//fmt.Printf("get cached #%d %x addr: %x\n", cachedStateObject.trie.Cache().Len(), cachedStateObject.Root(), addr[0:4])
stateObject.state = cachedStateObject
}
return stateObject
}
// Updates any given state object
func (s *State) UpdateStateObject(object *StateObject) {
addr := object.Address()
if object.state != nil && s.states[string(addr)] == nil {
s.states[string(addr)] = object.state
}
ethutil.Config.Db.Put(ethutil.Sha3Bin(object.Script()), object.Script())
s.trie.Update(string(addr), string(object.RlpEncode()))
s.manifest.AddObjectChange(object)
}
func (s *State) GetAccount(addr []byte) (account *StateObject) {
data := s.trie.Get(string(addr))
if data == "" {
account = NewAccount(addr, big.NewInt(0))
} else {
account = NewStateObjectFromBytes(addr, []byte(data))
}
// Check if there's a cached state for this contract
cachedStateObject := s.states[string(addr)]
if cachedStateObject != nil {
account.state = cachedStateObject
}
return
}
func (s *State) Copy() *State {
state := NewState(s.trie.Copy())
for k, subState := range s.states {
state.states[k] = subState.Copy()
}
return state
}
*/

View File

@ -97,100 +97,49 @@ func (sm *StateManager) BlockChain() *BlockChain {
return sm.bc return sm.bc
} }
func (sm *StateManager) MakeStateObject(state *State, tx *Transaction) *StateObject { func (self *StateManager) ProcessTransactions(coinbase *StateObject, state *State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) {
contract := MakeContract(tx, state) var (
if contract != nil { receipts Receipts
state.states[string(tx.CreationAddress())] = contract.state handled, unhandled Transactions
totalUsedGas = big.NewInt(0)
err error
)
return contract done:
} for i, tx := range txs {
txGas := new(big.Int).Set(tx.Gas)
return nil st := NewStateTransition(coinbase, tx, state, block)
} err = st.TransitionState()
// Apply transactions uses the transaction passed to it and applies them onto
// the current processing state.
func (sm *StateManager) ApplyTransactions(state *State, block *Block, txs []*Transaction) ([]*Receipt, []*Transaction) {
// Process each transaction/contract
var receipts []*Receipt
var validTxs []*Transaction
totalUsedGas := big.NewInt(0)
for _, tx := range txs {
usedGas, err := sm.ApplyTransaction(state, block, tx)
if err != nil { if err != nil {
if IsNonceErr(err) { switch {
case IsNonceErr(err):
err = nil // ignore error
continue continue
} case IsGasLimitErr(err):
unhandled = txs[i:]
ethutil.Config.Log.Infoln(err) break done
default:
ethutil.Config.Log.Infoln(err)
}
} }
accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, usedGas)) // Notify all subscribers
self.Ethereum.Reactor().Post("newTx:post", tx)
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)
} }
return receipts, validTxs fmt.Println("################# MADE\n", receipts, "\n############################")
}
func (sm *StateManager) ApplyTransaction(state *State, block *Block, tx *Transaction) (totalGasUsed *big.Int, err error) { parent.GasUsed = totalUsedGas
/*
Applies transactions to the given state and creates new
state objects where needed.
If said objects needs to be created return receipts, handled, unhandled, err
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)
// Apply the transaction to the current state
gas, err = sm.Ethereum.TxPool().ProcessTransaction(tx, state, false)
addTotalGas(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 {
// 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)
}
}
return
} }
func (sm *StateManager) Process(block *Block, dontReact bool) error { func (sm *StateManager) Process(block *Block, dontReact bool) error {
@ -212,7 +161,6 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea
hash := block.Hash() hash := block.Hash()
if sm.bc.HasBlock(hash) { if sm.bc.HasBlock(hash) {
//fmt.Println("[STATE] We already have this block, ignoring")
return nil return nil
} }
@ -227,9 +175,14 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea
if !sm.bc.HasBlock(block.PrevHash) && sm.bc.CurrentBlock != nil { if !sm.bc.HasBlock(block.PrevHash) && sm.bc.CurrentBlock != nil {
return ParentError(block.PrevHash) return ParentError(block.PrevHash)
} }
fmt.Println(block.Receipts())
coinbase := state.GetOrNewStateObject(block.Coinbase)
coinbase.SetGasPool(block.CalcGasLimit(parent))
// Process the transactions on to current block // Process the transactions on to current block
sm.ApplyTransactions(state, parent, block.Transactions()) //sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions())
sm.ProcessTransactions(coinbase, state, block, parent, block.Transactions())
// Block validation // Block validation
if err := sm.ValidateBlock(block); err != nil { if err := sm.ValidateBlock(block); err != nil {
@ -244,7 +197,6 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea
return err return err
} }
//if !sm.compState.Cmp(state) {
if !block.State().Cmp(state) { if !block.State().Cmp(state) {
return fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().trie.Root, state.trie.Root) return fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().trie.Root, state.trie.Root)
} }
@ -337,13 +289,6 @@ func CalculateBlockReward(block *Block, uncleLength int) *big.Int {
base.Add(base, UncleInclusionReward) base.Add(base, UncleInclusionReward)
} }
lastCumulGasUsed := big.NewInt(0)
for _, r := range block.Receipts() {
usedGas := new(big.Int).Sub(r.CumulativeGasUsed, lastCumulGasUsed)
usedGas.Add(usedGas, r.Tx.GasPrice)
base.Add(base, usedGas)
}
return base.Add(base, BlockReward) return base.Add(base, BlockReward)
} }
@ -375,35 +320,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

@ -17,6 +17,11 @@ type StateObject struct {
state *State state *State
script []byte script []byte
initScript []byte initScript []byte
// Total gas pool is the total amount of gas currently
// left if this object is the coinbase. Gas is directly
// purchased of the coinbase.
gasPool *big.Int
} }
// Converts an transaction in to a state object // Converts an transaction in to a state object
@ -38,6 +43,10 @@ func MakeContract(tx *Transaction, state *State) *StateObject {
return nil return nil
} }
func NewStateObject(addr []byte) *StateObject {
return &StateObject{address: addr, Amount: new(big.Int)}
}
func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject { func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject {
contract := &StateObject{address: address, Amount: Amount, Nonce: 0} contract := &StateObject{address: address, Amount: Amount, Nonce: 0}
contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root))) contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root)))
@ -77,7 +86,7 @@ func (c *StateObject) SetAddr(addr []byte, value interface{}) {
func (c *StateObject) SetStorage(num *big.Int, val *ethutil.Value) { func (c *StateObject) SetStorage(num *big.Int, val *ethutil.Value) {
addr := ethutil.BigToBytes(num, 256) addr := ethutil.BigToBytes(num, 256)
//fmt.Println("storing", val.BigInt(), "@", num) //fmt.Printf("sstore %x => %v\n", addr, val)
c.SetAddr(addr, val) c.SetAddr(addr, val)
} }
@ -102,16 +111,22 @@ func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value {
// Return the gas back to the origin. Used by the Virtual machine or Closures // Return the gas back to the origin. Used by the Virtual machine or Closures
func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) { func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) {
remainder := new(big.Int).Mul(gas, price) /*
c.AddAmount(remainder) remainder := new(big.Int).Mul(gas, price)
c.AddAmount(remainder)
*/
} }
func (c *StateObject) AddAmount(amount *big.Int) { func (c *StateObject) AddAmount(amount *big.Int) {
c.SetAmount(new(big.Int).Add(c.Amount, amount)) c.SetAmount(new(big.Int).Add(c.Amount, amount))
ethutil.Config.Log.Printf(ethutil.LogLevelSystem, "%x: #%d %v (+ %v)\n", c.Address(), c.Nonce, c.Amount, amount)
} }
func (c *StateObject) SubAmount(amount *big.Int) { func (c *StateObject) SubAmount(amount *big.Int) {
c.SetAmount(new(big.Int).Sub(c.Amount, amount)) c.SetAmount(new(big.Int).Sub(c.Amount, amount))
ethutil.Config.Log.Printf(ethutil.LogLevelSystem, "%x: #%d %v (- %v)\n", c.Address(), c.Nonce, c.Amount, amount)
} }
func (c *StateObject) SetAmount(amount *big.Int) { func (c *StateObject) SetAmount(amount *big.Int) {
@ -129,6 +144,53 @@ func (c *StateObject) ConvertGas(gas, price *big.Int) error {
return nil return nil
} }
func (self *StateObject) SetGasPool(gasLimit *big.Int) {
self.gasPool = new(big.Int).Set(gasLimit)
ethutil.Config.Log.Printf(ethutil.LogLevelSystem, "%x: fuel (+ %v)", self.Address(), self.gasPool)
}
func (self *StateObject) BuyGas(gas, price *big.Int) error {
if self.gasPool.Cmp(gas) < 0 {
return GasLimitError(self.gasPool, gas)
}
rGas := new(big.Int).Set(gas)
rGas.Mul(rGas, price)
self.AddAmount(rGas)
return nil
}
func (self *StateObject) RefundGas(gas, price *big.Int) {
self.gasPool.Add(self.gasPool, gas)
rGas := new(big.Int).Set(gas)
rGas.Mul(rGas, price)
self.Amount.Sub(self.Amount, rGas)
}
func (self *StateObject) Copy() *StateObject {
stCopy := &StateObject{}
stCopy.address = make([]byte, len(self.address))
copy(stCopy.address, self.address)
stCopy.Amount = new(big.Int).Set(self.Amount)
stCopy.ScriptHash = make([]byte, len(self.ScriptHash))
copy(stCopy.ScriptHash, self.ScriptHash)
stCopy.Nonce = self.Nonce
if self.state != nil {
stCopy.state = self.state.Copy()
}
stCopy.script = make([]byte, len(self.script))
copy(stCopy.script, self.script)
stCopy.initScript = make([]byte, len(self.initScript))
copy(stCopy.initScript, self.initScript)
return stCopy
}
// Returns the address of the contract/account // Returns the address of the contract/account
func (c *StateObject) Address() []byte { func (c *StateObject) Address() []byte {
return c.address return c.address
@ -153,14 +215,14 @@ func (c *StateObject) RlpEncode() []byte {
root = "" root = ""
} }
return ethutil.Encode([]interface{}{c.Amount, c.Nonce, root, ethutil.Sha3Bin(c.script)}) return ethutil.Encode([]interface{}{c.Nonce, c.Amount, root, ethutil.Sha3Bin(c.script)})
} }
func (c *StateObject) RlpDecode(data []byte) { func (c *StateObject) RlpDecode(data []byte) {
decoder := ethutil.NewValueFromBytes(data) decoder := ethutil.NewValueFromBytes(data)
c.Amount = decoder.Get(0).BigInt() c.Nonce = decoder.Get(0).Uint()
c.Nonce = decoder.Get(1).Uint() c.Amount = decoder.Get(1).BigInt()
c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())) c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()))
c.ScriptHash = decoder.Get(3).Bytes() c.ScriptHash = decoder.Get(3).Bytes()

View File

@ -0,0 +1,260 @@
package ethchain
import (
"fmt"
"github.com/ethereum/eth-go/ethutil"
"math/big"
)
/*
* The State transitioning model
*
* A state transition is a change made when a transaction is applied to the current world state
* The state transitioning model does all all the necessary work to work out a valid new state root.
* 1) Nonce handling
* 2) Pre pay / buy gas of the coinbase (miner)
* 3) Create a new state object if the recipient is \0*32
* 4) Value transfer
* == If contract creation ==
* 4a) Attempt to run transaction data
* 4b) If valid, use result as code for the new state object
* == end ==
* 5) Run Script section
* 6) Derive new state root
*/
type StateTransition struct {
coinbase []byte
tx *Transaction
gas *big.Int
state *State
block *Block
cb, rec, sen *StateObject
}
func NewStateTransition(coinbase *StateObject, tx *Transaction, state *State, block *Block) *StateTransition {
return &StateTransition{coinbase.Address(), tx, new(big.Int), state, block, coinbase, 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) MakeStateObject(state *State, tx *Transaction) *StateObject {
contract := MakeContract(tx, state)
if contract != nil {
state.states[string(tx.CreationAddress())] = contract.state
return contract
}
return nil
}
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 *StateTransition) RefundGas() {
coinbase, sender := self.Coinbase(), self.Sender()
coinbase.RefundGas(self.gas, self.tx.GasPrice)
// Return remaining gas
remaining := new(big.Int).Mul(self.gas, self.tx.GasPrice)
sender.AddAmount(remaining)
}
func (self *StateTransition) TransitionState() (err error) {
//snapshot := st.state.Snapshot()
/*
defer func() {
if r := recover(); r != nil {
ethutil.Config.Log.Infoln(r)
err = fmt.Errorf("state transition err %v", r)
}
}()
*/
var (
tx = self.tx
sender = self.Sender()
receiver *StateObject
)
// Make sure this transaction's nonce is correct
if sender.Nonce != tx.Nonce {
return NonceError(tx.Nonce, sender.Nonce)
}
// Pre-pay gas / Buy gas of the coinbase account
if err = self.BuyGas(); err != nil {
return err
}
// XXX Transactions after this point are considered valid.
defer func() {
self.RefundGas()
if sender != nil {
self.state.UpdateStateObject(sender)
}
if receiver != nil {
self.state.UpdateStateObject(receiver)
}
self.state.UpdateStateObject(self.Coinbase())
}()
// Increment the nonce for the next transaction
sender.Nonce += 1
// Get the receiver (TODO fix this, if coinbase is the receiver we need to save/retrieve)
receiver = self.Receiver()
// Transaction gas
if err = self.UseGas(GasTx); err != nil {
return err
}
// Pay data gas
dataPrice := big.NewInt(int64(len(tx.Data)))
dataPrice.Mul(dataPrice, GasData)
if err = self.UseGas(dataPrice); err != nil {
return err
}
// If the receiver is nil it's a contract (\0*32).
if receiver == nil {
// Create a new state object for the contract
receiver = self.MakeStateObject(self.state, tx)
if receiver == nil {
return fmt.Errorf("ERR. Unable to create contract with transaction %v", tx)
}
}
// Transfer value from sender to receiver
if err = self.transferValue(sender, receiver); err != nil {
return err
}
// Process the init code and create 'valid' contract
if tx.CreatesContract() {
// 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(receiver.Init(), receiver)
if err != nil {
return fmt.Errorf("Error during init script run %v", err)
}
receiver.script = code
} else {
if len(receiver.Script()) > 0 {
_, err := self.Eval(receiver.Script(), receiver)
if err != nil {
return fmt.Errorf("Error during code execution %v", err)
}
}
}
return nil
}
func (self *StateTransition) transferValue(sender, receiver *StateObject) error {
if sender.Amount.Cmp(self.tx.Value) < 0 {
return fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.tx.Value, sender.Amount)
}
if self.tx.Value.Cmp(ethutil.Big0) > 0 {
// Subtract the amount from the senders account
sender.SubAmount(self.tx.Value)
// Add the amount to receivers account which should conclude this transaction
receiver.AddAmount(self.tx.Value)
ethutil.Config.Log.Debugf("%x => %x (%v) %x\n", sender.Address()[:4], receiver.Address()[:4], self.tx.Value, self.tx.Hash())
}
return nil
}
func (self *StateTransition) Eval(script []byte, context *StateObject) (ret []byte, err error) {
var (
tx = self.tx
block = self.block
initiator = self.Sender()
state = self.state
)
closure := NewClosure(initiator, context, script, state, self.gas, tx.GasPrice)
vm := NewVm(state, nil, RuntimeVars{
Origin: initiator.Address(),
BlockNumber: block.BlockInfo().Number,
PrevHash: block.PrevHash,
Coinbase: block.Coinbase,
Time: block.Time,
Diff: block.Difficulty,
Value: tx.Value,
})
ret, _, err = closure.Call(vm, tx.Data, nil)
return
}

View File

@ -1,6 +1,7 @@
package ethchain package ethchain
import ( import (
"bytes"
"fmt" "fmt"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/secp256k1-go" "github.com/obscuren/secp256k1-go"
@ -24,7 +25,7 @@ type Transaction struct {
} }
func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction { func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction {
return &Transaction{Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true} return &Transaction{Recipient: ContractAddr, Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true}
} }
func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction { func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction {
@ -45,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())
} }
@ -144,7 +148,8 @@ func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
tx.v = byte(decoder.Get(6).Uint()) tx.v = byte(decoder.Get(6).Uint())
tx.r = decoder.Get(7).Bytes() tx.r = decoder.Get(7).Bytes()
tx.s = decoder.Get(8).Bytes() tx.s = decoder.Get(8).Bytes()
if len(tx.Recipient) == 0 {
if bytes.Compare(tx.Recipient, ContractAddr) == 0 {
tx.contractCreation = true tx.contractCreation = true
} }
} }
@ -183,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

@ -22,6 +22,7 @@ type TxMsgTy byte
const ( const (
TxPre = iota TxPre = iota
TxPost TxPost
minGasPrice = 1000000
) )
type TxMsg struct { type TxMsg struct {
@ -89,9 +90,11 @@ func (pool *TxPool) addTransaction(tx *Transaction) {
pool.Ethereum.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()}) pool.Ethereum.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()})
} }
/*
// Process transaction validates the Tx and processes funds from the // Process transaction validates the Tx and processes funds from the
// sender to the recipient. // sender to the recipient.
func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract bool) (gas *big.Int, err error) { func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract bool) (gas *big.Int, err error) {
fmt.Printf("state root before update %x\n", state.Root())
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ethutil.Config.Log.Infoln(r) ethutil.Config.Log.Infoln(r)
@ -101,6 +104,7 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract
gas = new(big.Int) gas = new(big.Int)
addGas := func(g *big.Int) { gas.Add(gas, g) } addGas := func(g *big.Int) { gas.Add(gas, g) }
addGas(GasTx)
// Get the sender // Get the sender
sender := state.GetAccount(tx.Sender()) sender := state.GetAccount(tx.Sender())
@ -110,28 +114,37 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract
return return
} }
sender.Nonce += 1
defer func() {
//state.UpdateStateObject(sender)
// Notify all subscribers
pool.Ethereum.Reactor().Post("newTx:post", tx)
}()
txTotalBytes := big.NewInt(int64(len(tx.Data))) txTotalBytes := big.NewInt(int64(len(tx.Data)))
txTotalBytes.Div(txTotalBytes, ethutil.Big32) txTotalBytes.Div(txTotalBytes, ethutil.Big32)
addGas(new(big.Int).Mul(txTotalBytes, GasSStore)) 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 // Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it. // funds won't invalidate this transaction but simple ignores it.
//totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat)) totAmount := new(big.Int).Add(tx.Value, rGas)
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(tx.Gas, tx.GasPrice))
if sender.Amount.Cmp(totAmount) < 0 { if sender.Amount.Cmp(totAmount) < 0 {
err = fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender()) err = fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
return return
} }
state.UpdateStateObject(sender)
fmt.Printf("state root after sender update %x\n", state.Root())
// Get the receiver // Get the receiver
receiver := state.GetAccount(tx.Recipient) receiver := state.GetAccount(tx.Recipient)
sender.Nonce += 1
// Send Tx to self // Send Tx to self
if bytes.Compare(tx.Recipient, tx.Sender()) == 0 { if bytes.Compare(tx.Recipient, tx.Sender()) == 0 {
addGas(GasTx)
// Subtract the fee // Subtract the fee
sender.SubAmount(new(big.Int).Mul(GasTx, tx.GasPrice)) sender.SubAmount(rGas)
} else { } else {
// Subtract the amount from the senders account // Subtract the amount from the senders account
sender.SubAmount(totAmount) sender.SubAmount(totAmount)
@ -140,17 +153,14 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract
receiver.AddAmount(tx.Value) receiver.AddAmount(tx.Value)
state.UpdateStateObject(receiver) state.UpdateStateObject(receiver)
fmt.Printf("state root after receiver update %x\n", state.Root())
} }
state.UpdateStateObject(sender)
ethutil.Config.Log.Infof("[TXPL] Processed Tx %x\n", tx.Hash()) ethutil.Config.Log.Infof("[TXPL] Processed Tx %x\n", tx.Hash())
// Notify all subscribers
pool.Ethereum.Reactor().Post("newTx:post", tx)
return return
} }
*/
func (pool *TxPool) ValidateTransaction(tx *Transaction) error { func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
// Get the last block so we can retrieve the sender and receiver from // Get the last block so we can retrieve the sender and receiver from
@ -161,17 +171,27 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
return errors.New("[TXPL] No last block on the block chain") return errors.New("[TXPL] No last block on the block chain")
} }
if len(tx.Recipient) != 20 {
return fmt.Errorf("[TXPL] Invalid recipient. len = %d", len(tx.Recipient))
}
// Get the sender // Get the sender
//sender := pool.Ethereum.StateManager().procState.GetAccount(tx.Sender()) //sender := pool.Ethereum.StateManager().procState.GetAccount(tx.Sender())
sender := pool.Ethereum.StateManager().CurrentState().GetAccount(tx.Sender()) sender := pool.Ethereum.StateManager().CurrentState().GetAccount(tx.Sender())
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat)) totAmount := new(big.Int).Set(tx.Value)
// Make sure there's enough in the sender's account. Having insufficient // Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it. // funds won't invalidate this transaction but simple ignores it.
if sender.Amount.Cmp(totAmount) < 0 { if sender.Amount.Cmp(totAmount) < 0 {
return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender()) return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
} }
if tx.IsContract() {
if tx.GasPrice.Cmp(big.NewInt(minGasPrice)) < 0 {
return fmt.Errorf("[TXPL] Gasprice to low, %s given should be at least %d.", tx.GasPrice, minGasPrice)
}
}
// Increment the nonce making each tx valid only once to prevent replay // Increment the nonce making each tx valid only once to prevent replay
// attacks // attacks
@ -200,6 +220,8 @@ out:
// Call blocking version. // Call blocking version.
pool.addTransaction(tx) pool.addTransaction(tx)
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

@ -1,5 +1,9 @@
package ethchain package ethchain
import (
"fmt"
)
type OpCode int type OpCode int
// Op codes // Op codes
@ -17,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
@ -37,7 +43,10 @@ const (
CALLVALUE = 0x34 CALLVALUE = 0x34
CALLDATALOAD = 0x35 CALLDATALOAD = 0x35
CALLDATASIZE = 0x36 CALLDATASIZE = 0x36
GASPRICE = 0x37 CALLDATACOPY = 0x37
CODESIZE = 0x38
CODECOPY = 0x39
GASPRICE = 0x3a
// 0x40 range - block operations // 0x40 range - block operations
PREVHASH = 0x40 PREVHASH = 0x40
@ -48,18 +57,19 @@ const (
GASLIMIT = 0x45 GASLIMIT = 0x45
// 0x50 range - 'storage' and execution // 0x50 range - 'storage' and execution
POP = 0x51 POP = 0x50
DUP = 0x52 DUP = 0x51
SWAP = 0x53 SWAP = 0x52
MLOAD = 0x54 MLOAD = 0x53
MSTORE = 0x55 MSTORE = 0x54
MSTORE8 = 0x56 MSTORE8 = 0x55
SLOAD = 0x57 SLOAD = 0x56
SSTORE = 0x58 SSTORE = 0x57
JUMP = 0x59 JUMP = 0x58
JUMPI = 0x5a JUMPI = 0x59
PC = 0x5b PC = 0x5a
MSIZE = 0x5c MSIZE = 0x5b
GAS = 0x5c
// 0x60 range // 0x60 range
PUSH1 = 0x60 PUSH1 = 0x60
@ -120,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",
@ -140,6 +152,9 @@ var opCodeToString = map[OpCode]string{
CALLVALUE: "CALLVALUE", CALLVALUE: "CALLVALUE",
CALLDATALOAD: "CALLDATALOAD", CALLDATALOAD: "CALLDATALOAD",
CALLDATASIZE: "CALLDATASIZE", CALLDATASIZE: "CALLDATASIZE",
CALLDATACOPY: "CALLDATACOPY",
CODESIZE: "CODESIZE",
CODECOPY: "CODECOPY",
GASPRICE: "TXGASPRICE", GASPRICE: "TXGASPRICE",
// 0x40 range - block operations // 0x40 range - block operations
@ -162,6 +177,7 @@ var opCodeToString = map[OpCode]string{
JUMPI: "JUMPI", JUMPI: "JUMPI",
PC: "PC", PC: "PC",
MSIZE: "MSIZE", MSIZE: "MSIZE",
GAS: "GAS",
// 0x60 range - push // 0x60 range - push
PUSH1: "PUSH1", PUSH1: "PUSH1",
@ -208,7 +224,12 @@ var opCodeToString = map[OpCode]string{
} }
func (o OpCode) String() string { func (o OpCode) String() string {
return opCodeToString[o] str := opCodeToString[o]
if len(str) == 0 {
return fmt.Sprintf("Missing opcode %#x", int(o))
}
return str
} }
// Op codes for assembling // Op codes for assembling

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)
) )
@ -112,15 +114,18 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
} }
gas := new(big.Int) gas := new(big.Int)
setStepGasUsage := func(amount *big.Int) { addStepGasUsage := func(amount *big.Int) {
gas.Add(gas, amount) gas.Add(gas, amount)
} }
addStepGasUsage(GasStep)
var newMemSize uint64 = 0
switch op { switch op {
case SHA3: case STOP:
setStepGasUsage(GasSha) case SUICIDE:
case SLOAD: case SLOAD:
setStepGasUsage(GasSLoad) gas.Set(GasSLoad)
case SSTORE: case SSTORE:
var mult *big.Int var mult *big.Int
y, x := stack.Peekn() y, x := stack.Peekn()
@ -132,22 +137,55 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
} else { } else {
mult = ethutil.Big1 mult = ethutil.Big1
} }
setStepGasUsage(new(big.Int).Mul(mult, GasSStore)) gas = new(big.Int).Mul(mult, GasSStore)
case BALANCE: case BALANCE:
setStepGasUsage(GasBalance) gas.Set(GasBalance)
case CREATE: case MSTORE:
require(2)
newMemSize = stack.Peek().Uint64() + 32
case MLOAD:
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)
gas.Set(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:
setStepGasUsage(GasCall) require(7)
case MLOAD, MSIZE, MSTORE8, MSTORE: gas.Set(GasCall)
setStepGasUsage(GasMemory) addStepGasUsage(stack.data[stack.Len()-2])
default:
setStepGasUsage(GasStep) 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)
gas.Set(GasCreate)
newMemSize = stack.data[stack.Len()-2].Uint64() + stack.data[stack.Len()-3].Uint64()
}
newMemSize = (newMemSize + 31) / 32 * 32
if newMemSize > uint64(mem.Len()) {
m := GasMemory.Uint64() * (newMemSize - uint64(mem.Len())) / 32
addStepGasUsage(big.NewInt(int64(m)))
} }
if !closure.UseGas(gas) { if !closure.UseGas(gas) {
@ -156,6 +194,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
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()
@ -320,7 +360,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case ORIGIN: case ORIGIN:
stack.Push(ethutil.BigD(vm.vars.Origin)) stack.Push(ethutil.BigD(vm.vars.Origin))
case CALLER: case CALLER:
stack.Push(ethutil.BigD(closure.Callee().Address())) stack.Push(ethutil.BigD(closure.caller.Address()))
case CALLVALUE: case CALLVALUE:
stack.Push(vm.vars.Value) stack.Push(vm.vars.Value)
case CALLDATALOAD: case CALLDATALOAD:
@ -337,6 +377,26 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
stack.Push(ethutil.BigD(data)) stack.Push(ethutil.BigD(data))
case CALLDATASIZE: case CALLDATASIZE:
stack.Push(big.NewInt(int64(len(closure.Args)))) stack.Push(big.NewInt(int64(len(closure.Args))))
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: case GASPRICE:
stack.Push(closure.Price) stack.Push(closure.Price)
@ -423,6 +483,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
stack.Push(pc) stack.Push(pc)
case MSIZE: case MSIZE:
stack.Push(big.NewInt(int64(mem.Len()))) stack.Push(big.NewInt(int64(mem.Len())))
case GAS:
stack.Push(closure.Gas)
// 0x60 range // 0x60 range
case CREATE: case CREATE:
require(3) require(3)
@ -435,7 +497,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
snapshot := vm.state.Snapshot() snapshot := vm.state.Snapshot()
// Generate a new address // Generate a new address
addr := ethutil.CreateAddress(closure.callee.Address(), closure.callee.N()) addr := ethutil.CreateAddress(closure.caller.Address(), closure.caller.N())
// Create a new contract // Create a new contract
contract := NewContract(addr, value, []byte("")) contract := NewContract(addr, value, []byte(""))
// Set the init script // Set the init script
@ -443,10 +505,10 @@ 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.caller,
closure.Object(), closure.Object(),
contract.initScript, contract.initScript,
vm.state, vm.state,
@ -467,6 +529,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
vm.state.UpdateStateObject(contract) vm.state.UpdateStateObject(contract)
} }
case CALL: case CALL:
// TODO RE-WRITE
require(7) require(7)
// Closure addr // Closure addr
addr := stack.Pop() addr := stack.Pop()
@ -476,51 +539,44 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
inSize, inOffset := stack.Popn() inSize, inOffset := stack.Popn()
// Pop return size and offset // Pop return size and offset
retSize, retOffset := stack.Popn() retSize, retOffset := stack.Popn()
// Make sure there's enough gas
if closure.Gas.Cmp(gas) < 0 {
stack.Push(ethutil.BigFalse)
break
}
// Get the arguments from the memory // Get the arguments from the memory
args := mem.Get(inOffset.Int64(), inSize.Int64()) args := mem.Get(inOffset.Int64(), inSize.Int64())
snapshot := vm.state.Snapshot() snapshot := vm.state.Snapshot()
// Fetch the contract which will serve as the closure body closure.object.Nonce += 1
contract := vm.state.GetStateObject(addr.Bytes()) if closure.object.Amount.Cmp(value) < 0 {
ethutil.Config.Log.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Amount)
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)
// Add the value to the state object
contract.AddAmount(value)
// Create a new callable closure
closure := NewClosure(closure, contract, contract.script, vm.state, gas, closure.Price)
// Executer the closure and get the return value (if any)
ret, _, err := closure.Call(vm, args, hook)
if err != nil {
stack.Push(ethutil.BigFalse)
// Reset the changes applied this object
vm.state.Revert(snapshot)
} else {
stack.Push(ethutil.BigTrue)
vm.state.UpdateStateObject(contract)
mem.Set(retOffset.Int64(), retSize.Int64(), ret)
}
} else {
ethutil.Config.Log.Debugf("Contract %x not found\n", addr.Bytes())
stack.Push(ethutil.BigFalse) stack.Push(ethutil.BigFalse)
} else {
// Fetch the contract which will serve as the closure body
contract := vm.state.GetStateObject(addr.Bytes())
if contract != nil {
// Add the value to the state object
contract.AddAmount(value)
// Create a new callable closure
closure := NewClosure(closure, contract, contract.script, vm.state, gas, closure.Price)
// Executer the closure and get the return value (if any)
ret, _, err := closure.Call(vm, args, hook)
if err != nil {
stack.Push(ethutil.BigFalse)
// Reset the changes applied this object
vm.state.Revert(snapshot)
} else {
stack.Push(ethutil.BigTrue)
vm.state.UpdateStateObject(contract)
mem.Set(retOffset.Int64(), retSize.Int64(), ret)
}
} else {
ethutil.Config.Log.Debugf("Contract %x not found\n", addr.Bytes())
stack.Push(ethutil.BigFalse)
}
} }
case RETURN: case RETURN:
require(2) require(2)

View File

@ -136,11 +136,21 @@ func (self *Miner) mineNewBlock() {
// Sort the transactions by nonce in case of odd network propagation // Sort the transactions by nonce in case of odd network propagation
sort.Sort(ethchain.TxByNonce{self.txs}) sort.Sort(ethchain.TxByNonce{self.txs})
// Accumulate all valid transaction and apply them to the new state // Accumulate all valid transaction and apply them to the new state
receipts, txs := stateManager.ApplyTransactions(self.block.State(), self.block, self.txs) // Error may be ignored. It's not important during mining
self.txs = txs parent := self.ethereum.BlockChain().GetBlock(self.block.PrevHash)
coinbase := self.block.State().GetOrNewStateObject(self.block.Coinbase)
coinbase.SetGasPool(self.block.CalcGasLimit(parent))
receipts, txs, unhandledTxs, err := stateManager.ProcessTransactions(coinbase, self.block.State(), self.block, self.block, self.txs)
if err != nil {
ethutil.Config.Log.Debugln("[MINER]", err)
}
self.txs = append(txs, unhandledTxs...)
// Set the transactions to the block so the new SHA3 can be calculated // Set the transactions to the block so the new SHA3 can be calculated
self.block.SetReceipts(receipts, txs) self.block.SetReceipts(receipts, txs)
// Accumulate the rewards included for this block // Accumulate the rewards included for this block
stateManager.AccumelateRewards(self.block.State(), self.block) stateManager.AccumelateRewards(self.block.State(), self.block)
@ -155,6 +165,7 @@ func (self *Miner) mineNewBlock() {
} else { } else {
self.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{self.block.Value().Val}) self.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{self.block.Value().Val})
ethutil.Config.Log.Infof("[MINER] 🔨 Mined block %x\n", self.block.Hash()) ethutil.Config.Log.Infof("[MINER] 🔨 Mined block %x\n", self.block.Hash())
ethutil.Config.Log.Infoln(self.block)
// Gather the new batch of transactions currently in the tx pool // Gather the new batch of transactions currently in the tx pool
self.txs = self.ethereum.TxPool().CurrentTransactions() self.txs = self.ethereum.TxPool().CurrentTransactions()
} }

View File

@ -115,9 +115,13 @@ var namereg = ethutil.FromHex("bb5f186604d057c1c5240ca2ae0f6430138ac010")
func GetAddressFromNameReg(stateManager *ethchain.StateManager, name string) []byte { func GetAddressFromNameReg(stateManager *ethchain.StateManager, name string) []byte {
recp := new(big.Int).SetBytes([]byte(name)) recp := new(big.Int).SetBytes([]byte(name))
object := stateManager.CurrentState().GetStateObject(namereg) object := stateManager.CurrentState().GetStateObject(namereg)
reg := object.GetStorage(recp) if object != nil {
reg := object.GetStorage(recp)
return reg.Bytes() return reg.Bytes()
}
return nil
} }
func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, scriptStr string) (*PReceipt, error) { func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, scriptStr string) (*PReceipt, error) {
@ -193,8 +197,6 @@ func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, sc
if contractCreation { if contractCreation {
ethutil.Config.Log.Infof("Contract addr %x", tx.CreationAddress()) ethutil.Config.Log.Infof("Contract addr %x", tx.CreationAddress())
} else {
ethutil.Config.Log.Infof("Tx hash %x", tx.Hash())
} }
return NewPReciept(contractCreation, tx.CreationAddress(), tx.Hash(), keyPair.Address()), nil return NewPReciept(contractCreation, tx.CreationAddress(), tx.Hash(), keyPair.Address()), nil

View File

@ -46,6 +46,8 @@ type PBlock struct {
Transactions string `json:"transactions"` Transactions string `json:"transactions"`
Time int64 `json:"time"` Time int64 `json:"time"`
Coinbase string `json:"coinbase"` Coinbase string `json:"coinbase"`
GasLimit string `json:"gasLimit"`
GasUsed string `json:"gasUsed"`
} }
// Creates a new QML Block from a chain block // Creates a new QML Block from a chain block
@ -64,7 +66,7 @@ func NewPBlock(block *ethchain.Block) *PBlock {
return nil return nil
} }
return &PBlock{ref: block, Number: int(block.Number.Uint64()), Hash: ethutil.Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Hex(block.Coinbase)} return &PBlock{ref: block, Number: int(block.Number.Uint64()), GasUsed: block.GasUsed.String(), GasLimit: block.GasLimit.String(), Hash: ethutil.Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Hex(block.Coinbase)}
} }
func (self *PBlock) ToString() string { func (self *PBlock) ToString() string {
@ -109,11 +111,12 @@ func NewPTx(tx *ethchain.Transaction) *PTx {
sender := hex.EncodeToString(tx.Sender()) sender := hex.EncodeToString(tx.Sender())
createsContract := tx.CreatesContract() createsContract := tx.CreatesContract()
data := strings.Join(ethchain.Disassemble(tx.Data), "\n") data := string(tx.Data)
if tx.CreatesContract() {
data = strings.Join(ethchain.Disassemble(tx.Data), "\n")
}
isContract := len(tx.Data) > 0 return &PTx{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: receiver, Contract: tx.CreatesContract(), Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: hex.EncodeToString(tx.Data)}
return &PTx{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: receiver, Contract: isContract, Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: hex.EncodeToString(tx.Data)}
} }
func (self *PTx) ToString() string { func (self *PTx) ToString() string {

View File

@ -68,8 +68,8 @@ func BigCopy(src *big.Int) *big.Int {
// Returns the maximum size big integer // Returns the maximum size big integer
func BigMax(x, y *big.Int) *big.Int { func BigMax(x, y *big.Int) *big.Int {
if x.Cmp(y) <= 0 { if x.Cmp(y) <= 0 {
return x return y
} }
return y return x
} }

View File

@ -18,8 +18,8 @@ var (
Wei = big.NewInt(1) Wei = big.NewInt(1)
) )
// Currency to string
// //
// Currency to string
// Returns a string representing a human readable format // Returns a string representing a human readable format
func CurrencyToString(num *big.Int) string { func CurrencyToString(num *big.Int) string {
switch { switch {
@ -47,7 +47,7 @@ func CurrencyToString(num *big.Int) string {
// Common big integers often used // Common big integers often used
var ( var (
Big1 = big.NewInt(1) Big1 = big.NewInt(1)
Big2 = big.NewInt(1) Big2 = big.NewInt(2)
Big0 = big.NewInt(0) Big0 = big.NewInt(0)
Big32 = big.NewInt(32) Big32 = big.NewInt(32)
Big256 = big.NewInt(0xff) Big256 = big.NewInt(0xff)

View File

@ -75,11 +75,11 @@ func ReadConfig(base string, logTypes LoggerType, g *globalconf.GlobalConf, id s
if Config == nil { if Config == nil {
path := ApplicationFolder(base) path := ApplicationFolder(base)
Config = &config{ExecPath: path, Debug: true, Ver: "0.5.0 RC12"} Config = &config{ExecPath: path, Debug: true, Ver: "0.5.13"}
Config.conf = g Config.conf = g
Config.Identifier = id Config.Identifier = id
Config.Log = NewLogger(logTypes, LogLevelDebug) Config.Log = NewLogger(logTypes, LogLevelDebug)
Config.SetClientString("/Ethereum(G)") Config.SetClientString("Ethereum(G)")
} }
return Config return Config
@ -88,11 +88,9 @@ func ReadConfig(base string, logTypes LoggerType, g *globalconf.GlobalConf, id s
// Set client string // Set client string
// //
func (c *config) SetClientString(str string) { func (c *config) SetClientString(str string) {
id := runtime.GOOS os := runtime.GOOS
if len(c.Identifier) > 0 { cust := c.Identifier
id = c.Identifier Config.ClientString = fmt.Sprintf("%s/v%s/%s/%s/Go", str, c.Ver, cust, os)
}
Config.ClientString = fmt.Sprintf("%s nv%s/%s", str, c.Ver, id)
} }
func (c *config) SetIdentifier(id string) { func (c *config) SetIdentifier(id string) {
@ -145,12 +143,17 @@ func NewLogger(flag LoggerType, level int) *Logger {
return &Logger{logSys: loggers, logLevel: level} return &Logger{logSys: loggers, logLevel: level}
} }
func (self *Logger) SetLevel(level int) {
self.logLevel = level
}
func (log *Logger) AddLogSystem(logger LogSystem) { func (log *Logger) AddLogSystem(logger LogSystem) {
log.logSys = append(log.logSys, logger) log.logSys = append(log.logSys, logger)
} }
const ( const (
LogLevelDebug = iota LogLevelSystem = iota
LogLevelDebug
LogLevelInfo LogLevelInfo
) )
@ -206,6 +209,26 @@ func (log *Logger) Fatal(v ...interface{}) {
os.Exit(1) os.Exit(1)
} }
func (log *Logger) Println(level int, v ...interface{}) {
if log.logLevel > level {
return
}
for _, logger := range log.logSys {
logger.Println(v...)
}
}
func (log *Logger) Printf(level int, format string, v ...interface{}) {
if log.logLevel > level {
return
}
for _, logger := range log.logSys {
logger.Printf(format, v...)
}
}
type confValue struct { type confValue struct {
value string value string
} }

View File

@ -12,6 +12,12 @@ type KeyPair struct {
account *StateObject account *StateObject
} }
func GenerateNewKeyPair() (*KeyPair, error) {
_, prv := secp256k1.GenerateKeyPair()
return NewKeyPairFromSec(prv)
}
func NewKeyPairFromSec(seckey []byte) (*KeyPair, error) { func NewKeyPairFromSec(seckey []byte) (*KeyPair, error) {
pubkey, err := secp256k1.GeneratePubKey(seckey) pubkey, err := secp256k1.GeneratePubKey(seckey)
if err != nil { if err != nil {

View File

@ -11,6 +11,7 @@ import (
type RlpEncodable interface { type RlpEncodable interface {
RlpEncode() []byte RlpEncode() []byte
RlpValue() []interface{}
} }
type RlpEncoder struct { type RlpEncoder struct {

View File

@ -1,3 +1,5 @@
// Package ethwire provides low level access to the Ethereum network and allows
// you to broadcast data over the network.
package ethwire package ethwire
import ( import (
@ -9,11 +11,13 @@ import (
"time" "time"
) )
// Message: // Connection interface describing the methods required to implement the wire protocol.
// [4 bytes token] RLP([TYPE, DATA]) type Conn interface {
// Refer to http://wiki.ethereum.org/index.php/Wire_Protocol Write(typ MsgType, v ...interface{}) error
Read() *Msg
}
// The magic token which should be the first 4 bytes of every message. // The magic token which should be the first 4 bytes of every message and can be used as separator between messages.
var MagicToken = []byte{34, 64, 8, 145} var MagicToken = []byte{34, 64, 8, 145}
type MsgType byte type MsgType byte
@ -68,6 +72,157 @@ func NewMessage(msgType MsgType, data interface{}) *Msg {
} }
} }
type Messages []*Msg
// The connection object allows you to set up a connection to the Ethereum network.
// The Connection object takes care of all encoding and sending objects properly over
// the network.
type Connection struct {
conn net.Conn
nTimeout time.Duration
pendingMessages Messages
}
// Create a new connection to the Ethereum network
func New(conn net.Conn) *Connection {
return &Connection{conn: conn, nTimeout: 500}
}
// Read, reads from the network. It will block until the next message is received.
func (self *Connection) Read() *Msg {
if len(self.pendingMessages) == 0 {
self.readMessages()
}
ret := self.pendingMessages[0]
self.pendingMessages = self.pendingMessages[1:]
return ret
}
// Write to the Ethereum network specifying the type of the message and
// the data. Data can be of type RlpEncodable or []interface{}. Returns
// nil or if something went wrong an error.
func (self *Connection) Write(typ MsgType, v ...interface{}) error {
var pack []byte
slice := [][]interface{}{[]interface{}{byte(typ)}}
for _, value := range v {
if encodable, ok := value.(ethutil.RlpEncodable); ok {
slice = append(slice, encodable.RlpValue())
} else if raw, ok := value.([]interface{}); ok {
slice = append(slice, raw)
} else {
panic(fmt.Sprintf("Unable to 'write' object of type %T", value))
}
}
// Encode the type and the (RLP encoded) data for sending over the wire
encoded := ethutil.NewValue(slice).Encode()
payloadLength := ethutil.NumberToBytes(uint32(len(encoded)), 32)
// Write magic token and payload length (first 8 bytes)
pack = append(MagicToken, payloadLength...)
pack = append(pack, encoded...)
// Write to the connection
_, err := self.conn.Write(pack)
if err != nil {
return err
}
return nil
}
func (self *Connection) readMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) {
if len(data) == 0 {
return nil, nil, true, nil
}
if len(data) <= 8 {
return nil, remaining, false, errors.New("Invalid message")
}
// Check if the received 4 first bytes are the magic token
if bytes.Compare(MagicToken, data[:4]) != 0 {
return nil, nil, false, fmt.Errorf("MagicToken mismatch. Received %v", data[:4])
}
messageLength := ethutil.BytesToNumber(data[4:8])
remaining = data[8+messageLength:]
if int(messageLength) > len(data[8:]) {
return nil, nil, false, fmt.Errorf("message length %d, expected %d", len(data[8:]), messageLength)
}
message := data[8 : 8+messageLength]
decoder := ethutil.NewValueFromBytes(message)
// Type of message
t := decoder.Get(0).Uint()
// Actual data
d := decoder.SliceFrom(1)
msg = &Msg{
Type: MsgType(t),
Data: d,
}
return
}
// The basic message reader waits for data on the given connection, decoding
// and doing a few sanity checks such as if there's a data type and
// unmarhals the given data
func (self *Connection) readMessages() (err error) {
// The recovering function in case anything goes horribly wrong
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("ethwire.ReadMessage error: %v", r)
}
}()
// Buff for writing network message to
//buff := make([]byte, 1440)
var buff []byte
var totalBytes int
for {
// Give buffering some time
self.conn.SetReadDeadline(time.Now().Add(self.nTimeout * time.Millisecond))
// Create a new temporarily buffer
b := make([]byte, 1440)
// Wait for a message from this peer
n, _ := self.conn.Read(b)
if err != nil && n == 0 {
if err.Error() != "EOF" {
fmt.Println("err now", err)
return err
} else {
break
}
// Messages can't be empty
} else if n == 0 {
break
}
buff = append(buff, b[:n]...)
totalBytes += n
}
// Reslice buffer
buff = buff[:totalBytes]
msg, remaining, done, err := self.readMessage(buff)
for ; done != true; msg, remaining, done, err = self.readMessage(remaining) {
//log.Println("rx", msg)
if msg != nil {
self.pendingMessages = append(self.pendingMessages, msg)
}
}
return
}
func ReadMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) { func ReadMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) {
if len(data) == 0 { if len(data) == 0 {
return nil, nil, true, nil return nil, nil, true, nil

30
peer.go
View File

@ -17,7 +17,9 @@ const (
// The size of the output buffer for writing messages // The size of the output buffer for writing messages
outputBufferSize = 50 outputBufferSize = 50
// Current protocol version // Current protocol version
ProtocolVersion = 17 ProtocolVersion = 20
// Interval for ping/pong message
pingPongTimer = 2 * time.Second
) )
type DiscReason byte type DiscReason byte
@ -151,11 +153,11 @@ func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer {
pubkey: pubkey, pubkey: pubkey,
blocksRequested: 10, blocksRequested: 10,
caps: ethereum.ServerCaps(), caps: ethereum.ServerCaps(),
version: ethutil.Config.ClientString,
} }
} }
func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer { func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
p := &Peer{ p := &Peer{
outputQueue: make(chan *ethwire.Msg, outputBufferSize), outputQueue: make(chan *ethwire.Msg, outputBufferSize),
quit: make(chan bool), quit: make(chan bool),
@ -241,9 +243,11 @@ func (p *Peer) writeMessage(msg *ethwire.Msg) {
} }
} }
ethutil.Config.Log.Println(ethutil.LogLevelSystem, "<=", msg.Type, msg.Data)
err := ethwire.WriteMessage(p.conn, msg) err := ethwire.WriteMessage(p.conn, msg)
if err != nil { if err != nil {
ethutil.Config.Log.Debugln("Can't send message:", err) ethutil.Config.Log.Debugln("[PEER] Can't send message:", err)
// Stop the client if there was an error writing to it // Stop the client if there was an error writing to it
p.Stop() p.Stop()
return return
@ -253,7 +257,7 @@ func (p *Peer) writeMessage(msg *ethwire.Msg) {
// Outbound message handler. Outbound messages are handled here // Outbound message handler. Outbound messages are handled here
func (p *Peer) HandleOutbound() { func (p *Peer) HandleOutbound() {
// The ping timer. Makes sure that every 2 minutes a ping is send to the peer // The ping timer. Makes sure that every 2 minutes a ping is send to the peer
pingTimer := time.NewTicker(30 * time.Second) pingTimer := time.NewTicker(pingPongTimer)
serviceTimer := time.NewTicker(5 * time.Minute) serviceTimer := time.NewTicker(5 * time.Minute)
out: out:
@ -264,8 +268,14 @@ out:
p.writeMessage(msg) p.writeMessage(msg)
p.lastSend = time.Now() p.lastSend = time.Now()
// Ping timer sends a ping to the peer each 2 minutes // Ping timer
case <-pingTimer.C: case <-pingTimer.C:
timeSince := time.Since(time.Unix(p.lastPong, 0))
if !p.pingStartTime.IsZero() && p.lastPong != 0 && timeSince > (pingPongTimer+30*time.Second) {
ethutil.Config.Log.Infof("[PEER] Peer did not respond to latest pong fast enough, it took %s, disconnecting.\n", timeSince)
p.Stop()
return
}
p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, "")) p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, ""))
p.pingStartTime = time.Now() p.pingStartTime = time.Now()
@ -307,6 +317,8 @@ func (p *Peer) HandleInbound() {
ethutil.Config.Log.Debugln(err) ethutil.Config.Log.Debugln(err)
} }
for _, msg := range msgs { for _, msg := range msgs {
ethutil.Config.Log.Println(ethutil.LogLevelSystem, "=>", msg.Type, msg.Data)
switch msg.Type { switch msg.Type {
case ethwire.MsgHandshakeTy: case ethwire.MsgHandshakeTy:
// Version message // Version message
@ -393,7 +405,7 @@ func (p *Peer) HandleInbound() {
if err != nil { if err != nil {
// If the parent is unknown try to catch up with this peer // If the parent is unknown try to catch up with this peer
if ethchain.IsParentErr(err) { if ethchain.IsParentErr(err) {
ethutil.Config.Log.Infoln("Attempting to catch up since we don't know the parent") ethutil.Config.Log.Infoln("Attempting to catch. Parent known")
p.catchingUp = false p.catchingUp = false
p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash()) p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
} else if ethchain.IsValidationErr(err) { } else if ethchain.IsValidationErr(err) {
@ -405,7 +417,7 @@ func (p *Peer) HandleInbound() {
if p.catchingUp && msg.Data.Len() > 1 { if p.catchingUp && msg.Data.Len() > 1 {
if lastBlock != nil { if lastBlock != nil {
blockInfo := lastBlock.BlockInfo() blockInfo := lastBlock.BlockInfo()
ethutil.Config.Log.Debugf("Synced to block height #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash) ethutil.Config.Log.Debugf("Synced chain to #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash)
} }
p.catchingUp = false p.catchingUp = false
@ -571,7 +583,7 @@ func (p *Peer) pushHandshake() error {
pubkey := keyRing.PublicKey pubkey := keyRing.PublicKey
msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{ msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{
uint32(ProtocolVersion), uint32(0), p.version, byte(p.caps), p.port, pubkey[1:], uint32(ProtocolVersion), uint32(0), []byte(p.version), byte(p.caps), p.port, pubkey[1:],
}) })
p.QueueMessage(msg) p.QueueMessage(msg)
@ -603,7 +615,7 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
c := msg.Data c := msg.Data
if c.Get(0).Uint() != ProtocolVersion { if c.Get(0).Uint() != ProtocolVersion {
ethutil.Config.Log.Debugln("Invalid peer version. Require protocol:", ProtocolVersion) ethutil.Config.Log.Debugf("Invalid peer version. Require protocol: %d. Received: %d\n", ProtocolVersion, c.Get(0).Uint())
p.Stop() p.Stop()
return return
} }