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 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
individual package for more information.

View File

@ -25,16 +25,10 @@ func Disassemble(script []byte) (asm []string) {
pc.Add(pc, ethutil.Big1)
a := int64(op) - int64(PUSH1) + 1
data := script[pc.Int64() : pc.Int64()+a]
val := ethutil.BigD(data)
var b []byte
if val.Int64() == 0 {
b = []byte{0}
} else {
b = val.Bytes()
if len(data) == 0 {
data = []byte{0}
}
asm = append(asm, fmt.Sprintf("0x%x", b))
asm = append(asm, fmt.Sprintf("%#x", data))
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
}
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 {
bi := BlockInfo{}
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))

View File

@ -55,6 +55,8 @@ func (bc *BlockChain) NewBlock(coinbase []byte) *Block {
nil,
"")
block.MinGasPrice = big.NewInt(10000000000000)
if bc.CurrentBlock != nil {
var mul *big.Int
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)
// max(10000, (parent gas limit * (1024 - 1) + (parent gas used * 6 / 5)) / 1024)
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)
block.GasLimit = block.CalcGasLimit(bc.CurrentBlock)
}
return block
@ -271,7 +261,7 @@ func (bc *BlockChain) GetChain(hash []byte, amount int) []*Block {
func AddTestNetFunds(block *Block) {
for _, addr := range []string{
"8a40bfaa73256b60764c1bf40675a99083efb075",
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
"e4157b34ea9615cfbde6b4fda419828124b70c78",
"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df",
"6c386a4b26f73c802f34673f7248bb118f97424a",
@ -285,7 +275,6 @@ func AddTestNetFunds(block *Block) {
account.Amount = ethutil.Big("1606938044258990275541962092341162602522202993782792835301376") //ethutil.BigPow(2, 200)
block.state.UpdateStateObject(account)
}
log.Printf("%x\n", block.RlpEncode())
}
func (bc *BlockChain) setLastBlock() {

View File

@ -17,7 +17,7 @@ type ClosureRef interface {
// Basic inline closure object which implement the 'closure' interface
type Closure struct {
callee ClosureRef
caller ClosureRef
object *StateObject
Script []byte
State *State
@ -28,12 +28,14 @@ type Closure struct {
}
// Create a new closure for the given data items
func NewClosure(callee ClosureRef, object *StateObject, script []byte, state *State, gas, price *big.Int) *Closure {
c := &Closure{callee: callee, object: object, Script: script, State: state, Args: nil}
func NewClosure(caller ClosureRef, object *StateObject, script []byte, state *State, gas, price *big.Int) *Closure {
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.
c.Gas = new(big.Int).Set(gas)
c.Price = new(big.Int).Set(price)
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 {
// Return the remaining gas to the callee
// If no callee is present return it to
// Return the remaining gas to the caller
// If no caller is present return it to
// the origin (i.e. contract or tx)
if c.callee != nil {
c.callee.ReturnGas(c.Gas, c.Price, c.State)
if c.caller != nil {
c.caller.ReturnGas(c.Gas, c.Price, c.State)
} else {
c.object.ReturnGas(c.Gas, c.Price, c.State)
}
@ -107,7 +109,7 @@ func (c *Closure) UseGas(gas *big.Int) bool {
return true
}
// Implement the Callee interface
// Implement the caller interface
func (c *Closure) ReturnGas(gas, price *big.Int, state *State) {
// Return the gas to the closure
c.Gas.Add(c.Gas, gas)
@ -118,8 +120,8 @@ func (c *Closure) Object() *StateObject {
return c.object
}
func (c *Closure) Callee() ClosureRef {
return c.callee
func (c *Closure) Caller() ClosureRef {
return c.caller
}
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 (
"fmt"
"math/big"
)
// Parent error. In case a parent is unknown this error will be thrown
@ -43,6 +44,23 @@ func IsValidationErr(err error) bool {
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 {
Message string
Is, Exp uint64
@ -61,3 +79,20 @@ func IsNonceErr(err error) bool {
return ok
}
type OutOfGasErr struct {
Message string
}
func OutOfGasError() *OutOfGasErr {
return &OutOfGasErr{Message: "Out of gas"}
}
func (self *OutOfGasErr) Error() string {
return self.Message
}
func IsOutOfGasErr(err error) bool {
_, ok := err.(*OutOfGasErr)
return ok
}

View File

@ -4,30 +4,6 @@ import (
"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 UncleReward *big.Int = big.NewInt(1.125e+18)
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)
}
func (m *Memory) Resize(size uint64) {
if uint64(m.Len()) < size {
m.store = append(m.store, make([]byte, size-uint64(m.Len()))...)
}
}
func (m *Memory) Get(offset, size int64) []byte {
return m.store[offset : offset+size]
}

View File

@ -16,12 +16,14 @@ type State struct {
// Nested states
states map[string]*State
stateObjects map[string]*StateObject
manifest *Manifest
}
// Create a new state from a given trie
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
@ -29,16 +31,24 @@ func (s *State) Reset() {
s.trie.Undo()
// Reset all nested states
for _, state := range s.states {
state.Reset()
for _, stateObject := range s.stateObjects {
if stateObject.state == nil {
continue
}
stateObject.state.Reset()
}
}
// Syncs the trie and all siblings
func (s *State) Sync() {
// Sync all nested states
for _, state := range s.states {
state.Sync()
for _, stateObject := range s.stateObjects {
if stateObject.state == nil {
continue
}
stateObject.state.Sync()
}
s.trie.Sync()
@ -54,62 +64,66 @@ func (s *State) EachStorage(cb ethutil.EachCallback) {
it.Each(cb)
}
func (s *State) GetStateObject(addr []byte) *StateObject {
data := s.trie.Get(string(addr))
if data == "" {
func (self *State) UpdateStateObject(stateObject *StateObject) {
addr := stateObject.Address()
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
}
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
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
}
func (self *State) GetOrNewStateObject(addr []byte) *StateObject {
stateObject := self.GetStateObject(addr)
if stateObject == nil {
stateObject = NewStateObject(addr)
self.stateObjects[string(addr)] = stateObject
}
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
//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 (self *State) GetAccount(addr []byte) *StateObject {
return self.GetOrNewStateObject(addr)
}
func (s *State) Cmp(other *State) bool {
return s.trie.Cmp(other.trie)
}
func (s *State) Copy() *State {
state := NewState(s.trie.Copy())
for k, subState := range s.states {
state.states[k] = subState.Copy()
func (self *State) Copy() *State {
if self.trie != nil {
state := NewState(self.trie.Copy())
for k, stateObject := range self.stateObjects {
state.stateObjects[k] = stateObject.Copy()
}
return state
}
return state
return nil
}
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
}
/*
// 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
}
func (sm *StateManager) MakeStateObject(state *State, tx *Transaction) *StateObject {
contract := MakeContract(tx, state)
if contract != nil {
state.states[string(tx.CreationAddress())] = contract.state
func (self *StateManager) ProcessTransactions(coinbase *StateObject, state *State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, error) {
var (
receipts Receipts
handled, unhandled Transactions
totalUsedGas = big.NewInt(0)
err error
)
return contract
}
return nil
}
// 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)
done:
for i, tx := range txs {
txGas := new(big.Int).Set(tx.Gas)
st := NewStateTransition(coinbase, tx, state, block)
err = st.TransitionState()
if err != nil {
if IsNonceErr(err) {
switch {
case IsNonceErr(err):
err = nil // ignore error
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}
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) {
/*
Applies transactions to the given state and creates new
state objects where needed.
parent.GasUsed = totalUsedGas
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)
// 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
return receipts, handled, unhandled, err
}
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()
if sm.bc.HasBlock(hash) {
//fmt.Println("[STATE] We already have this block, ignoring")
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 {
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
sm.ApplyTransactions(state, parent, block.Transactions())
//sm.ApplyTransactions(block.Coinbase, state, parent, block.Transactions())
sm.ProcessTransactions(coinbase, state, block, parent, block.Transactions())
// Block validation
if err := sm.ValidateBlock(block); err != nil {
@ -244,7 +197,6 @@ func (sm *StateManager) ProcessBlock(state *State, parent, block *Block, dontRea
return err
}
//if !sm.compState.Cmp(state) {
if !block.State().Cmp(state) {
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)
}
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)
}
@ -375,35 +320,6 @@ func (sm *StateManager) 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) {
for addr, stateObject := range state.manifest.objectChanges {
sm.Ethereum.Reactor().Post("object:"+addr, stateObject)

View File

@ -17,6 +17,11 @@ type StateObject struct {
state *State
script []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
@ -38,6 +43,10 @@ func MakeContract(tx *Transaction, state *State) *StateObject {
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 {
contract := &StateObject{address: address, Amount: Amount, Nonce: 0}
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) {
addr := ethutil.BigToBytes(num, 256)
//fmt.Println("storing", val.BigInt(), "@", num)
//fmt.Printf("sstore %x => %v\n", 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
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) {
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) {
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) {
@ -129,6 +144,53 @@ func (c *StateObject) ConvertGas(gas, price *big.Int) error {
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
func (c *StateObject) Address() []byte {
return c.address
@ -153,14 +215,14 @@ func (c *StateObject) RlpEncode() []byte {
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) {
decoder := ethutil.NewValueFromBytes(data)
c.Amount = decoder.Get(0).BigInt()
c.Nonce = decoder.Get(1).Uint()
c.Nonce = decoder.Get(0).Uint()
c.Amount = decoder.Get(1).BigInt()
c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()))
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
import (
"bytes"
"fmt"
"github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/secp256k1-go"
@ -24,7 +25,7 @@ type Transaction struct {
}
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 {
@ -45,15 +46,18 @@ func NewTransactionFromValue(val *ethutil.Value) *Transaction {
return tx
}
func (self *Transaction) GasValue() *big.Int {
return new(big.Int).Mul(self.Gas, self.GasPrice)
}
func (self *Transaction) TotalValue() *big.Int {
v := self.GasValue()
return v.Add(v, self.Value)
}
func (tx *Transaction) Hash() []byte {
data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data}
/*
if tx.contractCreation {
data = append(data, tx.Init)
}
*/
return ethutil.Sha3Bin(ethutil.NewValue(data).Encode())
}
@ -144,7 +148,8 @@ func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
tx.v = byte(decoder.Get(6).Uint())
tx.r = decoder.Get(7).Bytes()
tx.s = decoder.Get(8).Bytes()
if len(tx.Recipient) == 0 {
if bytes.Compare(tx.Recipient, ContractAddr) == 0 {
tx.contractCreation = true
}
}
@ -183,6 +188,7 @@ type Receipt struct {
PostState []byte
CumulativeGasUsed *big.Int
}
type Receipts []*Receipt
func NewRecieptFromValue(val *ethutil.Value) *Receipt {
r := &Receipt{}

View File

@ -22,6 +22,7 @@ type TxMsgTy byte
const (
TxPre = iota
TxPost
minGasPrice = 1000000
)
type TxMsg struct {
@ -89,9 +90,11 @@ func (pool *TxPool) addTransaction(tx *Transaction) {
pool.Ethereum.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()})
}
/*
// Process transaction validates the Tx and processes funds from the
// sender to the recipient.
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() {
if r := recover(); r != nil {
ethutil.Config.Log.Infoln(r)
@ -101,6 +104,7 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract
gas = new(big.Int)
addGas := func(g *big.Int) { gas.Add(gas, g) }
addGas(GasTx)
// Get the sender
sender := state.GetAccount(tx.Sender())
@ -110,28 +114,37 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract
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.Div(txTotalBytes, ethutil.Big32)
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, new(big.Int).Mul(TxFee, TxFeeRat))
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(tx.Gas, tx.GasPrice))
totAmount := new(big.Int).Add(tx.Value, rGas)
if sender.Amount.Cmp(totAmount) < 0 {
err = fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender())
return
}
state.UpdateStateObject(sender)
fmt.Printf("state root after sender update %x\n", state.Root())
// Get the receiver
receiver := state.GetAccount(tx.Recipient)
sender.Nonce += 1
// Send Tx to self
if bytes.Compare(tx.Recipient, tx.Sender()) == 0 {
addGas(GasTx)
// Subtract the fee
sender.SubAmount(new(big.Int).Mul(GasTx, tx.GasPrice))
sender.SubAmount(rGas)
} else {
// Subtract the amount from the senders account
sender.SubAmount(totAmount)
@ -140,17 +153,14 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract
receiver.AddAmount(tx.Value)
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())
// Notify all subscribers
pool.Ethereum.Reactor().Post("newTx:post", tx)
return
}
*/
func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
// 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")
}
if len(tx.Recipient) != 20 {
return fmt.Errorf("[TXPL] Invalid recipient. len = %d", len(tx.Recipient))
}
// Get the sender
//sender := pool.Ethereum.StateManager().procState.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
// funds won't invalidate this transaction but simple ignores it.
if sender.Amount.Cmp(totAmount) < 0 {
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
// attacks
@ -200,6 +220,8 @@ out:
// Call blocking version.
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
pool.Ethereum.Reactor().Post("newTx:pre", tx)
}

View File

@ -1,5 +1,9 @@
package ethchain
import (
"fmt"
)
type OpCode int
// Op codes
@ -17,8 +21,10 @@ const (
NEG = 0x09
LT = 0x0a
GT = 0x0b
EQ = 0x0c
NOT = 0x0d
SLT = 0x0c
SGT = 0x0d
EQ = 0x0e
NOT = 0x0f
// 0x10 range - bit ops
AND = 0x10
@ -37,7 +43,10 @@ const (
CALLVALUE = 0x34
CALLDATALOAD = 0x35
CALLDATASIZE = 0x36
GASPRICE = 0x37
CALLDATACOPY = 0x37
CODESIZE = 0x38
CODECOPY = 0x39
GASPRICE = 0x3a
// 0x40 range - block operations
PREVHASH = 0x40
@ -48,18 +57,19 @@ const (
GASLIMIT = 0x45
// 0x50 range - 'storage' and execution
POP = 0x51
DUP = 0x52
SWAP = 0x53
MLOAD = 0x54
MSTORE = 0x55
MSTORE8 = 0x56
SLOAD = 0x57
SSTORE = 0x58
JUMP = 0x59
JUMPI = 0x5a
PC = 0x5b
MSIZE = 0x5c
POP = 0x50
DUP = 0x51
SWAP = 0x52
MLOAD = 0x53
MSTORE = 0x54
MSTORE8 = 0x55
SLOAD = 0x56
SSTORE = 0x57
JUMP = 0x58
JUMPI = 0x59
PC = 0x5a
MSIZE = 0x5b
GAS = 0x5c
// 0x60 range
PUSH1 = 0x60
@ -120,6 +130,8 @@ var opCodeToString = map[OpCode]string{
NEG: "NEG",
LT: "LT",
GT: "GT",
SLT: "SLT",
SGT: "SGT",
EQ: "EQ",
NOT: "NOT",
@ -140,6 +152,9 @@ var opCodeToString = map[OpCode]string{
CALLVALUE: "CALLVALUE",
CALLDATALOAD: "CALLDATALOAD",
CALLDATASIZE: "CALLDATASIZE",
CALLDATACOPY: "CALLDATACOPY",
CODESIZE: "CODESIZE",
CODECOPY: "CODECOPY",
GASPRICE: "TXGASPRICE",
// 0x40 range - block operations
@ -162,6 +177,7 @@ var opCodeToString = map[OpCode]string{
JUMPI: "JUMPI",
PC: "PC",
MSIZE: "MSIZE",
GAS: "GAS",
// 0x60 range - push
PUSH1: "PUSH1",
@ -208,7 +224,12 @@ var opCodeToString = map[OpCode]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

View File

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/ethereum/eth-go/ethutil"
_ "github.com/obscuren/secp256k1-go"
"math"
_ "math"
"math/big"
)
@ -18,6 +19,7 @@ var (
GasCreate = big.NewInt(100)
GasCall = big.NewInt(20)
GasMemory = big.NewInt(1)
GasData = big.NewInt(5)
GasTx = big.NewInt(500)
)
@ -112,15 +114,18 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
}
gas := new(big.Int)
setStepGasUsage := func(amount *big.Int) {
addStepGasUsage := func(amount *big.Int) {
gas.Add(gas, amount)
}
addStepGasUsage(GasStep)
var newMemSize uint64 = 0
switch op {
case SHA3:
setStepGasUsage(GasSha)
case STOP:
case SUICIDE:
case SLOAD:
setStepGasUsage(GasSLoad)
gas.Set(GasSLoad)
case SSTORE:
var mult *big.Int
y, x := stack.Peekn()
@ -132,22 +137,55 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
} else {
mult = ethutil.Big1
}
setStepGasUsage(new(big.Int).Mul(mult, GasSStore))
gas = new(big.Int).Mul(mult, GasSStore)
case BALANCE:
setStepGasUsage(GasBalance)
case CREATE:
gas.Set(GasBalance)
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)
args := stack.Get(big.NewInt(3))
initSize := new(big.Int).Add(args[1], args[0])
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64()
case CODECOPY:
require(3)
setStepGasUsage(CalculateTxGas(initSize))
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64()
case CALL:
setStepGasUsage(GasCall)
case MLOAD, MSIZE, MSTORE8, MSTORE:
setStepGasUsage(GasMemory)
default:
setStepGasUsage(GasStep)
require(7)
gas.Set(GasCall)
addStepGasUsage(stack.data[stack.Len()-2])
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) {
@ -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)
}
mem.Resize(newMemSize)
switch op {
case LOG:
stack.Print()
@ -320,7 +360,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
case ORIGIN:
stack.Push(ethutil.BigD(vm.vars.Origin))
case CALLER:
stack.Push(ethutil.BigD(closure.Callee().Address()))
stack.Push(ethutil.BigD(closure.caller.Address()))
case CALLVALUE:
stack.Push(vm.vars.Value)
case CALLDATALOAD:
@ -337,6 +377,26 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
stack.Push(ethutil.BigD(data))
case CALLDATASIZE:
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:
stack.Push(closure.Price)
@ -423,6 +483,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
stack.Push(pc)
case MSIZE:
stack.Push(big.NewInt(int64(mem.Len())))
case GAS:
stack.Push(closure.Gas)
// 0x60 range
case CREATE:
require(3)
@ -435,7 +497,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
snapshot := vm.state.Snapshot()
// 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
contract := NewContract(addr, value, []byte(""))
// 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
// contract so it may run the init script
gas := new(big.Int).Set(closure.Gas)
closure.UseGas(gas)
//closure.UseGas(gas)
// Create the closure
c := NewClosure(closure.callee,
c := NewClosure(closure.caller,
closure.Object(),
contract.initScript,
vm.state,
@ -467,6 +529,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
vm.state.UpdateStateObject(contract)
}
case CALL:
// TODO RE-WRITE
require(7)
// Closure addr
addr := stack.Pop()
@ -476,51 +539,44 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro
inSize, inOffset := stack.Popn()
// Pop return size and offset
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
args := mem.Get(inOffset.Int64(), inSize.Int64())
snapshot := vm.state.Snapshot()
// Fetch the contract which will serve as the closure body
contract := vm.state.GetStateObject(addr.Bytes())
closure.object.Nonce += 1
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)
} 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:
require(2)

View File

@ -136,11 +136,21 @@ func (self *Miner) mineNewBlock() {
// Sort the transactions by nonce in case of odd network propagation
sort.Sort(ethchain.TxByNonce{self.txs})
// Accumulate all valid transaction and apply them to the new state
receipts, txs := stateManager.ApplyTransactions(self.block.State(), self.block, self.txs)
self.txs = txs
// Error may be ignored. It's not important during mining
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
self.block.SetReceipts(receipts, txs)
// Accumulate the rewards included for this block
stateManager.AccumelateRewards(self.block.State(), self.block)
@ -155,6 +165,7 @@ func (self *Miner) mineNewBlock() {
} else {
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.Infoln(self.block)
// Gather the new batch of transactions currently in the tx pool
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 {
recp := new(big.Int).SetBytes([]byte(name))
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) {
@ -193,8 +197,6 @@ func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, sc
if contractCreation {
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

View File

@ -46,6 +46,8 @@ type PBlock struct {
Transactions string `json:"transactions"`
Time int64 `json:"time"`
Coinbase string `json:"coinbase"`
GasLimit string `json:"gasLimit"`
GasUsed string `json:"gasUsed"`
}
// Creates a new QML Block from a chain block
@ -64,7 +66,7 @@ func NewPBlock(block *ethchain.Block) *PBlock {
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 {
@ -109,11 +111,12 @@ func NewPTx(tx *ethchain.Transaction) *PTx {
sender := hex.EncodeToString(tx.Sender())
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: isContract, 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: tx.CreatesContract(), Gas: tx.Gas.String(), GasPrice: tx.GasPrice.String(), Data: data, Sender: sender, CreatesContract: createsContract, RawData: hex.EncodeToString(tx.Data)}
}
func (self *PTx) ToString() string {

View File

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

View File

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

View File

@ -75,11 +75,11 @@ func ReadConfig(base string, logTypes LoggerType, g *globalconf.GlobalConf, id s
if Config == nil {
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.Identifier = id
Config.Log = NewLogger(logTypes, LogLevelDebug)
Config.SetClientString("/Ethereum(G)")
Config.SetClientString("Ethereum(G)")
}
return Config
@ -88,11 +88,9 @@ func ReadConfig(base string, logTypes LoggerType, g *globalconf.GlobalConf, id s
// Set client string
//
func (c *config) SetClientString(str string) {
id := runtime.GOOS
if len(c.Identifier) > 0 {
id = c.Identifier
}
Config.ClientString = fmt.Sprintf("%s nv%s/%s", str, c.Ver, id)
os := runtime.GOOS
cust := c.Identifier
Config.ClientString = fmt.Sprintf("%s/v%s/%s/%s/Go", str, c.Ver, cust, os)
}
func (c *config) SetIdentifier(id string) {
@ -145,12 +143,17 @@ func NewLogger(flag LoggerType, level int) *Logger {
return &Logger{logSys: loggers, logLevel: level}
}
func (self *Logger) SetLevel(level int) {
self.logLevel = level
}
func (log *Logger) AddLogSystem(logger LogSystem) {
log.logSys = append(log.logSys, logger)
}
const (
LogLevelDebug = iota
LogLevelSystem = iota
LogLevelDebug
LogLevelInfo
)
@ -206,6 +209,26 @@ func (log *Logger) Fatal(v ...interface{}) {
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 {
value string
}

View File

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

View File

@ -11,6 +11,7 @@ import (
type RlpEncodable interface {
RlpEncode() []byte
RlpValue() []interface{}
}
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
import (
@ -9,11 +11,13 @@ import (
"time"
)
// Message:
// [4 bytes token] RLP([TYPE, DATA])
// Refer to http://wiki.ethereum.org/index.php/Wire_Protocol
// Connection interface describing the methods required to implement the wire protocol.
type Conn interface {
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}
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) {
if len(data) == 0 {
return nil, nil, true, nil

30
peer.go
View File

@ -17,7 +17,9 @@ const (
// The size of the output buffer for writing messages
outputBufferSize = 50
// Current protocol version
ProtocolVersion = 17
ProtocolVersion = 20
// Interval for ping/pong message
pingPongTimer = 2 * time.Second
)
type DiscReason byte
@ -151,11 +153,11 @@ func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer {
pubkey: pubkey,
blocksRequested: 10,
caps: ethereum.ServerCaps(),
version: ethutil.Config.ClientString,
}
}
func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
p := &Peer{
outputQueue: make(chan *ethwire.Msg, outputBufferSize),
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)
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
p.Stop()
return
@ -253,7 +257,7 @@ func (p *Peer) writeMessage(msg *ethwire.Msg) {
// Outbound message handler. Outbound messages are handled here
func (p *Peer) HandleOutbound() {
// 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)
out:
@ -264,8 +268,14 @@ out:
p.writeMessage(msg)
p.lastSend = time.Now()
// Ping timer sends a ping to the peer each 2 minutes
// Ping timer
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.pingStartTime = time.Now()
@ -307,6 +317,8 @@ func (p *Peer) HandleInbound() {
ethutil.Config.Log.Debugln(err)
}
for _, msg := range msgs {
ethutil.Config.Log.Println(ethutil.LogLevelSystem, "=>", msg.Type, msg.Data)
switch msg.Type {
case ethwire.MsgHandshakeTy:
// Version message
@ -393,7 +405,7 @@ func (p *Peer) HandleInbound() {
if err != nil {
// If the parent is unknown try to catch up with this peer
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.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
} else if ethchain.IsValidationErr(err) {
@ -405,7 +417,7 @@ func (p *Peer) HandleInbound() {
if p.catchingUp && msg.Data.Len() > 1 {
if lastBlock != nil {
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
@ -571,7 +583,7 @@ func (p *Peer) pushHandshake() error {
pubkey := keyRing.PublicKey
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)
@ -603,7 +615,7 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
c := msg.Data
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()
return
}