mirror of
https://github.com/arnaucube/go-ethereum.git
synced 2026-02-28 05:56:45 +01:00
Renamed chain => core
This commit is contained in:
12
core/.gitignore
vendored
Normal file
12
core/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
#
|
||||
# If you find yourself ignoring temporary files generated by your text editor
|
||||
# or operating system, you probably want to add a global ignore instead:
|
||||
# git config --global core.excludesfile ~/.gitignore_global
|
||||
|
||||
/tmp
|
||||
*/**/*un~
|
||||
*un~
|
||||
.DS_Store
|
||||
*/**/.DS_Store
|
||||
|
||||
50
core/asm.go
Normal file
50
core/asm.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/vm"
|
||||
)
|
||||
|
||||
func Disassemble(script []byte) (asm []string) {
|
||||
pc := new(big.Int)
|
||||
for {
|
||||
if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the memory location of pc
|
||||
val := script[pc.Int64()]
|
||||
// Get the opcode (it must be an opcode!)
|
||||
op := vm.OpCode(val)
|
||||
|
||||
asm = append(asm, fmt.Sprintf("%04v: %v", pc, op))
|
||||
|
||||
switch op {
|
||||
case vm.PUSH1, vm.PUSH2, vm.PUSH3, vm.PUSH4, vm.PUSH5, vm.PUSH6, vm.PUSH7, vm.PUSH8,
|
||||
vm.PUSH9, vm.PUSH10, vm.PUSH11, vm.PUSH12, vm.PUSH13, vm.PUSH14, vm.PUSH15,
|
||||
vm.PUSH16, vm.PUSH17, vm.PUSH18, vm.PUSH19, vm.PUSH20, vm.PUSH21, vm.PUSH22,
|
||||
vm.PUSH23, vm.PUSH24, vm.PUSH25, vm.PUSH26, vm.PUSH27, vm.PUSH28, vm.PUSH29,
|
||||
vm.PUSH30, vm.PUSH31, vm.PUSH32:
|
||||
pc.Add(pc, ethutil.Big1)
|
||||
a := int64(op) - int64(vm.PUSH1) + 1
|
||||
if int(pc.Int64()+a) > len(script) {
|
||||
return
|
||||
}
|
||||
|
||||
data := script[pc.Int64() : pc.Int64()+a]
|
||||
if len(data) == 0 {
|
||||
data = []byte{0}
|
||||
}
|
||||
asm = append(asm, fmt.Sprintf("%04v: 0x%x", pc, data))
|
||||
|
||||
pc.Add(pc, big.NewInt(a-1))
|
||||
}
|
||||
|
||||
pc.Add(pc, ethutil.Big1)
|
||||
}
|
||||
|
||||
return asm
|
||||
}
|
||||
414
core/block_manager.go
Normal file
414
core/block_manager.go
Normal file
@@ -0,0 +1,414 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
)
|
||||
|
||||
var statelogger = logger.NewLogger("BLOCK")
|
||||
|
||||
type Peer interface {
|
||||
Inbound() bool
|
||||
LastSend() time.Time
|
||||
LastPong() int64
|
||||
Host() []byte
|
||||
Port() uint16
|
||||
Version() string
|
||||
PingTime() string
|
||||
Connected() *int32
|
||||
Caps() *ethutil.Value
|
||||
}
|
||||
|
||||
type EthManager interface {
|
||||
BlockManager() *BlockManager
|
||||
ChainManager() *ChainManager
|
||||
TxPool() *TxPool
|
||||
Broadcast(msgType wire.MsgType, data []interface{})
|
||||
PeerCount() int
|
||||
IsMining() bool
|
||||
IsListening() bool
|
||||
Peers() *list.List
|
||||
KeyManager() *crypto.KeyManager
|
||||
ClientIdentity() wire.ClientIdentity
|
||||
Db() ethutil.Database
|
||||
EventMux() *event.TypeMux
|
||||
}
|
||||
|
||||
type BlockManager struct {
|
||||
// Mutex for locking the block processor. Blocks can only be handled one at a time
|
||||
mutex sync.Mutex
|
||||
// Canonical block chain
|
||||
bc *ChainManager
|
||||
// non-persistent key/value memory storage
|
||||
mem map[string]*big.Int
|
||||
// Proof of work used for validating
|
||||
Pow PoW
|
||||
// The ethereum manager interface
|
||||
eth EthManager
|
||||
// The managed states
|
||||
// Transiently state. The trans state isn't ever saved, validated and
|
||||
// it could be used for setting account nonces without effecting
|
||||
// the main states.
|
||||
transState *state.State
|
||||
// Mining state. The mining state is used purely and solely by the mining
|
||||
// operation.
|
||||
miningState *state.State
|
||||
|
||||
// The last attempted block is mainly used for debugging purposes
|
||||
// This does not have to be a valid block and will be set during
|
||||
// 'Process' & canonical validation.
|
||||
lastAttemptedBlock *types.Block
|
||||
|
||||
events event.Subscription
|
||||
}
|
||||
|
||||
func NewBlockManager(ethereum EthManager) *BlockManager {
|
||||
sm := &BlockManager{
|
||||
mem: make(map[string]*big.Int),
|
||||
Pow: &EasyPow{},
|
||||
eth: ethereum,
|
||||
bc: ethereum.ChainManager(),
|
||||
}
|
||||
sm.transState = ethereum.ChainManager().CurrentBlock.State().Copy()
|
||||
sm.miningState = ethereum.ChainManager().CurrentBlock.State().Copy()
|
||||
|
||||
return sm
|
||||
}
|
||||
|
||||
func (self *BlockManager) Start() {
|
||||
statelogger.Debugln("Starting block manager")
|
||||
}
|
||||
|
||||
func (self *BlockManager) Stop() {
|
||||
statelogger.Debugln("Stopping state manager")
|
||||
}
|
||||
|
||||
func (sm *BlockManager) CurrentState() *state.State {
|
||||
return sm.eth.ChainManager().CurrentBlock.State()
|
||||
}
|
||||
|
||||
func (sm *BlockManager) TransState() *state.State {
|
||||
return sm.transState
|
||||
}
|
||||
|
||||
func (sm *BlockManager) MiningState() *state.State {
|
||||
return sm.miningState
|
||||
}
|
||||
|
||||
func (sm *BlockManager) NewMiningState() *state.State {
|
||||
sm.miningState = sm.eth.ChainManager().CurrentBlock.State().Copy()
|
||||
|
||||
return sm.miningState
|
||||
}
|
||||
|
||||
func (sm *BlockManager) ChainManager() *ChainManager {
|
||||
return sm.bc
|
||||
}
|
||||
|
||||
func (sm *BlockManager) TransitionState(statedb *state.State, parent, block *types.Block) (receipts types.Receipts, err error) {
|
||||
coinbase := statedb.GetOrNewStateObject(block.Coinbase)
|
||||
coinbase.SetGasPool(block.CalcGasLimit(parent))
|
||||
|
||||
// Process the transactions on to current block
|
||||
receipts, _, _, _, err = sm.ProcessTransactions(coinbase, statedb, block, parent, block.Transactions())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return receipts, nil
|
||||
}
|
||||
|
||||
func (self *BlockManager) ProcessTransactions(coinbase *state.StateObject, state *state.State, block, parent *types.Block, txs types.Transactions) (types.Receipts, types.Transactions, types.Transactions, types.Transactions, error) {
|
||||
var (
|
||||
receipts types.Receipts
|
||||
handled, unhandled types.Transactions
|
||||
erroneous types.Transactions
|
||||
totalUsedGas = big.NewInt(0)
|
||||
err error
|
||||
cumulativeSum = new(big.Int)
|
||||
)
|
||||
|
||||
done:
|
||||
for i, tx := range txs {
|
||||
// If we are mining this block and validating we want to set the logs back to 0
|
||||
state.EmptyLogs()
|
||||
|
||||
txGas := new(big.Int).Set(tx.Gas)
|
||||
|
||||
cb := state.GetStateObject(coinbase.Address())
|
||||
st := NewStateTransition(cb, tx, state, block)
|
||||
err = st.TransitionState()
|
||||
if err != nil {
|
||||
statelogger.Infoln(err)
|
||||
switch {
|
||||
case IsNonceErr(err):
|
||||
err = nil // ignore error
|
||||
continue
|
||||
case IsGasLimitErr(err):
|
||||
unhandled = txs[i:]
|
||||
|
||||
break done
|
||||
default:
|
||||
statelogger.Infoln(err)
|
||||
erroneous = append(erroneous, tx)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
txGas.Sub(txGas, st.gas)
|
||||
cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice))
|
||||
|
||||
// Update the state with pending changes
|
||||
state.Update(txGas)
|
||||
|
||||
cumulative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, txGas))
|
||||
receipt := types.NewReceipt(state.Root(), cumulative)
|
||||
receipt.SetLogs(state.Logs())
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||
|
||||
// Notify all subscribers
|
||||
go self.eth.EventMux().Post(TxPostEvent{tx})
|
||||
|
||||
receipts = append(receipts, receipt)
|
||||
handled = append(handled, tx)
|
||||
|
||||
if ethutil.Config.Diff && ethutil.Config.DiffType == "all" {
|
||||
state.CreateOutputForDiff()
|
||||
}
|
||||
}
|
||||
|
||||
block.Reward = cumulativeSum
|
||||
block.GasUsed = totalUsedGas
|
||||
|
||||
return receipts, handled, unhandled, erroneous, err
|
||||
}
|
||||
|
||||
func (sm *BlockManager) Process(block *types.Block) (td *big.Int, msgs state.Messages, err error) {
|
||||
// Processing a blocks may never happen simultaneously
|
||||
sm.mutex.Lock()
|
||||
defer sm.mutex.Unlock()
|
||||
|
||||
if sm.bc.HasBlock(block.Hash()) {
|
||||
return nil, nil, &KnownBlockError{block.Number, block.Hash()}
|
||||
}
|
||||
|
||||
if !sm.bc.HasBlock(block.PrevHash) {
|
||||
return nil, nil, ParentError(block.PrevHash)
|
||||
}
|
||||
parent := sm.bc.GetBlock(block.PrevHash)
|
||||
|
||||
return sm.ProcessWithParent(block, parent)
|
||||
}
|
||||
|
||||
func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) {
|
||||
sm.lastAttemptedBlock = block
|
||||
|
||||
state := parent.State().Copy()
|
||||
|
||||
// Defer the Undo on the Trie. If the block processing happened
|
||||
// we don't want to undo but since undo only happens on dirty
|
||||
// nodes this won't happen because Commit would have been called
|
||||
// before that.
|
||||
defer state.Reset()
|
||||
|
||||
if ethutil.Config.Diff && ethutil.Config.DiffType == "all" {
|
||||
fmt.Printf("## %x %x ##\n", block.Hash(), block.Number)
|
||||
}
|
||||
|
||||
_, err = sm.TransitionState(state, parent, block)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
txSha := types.DeriveSha(block.Transactions())
|
||||
if bytes.Compare(txSha, block.TxSha) != 0 {
|
||||
err = fmt.Errorf("validating transaction root. received=%x got=%x", block.TxSha, txSha)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
receiptSha := types.DeriveSha(receipts)
|
||||
if bytes.Compare(receiptSha, block.ReceiptSha) != 0 {
|
||||
err = fmt.Errorf("validating receipt root. received=%x got=%x", block.ReceiptSha, receiptSha)
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
// Block validation
|
||||
if err = sm.ValidateBlock(block, parent); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = sm.AccumelateRewards(state, block, parent); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
//block.receipts = receipts // although this isn't necessary it be in the future
|
||||
rbloom := types.CreateBloom(receipts)
|
||||
if bytes.Compare(rbloom, block.LogsBloom) != 0 {
|
||||
err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
state.Update(ethutil.Big0)
|
||||
|
||||
if !block.State().Cmp(state) {
|
||||
err = fmt.Errorf("invalid merkle root. received=%x got=%x", block.Root(), state.Root())
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate the new total difficulty and sync back to the db
|
||||
if td, ok := sm.CalculateTD(block); ok {
|
||||
// Sync the current block's state to the database and cancelling out the deferred Undo
|
||||
state.Sync()
|
||||
|
||||
messages := state.Manifest().Messages
|
||||
state.Manifest().Reset()
|
||||
|
||||
chainlogger.Infof("Processed block #%d (%x...)\n", block.Number, block.Hash()[0:4])
|
||||
|
||||
sm.transState = state.Copy()
|
||||
|
||||
sm.eth.TxPool().RemoveSet(block.Transactions())
|
||||
|
||||
return td, messages, nil
|
||||
} else {
|
||||
return nil, nil, errors.New("total diff failed")
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) {
|
||||
uncleDiff := new(big.Int)
|
||||
for _, uncle := range block.Uncles {
|
||||
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
|
||||
}
|
||||
|
||||
// TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
|
||||
td := new(big.Int)
|
||||
td = td.Add(sm.bc.TD, uncleDiff)
|
||||
td = td.Add(td, block.Difficulty)
|
||||
|
||||
// The new TD will only be accepted if the new difficulty is
|
||||
// is greater than the previous.
|
||||
if td.Cmp(sm.bc.TD) > 0 {
|
||||
return td, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Validates the current block. Returns an error if the block was invalid,
|
||||
// an uncle or anything that isn't on the current block chain.
|
||||
// Validation validates easy over difficult (dagger takes longer time = difficult)
|
||||
func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
|
||||
expd := CalcDifficulty(block, parent)
|
||||
if expd.Cmp(block.Difficulty) < 0 {
|
||||
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
|
||||
}
|
||||
|
||||
diff := block.Time - parent.Time
|
||||
if diff < 0 {
|
||||
return ValidationError("Block timestamp less then prev block %v (%v - %v)", diff, block.Time, sm.bc.CurrentBlock.Time)
|
||||
}
|
||||
|
||||
/* XXX
|
||||
// New blocks must be within the 15 minute range of the last block.
|
||||
if diff > int64(15*time.Minute) {
|
||||
return ValidationError("Block is too far in the future of last block (> 15 minutes)")
|
||||
}
|
||||
*/
|
||||
|
||||
// Verify the nonce of the block. Return an error if it's not valid
|
||||
if !sm.Pow.Verify(block.HashNoNonce(), block.Difficulty, block.Nonce) {
|
||||
return ValidationError("Block's nonce is invalid (= %v)", ethutil.Bytes2Hex(block.Nonce))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sm *BlockManager) AccumelateRewards(statedb *state.State, block, parent *types.Block) error {
|
||||
reward := new(big.Int).Set(BlockReward)
|
||||
|
||||
knownUncles := ethutil.Set(parent.Uncles)
|
||||
nonces := ethutil.NewSet(block.Nonce)
|
||||
for _, uncle := range block.Uncles {
|
||||
if nonces.Include(uncle.Nonce) {
|
||||
// Error not unique
|
||||
return UncleError("Uncle not unique")
|
||||
}
|
||||
|
||||
uncleParent := sm.bc.GetBlock(uncle.PrevHash)
|
||||
if uncleParent == nil {
|
||||
return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.PrevHash[0:4]))
|
||||
}
|
||||
|
||||
if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 {
|
||||
return UncleError("Uncle too old")
|
||||
}
|
||||
|
||||
if knownUncles.Include(uncle.Hash()) {
|
||||
return UncleError("Uncle in chain")
|
||||
}
|
||||
|
||||
nonces.Insert(uncle.Nonce)
|
||||
|
||||
r := new(big.Int)
|
||||
r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16))
|
||||
|
||||
uncleAccount := statedb.GetAccount(uncle.Coinbase)
|
||||
uncleAccount.AddAmount(r)
|
||||
|
||||
reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
|
||||
}
|
||||
|
||||
// Get the account associated with the coinbase
|
||||
account := statedb.GetAccount(block.Coinbase)
|
||||
// Reward amount of ether to the coinbase address
|
||||
account.AddAmount(reward)
|
||||
|
||||
statedb.Manifest().AddMessage(&state.Message{
|
||||
To: block.Coinbase, From: block.Coinbase,
|
||||
Input: nil,
|
||||
Origin: nil,
|
||||
Block: block.Hash(), Timestamp: block.Time, Coinbase: block.Coinbase, Number: block.Number,
|
||||
Value: new(big.Int).Add(reward, block.Reward),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sm *BlockManager) GetMessages(block *types.Block) (messages []*state.Message, err error) {
|
||||
if !sm.bc.HasBlock(block.PrevHash) {
|
||||
return nil, ParentError(block.PrevHash)
|
||||
}
|
||||
|
||||
sm.lastAttemptedBlock = block
|
||||
|
||||
var (
|
||||
parent = sm.bc.GetBlock(block.PrevHash)
|
||||
state = parent.State().Copy()
|
||||
)
|
||||
|
||||
defer state.Reset()
|
||||
|
||||
sm.TransitionState(state, parent, block)
|
||||
sm.AccumelateRewards(state, block, parent)
|
||||
|
||||
return state.Manifest().Messages, nil
|
||||
}
|
||||
276
core/chain_manager.go
Normal file
276
core/chain_manager.go
Normal file
@@ -0,0 +1,276 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
)
|
||||
|
||||
var chainlogger = logger.NewLogger("CHAIN")
|
||||
|
||||
func AddTestNetFunds(block *types.Block) {
|
||||
for _, addr := range []string{
|
||||
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
||||
"e4157b34ea9615cfbde6b4fda419828124b70c78",
|
||||
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
|
||||
"6c386a4b26f73c802f34673f7248bb118f97424a",
|
||||
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
|
||||
"2ef47100e0787b915105fd5e3f4ff6752079d5cb",
|
||||
"e6716f9544a56c530d868e4bfbacb172315bdead",
|
||||
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
|
||||
} {
|
||||
codedAddr := ethutil.Hex2Bytes(addr)
|
||||
account := block.State().GetAccount(codedAddr)
|
||||
account.SetBalance(ethutil.Big("1606938044258990275541962092341162602522202993782792835301376")) //ethutil.BigPow(2, 200)
|
||||
block.State().UpdateStateObject(account)
|
||||
}
|
||||
}
|
||||
|
||||
func CalcDifficulty(block, parent *types.Block) *big.Int {
|
||||
diff := new(big.Int)
|
||||
|
||||
adjust := new(big.Int).Rsh(parent.Difficulty, 10)
|
||||
if block.Time >= parent.Time+5 {
|
||||
diff.Sub(parent.Difficulty, adjust)
|
||||
} else {
|
||||
diff.Add(parent.Difficulty, adjust)
|
||||
}
|
||||
|
||||
return diff
|
||||
}
|
||||
|
||||
type ChainManager struct {
|
||||
//eth EthManager
|
||||
processor types.BlockProcessor
|
||||
eventMux *event.TypeMux
|
||||
genesisBlock *types.Block
|
||||
// Last known total difficulty
|
||||
TD *big.Int
|
||||
|
||||
LastBlockNumber uint64
|
||||
|
||||
CurrentBlock *types.Block
|
||||
LastBlockHash []byte
|
||||
}
|
||||
|
||||
func NewChainManager(mux *event.TypeMux) *ChainManager {
|
||||
bc := &ChainManager{}
|
||||
bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis))
|
||||
bc.eventMux = mux
|
||||
|
||||
bc.setLastBlock()
|
||||
|
||||
return bc
|
||||
}
|
||||
|
||||
func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
|
||||
self.processor = proc
|
||||
}
|
||||
|
||||
func (bc *ChainManager) setLastBlock() {
|
||||
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
|
||||
if len(data) != 0 {
|
||||
// Prep genesis
|
||||
AddTestNetFunds(bc.genesisBlock)
|
||||
|
||||
block := types.NewBlockFromBytes(data)
|
||||
bc.CurrentBlock = block
|
||||
bc.LastBlockHash = block.Hash()
|
||||
bc.LastBlockNumber = block.Number.Uint64()
|
||||
|
||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
||||
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
|
||||
} else {
|
||||
bc.Reset()
|
||||
}
|
||||
|
||||
chainlogger.Infof("Last block (#%d) %x\n", bc.LastBlockNumber, bc.CurrentBlock.Hash())
|
||||
}
|
||||
|
||||
// Block creation & chain handling
|
||||
func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
|
||||
var root interface{}
|
||||
hash := ZeroHash256
|
||||
|
||||
if bc.CurrentBlock != nil {
|
||||
root = bc.CurrentBlock.Root()
|
||||
hash = bc.LastBlockHash
|
||||
}
|
||||
|
||||
block := types.CreateBlock(
|
||||
root,
|
||||
hash,
|
||||
coinbase,
|
||||
ethutil.BigPow(2, 32),
|
||||
nil,
|
||||
"")
|
||||
|
||||
parent := bc.CurrentBlock
|
||||
if parent != nil {
|
||||
block.Difficulty = CalcDifficulty(block, parent)
|
||||
block.Number = new(big.Int).Add(bc.CurrentBlock.Number, ethutil.Big1)
|
||||
block.GasLimit = block.CalcGasLimit(bc.CurrentBlock)
|
||||
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func (bc *ChainManager) Reset() {
|
||||
AddTestNetFunds(bc.genesisBlock)
|
||||
|
||||
bc.genesisBlock.Trie().Sync()
|
||||
// Prepare the genesis block
|
||||
bc.add(bc.genesisBlock)
|
||||
bc.CurrentBlock = bc.genesisBlock
|
||||
|
||||
bc.SetTotalDifficulty(ethutil.Big("0"))
|
||||
|
||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
||||
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
|
||||
}
|
||||
|
||||
// Add a block to the chain and record addition information
|
||||
func (bc *ChainManager) add(block *types.Block) {
|
||||
bc.writeBlockInfo(block)
|
||||
|
||||
bc.CurrentBlock = block
|
||||
bc.LastBlockHash = block.Hash()
|
||||
|
||||
encodedBlock := block.RlpEncode()
|
||||
ethutil.Config.Db.Put(block.Hash(), encodedBlock)
|
||||
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
|
||||
|
||||
//chainlogger.Infof("Imported block #%d (%x...)\n", block.Number, block.Hash()[0:4])
|
||||
}
|
||||
|
||||
// Accessors
|
||||
func (bc *ChainManager) Genesis() *types.Block {
|
||||
return bc.genesisBlock
|
||||
}
|
||||
|
||||
// Block fetching methods
|
||||
func (bc *ChainManager) HasBlock(hash []byte) bool {
|
||||
data, _ := ethutil.Config.Db.Get(hash)
|
||||
return len(data) != 0
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain [][]byte) {
|
||||
block := self.GetBlock(hash)
|
||||
if block == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// XXX Could be optimised by using a different database which only holds hashes (i.e., linked list)
|
||||
for i := uint64(0); i < max; i++ {
|
||||
|
||||
chain = append(chain, block.Hash())
|
||||
|
||||
if block.Number.Cmp(ethutil.Big0) <= 0 {
|
||||
break
|
||||
}
|
||||
|
||||
block = self.GetBlock(block.PrevHash)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetBlock(hash []byte) *types.Block {
|
||||
data, _ := ethutil.Config.Db.Get(hash)
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return types.NewBlockFromBytes(data)
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
|
||||
block := self.CurrentBlock
|
||||
for ; block != nil; block = self.GetBlock(block.PrevHash) {
|
||||
if block.Number.Uint64() == num {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if block != nil && block.Number.Uint64() == 0 && num != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func (bc *ChainManager) SetTotalDifficulty(td *big.Int) {
|
||||
ethutil.Config.Db.Put([]byte("LTD"), td.Bytes())
|
||||
bc.TD = td
|
||||
}
|
||||
|
||||
func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
|
||||
parent := self.GetBlock(block.PrevHash)
|
||||
if parent == nil {
|
||||
return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.PrevHash)
|
||||
}
|
||||
|
||||
parentTd := parent.BlockInfo().TD
|
||||
|
||||
uncleDiff := new(big.Int)
|
||||
for _, uncle := range block.Uncles {
|
||||
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
|
||||
}
|
||||
|
||||
td := new(big.Int)
|
||||
td = td.Add(parentTd, uncleDiff)
|
||||
td = td.Add(td, block.Difficulty)
|
||||
|
||||
return td, nil
|
||||
}
|
||||
|
||||
func (bc *ChainManager) BlockInfo(block *types.Block) types.BlockInfo {
|
||||
bi := types.BlockInfo{}
|
||||
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
|
||||
bi.RlpDecode(data)
|
||||
|
||||
return bi
|
||||
}
|
||||
|
||||
// Unexported method for writing extra non-essential block info to the db
|
||||
func (bc *ChainManager) writeBlockInfo(block *types.Block) {
|
||||
bc.LastBlockNumber++
|
||||
bi := types.BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash, TD: bc.TD}
|
||||
|
||||
// For now we use the block hash with the words "info" appended as key
|
||||
ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
|
||||
}
|
||||
|
||||
func (bc *ChainManager) Stop() {
|
||||
if bc.CurrentBlock != nil {
|
||||
chainlogger.Infoln("Stopped")
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ChainManager) InsertChain(chain types.Blocks) error {
|
||||
for _, block := range chain {
|
||||
td, messages, err := self.processor.Process(block)
|
||||
if err != nil {
|
||||
if IsKnownBlockErr(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
chainlogger.Infof("block #%v process failed (%x)\n", block.Number, block.Hash()[:4])
|
||||
chainlogger.Infoln(block)
|
||||
chainlogger.Infoln(err)
|
||||
return err
|
||||
}
|
||||
|
||||
self.add(block)
|
||||
self.SetTotalDifficulty(td)
|
||||
self.eventMux.Post(NewBlockEvent{block})
|
||||
self.eventMux.Post(messages)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
116
core/chain_manager_test.go
Normal file
116
core/chain_manager_test.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/chain/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
var TD *big.Int
|
||||
|
||||
func init() {
|
||||
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
|
||||
ethutil.Config.Db, _ = ethdb.NewMemDatabase()
|
||||
}
|
||||
|
||||
type fakeproc struct {
|
||||
}
|
||||
|
||||
func (self fakeproc) ProcessWithParent(a, b *types.Block) (*big.Int, state.Messages, error) {
|
||||
TD = new(big.Int).Add(TD, big.NewInt(1))
|
||||
return TD, nil, nil
|
||||
}
|
||||
|
||||
func makechain(cman *ChainManager, max int) *BlockChain {
|
||||
blocks := make(types.Blocks, max)
|
||||
for i := 0; i < max; i++ {
|
||||
addr := ethutil.LeftPadBytes([]byte{byte(i)}, 20)
|
||||
block := cman.NewBlock(addr)
|
||||
if i != 0 {
|
||||
cman.CurrentBlock = blocks[i-1]
|
||||
}
|
||||
blocks[i] = block
|
||||
}
|
||||
return NewChain(blocks)
|
||||
}
|
||||
|
||||
func TestLongerFork(t *testing.T) {
|
||||
cman := NewChainManager()
|
||||
cman.SetProcessor(fakeproc{})
|
||||
|
||||
TD = big.NewInt(1)
|
||||
chainA := makechain(cman, 5)
|
||||
|
||||
TD = big.NewInt(1)
|
||||
chainB := makechain(cman, 10)
|
||||
|
||||
td, err := cman.TestChain(chainA)
|
||||
if err != nil {
|
||||
t.Error("unable to create new TD from chainA:", err)
|
||||
}
|
||||
cman.TD = td
|
||||
|
||||
_, err = cman.TestChain(chainB)
|
||||
if err != nil {
|
||||
t.Error("expected chainB not to give errors:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEqualFork(t *testing.T) {
|
||||
cman := NewChainManager()
|
||||
cman.SetProcessor(fakeproc{})
|
||||
|
||||
TD = big.NewInt(1)
|
||||
chainA := makechain(cman, 5)
|
||||
|
||||
TD = big.NewInt(2)
|
||||
chainB := makechain(cman, 5)
|
||||
|
||||
td, err := cman.TestChain(chainA)
|
||||
if err != nil {
|
||||
t.Error("unable to create new TD from chainA:", err)
|
||||
}
|
||||
cman.TD = td
|
||||
|
||||
_, err = cman.TestChain(chainB)
|
||||
if err != nil {
|
||||
t.Error("expected chainB not to give errors:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBrokenChain(t *testing.T) {
|
||||
cman := NewChainManager()
|
||||
cman.SetProcessor(fakeproc{})
|
||||
|
||||
TD = big.NewInt(1)
|
||||
chain := makechain(cman, 5)
|
||||
chain.Remove(chain.Front())
|
||||
|
||||
_, err := cman.TestChain(chain)
|
||||
if err == nil {
|
||||
t.Error("expected broken chain to return error")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkChainTesting(b *testing.B) {
|
||||
const chainlen = 1000
|
||||
|
||||
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
|
||||
ethutil.Config.Db, _ = ethdb.NewMemDatabase()
|
||||
|
||||
cman := NewChainManager()
|
||||
cman.SetProcessor(fakeproc{})
|
||||
|
||||
TD = big.NewInt(1)
|
||||
chain := makechain(cman, chainlen)
|
||||
|
||||
stime := time.Now()
|
||||
cman.TestChain(chain)
|
||||
fmt.Println(chainlen, "took", time.Since(stime))
|
||||
}
|
||||
245
core/dagger.go
Normal file
245
core/dagger.go
Normal file
@@ -0,0 +1,245 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/obscuren/sha3"
|
||||
)
|
||||
|
||||
var powlogger = logger.NewLogger("POW")
|
||||
|
||||
type PoW interface {
|
||||
Search(block *types.Block, stop <-chan struct{}) []byte
|
||||
Verify(hash []byte, diff *big.Int, nonce []byte) bool
|
||||
GetHashrate() int64
|
||||
Turbo(bool)
|
||||
}
|
||||
|
||||
type EasyPow struct {
|
||||
hash *big.Int
|
||||
HashRate int64
|
||||
turbo bool
|
||||
}
|
||||
|
||||
func (pow *EasyPow) GetHashrate() int64 {
|
||||
return pow.HashRate
|
||||
}
|
||||
|
||||
func (pow *EasyPow) Turbo(on bool) {
|
||||
pow.turbo = on
|
||||
}
|
||||
|
||||
func (pow *EasyPow) Search(block *types.Block, stop <-chan struct{}) []byte {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
hash := block.HashNoNonce()
|
||||
diff := block.Difficulty
|
||||
i := int64(0)
|
||||
start := time.Now().UnixNano()
|
||||
t := time.Now()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-stop:
|
||||
powlogger.Infoln("Breaking from mining")
|
||||
pow.HashRate = 0
|
||||
return nil
|
||||
default:
|
||||
i++
|
||||
|
||||
if time.Since(t) > (1 * time.Second) {
|
||||
elapsed := time.Now().UnixNano() - start
|
||||
hashes := ((float64(1e9) / float64(elapsed)) * float64(i)) / 1000
|
||||
pow.HashRate = int64(hashes)
|
||||
powlogger.Infoln("Hashing @", pow.HashRate, "khash")
|
||||
|
||||
t = time.Now()
|
||||
}
|
||||
|
||||
sha := crypto.Sha3(big.NewInt(r.Int63()).Bytes())
|
||||
if pow.Verify(hash, diff, sha) {
|
||||
return sha
|
||||
}
|
||||
}
|
||||
|
||||
if !pow.turbo {
|
||||
time.Sleep(20 * time.Microsecond)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pow *EasyPow) Verify(hash []byte, diff *big.Int, nonce []byte) bool {
|
||||
sha := sha3.NewKeccak256()
|
||||
|
||||
d := append(hash, nonce...)
|
||||
sha.Write(d)
|
||||
|
||||
verification := new(big.Int).Div(ethutil.BigPow(2, 256), diff)
|
||||
res := ethutil.U256(ethutil.BigD(sha.Sum(nil)))
|
||||
|
||||
/*
|
||||
fmt.Printf("hash w/o nonce %x\n", hash)
|
||||
fmt.Printf("2**256 / %v = %v\n", diff, verification)
|
||||
fmt.Printf("%v <= %v\n", res, verification)
|
||||
fmt.Printf("vlen: %d rlen: %d\n", len(verification.Bytes()), len(res.Bytes()))
|
||||
*/
|
||||
|
||||
return res.Cmp(verification) <= 0
|
||||
}
|
||||
|
||||
func (pow *EasyPow) SetHash(hash *big.Int) {
|
||||
}
|
||||
|
||||
type Dagger struct {
|
||||
hash *big.Int
|
||||
xn *big.Int
|
||||
}
|
||||
|
||||
var Found bool
|
||||
|
||||
func (dag *Dagger) Find(obj *big.Int, resChan chan int64) {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
rnd := r.Int63()
|
||||
|
||||
res := dag.Eval(big.NewInt(rnd))
|
||||
powlogger.Infof("rnd %v\nres %v\nobj %v\n", rnd, res, obj)
|
||||
if res.Cmp(obj) < 0 {
|
||||
// Post back result on the channel
|
||||
resChan <- rnd
|
||||
// Notify other threads we've found a valid nonce
|
||||
Found = true
|
||||
}
|
||||
|
||||
// Break out if found
|
||||
if Found {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
resChan <- 0
|
||||
}
|
||||
|
||||
func (dag *Dagger) Search(hash, diff *big.Int) *big.Int {
|
||||
// TODO fix multi threading. Somehow it results in the wrong nonce
|
||||
amountOfRoutines := 1
|
||||
|
||||
dag.hash = hash
|
||||
|
||||
obj := ethutil.BigPow(2, 256)
|
||||
obj = obj.Div(obj, diff)
|
||||
|
||||
Found = false
|
||||
resChan := make(chan int64, 3)
|
||||
var res int64
|
||||
|
||||
for k := 0; k < amountOfRoutines; k++ {
|
||||
go dag.Find(obj, resChan)
|
||||
|
||||
// Wait for each go routine to finish
|
||||
}
|
||||
for k := 0; k < amountOfRoutines; k++ {
|
||||
// Get the result from the channel. 0 = quit
|
||||
if r := <-resChan; r != 0 {
|
||||
res = r
|
||||
}
|
||||
}
|
||||
|
||||
return big.NewInt(res)
|
||||
}
|
||||
|
||||
func (dag *Dagger) Verify(hash, diff, nonce *big.Int) bool {
|
||||
dag.hash = hash
|
||||
|
||||
obj := ethutil.BigPow(2, 256)
|
||||
obj = obj.Div(obj, diff)
|
||||
|
||||
return dag.Eval(nonce).Cmp(obj) < 0
|
||||
}
|
||||
|
||||
func DaggerVerify(hash, diff, nonce *big.Int) bool {
|
||||
dagger := &Dagger{}
|
||||
dagger.hash = hash
|
||||
|
||||
obj := ethutil.BigPow(2, 256)
|
||||
obj = obj.Div(obj, diff)
|
||||
|
||||
return dagger.Eval(nonce).Cmp(obj) < 0
|
||||
}
|
||||
|
||||
func (dag *Dagger) Node(L uint64, i uint64) *big.Int {
|
||||
if L == i {
|
||||
return dag.hash
|
||||
}
|
||||
|
||||
var m *big.Int
|
||||
if L == 9 {
|
||||
m = big.NewInt(16)
|
||||
} else {
|
||||
m = big.NewInt(3)
|
||||
}
|
||||
|
||||
sha := sha3.NewKeccak256()
|
||||
sha.Reset()
|
||||
d := sha3.NewKeccak256()
|
||||
b := new(big.Int)
|
||||
ret := new(big.Int)
|
||||
|
||||
for k := 0; k < int(m.Uint64()); k++ {
|
||||
d.Reset()
|
||||
d.Write(dag.hash.Bytes())
|
||||
d.Write(dag.xn.Bytes())
|
||||
d.Write(big.NewInt(int64(L)).Bytes())
|
||||
d.Write(big.NewInt(int64(i)).Bytes())
|
||||
d.Write(big.NewInt(int64(k)).Bytes())
|
||||
|
||||
b.SetBytes(Sum(d))
|
||||
pk := b.Uint64() & ((1 << ((L - 1) * 3)) - 1)
|
||||
sha.Write(dag.Node(L-1, pk).Bytes())
|
||||
}
|
||||
|
||||
ret.SetBytes(Sum(sha))
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func Sum(sha hash.Hash) []byte {
|
||||
//in := make([]byte, 32)
|
||||
return sha.Sum(nil)
|
||||
}
|
||||
|
||||
func (dag *Dagger) Eval(N *big.Int) *big.Int {
|
||||
pow := ethutil.BigPow(2, 26)
|
||||
dag.xn = pow.Div(N, pow)
|
||||
|
||||
sha := sha3.NewKeccak256()
|
||||
sha.Reset()
|
||||
ret := new(big.Int)
|
||||
|
||||
for k := 0; k < 4; k++ {
|
||||
d := sha3.NewKeccak256()
|
||||
b := new(big.Int)
|
||||
|
||||
d.Reset()
|
||||
d.Write(dag.hash.Bytes())
|
||||
d.Write(dag.xn.Bytes())
|
||||
d.Write(N.Bytes())
|
||||
d.Write(big.NewInt(int64(k)).Bytes())
|
||||
|
||||
b.SetBytes(Sum(d))
|
||||
pk := (b.Uint64() & 0x1ffffff)
|
||||
|
||||
sha.Write(dag.Node(9, pk).Bytes())
|
||||
}
|
||||
|
||||
return ret.SetBytes(Sum(sha))
|
||||
}
|
||||
19
core/dagger_test.go
Normal file
19
core/dagger_test.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
)
|
||||
|
||||
func BenchmarkDaggerSearch(b *testing.B) {
|
||||
hash := big.NewInt(0)
|
||||
diff := ethutil.BigPow(2, 36)
|
||||
o := big.NewInt(0) // nonce doesn't matter. We're only testing against speed, not validity
|
||||
|
||||
// Reset timer so the big generation isn't included in the benchmark
|
||||
b.ResetTimer()
|
||||
// Validate
|
||||
DaggerVerify(hash, diff, o)
|
||||
}
|
||||
141
core/error.go
Normal file
141
core/error.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// Parent error. In case a parent is unknown this error will be thrown
|
||||
// by the block manager
|
||||
type ParentErr struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (err *ParentErr) Error() string {
|
||||
return err.Message
|
||||
}
|
||||
|
||||
func ParentError(hash []byte) error {
|
||||
return &ParentErr{Message: fmt.Sprintf("Block's parent unkown %x", hash)}
|
||||
}
|
||||
|
||||
func IsParentErr(err error) bool {
|
||||
_, ok := err.(*ParentErr)
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
type UncleErr struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (err *UncleErr) Error() string {
|
||||
return err.Message
|
||||
}
|
||||
|
||||
func UncleError(str string) error {
|
||||
return &UncleErr{Message: str}
|
||||
}
|
||||
|
||||
func IsUncleErr(err error) bool {
|
||||
_, ok := err.(*UncleErr)
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// Block validation error. If any validation fails, this error will be thrown
|
||||
type ValidationErr struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
func (err *ValidationErr) Error() string {
|
||||
return err.Message
|
||||
}
|
||||
|
||||
func ValidationError(format string, v ...interface{}) *ValidationErr {
|
||||
return &ValidationErr{Message: fmt.Sprintf(format, v...)}
|
||||
}
|
||||
|
||||
func IsValidationErr(err error) bool {
|
||||
_, ok := err.(*ValidationErr)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (err *NonceErr) Error() string {
|
||||
return err.Message
|
||||
}
|
||||
|
||||
func NonceError(is, exp uint64) *NonceErr {
|
||||
return &NonceErr{Message: fmt.Sprintf("Nonce err. Is %d, expected %d", is, exp), Is: is, Exp: exp}
|
||||
}
|
||||
|
||||
func IsNonceErr(err error) bool {
|
||||
_, ok := err.(*NonceErr)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type TDError struct {
|
||||
a, b *big.Int
|
||||
}
|
||||
|
||||
func (self *TDError) Error() string {
|
||||
return fmt.Sprintf("incoming chain has a lower or equal TD (%v <= %v)", self.a, self.b)
|
||||
}
|
||||
func IsTDError(e error) bool {
|
||||
_, ok := e.(*TDError)
|
||||
return ok
|
||||
}
|
||||
|
||||
type KnownBlockError struct {
|
||||
number *big.Int
|
||||
hash []byte
|
||||
}
|
||||
|
||||
func (self *KnownBlockError) Error() string {
|
||||
return fmt.Sprintf("block %v already known (%x)", self.number, self.hash[0:4])
|
||||
}
|
||||
func IsKnownBlockErr(e error) bool {
|
||||
_, ok := e.(*KnownBlockError)
|
||||
return ok
|
||||
}
|
||||
12
core/events.go
Normal file
12
core/events.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package core
|
||||
|
||||
import "github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
// TxPreEvent is posted when a transaction enters the transaction pool.
|
||||
type TxPreEvent struct{ Tx *types.Transaction }
|
||||
|
||||
// TxPostEvent is posted when a transaction has been processed.
|
||||
type TxPostEvent struct{ Tx *types.Transaction }
|
||||
|
||||
// NewBlockEvent is posted when a block has been imported.
|
||||
type NewBlockEvent struct{ Block *types.Block }
|
||||
80
core/execution.go
Normal file
80
core/execution.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/vm"
|
||||
)
|
||||
|
||||
type Execution struct {
|
||||
vm vm.VirtualMachine
|
||||
address, input []byte
|
||||
Gas, price, value *big.Int
|
||||
object *state.StateObject
|
||||
SkipTransfer bool
|
||||
}
|
||||
|
||||
func NewExecution(vm vm.VirtualMachine, address, input []byte, gas, gasPrice, value *big.Int) *Execution {
|
||||
return &Execution{vm: vm, address: address, input: input, Gas: gas, price: gasPrice, value: value}
|
||||
}
|
||||
|
||||
func (self *Execution) Addr() []byte {
|
||||
return self.address
|
||||
}
|
||||
|
||||
func (self *Execution) Call(codeAddr []byte, caller vm.ClosureRef) ([]byte, error) {
|
||||
// Retrieve the executing code
|
||||
code := self.vm.Env().State().GetCode(codeAddr)
|
||||
|
||||
return self.exec(code, codeAddr, caller)
|
||||
}
|
||||
|
||||
func (self *Execution) exec(code, caddr []byte, caller vm.ClosureRef) (ret []byte, err error) {
|
||||
env := self.vm.Env()
|
||||
|
||||
chainlogger.Debugf("pre state %x\n", env.State().Root())
|
||||
snapshot := env.State().Copy()
|
||||
defer func() {
|
||||
if vm.IsDepthErr(err) || vm.IsOOGErr(err) {
|
||||
env.State().Set(snapshot)
|
||||
}
|
||||
chainlogger.Debugf("post state %x\n", env.State().Root())
|
||||
}()
|
||||
|
||||
from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address)
|
||||
// Skipping transfer is used on testing for the initial call
|
||||
if !self.SkipTransfer {
|
||||
err = env.Transfer(from, to, self.value)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
caller.ReturnGas(self.Gas, self.price)
|
||||
|
||||
err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, from.Balance)
|
||||
} else {
|
||||
self.object = to
|
||||
// Pre-compiled contracts (address.go) 1, 2 & 3.
|
||||
naddr := ethutil.BigD(caddr).Uint64()
|
||||
if p := vm.Precompiled[naddr]; p != nil {
|
||||
if self.Gas.Cmp(p.Gas) >= 0 {
|
||||
ret = p.Call(self.input)
|
||||
self.vm.Printf("NATIVE_FUNC(%x) => %x", naddr, ret)
|
||||
self.vm.Endl()
|
||||
}
|
||||
} else {
|
||||
ret, err = self.vm.Run(to, caller, code, self.value, self.Gas, self.price, self.input)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Execution) Create(caller vm.ClosureRef) (ret []byte, err error, account *state.StateObject) {
|
||||
ret, err = self.exec(self.input, nil, caller)
|
||||
account = self.vm.Env().State().GetStateObject(self.address)
|
||||
|
||||
return
|
||||
}
|
||||
7
core/fees.go
Normal file
7
core/fees.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var BlockReward *big.Int = big.NewInt(1.5e+18)
|
||||
200
core/filter.go
Normal file
200
core/filter.go
Normal file
@@ -0,0 +1,200 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
type AccountChange struct {
|
||||
Address, StateAddress []byte
|
||||
}
|
||||
|
||||
// Filtering interface
|
||||
type Filter struct {
|
||||
eth EthManager
|
||||
earliest int64
|
||||
latest int64
|
||||
skip int
|
||||
from, to [][]byte
|
||||
max int
|
||||
|
||||
Altered []AccountChange
|
||||
|
||||
BlockCallback func(*types.Block)
|
||||
MessageCallback func(state.Messages)
|
||||
}
|
||||
|
||||
// Create a new filter which uses a bloom filter on blocks to figure out whether a particular block
|
||||
// is interesting or not.
|
||||
func NewFilter(eth EthManager) *Filter {
|
||||
return &Filter{eth: eth}
|
||||
}
|
||||
|
||||
func (self *Filter) AddAltered(address, stateAddress []byte) {
|
||||
self.Altered = append(self.Altered, AccountChange{address, stateAddress})
|
||||
}
|
||||
|
||||
// Set the earliest and latest block for filtering.
|
||||
// -1 = latest block (i.e., the current block)
|
||||
// hash = particular hash from-to
|
||||
func (self *Filter) SetEarliestBlock(earliest int64) {
|
||||
self.earliest = earliest
|
||||
}
|
||||
|
||||
func (self *Filter) SetLatestBlock(latest int64) {
|
||||
self.latest = latest
|
||||
}
|
||||
|
||||
func (self *Filter) SetFrom(addr [][]byte) {
|
||||
self.from = addr
|
||||
}
|
||||
|
||||
func (self *Filter) AddFrom(addr []byte) {
|
||||
self.from = append(self.from, addr)
|
||||
}
|
||||
|
||||
func (self *Filter) SetTo(addr [][]byte) {
|
||||
self.to = addr
|
||||
}
|
||||
|
||||
func (self *Filter) AddTo(addr []byte) {
|
||||
self.to = append(self.to, addr)
|
||||
}
|
||||
|
||||
func (self *Filter) SetMax(max int) {
|
||||
self.max = max
|
||||
}
|
||||
|
||||
func (self *Filter) SetSkip(skip int) {
|
||||
self.skip = skip
|
||||
}
|
||||
|
||||
// Run filters messages with the current parameters set
|
||||
func (self *Filter) Find() []*state.Message {
|
||||
var earliestBlockNo uint64 = uint64(self.earliest)
|
||||
if self.earliest == -1 {
|
||||
earliestBlockNo = self.eth.ChainManager().CurrentBlock.Number.Uint64()
|
||||
}
|
||||
var latestBlockNo uint64 = uint64(self.latest)
|
||||
if self.latest == -1 {
|
||||
latestBlockNo = self.eth.ChainManager().CurrentBlock.Number.Uint64()
|
||||
}
|
||||
|
||||
var (
|
||||
messages []*state.Message
|
||||
block = self.eth.ChainManager().GetBlockByNumber(latestBlockNo)
|
||||
quit bool
|
||||
)
|
||||
for i := 0; !quit && block != nil; i++ {
|
||||
// Quit on latest
|
||||
switch {
|
||||
case block.Number.Uint64() == earliestBlockNo, block.Number.Uint64() == 0:
|
||||
quit = true
|
||||
case self.max <= len(messages):
|
||||
break
|
||||
}
|
||||
|
||||
// Use bloom filtering to see if this block is interesting given the
|
||||
// current parameters
|
||||
if self.bloomFilter(block) {
|
||||
// Get the messages of the block
|
||||
msgs, err := self.eth.BlockManager().GetMessages(block)
|
||||
if err != nil {
|
||||
chainlogger.Warnln("err: filter get messages ", err)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
messages = append(messages, self.FilterMessages(msgs)...)
|
||||
}
|
||||
|
||||
block = self.eth.ChainManager().GetBlock(block.PrevHash)
|
||||
}
|
||||
|
||||
skip := int(math.Min(float64(len(messages)), float64(self.skip)))
|
||||
|
||||
return messages[skip:]
|
||||
}
|
||||
|
||||
func includes(addresses [][]byte, a []byte) (found bool) {
|
||||
for _, addr := range addresses {
|
||||
if bytes.Compare(addr, a) == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Filter) FilterMessages(msgs []*state.Message) []*state.Message {
|
||||
var messages []*state.Message
|
||||
|
||||
// Filter the messages for interesting stuff
|
||||
for _, message := range msgs {
|
||||
if len(self.to) > 0 && !includes(self.to, message.To) {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(self.from) > 0 && !includes(self.from, message.From) {
|
||||
continue
|
||||
}
|
||||
|
||||
var match bool
|
||||
if len(self.Altered) == 0 {
|
||||
match = true
|
||||
}
|
||||
|
||||
for _, accountChange := range self.Altered {
|
||||
if len(accountChange.Address) > 0 && bytes.Compare(message.To, accountChange.Address) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(accountChange.StateAddress) > 0 && !includes(message.ChangedAddresses, accountChange.StateAddress) {
|
||||
continue
|
||||
}
|
||||
|
||||
match = true
|
||||
break
|
||||
}
|
||||
|
||||
if !match {
|
||||
continue
|
||||
}
|
||||
|
||||
messages = append(messages, message)
|
||||
}
|
||||
|
||||
return messages
|
||||
}
|
||||
|
||||
func (self *Filter) bloomFilter(block *types.Block) bool {
|
||||
var fromIncluded, toIncluded bool
|
||||
if len(self.from) > 0 {
|
||||
for _, from := range self.from {
|
||||
if types.BloomLookup(block.LogsBloom, from) || bytes.Equal(block.Coinbase, from) {
|
||||
fromIncluded = true
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fromIncluded = true
|
||||
}
|
||||
|
||||
if len(self.to) > 0 {
|
||||
for _, to := range self.to {
|
||||
if types.BloomLookup(block.LogsBloom, ethutil.U256(new(big.Int).Add(ethutil.Big1, ethutil.BigD(to))).Bytes()) || bytes.Equal(block.Coinbase, to) {
|
||||
toIncluded = true
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
toIncluded = true
|
||||
}
|
||||
|
||||
return fromIncluded && toIncluded
|
||||
}
|
||||
7
core/filter_test.go
Normal file
7
core/filter_test.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package core
|
||||
|
||||
// import "testing"
|
||||
|
||||
// func TestFilter(t *testing.T) {
|
||||
// NewFilter(NewTestManager())
|
||||
// }
|
||||
52
core/genesis.go
Normal file
52
core/genesis.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
)
|
||||
|
||||
/*
|
||||
* This is the special genesis block.
|
||||
*/
|
||||
|
||||
var ZeroHash256 = make([]byte, 32)
|
||||
var ZeroHash160 = make([]byte, 20)
|
||||
var ZeroHash512 = make([]byte, 64)
|
||||
var EmptyShaList = crypto.Sha3(ethutil.Encode([]interface{}{}))
|
||||
var EmptyListRoot = crypto.Sha3(ethutil.Encode(""))
|
||||
|
||||
var GenesisHeader = []interface{}{
|
||||
// Previous hash (none)
|
||||
ZeroHash256,
|
||||
// Empty uncles
|
||||
EmptyShaList,
|
||||
// Coinbase
|
||||
ZeroHash160,
|
||||
// Root state
|
||||
EmptyShaList,
|
||||
// tx root
|
||||
EmptyListRoot,
|
||||
// receipt root
|
||||
EmptyListRoot,
|
||||
// bloom
|
||||
ZeroHash512,
|
||||
// Difficulty
|
||||
//ethutil.BigPow(2, 22),
|
||||
big.NewInt(131072),
|
||||
// Number
|
||||
ethutil.Big0,
|
||||
// Block upper gas bound
|
||||
big.NewInt(1000000),
|
||||
// Block gas used
|
||||
ethutil.Big0,
|
||||
// Time
|
||||
ethutil.Big0,
|
||||
// Extra
|
||||
nil,
|
||||
// Nonce
|
||||
crypto.Sha3(big.NewInt(42).Bytes()),
|
||||
}
|
||||
|
||||
var Genesis = []interface{}{GenesisHeader, []interface{}{}, []interface{}{}}
|
||||
93
core/helper_test.go
Normal file
93
core/helper_test.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/chain/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
)
|
||||
|
||||
// Implement our EthTest Manager
|
||||
type TestManager struct {
|
||||
// stateManager *StateManager
|
||||
eventMux *event.TypeMux
|
||||
|
||||
db ethutil.Database
|
||||
txPool *TxPool
|
||||
blockChain *ChainManager
|
||||
Blocks []*types.Block
|
||||
}
|
||||
|
||||
func (s *TestManager) IsListening() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *TestManager) IsMining() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *TestManager) PeerCount() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *TestManager) Peers() *list.List {
|
||||
return list.New()
|
||||
}
|
||||
|
||||
func (s *TestManager) ChainManager() *ChainManager {
|
||||
return s.blockChain
|
||||
}
|
||||
|
||||
func (tm *TestManager) TxPool() *TxPool {
|
||||
return tm.txPool
|
||||
}
|
||||
|
||||
// func (tm *TestManager) StateManager() *StateManager {
|
||||
// return tm.stateManager
|
||||
// }
|
||||
|
||||
func (tm *TestManager) EventMux() *event.TypeMux {
|
||||
return tm.eventMux
|
||||
}
|
||||
func (tm *TestManager) Broadcast(msgType wire.MsgType, data []interface{}) {
|
||||
fmt.Println("Broadcast not implemented")
|
||||
}
|
||||
|
||||
func (tm *TestManager) ClientIdentity() wire.ClientIdentity {
|
||||
return nil
|
||||
}
|
||||
func (tm *TestManager) KeyManager() *crypto.KeyManager {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tm *TestManager) Db() ethutil.Database {
|
||||
return tm.db
|
||||
}
|
||||
|
||||
func NewTestManager() *TestManager {
|
||||
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "ETH")
|
||||
|
||||
db, err := ethdb.NewMemDatabase()
|
||||
if err != nil {
|
||||
fmt.Println("Could not create mem-db, failing")
|
||||
return nil
|
||||
}
|
||||
ethutil.Config.Db = db
|
||||
|
||||
testManager := &TestManager{}
|
||||
testManager.eventMux = new(event.TypeMux)
|
||||
testManager.db = db
|
||||
// testManager.txPool = NewTxPool(testManager)
|
||||
// testManager.blockChain = NewChainManager(testManager)
|
||||
// testManager.stateManager = NewStateManager(testManager)
|
||||
|
||||
// Start the tx pool
|
||||
testManager.txPool.Start()
|
||||
|
||||
return testManager
|
||||
}
|
||||
198
core/state_transition.go
Normal file
198
core/state_transition.go
Normal file
@@ -0,0 +1,198 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/vm"
|
||||
)
|
||||
|
||||
/*
|
||||
* 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, receiver []byte
|
||||
tx *types.Transaction
|
||||
gas, gasPrice *big.Int
|
||||
value *big.Int
|
||||
data []byte
|
||||
state *state.State
|
||||
block *types.Block
|
||||
|
||||
cb, rec, sen *state.StateObject
|
||||
}
|
||||
|
||||
func NewStateTransition(coinbase *state.StateObject, tx *types.Transaction, state *state.State, block *types.Block) *StateTransition {
|
||||
return &StateTransition{coinbase.Address(), tx.Recipient, tx, new(big.Int), new(big.Int).Set(tx.GasPrice), tx.Value, tx.Data, state, block, coinbase, nil, nil}
|
||||
}
|
||||
|
||||
func (self *StateTransition) Coinbase() *state.StateObject {
|
||||
if self.cb != nil {
|
||||
return self.cb
|
||||
}
|
||||
|
||||
self.cb = self.state.GetOrNewStateObject(self.coinbase)
|
||||
return self.cb
|
||||
}
|
||||
func (self *StateTransition) Sender() *state.StateObject {
|
||||
if self.sen != nil {
|
||||
return self.sen
|
||||
}
|
||||
|
||||
self.sen = self.state.GetOrNewStateObject(self.tx.Sender())
|
||||
|
||||
return self.sen
|
||||
}
|
||||
func (self *StateTransition) Receiver() *state.StateObject {
|
||||
if self.tx != nil && self.tx.CreatesContract() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if self.rec != nil {
|
||||
return self.rec
|
||||
}
|
||||
|
||||
self.rec = self.state.GetOrNewStateObject(self.tx.Recipient)
|
||||
return self.rec
|
||||
}
|
||||
|
||||
func (self *StateTransition) UseGas(amount *big.Int) error {
|
||||
if self.gas.Cmp(amount) < 0 {
|
||||
return OutOfGasError()
|
||||
}
|
||||
self.gas.Sub(self.gas, amount)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *StateTransition) AddGas(amount *big.Int) {
|
||||
self.gas.Add(self.gas, amount)
|
||||
}
|
||||
|
||||
func (self *StateTransition) BuyGas() error {
|
||||
var err error
|
||||
|
||||
sender := self.Sender()
|
||||
if sender.Balance().Cmp(self.tx.GasValue()) < 0 {
|
||||
return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Balance())
|
||||
}
|
||||
|
||||
coinbase := self.Coinbase()
|
||||
err = coinbase.BuyGas(self.tx.Gas, self.tx.GasPrice)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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) preCheck() (err error) {
|
||||
var (
|
||||
tx = self.tx
|
||||
sender = self.Sender()
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *StateTransition) TransitionState() (err error) {
|
||||
statelogger.Debugf("(~) %x\n", self.tx.Hash())
|
||||
|
||||
// XXX Transactions after this point are considered valid.
|
||||
if err = self.preCheck(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
tx = self.tx
|
||||
sender = self.Sender()
|
||||
receiver *state.StateObject
|
||||
)
|
||||
|
||||
defer self.RefundGas()
|
||||
|
||||
// Increment the nonce for the next transaction
|
||||
sender.Nonce += 1
|
||||
|
||||
// Transaction gas
|
||||
if err = self.UseGas(vm.GasTx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Pay data gas
|
||||
var dgas int64
|
||||
for _, byt := range self.data {
|
||||
if byt != 0 {
|
||||
dgas += vm.GasData.Int64()
|
||||
} else {
|
||||
dgas += 1 // This is 1/5. If GasData changes this fails
|
||||
}
|
||||
}
|
||||
if err = self.UseGas(big.NewInt(dgas)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var ret []byte
|
||||
vmenv := NewEnv(self.state, self.tx, self.block)
|
||||
var ref vm.ClosureRef
|
||||
if tx.CreatesContract() {
|
||||
self.rec = MakeContract(tx, self.state)
|
||||
|
||||
ret, err, ref = vmenv.Create(sender, receiver.Address(), self.tx.Data, self.gas, self.gasPrice, self.value)
|
||||
ref.SetCode(ret)
|
||||
} else {
|
||||
ret, err = vmenv.Call(self.Sender(), self.Receiver().Address(), self.tx.Data, self.gas, self.gasPrice, self.value)
|
||||
}
|
||||
if err != nil {
|
||||
statelogger.Debugln(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Converts an transaction in to a state object
|
||||
func MakeContract(tx *types.Transaction, state *state.State) *state.StateObject {
|
||||
addr := tx.CreationAddress(state)
|
||||
|
||||
contract := state.GetOrNewStateObject(addr)
|
||||
contract.InitCode = tx.Data
|
||||
|
||||
return contract
|
||||
}
|
||||
236
core/transaction_pool.go
Normal file
236
core/transaction_pool.go
Normal file
@@ -0,0 +1,236 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
)
|
||||
|
||||
var txplogger = logger.NewLogger("TXP")
|
||||
|
||||
const txPoolQueueSize = 50
|
||||
|
||||
type TxPoolHook chan *types.Transaction
|
||||
type TxMsgTy byte
|
||||
|
||||
const (
|
||||
minGasPrice = 1000000
|
||||
)
|
||||
|
||||
var MinGasPrice = big.NewInt(10000000000000)
|
||||
|
||||
type TxMsg struct {
|
||||
Tx *types.Transaction
|
||||
Type TxMsgTy
|
||||
}
|
||||
|
||||
func EachTx(pool *list.List, it func(*types.Transaction, *list.Element) bool) {
|
||||
for e := pool.Front(); e != nil; e = e.Next() {
|
||||
if it(e.Value.(*types.Transaction), e) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func FindTx(pool *list.List, finder func(*types.Transaction, *list.Element) bool) *types.Transaction {
|
||||
for e := pool.Front(); e != nil; e = e.Next() {
|
||||
if tx, ok := e.Value.(*types.Transaction); ok {
|
||||
if finder(tx, e) {
|
||||
return tx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type TxProcessor interface {
|
||||
ProcessTransaction(tx *types.Transaction)
|
||||
}
|
||||
|
||||
// The tx pool a thread safe transaction pool handler. In order to
|
||||
// guarantee a non blocking pool we use a queue channel which can be
|
||||
// independently read without needing access to the actual pool. If the
|
||||
// pool is being drained or synced for whatever reason the transactions
|
||||
// will simple queue up and handled when the mutex is freed.
|
||||
type TxPool struct {
|
||||
Ethereum EthManager
|
||||
// The mutex for accessing the Tx pool.
|
||||
mutex sync.Mutex
|
||||
// Queueing channel for reading and writing incoming
|
||||
// transactions to
|
||||
queueChan chan *types.Transaction
|
||||
// Quiting channel
|
||||
quit chan bool
|
||||
// The actual pool
|
||||
pool *list.List
|
||||
|
||||
SecondaryProcessor TxProcessor
|
||||
|
||||
subscribers []chan TxMsg
|
||||
}
|
||||
|
||||
func NewTxPool(ethereum EthManager) *TxPool {
|
||||
return &TxPool{
|
||||
pool: list.New(),
|
||||
queueChan: make(chan *types.Transaction, txPoolQueueSize),
|
||||
quit: make(chan bool),
|
||||
Ethereum: ethereum,
|
||||
}
|
||||
}
|
||||
|
||||
// Blocking function. Don't use directly. Use QueueTransaction instead
|
||||
func (pool *TxPool) addTransaction(tx *types.Transaction) {
|
||||
pool.mutex.Lock()
|
||||
defer pool.mutex.Unlock()
|
||||
|
||||
pool.pool.PushBack(tx)
|
||||
|
||||
// Broadcast the transaction to the rest of the peers
|
||||
pool.Ethereum.Broadcast(wire.MsgTxTy, []interface{}{tx.RlpData()})
|
||||
}
|
||||
|
||||
func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
|
||||
// Get the last block so we can retrieve the sender and receiver from
|
||||
// the merkle trie
|
||||
block := pool.Ethereum.ChainManager().CurrentBlock
|
||||
// Something has gone horribly wrong if this happens
|
||||
if block == nil {
|
||||
return fmt.Errorf("No last block on the block chain")
|
||||
}
|
||||
|
||||
if len(tx.Recipient) != 0 && len(tx.Recipient) != 20 {
|
||||
return fmt.Errorf("Invalid recipient. len = %d", len(tx.Recipient))
|
||||
}
|
||||
|
||||
v, _, _ := tx.Curve()
|
||||
if v > 28 || v < 27 {
|
||||
return fmt.Errorf("tx.v != (28 || 27)")
|
||||
}
|
||||
|
||||
if tx.GasPrice.Cmp(MinGasPrice) < 0 {
|
||||
return fmt.Errorf("Gas price to low. Require %v > Got %v", MinGasPrice, tx.GasPrice)
|
||||
}
|
||||
|
||||
// Get the sender
|
||||
sender := pool.Ethereum.BlockManager().CurrentState().GetAccount(tx.Sender())
|
||||
|
||||
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.Balance().Cmp(totAmount) < 0 {
|
||||
return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.Sender())
|
||||
}
|
||||
|
||||
if tx.IsContract() {
|
||||
if tx.GasPrice.Cmp(big.NewInt(minGasPrice)) < 0 {
|
||||
return fmt.Errorf("Gasprice too low, %s given should be at least %d.", tx.GasPrice, minGasPrice)
|
||||
}
|
||||
}
|
||||
|
||||
// Increment the nonce making each tx valid only once to prevent replay
|
||||
// attacks
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *TxPool) Add(tx *types.Transaction) error {
|
||||
hash := tx.Hash()
|
||||
foundTx := FindTx(self.pool, func(tx *types.Transaction, e *list.Element) bool {
|
||||
return bytes.Compare(tx.Hash(), hash) == 0
|
||||
})
|
||||
|
||||
if foundTx != nil {
|
||||
return fmt.Errorf("Known transaction (%x)", hash[0:4])
|
||||
}
|
||||
|
||||
err := self.ValidateTransaction(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.addTransaction(tx)
|
||||
|
||||
tmp := make([]byte, 4)
|
||||
copy(tmp, tx.Recipient)
|
||||
|
||||
txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.Sender()[:4], tmp, tx.Value, tx.Hash())
|
||||
|
||||
// Notify the subscribers
|
||||
self.Ethereum.EventMux().Post(TxPreEvent{tx})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pool *TxPool) CurrentTransactions() []*types.Transaction {
|
||||
pool.mutex.Lock()
|
||||
defer pool.mutex.Unlock()
|
||||
|
||||
txList := make([]*types.Transaction, pool.pool.Len())
|
||||
i := 0
|
||||
for e := pool.pool.Front(); e != nil; e = e.Next() {
|
||||
tx := e.Value.(*types.Transaction)
|
||||
|
||||
txList[i] = tx
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
return txList
|
||||
}
|
||||
|
||||
func (pool *TxPool) RemoveInvalid(state *state.State) {
|
||||
pool.mutex.Lock()
|
||||
defer pool.mutex.Unlock()
|
||||
|
||||
for e := pool.pool.Front(); e != nil; e = e.Next() {
|
||||
tx := e.Value.(*types.Transaction)
|
||||
sender := state.GetAccount(tx.Sender())
|
||||
err := pool.ValidateTransaction(tx)
|
||||
if err != nil || sender.Nonce >= tx.Nonce {
|
||||
pool.pool.Remove(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *TxPool) RemoveSet(txs types.Transactions) {
|
||||
self.mutex.Lock()
|
||||
defer self.mutex.Unlock()
|
||||
|
||||
for _, tx := range txs {
|
||||
EachTx(self.pool, func(t *types.Transaction, element *list.Element) bool {
|
||||
if t == tx {
|
||||
self.pool.Remove(element)
|
||||
return true // To stop the loop
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (pool *TxPool) Flush() []*types.Transaction {
|
||||
txList := pool.CurrentTransactions()
|
||||
|
||||
// Recreate a new list all together
|
||||
// XXX Is this the fastest way?
|
||||
pool.pool = list.New()
|
||||
|
||||
return txList
|
||||
}
|
||||
|
||||
func (pool *TxPool) Start() {
|
||||
//go pool.queueHandler()
|
||||
}
|
||||
|
||||
func (pool *TxPool) Stop() {
|
||||
pool.Flush()
|
||||
|
||||
txplogger.Infoln("Stopped")
|
||||
}
|
||||
413
core/types/block.go
Normal file
413
core/types/block.go
Normal file
@@ -0,0 +1,413 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
type BlockInfo struct {
|
||||
Number uint64
|
||||
Hash []byte
|
||||
Parent []byte
|
||||
TD *big.Int
|
||||
}
|
||||
|
||||
func (bi *BlockInfo) RlpDecode(data []byte) {
|
||||
decoder := ethutil.NewValueFromBytes(data)
|
||||
|
||||
bi.Number = decoder.Get(0).Uint()
|
||||
bi.Hash = decoder.Get(1).Bytes()
|
||||
bi.Parent = decoder.Get(2).Bytes()
|
||||
bi.TD = decoder.Get(3).BigInt()
|
||||
}
|
||||
|
||||
func (bi *BlockInfo) RlpEncode() []byte {
|
||||
return ethutil.Encode([]interface{}{bi.Number, bi.Hash, bi.Parent, bi.TD})
|
||||
}
|
||||
|
||||
type Blocks []*Block
|
||||
|
||||
func (self Blocks) AsSet() ethutil.UniqueSet {
|
||||
set := make(ethutil.UniqueSet)
|
||||
for _, block := range self {
|
||||
set.Insert(block.Hash())
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
type BlockBy func(b1, b2 *Block) bool
|
||||
|
||||
func (self BlockBy) Sort(blocks Blocks) {
|
||||
bs := blockSorter{
|
||||
blocks: blocks,
|
||||
by: self,
|
||||
}
|
||||
sort.Sort(bs)
|
||||
}
|
||||
|
||||
type blockSorter struct {
|
||||
blocks Blocks
|
||||
by func(b1, b2 *Block) bool
|
||||
}
|
||||
|
||||
func (self blockSorter) Len() int { return len(self.blocks) }
|
||||
func (self blockSorter) Swap(i, j int) {
|
||||
self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i]
|
||||
}
|
||||
func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) }
|
||||
|
||||
func Number(b1, b2 *Block) bool { return b1.Number.Cmp(b2.Number) < 0 }
|
||||
|
||||
type Block struct {
|
||||
// Hash to the previous block
|
||||
PrevHash ethutil.Bytes
|
||||
// Uncles of this block
|
||||
Uncles Blocks
|
||||
UncleSha []byte
|
||||
// The coin base address
|
||||
Coinbase []byte
|
||||
// Block Trie state
|
||||
//state *ethutil.Trie
|
||||
state *state.State
|
||||
// Difficulty for the current block
|
||||
Difficulty *big.Int
|
||||
// Creation time
|
||||
Time int64
|
||||
// The block number
|
||||
Number *big.Int
|
||||
// Gas limit
|
||||
GasLimit *big.Int
|
||||
// Gas used
|
||||
GasUsed *big.Int
|
||||
// Extra data
|
||||
Extra string
|
||||
// Block Nonce for verification
|
||||
Nonce ethutil.Bytes
|
||||
// List of transactions and/or contracts
|
||||
transactions Transactions
|
||||
receipts Receipts
|
||||
TxSha, ReceiptSha []byte
|
||||
LogsBloom []byte
|
||||
|
||||
Reward *big.Int
|
||||
}
|
||||
|
||||
func NewBlockFromBytes(raw []byte) *Block {
|
||||
block := &Block{}
|
||||
block.RlpDecode(raw)
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
// New block takes a raw encoded string
|
||||
func NewBlockFromRlpValue(rlpValue *ethutil.Value) *Block {
|
||||
block := &Block{}
|
||||
block.RlpValueDecode(rlpValue)
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func CreateBlock(root interface{},
|
||||
prevHash []byte,
|
||||
base []byte,
|
||||
Difficulty *big.Int,
|
||||
Nonce []byte,
|
||||
extra string) *Block {
|
||||
|
||||
block := &Block{
|
||||
PrevHash: prevHash,
|
||||
Coinbase: base,
|
||||
Difficulty: Difficulty,
|
||||
Nonce: Nonce,
|
||||
Time: time.Now().Unix(),
|
||||
Extra: extra,
|
||||
UncleSha: nil,
|
||||
GasUsed: new(big.Int),
|
||||
GasLimit: new(big.Int),
|
||||
}
|
||||
block.SetUncles([]*Block{})
|
||||
|
||||
block.state = state.New(trie.New(ethutil.Config.Db, root))
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
// Returns a hash of the block
|
||||
func (block *Block) Hash() ethutil.Bytes {
|
||||
return crypto.Sha3(ethutil.NewValue(block.header()).Encode())
|
||||
//return crypto.Sha3(block.Value().Encode())
|
||||
}
|
||||
|
||||
func (block *Block) HashNoNonce() []byte {
|
||||
return crypto.Sha3(ethutil.Encode(block.miningHeader()))
|
||||
}
|
||||
|
||||
func (block *Block) State() *state.State {
|
||||
return block.state
|
||||
}
|
||||
|
||||
func (block *Block) Transactions() Transactions {
|
||||
return block.transactions
|
||||
}
|
||||
|
||||
func (block *Block) CalcGasLimit(parent *Block) *big.Int {
|
||||
if block.Number.Cmp(big.NewInt(0)) == 0 {
|
||||
return ethutil.BigPow(10, 6)
|
||||
}
|
||||
|
||||
// ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024
|
||||
|
||||
previous := new(big.Int).Mul(big.NewInt(1024-1), parent.GasLimit)
|
||||
current := new(big.Rat).Mul(new(big.Rat).SetInt(parent.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 := big.NewInt(125000)
|
||||
|
||||
return ethutil.BigMax(min, result)
|
||||
}
|
||||
|
||||
func (block *Block) BlockInfo() BlockInfo {
|
||||
bi := BlockInfo{}
|
||||
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
|
||||
bi.RlpDecode(data)
|
||||
|
||||
return bi
|
||||
}
|
||||
|
||||
func (self *Block) GetTransaction(hash []byte) *Transaction {
|
||||
for _, tx := range self.transactions {
|
||||
if bytes.Compare(tx.Hash(), hash) == 0 {
|
||||
return tx
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sync the block's state and contract respectively
|
||||
func (block *Block) Sync() {
|
||||
block.state.Sync()
|
||||
}
|
||||
|
||||
func (block *Block) Undo() {
|
||||
// Sync the block state itself
|
||||
block.state.Reset()
|
||||
}
|
||||
|
||||
/////// Block Encoding
|
||||
func (block *Block) rlpReceipts() interface{} {
|
||||
// Marshal the transactions of this block
|
||||
encR := make([]interface{}, len(block.receipts))
|
||||
for i, r := range block.receipts {
|
||||
// Cast it to a string (safe)
|
||||
encR[i] = r.RlpData()
|
||||
}
|
||||
|
||||
return encR
|
||||
}
|
||||
|
||||
func (block *Block) rlpUncles() interface{} {
|
||||
// Marshal the transactions of this block
|
||||
uncles := make([]interface{}, len(block.Uncles))
|
||||
for i, uncle := range block.Uncles {
|
||||
// Cast it to a string (safe)
|
||||
uncles[i] = uncle.header()
|
||||
}
|
||||
|
||||
return uncles
|
||||
}
|
||||
|
||||
func (block *Block) SetUncles(uncles []*Block) {
|
||||
block.Uncles = uncles
|
||||
block.UncleSha = crypto.Sha3(ethutil.Encode(block.rlpUncles()))
|
||||
}
|
||||
|
||||
func (self *Block) SetReceipts(receipts Receipts) {
|
||||
self.receipts = receipts
|
||||
self.ReceiptSha = DeriveSha(receipts)
|
||||
self.LogsBloom = CreateBloom(receipts)
|
||||
}
|
||||
|
||||
func (self *Block) SetTransactions(txs Transactions) {
|
||||
self.transactions = txs
|
||||
self.TxSha = DeriveSha(txs)
|
||||
}
|
||||
|
||||
func (block *Block) Value() *ethutil.Value {
|
||||
return ethutil.NewValue([]interface{}{block.header(), block.transactions, block.rlpUncles()})
|
||||
}
|
||||
|
||||
func (block *Block) RlpEncode() []byte {
|
||||
// Encode a slice interface which contains the header and the list of
|
||||
// transactions.
|
||||
return block.Value().Encode()
|
||||
}
|
||||
|
||||
func (block *Block) RlpDecode(data []byte) {
|
||||
rlpValue := ethutil.NewValueFromBytes(data)
|
||||
block.RlpValueDecode(rlpValue)
|
||||
}
|
||||
|
||||
func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
|
||||
block.setHeader(decoder.Get(0))
|
||||
|
||||
// Tx list might be empty if this is an uncle. Uncles only have their
|
||||
// header set.
|
||||
if decoder.Get(1).IsNil() == false { // Yes explicitness
|
||||
//receipts := decoder.Get(1)
|
||||
//block.receipts = make([]*Receipt, receipts.Len())
|
||||
txs := decoder.Get(1)
|
||||
block.transactions = make(Transactions, txs.Len())
|
||||
for i := 0; i < txs.Len(); i++ {
|
||||
block.transactions[i] = NewTransactionFromValue(txs.Get(i))
|
||||
//receipt := NewRecieptFromValue(receipts.Get(i))
|
||||
//block.transactions[i] = receipt.Tx
|
||||
//block.receipts[i] = receipt
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if decoder.Get(2).IsNil() == false { // Yes explicitness
|
||||
uncles := decoder.Get(2)
|
||||
block.Uncles = make([]*Block, uncles.Len())
|
||||
for i := 0; i < uncles.Len(); i++ {
|
||||
block.Uncles[i] = NewUncleBlockFromValue(uncles.Get(i))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (self *Block) setHeader(header *ethutil.Value) {
|
||||
self.PrevHash = header.Get(0).Bytes()
|
||||
self.UncleSha = header.Get(1).Bytes()
|
||||
self.Coinbase = header.Get(2).Bytes()
|
||||
self.state = state.New(trie.New(ethutil.Config.Db, header.Get(3).Val))
|
||||
self.TxSha = header.Get(4).Bytes()
|
||||
self.ReceiptSha = header.Get(5).Bytes()
|
||||
self.LogsBloom = header.Get(6).Bytes()
|
||||
self.Difficulty = header.Get(7).BigInt()
|
||||
self.Number = header.Get(8).BigInt()
|
||||
self.GasLimit = header.Get(9).BigInt()
|
||||
self.GasUsed = header.Get(10).BigInt()
|
||||
self.Time = int64(header.Get(11).BigInt().Uint64())
|
||||
self.Extra = header.Get(12).Str()
|
||||
self.Nonce = header.Get(13).Bytes()
|
||||
}
|
||||
|
||||
func NewUncleBlockFromValue(header *ethutil.Value) *Block {
|
||||
block := &Block{}
|
||||
block.setHeader(header)
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func (block *Block) Trie() *trie.Trie {
|
||||
return block.state.Trie
|
||||
}
|
||||
|
||||
func (block *Block) Root() interface{} {
|
||||
return block.state.Root()
|
||||
}
|
||||
|
||||
func (block *Block) Diff() *big.Int {
|
||||
return block.Difficulty
|
||||
}
|
||||
|
||||
func (self *Block) Receipts() []*Receipt {
|
||||
return self.receipts
|
||||
}
|
||||
|
||||
func (block *Block) miningHeader() []interface{} {
|
||||
return []interface{}{
|
||||
// Sha of the previous block
|
||||
block.PrevHash,
|
||||
// Sha of uncles
|
||||
block.UncleSha,
|
||||
// Coinbase address
|
||||
block.Coinbase,
|
||||
// root state
|
||||
block.Root(),
|
||||
// tx root
|
||||
block.TxSha,
|
||||
// Sha of tx
|
||||
block.ReceiptSha,
|
||||
// Bloom
|
||||
block.LogsBloom,
|
||||
// Current block Difficulty
|
||||
block.Difficulty,
|
||||
// The block number
|
||||
block.Number,
|
||||
// Block upper gas bound
|
||||
block.GasLimit,
|
||||
// Block gas used
|
||||
block.GasUsed,
|
||||
// Time the block was found?
|
||||
block.Time,
|
||||
// Extra data
|
||||
block.Extra,
|
||||
}
|
||||
}
|
||||
|
||||
func (block *Block) header() []interface{} {
|
||||
return append(block.miningHeader(), block.Nonce)
|
||||
}
|
||||
|
||||
func (block *Block) String() string {
|
||||
return fmt.Sprintf(`
|
||||
BLOCK(%x): Size: %v
|
||||
PrevHash: %x
|
||||
UncleSha: %x
|
||||
Coinbase: %x
|
||||
Root: %x
|
||||
TxSha %x
|
||||
ReceiptSha: %x
|
||||
Bloom: %x
|
||||
Difficulty: %v
|
||||
Number: %v
|
||||
MaxLimit: %v
|
||||
GasUsed: %v
|
||||
Time: %v
|
||||
Extra: %v
|
||||
Nonce: %x
|
||||
NumTx: %v
|
||||
`,
|
||||
block.Hash(),
|
||||
block.Size(),
|
||||
block.PrevHash,
|
||||
block.UncleSha,
|
||||
block.Coinbase,
|
||||
block.Root(),
|
||||
block.TxSha,
|
||||
block.ReceiptSha,
|
||||
block.LogsBloom,
|
||||
block.Difficulty,
|
||||
block.Number,
|
||||
block.GasLimit,
|
||||
block.GasUsed,
|
||||
block.Time,
|
||||
block.Extra,
|
||||
block.Nonce,
|
||||
len(block.transactions),
|
||||
)
|
||||
}
|
||||
|
||||
func (self *Block) Size() ethutil.StorageSize {
|
||||
return ethutil.StorageSize(len(self.RlpEncode()))
|
||||
}
|
||||
|
||||
// Implement RlpEncodable
|
||||
func (self *Block) RlpData() interface{} {
|
||||
return self.Value().Val
|
||||
}
|
||||
56
core/types/bloom9.go
Normal file
56
core/types/bloom9.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
func CreateBloom(receipts Receipts) []byte {
|
||||
bin := new(big.Int)
|
||||
for _, receipt := range receipts {
|
||||
bin.Or(bin, LogsBloom(receipt.logs))
|
||||
}
|
||||
|
||||
return ethutil.LeftPadBytes(bin.Bytes(), 64)
|
||||
}
|
||||
|
||||
func LogsBloom(logs state.Logs) *big.Int {
|
||||
bin := new(big.Int)
|
||||
for _, log := range logs {
|
||||
data := [][]byte{log.Address}
|
||||
for _, topic := range log.Topics {
|
||||
data = append(data, topic)
|
||||
}
|
||||
|
||||
for _, b := range data {
|
||||
bin.Or(bin, ethutil.BigD(bloom9(crypto.Sha3(b)).Bytes()))
|
||||
}
|
||||
|
||||
//if log.Data != nil {
|
||||
// data = append(data, log.Data)
|
||||
//}
|
||||
}
|
||||
|
||||
return bin
|
||||
}
|
||||
|
||||
func bloom9(b []byte) *big.Int {
|
||||
r := new(big.Int)
|
||||
for _, i := range []int{0, 2, 4} {
|
||||
t := big.NewInt(1)
|
||||
b := uint(b[i+1]) + 256*(uint(b[i])&1)
|
||||
r.Or(r, t.Lsh(t, b))
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func BloomLookup(bin, topic []byte) bool {
|
||||
bloom := ethutil.BigD(bin)
|
||||
cmp := bloom9(crypto.Sha3(topic))
|
||||
|
||||
return bloom.And(bloom, cmp).Cmp(cmp) == 0
|
||||
}
|
||||
31
core/types/bloom9_test.go
Normal file
31
core/types/bloom9_test.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package types
|
||||
|
||||
/*
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
func TestBloom9(t *testing.T) {
|
||||
testCase := []byte("testtest")
|
||||
bin := LogsBloom([]state.Log{
|
||||
{testCase, [][]byte{[]byte("hellohello")}, nil},
|
||||
}).Bytes()
|
||||
res := BloomLookup(bin, testCase)
|
||||
|
||||
if !res {
|
||||
t.Errorf("Bloom lookup failed")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func TestAddress(t *testing.T) {
|
||||
block := &Block{}
|
||||
block.Coinbase = ethutil.Hex2Bytes("22341ae42d6dd7384bc8584e50419ea3ac75b83f")
|
||||
fmt.Printf("%x\n", crypto.Sha3(block.Coinbase))
|
||||
|
||||
bin := CreateBloom(block)
|
||||
fmt.Printf("bin = %x\n", ethutil.LeftPadBytes(bin, 64))
|
||||
}
|
||||
*/
|
||||
11
core/types/common.go
Normal file
11
core/types/common.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
type BlockProcessor interface {
|
||||
Process(*Block) (*big.Int, state.Messages, error)
|
||||
}
|
||||
20
core/types/derive_sha.go
Normal file
20
core/types/derive_sha.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
type DerivableList interface {
|
||||
Len() int
|
||||
GetRlp(i int) []byte
|
||||
}
|
||||
|
||||
func DeriveSha(list DerivableList) []byte {
|
||||
trie := trie.New(ethutil.Config.Db, "")
|
||||
for i := 0; i < list.Len(); i++ {
|
||||
trie.Update(string(ethutil.NewValue(i).Encode()), string(list.GetRlp(i)))
|
||||
}
|
||||
|
||||
return trie.GetRoot()
|
||||
}
|
||||
68
core/types/receipt.go
Normal file
68
core/types/receipt.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
type Receipt struct {
|
||||
PostState []byte
|
||||
CumulativeGasUsed *big.Int
|
||||
Bloom []byte
|
||||
logs state.Logs
|
||||
}
|
||||
|
||||
func NewReceipt(root []byte, cumalativeGasUsed *big.Int) *Receipt {
|
||||
return &Receipt{PostState: ethutil.CopyBytes(root), CumulativeGasUsed: cumalativeGasUsed}
|
||||
}
|
||||
|
||||
func NewRecieptFromValue(val *ethutil.Value) *Receipt {
|
||||
r := &Receipt{}
|
||||
r.RlpValueDecode(val)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (self *Receipt) SetLogs(logs state.Logs) {
|
||||
self.logs = logs
|
||||
}
|
||||
|
||||
func (self *Receipt) RlpValueDecode(decoder *ethutil.Value) {
|
||||
self.PostState = decoder.Get(0).Bytes()
|
||||
self.CumulativeGasUsed = decoder.Get(1).BigInt()
|
||||
self.Bloom = decoder.Get(2).Bytes()
|
||||
|
||||
it := decoder.Get(3).NewIterator()
|
||||
for it.Next() {
|
||||
self.logs = append(self.logs, state.NewLogFromValue(it.Value()))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Receipt) RlpData() interface{} {
|
||||
return []interface{}{self.PostState, self.CumulativeGasUsed, self.Bloom, self.logs.RlpData()}
|
||||
}
|
||||
|
||||
func (self *Receipt) RlpEncode() []byte {
|
||||
return ethutil.Encode(self.RlpData())
|
||||
}
|
||||
|
||||
func (self *Receipt) Cmp(other *Receipt) bool {
|
||||
if bytes.Compare(self.PostState, other.PostState) != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *Receipt) String() string {
|
||||
return fmt.Sprintf("receipt{med=%x cgas=%v bloom=%x logs=%v}", self.PostState, self.CumulativeGasUsed, self.Bloom, self.logs)
|
||||
}
|
||||
|
||||
type Receipts []*Receipt
|
||||
|
||||
func (self Receipts) Len() int { return len(self) }
|
||||
func (self Receipts) GetRlp(i int) []byte { return ethutil.Rlp(self[i]) }
|
||||
225
core/types/transaction.go
Normal file
225
core/types/transaction.go
Normal file
@@ -0,0 +1,225 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/obscuren/secp256k1-go"
|
||||
)
|
||||
|
||||
var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
func IsContractAddr(addr []byte) bool {
|
||||
return len(addr) == 0
|
||||
//return bytes.Compare(addr, ContractAddr) == 0
|
||||
}
|
||||
|
||||
type Transaction struct {
|
||||
Nonce uint64
|
||||
Recipient []byte
|
||||
Value *big.Int
|
||||
Gas *big.Int
|
||||
GasPrice *big.Int
|
||||
Data []byte
|
||||
v byte
|
||||
r, s []byte
|
||||
|
||||
// Indicates whether this tx is a contract creation transaction
|
||||
contractCreation bool
|
||||
}
|
||||
|
||||
func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction {
|
||||
return &Transaction{Recipient: nil, Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true}
|
||||
}
|
||||
|
||||
func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction {
|
||||
return &Transaction{Recipient: to, Value: value, GasPrice: gasPrice, Gas: gas, Data: data, contractCreation: IsContractAddr(to)}
|
||||
}
|
||||
|
||||
func NewTransactionFromBytes(data []byte) *Transaction {
|
||||
tx := &Transaction{}
|
||||
tx.RlpDecode(data)
|
||||
|
||||
return tx
|
||||
}
|
||||
|
||||
func NewTransactionFromValue(val *ethutil.Value) *Transaction {
|
||||
tx := &Transaction{}
|
||||
tx.RlpValueDecode(val)
|
||||
|
||||
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}
|
||||
|
||||
return crypto.Sha3(ethutil.NewValue(data).Encode())
|
||||
}
|
||||
|
||||
func (tx *Transaction) CreatesContract() bool {
|
||||
return tx.contractCreation
|
||||
}
|
||||
|
||||
/* Deprecated */
|
||||
func (tx *Transaction) IsContract() bool {
|
||||
return tx.CreatesContract()
|
||||
}
|
||||
|
||||
func (tx *Transaction) CreationAddress(state *state.State) []byte {
|
||||
// Generate a new address
|
||||
return crypto.Sha3(ethutil.NewValue([]interface{}{tx.Sender(), tx.Nonce}).Encode())[12:]
|
||||
}
|
||||
|
||||
func (tx *Transaction) Curve() (v byte, r []byte, s []byte) {
|
||||
v = tx.v
|
||||
r = ethutil.LeftPadBytes(tx.r, 32)
|
||||
s = ethutil.LeftPadBytes(tx.s, 32)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (tx *Transaction) Signature(key []byte) []byte {
|
||||
hash := tx.Hash()
|
||||
|
||||
sig, _ := secp256k1.Sign(hash, key)
|
||||
|
||||
return sig
|
||||
}
|
||||
|
||||
func (tx *Transaction) PublicKey() []byte {
|
||||
hash := tx.Hash()
|
||||
|
||||
v, r, s := tx.Curve()
|
||||
|
||||
sig := append(r, s...)
|
||||
sig = append(sig, v-27)
|
||||
|
||||
pubkey := crypto.Ecrecover(append(hash, sig...))
|
||||
//pubkey, _ := secp256k1.RecoverPubkey(hash, sig)
|
||||
|
||||
return pubkey
|
||||
}
|
||||
|
||||
func (tx *Transaction) Sender() []byte {
|
||||
pubkey := tx.PublicKey()
|
||||
|
||||
// Validate the returned key.
|
||||
// Return nil if public key isn't in full format
|
||||
if len(pubkey) != 0 && pubkey[0] != 4 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return crypto.Sha3(pubkey[1:])[12:]
|
||||
}
|
||||
|
||||
func (tx *Transaction) Sign(privk []byte) error {
|
||||
|
||||
sig := tx.Signature(privk)
|
||||
|
||||
tx.r = sig[:32]
|
||||
tx.s = sig[32:64]
|
||||
tx.v = sig[64] + 27
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpData() interface{} {
|
||||
data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data}
|
||||
|
||||
// TODO Remove prefixing zero's
|
||||
|
||||
return append(data, tx.v, new(big.Int).SetBytes(tx.r).Bytes(), new(big.Int).SetBytes(tx.s).Bytes())
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpValue() *ethutil.Value {
|
||||
return ethutil.NewValue(tx.RlpData())
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpEncode() []byte {
|
||||
return tx.RlpValue().Encode()
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpDecode(data []byte) {
|
||||
tx.RlpValueDecode(ethutil.NewValueFromBytes(data))
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
|
||||
tx.Nonce = decoder.Get(0).Uint()
|
||||
tx.GasPrice = decoder.Get(1).BigInt()
|
||||
tx.Gas = decoder.Get(2).BigInt()
|
||||
tx.Recipient = decoder.Get(3).Bytes()
|
||||
tx.Value = decoder.Get(4).BigInt()
|
||||
tx.Data = decoder.Get(5).Bytes()
|
||||
tx.v = byte(decoder.Get(6).Uint())
|
||||
|
||||
tx.r = decoder.Get(7).Bytes()
|
||||
tx.s = decoder.Get(8).Bytes()
|
||||
|
||||
if IsContractAddr(tx.Recipient) {
|
||||
tx.contractCreation = true
|
||||
}
|
||||
}
|
||||
|
||||
func (tx *Transaction) String() string {
|
||||
return fmt.Sprintf(`
|
||||
TX(%x)
|
||||
Contract: %v
|
||||
From: %x
|
||||
To: %x
|
||||
Nonce: %v
|
||||
GasPrice: %v
|
||||
Gas: %v
|
||||
Value: %v
|
||||
Data: 0x%x
|
||||
V: 0x%x
|
||||
R: 0x%x
|
||||
S: 0x%x
|
||||
`,
|
||||
tx.Hash(),
|
||||
len(tx.Recipient) == 0,
|
||||
tx.Sender(),
|
||||
tx.Recipient,
|
||||
tx.Nonce,
|
||||
tx.GasPrice,
|
||||
tx.Gas,
|
||||
tx.Value,
|
||||
tx.Data,
|
||||
tx.v,
|
||||
tx.r,
|
||||
tx.s)
|
||||
}
|
||||
|
||||
// Transaction slice type for basic sorting
|
||||
type Transactions []*Transaction
|
||||
|
||||
func (self Transactions) RlpData() interface{} {
|
||||
// Marshal the transactions of this block
|
||||
enc := make([]interface{}, len(self))
|
||||
for i, tx := range self {
|
||||
// Cast it to a string (safe)
|
||||
enc[i] = tx.RlpData()
|
||||
}
|
||||
|
||||
return enc
|
||||
}
|
||||
func (s Transactions) Len() int { return len(s) }
|
||||
func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s Transactions) GetRlp(i int) []byte { return ethutil.Rlp(s[i]) }
|
||||
|
||||
type TxByNonce struct{ Transactions }
|
||||
|
||||
func (s TxByNonce) Less(i, j int) bool {
|
||||
return s.Transactions[i].Nonce < s.Transactions[j].Nonce
|
||||
}
|
||||
1
core/types/transaction_test.go
Normal file
1
core/types/transaction_test.go
Normal file
@@ -0,0 +1 @@
|
||||
package types
|
||||
63
core/vm_env.go
Normal file
63
core/vm_env.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/vm"
|
||||
)
|
||||
|
||||
type VMEnv struct {
|
||||
state *state.State
|
||||
block *types.Block
|
||||
tx *types.Transaction
|
||||
depth int
|
||||
}
|
||||
|
||||
func NewEnv(state *state.State, tx *types.Transaction, block *types.Block) *VMEnv {
|
||||
return &VMEnv{
|
||||
state: state,
|
||||
block: block,
|
||||
tx: tx,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *VMEnv) Origin() []byte { return self.tx.Sender() }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number }
|
||||
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
|
||||
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
|
||||
func (self *VMEnv) Time() int64 { return self.block.Time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
|
||||
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
|
||||
func (self *VMEnv) Value() *big.Int { return self.tx.Value }
|
||||
func (self *VMEnv) State() *state.State { return self.state }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit }
|
||||
func (self *VMEnv) Depth() int { return self.depth }
|
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||
func (self *VMEnv) AddLog(log *state.Log) {
|
||||
self.state.AddLog(log)
|
||||
}
|
||||
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
|
||||
return vm.Transfer(from, to, amount)
|
||||
}
|
||||
|
||||
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *Execution {
|
||||
evm := vm.New(self, vm.DebugVmTy)
|
||||
|
||||
return NewExecution(evm, addr, data, gas, price, value)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Call(me vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
exe := self.vm(addr, data, gas, price, value)
|
||||
return exe.Call(addr, me)
|
||||
}
|
||||
func (self *VMEnv) CallCode(me vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
exe := self.vm(me.Address(), data, gas, price, value)
|
||||
return exe.Call(addr, me)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Create(me vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ClosureRef) {
|
||||
exe := self.vm(addr, data, gas, price, value)
|
||||
return exe.Create(me)
|
||||
}
|
||||
Reference in New Issue
Block a user