forked from cerc-io/plugeth
Rearrange packages
This commit is contained in:
parent
3616080db4
commit
477e8a7a73
591
block_manager.go
591
block_manager.go
@ -1,591 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"github.com/ethereum/ethutil-go"
|
|
||||||
"github.com/obscuren/secp256k1-go"
|
|
||||||
"log"
|
|
||||||
"math"
|
|
||||||
"math/big"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BlockChain struct {
|
|
||||||
// Last block
|
|
||||||
LastBlock *ethutil.Block
|
|
||||||
// The famous, the fabulous Mister GENESIIIIIIS (block)
|
|
||||||
genesisBlock *ethutil.Block
|
|
||||||
// Last known total difficulty
|
|
||||||
TD *big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBlockChain() *BlockChain {
|
|
||||||
bc := &BlockChain{}
|
|
||||||
bc.genesisBlock = ethutil.NewBlock(ethutil.Encode(ethutil.Genesis))
|
|
||||||
|
|
||||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
|
||||||
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
|
|
||||||
|
|
||||||
// TODO get last block from the database
|
|
||||||
bc.LastBlock = bc.genesisBlock
|
|
||||||
|
|
||||||
return bc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bc *BlockChain) HasBlock(hash string) bool {
|
|
||||||
data, _ := ethutil.Config.Db.Get([]byte(hash))
|
|
||||||
return len(data) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bc *BlockChain) GenesisBlock() *ethutil.Block {
|
|
||||||
return bc.genesisBlock
|
|
||||||
}
|
|
||||||
|
|
||||||
type BlockManager struct {
|
|
||||||
server *Server
|
|
||||||
// The block chain :)
|
|
||||||
bc *BlockChain
|
|
||||||
|
|
||||||
// Last known block number
|
|
||||||
LastBlockNumber *big.Int
|
|
||||||
|
|
||||||
// Stack for processing contracts
|
|
||||||
stack *Stack
|
|
||||||
// non-persistent key/value memory storage
|
|
||||||
mem map[string]*big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBlockManager(s *Server) *BlockManager {
|
|
||||||
bm := &BlockManager{
|
|
||||||
server: s,
|
|
||||||
bc: NewBlockChain(),
|
|
||||||
stack: NewStack(),
|
|
||||||
mem: make(map[string]*big.Int),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the last known block number based on the blockchains last
|
|
||||||
// block
|
|
||||||
bm.LastBlockNumber = bm.BlockInfo(bm.bc.LastBlock).Number
|
|
||||||
|
|
||||||
return bm
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process a block.
|
|
||||||
func (bm *BlockManager) ProcessBlock(block *ethutil.Block) error {
|
|
||||||
// Block validation
|
|
||||||
if err := bm.ValidateBlock(block); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// I'm not sure, but I don't know if there should be thrown
|
|
||||||
// any errors at this time.
|
|
||||||
if err := bm.AccumelateRewards(block); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the tx count. Used to create enough channels to 'join' the go routines
|
|
||||||
txCount := len(block.Transactions())
|
|
||||||
// Locking channel. When it has been fully buffered this method will return
|
|
||||||
lockChan := make(chan bool, txCount)
|
|
||||||
|
|
||||||
// Process each transaction/contract
|
|
||||||
for _, tx := range block.Transactions() {
|
|
||||||
// If there's no recipient, it's a contract
|
|
||||||
if tx.IsContract() {
|
|
||||||
go bm.ProcessContract(tx, block, lockChan)
|
|
||||||
} else {
|
|
||||||
// "finish" tx which isn't a contract
|
|
||||||
lockChan <- true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for all Tx to finish processing
|
|
||||||
for i := 0; i < txCount; i++ {
|
|
||||||
<-lockChan
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the new total difficulty and sync back to the db
|
|
||||||
if bm.CalculateTD(block) {
|
|
||||||
ethutil.Config.Db.Put(block.Hash(), block.RlpEncode())
|
|
||||||
bm.bc.LastBlock = block
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unexported method for writing extra non-essential block info to the db
|
|
||||||
func (bm *BlockManager) writeBlockInfo(block *ethutil.Block) {
|
|
||||||
bi := ethutil.BlockInfo{Number: bm.LastBlockNumber.Add(bm.LastBlockNumber, big.NewInt(1))}
|
|
||||||
|
|
||||||
// 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 (bm *BlockManager) BlockInfo(block *ethutil.Block) ethutil.BlockInfo {
|
|
||||||
bi := ethutil.BlockInfo{}
|
|
||||||
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
|
|
||||||
bi.RlpDecode(data)
|
|
||||||
|
|
||||||
return bi
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bm *BlockManager) CalculateTD(block *ethutil.Block) 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(bm.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(bm.bc.TD) > 0 {
|
|
||||||
bm.bc.LastBlock = block
|
|
||||||
// Set the new total difficulty back to the block chain
|
|
||||||
bm.bc.TD = td
|
|
||||||
|
|
||||||
if Debug {
|
|
||||||
log.Println("TD(block) =", td)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return 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 (bm *BlockManager) ValidateBlock(block *ethutil.Block) error {
|
|
||||||
// Genesis block
|
|
||||||
if bm.bc.LastBlock == nil && block.PrevHash == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// TODO
|
|
||||||
// 2. Check if the difficulty is correct
|
|
||||||
|
|
||||||
// Check if we have the parent hash, if it isn't known we discard it
|
|
||||||
// Reasons might be catching up or simply an invalid block
|
|
||||||
if !bm.bc.HasBlock(block.PrevHash) {
|
|
||||||
return errors.New("Block's parent unknown")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check each uncle's previous hash. In order for it to be valid
|
|
||||||
// is if it has the same block hash as the current
|
|
||||||
for _, uncle := range block.Uncles {
|
|
||||||
if uncle.PrevHash != block.PrevHash {
|
|
||||||
if Debug {
|
|
||||||
log.Printf("Uncle prvhash mismatch %x %x\n", block.PrevHash, uncle.PrevHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New("Mismatching Prvhash from uncle")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diff := block.Time - bm.bc.LastBlock.Time
|
|
||||||
if diff < 0 {
|
|
||||||
return fmt.Errorf("Block timestamp less then prev block %v", diff)
|
|
||||||
}
|
|
||||||
|
|
||||||
// New blocks must be within the 15 minute range of the last block.
|
|
||||||
if diff > int64(15*time.Minute) {
|
|
||||||
return errors.New("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 !DaggerVerify(ethutil.BigD(block.Hash()), block.Difficulty, block.Nonce) {
|
|
||||||
|
|
||||||
return errors.New("Block's nonce is invalid")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Block validation PASSED")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bm *BlockManager) AccumelateRewards(block *ethutil.Block) error {
|
|
||||||
// Get the coinbase rlp data
|
|
||||||
d := block.State().Get(block.Coinbase)
|
|
||||||
|
|
||||||
ether := ethutil.NewEtherFromData([]byte(d))
|
|
||||||
|
|
||||||
// Reward amount of ether to the coinbase address
|
|
||||||
ether.AddFee(ethutil.CalculateBlockReward(block, len(block.Uncles)))
|
|
||||||
block.State().Update(block.Coinbase, string(ether.RlpEncode()))
|
|
||||||
|
|
||||||
// TODO Reward each uncle
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bm *BlockManager) ProcessContract(tx *ethutil.Transaction, block *ethutil.Block, lockChan chan bool) {
|
|
||||||
// Recovering function in case the VM had any errors
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
fmt.Println("Recovered from VM execution with err =", r)
|
|
||||||
// Let the channel know where done even though it failed (so the execution may resume normally)
|
|
||||||
lockChan <- true
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Process contract
|
|
||||||
bm.ProcContract(tx, block, func(opType OpType) bool {
|
|
||||||
// TODO turn on once big ints are in place
|
|
||||||
//if !block.PayFee(tx.Hash(), StepFee.Uint64()) {
|
|
||||||
// return false
|
|
||||||
//}
|
|
||||||
|
|
||||||
return true // Continue
|
|
||||||
})
|
|
||||||
|
|
||||||
// Broadcast we're done
|
|
||||||
lockChan <- true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contract evaluation is done here.
|
|
||||||
func (bm *BlockManager) ProcContract(tx *ethutil.Transaction, block *ethutil.Block, cb TxCallback) {
|
|
||||||
// Instruction pointer
|
|
||||||
pc := 0
|
|
||||||
blockInfo := bm.BlockInfo(block)
|
|
||||||
|
|
||||||
contract := block.GetContract(tx.Hash())
|
|
||||||
if contract == nil {
|
|
||||||
fmt.Println("Contract not found")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Pow256 := ethutil.BigPow(2, 256)
|
|
||||||
|
|
||||||
//fmt.Printf("# op arg\n")
|
|
||||||
out:
|
|
||||||
for {
|
|
||||||
// The base big int for all calculations. Use this for any results.
|
|
||||||
base := new(big.Int)
|
|
||||||
// XXX Should Instr return big int slice instead of string slice?
|
|
||||||
// Get the next instruction from the contract
|
|
||||||
//op, _, _ := Instr(contract.state.Get(string(Encode(uint32(pc)))))
|
|
||||||
nb := ethutil.NumberToBytes(uint64(pc), 32)
|
|
||||||
o, _, _ := ethutil.Instr(contract.State().Get(string(nb)))
|
|
||||||
op := OpCode(o)
|
|
||||||
|
|
||||||
if !cb(0) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if Debug {
|
|
||||||
//fmt.Printf("%-3d %-4s\n", pc, op.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
switch op {
|
|
||||||
case oSTOP:
|
|
||||||
break out
|
|
||||||
case oADD:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// (x + y) % 2 ** 256
|
|
||||||
base.Add(x, y)
|
|
||||||
base.Mod(base, Pow256)
|
|
||||||
// Pop result back on the stack
|
|
||||||
bm.stack.Push(base)
|
|
||||||
case oSUB:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// (x - y) % 2 ** 256
|
|
||||||
base.Sub(x, y)
|
|
||||||
base.Mod(base, Pow256)
|
|
||||||
// Pop result back on the stack
|
|
||||||
bm.stack.Push(base)
|
|
||||||
case oMUL:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// (x * y) % 2 ** 256
|
|
||||||
base.Mul(x, y)
|
|
||||||
base.Mod(base, Pow256)
|
|
||||||
// Pop result back on the stack
|
|
||||||
bm.stack.Push(base)
|
|
||||||
case oDIV:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// floor(x / y)
|
|
||||||
base.Div(x, y)
|
|
||||||
// Pop result back on the stack
|
|
||||||
bm.stack.Push(base)
|
|
||||||
case oSDIV:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// n > 2**255
|
|
||||||
if x.Cmp(Pow256) > 0 {
|
|
||||||
x.Sub(Pow256, x)
|
|
||||||
}
|
|
||||||
if y.Cmp(Pow256) > 0 {
|
|
||||||
y.Sub(Pow256, y)
|
|
||||||
}
|
|
||||||
z := new(big.Int)
|
|
||||||
z.Div(x, y)
|
|
||||||
if z.Cmp(Pow256) > 0 {
|
|
||||||
z.Sub(Pow256, z)
|
|
||||||
}
|
|
||||||
// Push result on to the stack
|
|
||||||
bm.stack.Push(z)
|
|
||||||
case oMOD:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
base.Mod(x, y)
|
|
||||||
bm.stack.Push(base)
|
|
||||||
case oSMOD:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// n > 2**255
|
|
||||||
if x.Cmp(Pow256) > 0 {
|
|
||||||
x.Sub(Pow256, x)
|
|
||||||
}
|
|
||||||
if y.Cmp(Pow256) > 0 {
|
|
||||||
y.Sub(Pow256, y)
|
|
||||||
}
|
|
||||||
z := new(big.Int)
|
|
||||||
z.Mod(x, y)
|
|
||||||
if z.Cmp(Pow256) > 0 {
|
|
||||||
z.Sub(Pow256, z)
|
|
||||||
}
|
|
||||||
// Push result on to the stack
|
|
||||||
bm.stack.Push(z)
|
|
||||||
case oEXP:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
base.Exp(x, y, Pow256)
|
|
||||||
|
|
||||||
bm.stack.Push(base)
|
|
||||||
case oNEG:
|
|
||||||
base.Sub(Pow256, bm.stack.Pop())
|
|
||||||
bm.stack.Push(base)
|
|
||||||
case oLT:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// x < y
|
|
||||||
if x.Cmp(y) < 0 {
|
|
||||||
bm.stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
case oLE:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// x <= y
|
|
||||||
if x.Cmp(y) < 1 {
|
|
||||||
bm.stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
case oGT:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// x > y
|
|
||||||
if x.Cmp(y) > 0 {
|
|
||||||
bm.stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
case oGE:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// x >= y
|
|
||||||
if x.Cmp(y) > -1 {
|
|
||||||
bm.stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
case oNOT:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
// x != y
|
|
||||||
if x.Cmp(y) != 0 {
|
|
||||||
bm.stack.Push(ethutil.BigTrue)
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Please note that the following code contains some
|
|
||||||
// ugly string casting. This will have to change to big
|
|
||||||
// ints. TODO :)
|
|
||||||
case oMYADDRESS:
|
|
||||||
bm.stack.Push(ethutil.BigD(tx.Hash()))
|
|
||||||
case oTXSENDER:
|
|
||||||
bm.stack.Push(ethutil.BigD(tx.Sender()))
|
|
||||||
case oTXVALUE:
|
|
||||||
bm.stack.Push(tx.Value)
|
|
||||||
case oTXDATAN:
|
|
||||||
bm.stack.Push(big.NewInt(int64(len(tx.Data))))
|
|
||||||
case oTXDATA:
|
|
||||||
v := bm.stack.Pop()
|
|
||||||
// v >= len(data)
|
|
||||||
if v.Cmp(big.NewInt(int64(len(tx.Data)))) >= 0 {
|
|
||||||
bm.stack.Push(ethutil.Big("0"))
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(ethutil.Big(tx.Data[v.Uint64()]))
|
|
||||||
}
|
|
||||||
case oBLK_PREVHASH:
|
|
||||||
bm.stack.Push(ethutil.Big(block.PrevHash))
|
|
||||||
case oBLK_COINBASE:
|
|
||||||
bm.stack.Push(ethutil.Big(block.Coinbase))
|
|
||||||
case oBLK_TIMESTAMP:
|
|
||||||
bm.stack.Push(big.NewInt(block.Time))
|
|
||||||
case oBLK_NUMBER:
|
|
||||||
bm.stack.Push(blockInfo.Number)
|
|
||||||
case oBLK_DIFFICULTY:
|
|
||||||
bm.stack.Push(block.Difficulty)
|
|
||||||
case oBASEFEE:
|
|
||||||
// e = 10^21
|
|
||||||
e := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(21), big.NewInt(0))
|
|
||||||
d := new(big.Rat)
|
|
||||||
d.SetInt(block.Difficulty)
|
|
||||||
c := new(big.Rat)
|
|
||||||
c.SetFloat64(0.5)
|
|
||||||
// d = diff / 0.5
|
|
||||||
d.Quo(d, c)
|
|
||||||
// base = floor(d)
|
|
||||||
base.Div(d.Num(), d.Denom())
|
|
||||||
|
|
||||||
x := new(big.Int)
|
|
||||||
x.Div(e, base)
|
|
||||||
|
|
||||||
// x = floor(10^21 / floor(diff^0.5))
|
|
||||||
bm.stack.Push(x)
|
|
||||||
case oSHA256, oSHA3, oRIPEMD160:
|
|
||||||
// This is probably save
|
|
||||||
// ceil(pop / 32)
|
|
||||||
length := int(math.Ceil(float64(bm.stack.Pop().Uint64()) / 32.0))
|
|
||||||
// New buffer which will contain the concatenated popped items
|
|
||||||
data := new(bytes.Buffer)
|
|
||||||
for i := 0; i < length; i++ {
|
|
||||||
// Encode the number to bytes and have it 32bytes long
|
|
||||||
num := ethutil.NumberToBytes(bm.stack.Pop().Bytes(), 256)
|
|
||||||
data.WriteString(string(num))
|
|
||||||
}
|
|
||||||
|
|
||||||
if op == oSHA256 {
|
|
||||||
bm.stack.Push(base.SetBytes(ethutil.Sha256Bin(data.Bytes())))
|
|
||||||
} else if op == oSHA3 {
|
|
||||||
bm.stack.Push(base.SetBytes(ethutil.Sha3Bin(data.Bytes())))
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(base.SetBytes(ethutil.Ripemd160(data.Bytes())))
|
|
||||||
}
|
|
||||||
case oECMUL:
|
|
||||||
y := bm.stack.Pop()
|
|
||||||
x := bm.stack.Pop()
|
|
||||||
//n := bm.stack.Pop()
|
|
||||||
|
|
||||||
//if ethutil.Big(x).Cmp(ethutil.Big(y)) {
|
|
||||||
data := new(bytes.Buffer)
|
|
||||||
data.WriteString(x.String())
|
|
||||||
data.WriteString(y.String())
|
|
||||||
if secp256k1.VerifyPubkeyValidity(data.Bytes()) == 1 {
|
|
||||||
// TODO
|
|
||||||
} else {
|
|
||||||
// Invalid, push infinity
|
|
||||||
bm.stack.Push(ethutil.Big("0"))
|
|
||||||
bm.stack.Push(ethutil.Big("0"))
|
|
||||||
}
|
|
||||||
//} else {
|
|
||||||
// // Invalid, push infinity
|
|
||||||
// bm.stack.Push("0")
|
|
||||||
// bm.stack.Push("0")
|
|
||||||
//}
|
|
||||||
|
|
||||||
case oECADD:
|
|
||||||
case oECSIGN:
|
|
||||||
case oECRECOVER:
|
|
||||||
case oECVALID:
|
|
||||||
case oPUSH:
|
|
||||||
pc++
|
|
||||||
bm.stack.Push(bm.mem[strconv.Itoa(pc)])
|
|
||||||
case oPOP:
|
|
||||||
// Pop current value of the stack
|
|
||||||
bm.stack.Pop()
|
|
||||||
case oDUP:
|
|
||||||
// Dup top stack
|
|
||||||
x := bm.stack.Pop()
|
|
||||||
bm.stack.Push(x)
|
|
||||||
bm.stack.Push(x)
|
|
||||||
case oSWAP:
|
|
||||||
// Swap two top most values
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
bm.stack.Push(y)
|
|
||||||
bm.stack.Push(x)
|
|
||||||
case oMLOAD:
|
|
||||||
x := bm.stack.Pop()
|
|
||||||
bm.stack.Push(bm.mem[x.String()])
|
|
||||||
case oMSTORE:
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
bm.mem[x.String()] = y
|
|
||||||
case oSLOAD:
|
|
||||||
// Load the value in storage and push it on the stack
|
|
||||||
x := bm.stack.Pop()
|
|
||||||
// decode the object as a big integer
|
|
||||||
decoder := ethutil.NewRlpDecoder([]byte(contract.State().Get(x.String())))
|
|
||||||
if !decoder.IsNil() {
|
|
||||||
bm.stack.Push(decoder.AsBigInt())
|
|
||||||
} else {
|
|
||||||
bm.stack.Push(ethutil.BigFalse)
|
|
||||||
}
|
|
||||||
case oSSTORE:
|
|
||||||
// Store Y at index X
|
|
||||||
x, y := bm.stack.Popn()
|
|
||||||
contract.State().Update(x.String(), string(ethutil.Encode(y)))
|
|
||||||
case oJMP:
|
|
||||||
x := int(bm.stack.Pop().Uint64())
|
|
||||||
// Set pc to x - 1 (minus one so the incrementing at the end won't effect it)
|
|
||||||
pc = x
|
|
||||||
pc--
|
|
||||||
case oJMPI:
|
|
||||||
x := bm.stack.Pop()
|
|
||||||
// Set pc to x if it's non zero
|
|
||||||
if x.Cmp(ethutil.BigFalse) != 0 {
|
|
||||||
pc = int(x.Uint64())
|
|
||||||
pc--
|
|
||||||
}
|
|
||||||
case oIND:
|
|
||||||
bm.stack.Push(big.NewInt(int64(pc)))
|
|
||||||
case oEXTRO:
|
|
||||||
memAddr := bm.stack.Pop()
|
|
||||||
contractAddr := bm.stack.Pop().Bytes()
|
|
||||||
|
|
||||||
// Push the contract's memory on to the stack
|
|
||||||
bm.stack.Push(getContractMemory(block, contractAddr, memAddr))
|
|
||||||
case oBALANCE:
|
|
||||||
// Pushes the balance of the popped value on to the stack
|
|
||||||
d := block.State().Get(bm.stack.Pop().String())
|
|
||||||
ether := ethutil.NewEtherFromData([]byte(d))
|
|
||||||
bm.stack.Push(ether.Amount)
|
|
||||||
case oMKTX:
|
|
||||||
value, addr := bm.stack.Popn()
|
|
||||||
from, length := bm.stack.Popn()
|
|
||||||
|
|
||||||
j := 0
|
|
||||||
dataItems := make([]string, int(length.Uint64()))
|
|
||||||
for i := from.Uint64(); i < length.Uint64(); i++ {
|
|
||||||
dataItems[j] = string(bm.mem[strconv.Itoa(int(i))].Bytes())
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
// TODO sign it?
|
|
||||||
tx := ethutil.NewTransaction(string(addr.Bytes()), value, dataItems)
|
|
||||||
// Add the transaction to the tx pool
|
|
||||||
bm.server.txPool.QueueTransaction(tx)
|
|
||||||
case oSUICIDE:
|
|
||||||
//addr := bm.stack.Pop()
|
|
||||||
}
|
|
||||||
pc++
|
|
||||||
}
|
|
||||||
|
|
||||||
bm.stack.Print()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns an address from the specified contract's address
|
|
||||||
func getContractMemory(block *ethutil.Block, contractAddr []byte, memAddr *big.Int) *big.Int {
|
|
||||||
contract := block.GetContract(contractAddr)
|
|
||||||
if contract == nil {
|
|
||||||
log.Panicf("invalid contract addr %x", contractAddr)
|
|
||||||
}
|
|
||||||
val := contract.State().Get(memAddr.String())
|
|
||||||
|
|
||||||
// decode the object as a big integer
|
|
||||||
decoder := ethutil.NewRlpDecoder([]byte(val))
|
|
||||||
if decoder.IsNil() {
|
|
||||||
return ethutil.BigFalse
|
|
||||||
}
|
|
||||||
|
|
||||||
return decoder.AsBigInt()
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestVm(t *testing.T) {
|
|
||||||
InitFees()
|
|
||||||
|
|
||||||
db, _ := NewMemDatabase()
|
|
||||||
Db = db
|
|
||||||
|
|
||||||
ctrct := NewTransaction("", 200000000, []string{
|
|
||||||
"PUSH", "1a2f2e",
|
|
||||||
"PUSH", "hallo",
|
|
||||||
"POP", // POP hallo
|
|
||||||
"PUSH", "3",
|
|
||||||
"LOAD", // Load hallo back on the stack
|
|
||||||
|
|
||||||
"PUSH", "1",
|
|
||||||
"PUSH", "2",
|
|
||||||
"ADD",
|
|
||||||
|
|
||||||
"PUSH", "2",
|
|
||||||
"PUSH", "1",
|
|
||||||
"SUB",
|
|
||||||
|
|
||||||
"PUSH", "100000000000000000000000",
|
|
||||||
"PUSH", "10000000000000",
|
|
||||||
"SDIV",
|
|
||||||
|
|
||||||
"PUSH", "105",
|
|
||||||
"PUSH", "200",
|
|
||||||
"MOD",
|
|
||||||
|
|
||||||
"PUSH", "100000000000000000000000",
|
|
||||||
"PUSH", "10000000000000",
|
|
||||||
"SMOD",
|
|
||||||
|
|
||||||
"PUSH", "5",
|
|
||||||
"PUSH", "10",
|
|
||||||
"LT",
|
|
||||||
|
|
||||||
"PUSH", "5",
|
|
||||||
"PUSH", "5",
|
|
||||||
"LE",
|
|
||||||
|
|
||||||
"PUSH", "50",
|
|
||||||
"PUSH", "5",
|
|
||||||
"GT",
|
|
||||||
|
|
||||||
"PUSH", "5",
|
|
||||||
"PUSH", "5",
|
|
||||||
"GE",
|
|
||||||
|
|
||||||
"PUSH", "10",
|
|
||||||
"PUSH", "10",
|
|
||||||
"NOT",
|
|
||||||
|
|
||||||
"MYADDRESS",
|
|
||||||
"TXSENDER",
|
|
||||||
|
|
||||||
"STOP",
|
|
||||||
})
|
|
||||||
tx := NewTransaction("1e8a42ea8cce13", 100, []string{})
|
|
||||||
|
|
||||||
block := CreateBlock("", 0, "", "c014ba53", 0, 0, "", []*Transaction{ctrct, tx})
|
|
||||||
db.Put(block.Hash(), block.RlpEncode())
|
|
||||||
|
|
||||||
bm := NewBlockManager()
|
|
||||||
bm.ProcessBlock(block)
|
|
||||||
}
|
|
149
dagger.go
149
dagger.go
@ -1,149 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ethereum/ethutil-go"
|
|
||||||
"github.com/obscuren/sha3"
|
|
||||||
"hash"
|
|
||||||
"math/big"
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
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))
|
|
||||||
log.Printf("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 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))
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ethereum/ethutil-go"
|
|
||||||
"math/big"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
@ -5,6 +5,8 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ethereum/eth-go"
|
||||||
|
"github.com/ethereum/ethchain-go"
|
||||||
"github.com/ethereum/ethdb-go"
|
"github.com/ethereum/ethdb-go"
|
||||||
"github.com/ethereum/ethutil-go"
|
"github.com/ethereum/ethutil-go"
|
||||||
"os"
|
"os"
|
||||||
@ -12,16 +14,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Console struct {
|
type Console struct {
|
||||||
db *ethdb.MemDatabase
|
db *ethdb.MemDatabase
|
||||||
trie *ethutil.Trie
|
trie *ethutil.Trie
|
||||||
server *Server
|
ethereum *eth.Ethereum
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConsole(s *Server) *Console {
|
func NewConsole(s *eth.Ethereum) *Console {
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
trie := ethutil.NewTrie(db, "")
|
trie := ethutil.NewTrie(db, "")
|
||||||
|
|
||||||
return &Console{db: db, trie: trie, server: s}
|
return &Console{db: db, trie: trie, ethereum: s}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Console) ValidateInput(action string, argumentLength int) error {
|
func (i *Console) ValidateInput(action string, argumentLength int) error {
|
||||||
@ -101,7 +103,7 @@ func (i *Console) ParseInput(input string) bool {
|
|||||||
case "print":
|
case "print":
|
||||||
i.db.Print()
|
i.db.Print()
|
||||||
case "dag":
|
case "dag":
|
||||||
fmt.Println(DaggerVerify(ethutil.Big(tokens[1]), // hash
|
fmt.Println(ethchain.DaggerVerify(ethutil.Big(tokens[1]), // hash
|
||||||
ethutil.BigPow(2, 36), // diff
|
ethutil.BigPow(2, 36), // diff
|
||||||
ethutil.Big(tokens[2]))) // nonce
|
ethutil.Big(tokens[2]))) // nonce
|
||||||
case "decode":
|
case "decode":
|
||||||
@ -112,7 +114,7 @@ func (i *Console) ParseInput(input string) bool {
|
|||||||
case "tx":
|
case "tx":
|
||||||
tx := ethutil.NewTransaction(tokens[1], ethutil.Big(tokens[2]), []string{""})
|
tx := ethutil.NewTransaction(tokens[1], ethutil.Big(tokens[2]), []string{""})
|
||||||
|
|
||||||
i.server.txPool.QueueTransaction(tx)
|
i.ethereum.TxPool.QueueTransaction(tx)
|
||||||
case "exit", "quit", "q":
|
case "exit", "quit", "q":
|
||||||
return false
|
return false
|
||||||
case "help":
|
case "help":
|
||||||
|
26
ethereum.go
26
ethereum.go
@ -3,6 +3,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ethereum/eth-go"
|
||||||
|
"github.com/ethereum/ethchain-go"
|
||||||
"github.com/ethereum/ethutil-go"
|
"github.com/ethereum/ethutil-go"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@ -23,8 +25,8 @@ func Init() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register interrupt handlers so we can stop the server
|
// Register interrupt handlers so we can stop the ethereum
|
||||||
func RegisterInterupts(s *Server) {
|
func RegisterInterupts(s *eth.Ethereum) {
|
||||||
// Buffered chan of one is enough
|
// Buffered chan of one is enough
|
||||||
c := make(chan os.Signal, 1)
|
c := make(chan os.Signal, 1)
|
||||||
// Notify about interrupts for now
|
// Notify about interrupts for now
|
||||||
@ -40,15 +42,13 @@ func RegisterInterupts(s *Server) {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
||||||
ethutil.InitFees()
|
|
||||||
|
|
||||||
Init()
|
Init()
|
||||||
|
|
||||||
|
ethutil.InitFees()
|
||||||
ethutil.ReadConfig()
|
ethutil.ReadConfig()
|
||||||
|
|
||||||
server, err := NewServer()
|
// Instantiated a eth stack
|
||||||
|
ethereum, err := eth.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return
|
return
|
||||||
@ -70,29 +70,29 @@ func main() {
|
|||||||
|
|
||||||
ethutil.Config.Log = log.New(file, "", 0)
|
ethutil.Config.Log = log.New(file, "", 0)
|
||||||
|
|
||||||
console := NewConsole(server)
|
console := NewConsole(ethereum)
|
||||||
go console.Start()
|
go console.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Starting Ethereum")
|
log.Println("Starting Ethereum")
|
||||||
|
|
||||||
RegisterInterupts(server)
|
RegisterInterupts(ethereum)
|
||||||
|
|
||||||
if StartMining {
|
if StartMining {
|
||||||
log.Println("Mining started")
|
log.Println("Mining started")
|
||||||
dagger := &Dagger{}
|
dagger := ðchain.Dagger{}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
res := dagger.Search(ethutil.Big("01001"), ethutil.BigPow(2, 36))
|
res := dagger.Search(ethutil.Big("01001"), ethutil.BigPow(2, 36))
|
||||||
log.Println("Res dagger", res)
|
log.Println("Res dagger", res)
|
||||||
//server.Broadcast("blockmine", ethutil.Encode(res.String()))
|
//ethereum.Broadcast("blockmine", ethutil.Encode(res.String()))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
server.Start()
|
ethereum.Start()
|
||||||
|
|
||||||
// Wait for shutdown
|
// Wait for shutdown
|
||||||
server.WaitForShutdown()
|
ethereum.WaitForShutdown()
|
||||||
}
|
}
|
||||||
|
303
peer.go
303
peer.go
@ -1,303 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ethereum/ethutil-go"
|
|
||||||
"github.com/ethereum/ethwire-go"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// The size of the output buffer for writing messages
|
|
||||||
outputBufferSize = 50
|
|
||||||
)
|
|
||||||
|
|
||||||
type Peer struct {
|
|
||||||
// Server interface
|
|
||||||
server *Server
|
|
||||||
// Net connection
|
|
||||||
conn net.Conn
|
|
||||||
// Output queue which is used to communicate and handle messages
|
|
||||||
outputQueue chan *ethwire.Msg
|
|
||||||
// Quit channel
|
|
||||||
quit chan bool
|
|
||||||
// Determines whether it's an inbound or outbound peer
|
|
||||||
inbound bool
|
|
||||||
// Flag for checking the peer's connectivity state
|
|
||||||
connected int32
|
|
||||||
disconnect int32
|
|
||||||
// Last known message send
|
|
||||||
lastSend time.Time
|
|
||||||
// Indicated whether a verack has been send or not
|
|
||||||
// This flag is used by writeMessage to check if messages are allowed
|
|
||||||
// to be send or not. If no version is known all messages are ignored.
|
|
||||||
versionKnown bool
|
|
||||||
|
|
||||||
// Last received pong message
|
|
||||||
lastPong int64
|
|
||||||
// Indicates whether a MsgGetPeersTy was requested of the peer
|
|
||||||
// this to prevent receiving false peers.
|
|
||||||
requestedPeerList bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPeer(conn net.Conn, server *Server, inbound bool) *Peer {
|
|
||||||
return &Peer{
|
|
||||||
outputQueue: make(chan *ethwire.Msg, outputBufferSize),
|
|
||||||
quit: make(chan bool),
|
|
||||||
server: server,
|
|
||||||
conn: conn,
|
|
||||||
inbound: inbound,
|
|
||||||
disconnect: 0,
|
|
||||||
connected: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewOutboundPeer(addr string, server *Server) *Peer {
|
|
||||||
p := &Peer{
|
|
||||||
outputQueue: make(chan *ethwire.Msg, outputBufferSize),
|
|
||||||
quit: make(chan bool),
|
|
||||||
server: server,
|
|
||||||
inbound: false,
|
|
||||||
connected: 0,
|
|
||||||
disconnect: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up the connection in another goroutine so we don't block the main thread
|
|
||||||
go func() {
|
|
||||||
conn, err := net.Dial("tcp", addr)
|
|
||||||
if err != nil {
|
|
||||||
p.Stop()
|
|
||||||
}
|
|
||||||
p.conn = conn
|
|
||||||
|
|
||||||
// Atomically set the connection state
|
|
||||||
atomic.StoreInt32(&p.connected, 1)
|
|
||||||
atomic.StoreInt32(&p.disconnect, 0)
|
|
||||||
|
|
||||||
log.Println("Connected to peer ::", conn.RemoteAddr())
|
|
||||||
|
|
||||||
p.Start()
|
|
||||||
}()
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Outputs any RLP encoded data to the peer
|
|
||||||
func (p *Peer) QueueMessage(msg *ethwire.Msg) {
|
|
||||||
p.outputQueue <- msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Peer) writeMessage(msg *ethwire.Msg) {
|
|
||||||
// Ignore the write if we're not connected
|
|
||||||
if atomic.LoadInt32(&p.connected) != 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !p.versionKnown {
|
|
||||||
switch msg.Type {
|
|
||||||
case ethwire.MsgHandshakeTy: // Ok
|
|
||||||
default: // Anything but ack is allowed
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := ethwire.WriteMessage(p.conn, msg)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Can't send message:", err)
|
|
||||||
// Stop the client if there was an error writing to it
|
|
||||||
p.Stop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
tickleTimer := time.NewTicker(2 * time.Minute)
|
|
||||||
out:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
// Main message queue. All outbound messages are processed through here
|
|
||||||
case msg := <-p.outputQueue:
|
|
||||||
p.writeMessage(msg)
|
|
||||||
|
|
||||||
p.lastSend = time.Now()
|
|
||||||
|
|
||||||
case <-tickleTimer.C:
|
|
||||||
p.writeMessage(ðwire.Msg{Type: ethwire.MsgPingTy})
|
|
||||||
|
|
||||||
// Break out of the for loop if a quit message is posted
|
|
||||||
case <-p.quit:
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clean:
|
|
||||||
// This loop is for draining the output queue and anybody waiting for us
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-p.outputQueue:
|
|
||||||
// TODO
|
|
||||||
default:
|
|
||||||
break clean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inbound handler. Inbound messages are received here and passed to the appropriate methods
|
|
||||||
func (p *Peer) HandleInbound() {
|
|
||||||
|
|
||||||
out:
|
|
||||||
for atomic.LoadInt32(&p.disconnect) == 0 {
|
|
||||||
// Wait for a message from the peer
|
|
||||||
msg, err := ethwire.ReadMessage(p.conn)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
|
|
||||||
if Debug {
|
|
||||||
log.Printf("Received %s\n", msg.Type.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
switch msg.Type {
|
|
||||||
case ethwire.MsgHandshakeTy:
|
|
||||||
// Version message
|
|
||||||
p.handleHandshake(msg)
|
|
||||||
case ethwire.MsgBlockTy:
|
|
||||||
err := p.server.blockManager.ProcessBlock(ethutil.NewBlock(msg.Data))
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
case ethwire.MsgTxTy:
|
|
||||||
p.server.txPool.QueueTransaction(ethutil.NewTransactionFromData(msg.Data))
|
|
||||||
case ethwire.MsgInvTy:
|
|
||||||
case ethwire.MsgGetPeersTy:
|
|
||||||
p.requestedPeerList = true
|
|
||||||
// Peer asked for list of connected peers
|
|
||||||
p.pushPeers()
|
|
||||||
case ethwire.MsgPeersTy:
|
|
||||||
// Received a list of peers (probably because MsgGetPeersTy was send)
|
|
||||||
// Only act on message if we actually requested for a peers list
|
|
||||||
if p.requestedPeerList {
|
|
||||||
data := ethutil.Conv(msg.Data)
|
|
||||||
// Create new list of possible peers for the server to process
|
|
||||||
peers := make([]string, data.Length())
|
|
||||||
// Parse each possible peer
|
|
||||||
for i := 0; i < data.Length(); i++ {
|
|
||||||
peers[i] = data.Get(i).AsString() + strconv.Itoa(int(data.Get(i).AsUint()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to the list of peers
|
|
||||||
p.server.ProcessPeerList(peers)
|
|
||||||
// Mark unrequested again
|
|
||||||
p.requestedPeerList = false
|
|
||||||
}
|
|
||||||
case ethwire.MsgPingTy:
|
|
||||||
// Respond back with pong
|
|
||||||
p.QueueMessage(ðwire.Msg{Type: ethwire.MsgPongTy})
|
|
||||||
case ethwire.MsgPongTy:
|
|
||||||
p.lastPong = time.Now().Unix()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Peer) Start() {
|
|
||||||
if !p.inbound {
|
|
||||||
err := p.pushHandshake()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Peer can't send outbound version ack", err)
|
|
||||||
|
|
||||||
p.Stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the outbound handler in a new goroutine
|
|
||||||
go p.HandleOutbound()
|
|
||||||
// Run the inbound handler in a new goroutine
|
|
||||||
go p.HandleInbound()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Peer) Stop() {
|
|
||||||
if atomic.AddInt32(&p.disconnect, 1) != 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
close(p.quit)
|
|
||||||
if atomic.LoadInt32(&p.connected) != 0 {
|
|
||||||
p.conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Peer shutdown")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Peer) pushHandshake() error {
|
|
||||||
msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, ethutil.Encode([]interface{}{
|
|
||||||
1, 0, p.server.Nonce,
|
|
||||||
}))
|
|
||||||
|
|
||||||
p.QueueMessage(msg)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pushes the list of outbound peers to the client when requested
|
|
||||||
func (p *Peer) pushPeers() {
|
|
||||||
outPeers := make([]interface{}, len(p.server.OutboundPeers()))
|
|
||||||
// Serialise each peer
|
|
||||||
for i, peer := range p.server.OutboundPeers() {
|
|
||||||
outPeers[i] = peer.RlpEncode()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send message to the peer with the known list of connected clients
|
|
||||||
msg := ethwire.NewMessage(ethwire.MsgPeersTy, ethutil.Encode(outPeers))
|
|
||||||
|
|
||||||
p.QueueMessage(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Peer) handleHandshake(msg *ethwire.Msg) {
|
|
||||||
c := ethutil.Conv(msg.Data)
|
|
||||||
// [PROTOCOL_VERSION, NETWORK_ID, CLIENT_ID]
|
|
||||||
if c.Get(2).AsUint() == p.server.Nonce {
|
|
||||||
//if msg.Nonce == p.server.Nonce {
|
|
||||||
log.Println("Peer connected to self, disconnecting")
|
|
||||||
|
|
||||||
p.Stop()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
p.versionKnown = true
|
|
||||||
|
|
||||||
// If this is an inbound connection send an ack back
|
|
||||||
if p.inbound {
|
|
||||||
err := p.pushHandshake()
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Peer can't send ack back")
|
|
||||||
|
|
||||||
p.Stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Peer) RlpEncode() []byte {
|
|
||||||
host, prt, err := net.SplitHostPort(p.conn.RemoteAddr().String())
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
i, err := strconv.Atoi(prt)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
port := ethutil.NumberToBytes(uint16(i), 16)
|
|
||||||
|
|
||||||
return ethutil.Encode([]interface{}{host, port})
|
|
||||||
}
|
|
208
server.go
208
server.go
@ -1,208 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/list"
|
|
||||||
"github.com/ethereum/ethdb-go"
|
|
||||||
"github.com/ethereum/ethutil-go"
|
|
||||||
"github.com/ethereum/ethwire-go"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func eachPeer(peers *list.List, callback func(*Peer, *list.Element)) {
|
|
||||||
// Loop thru the peers and close them (if we had them)
|
|
||||||
for e := peers.Front(); e != nil; e = e.Next() {
|
|
||||||
if peer, ok := e.Value.(*Peer); ok {
|
|
||||||
callback(peer, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
processReapingTimeout = 60 // TODO increase
|
|
||||||
)
|
|
||||||
|
|
||||||
type Server struct {
|
|
||||||
// Channel for shutting down the server
|
|
||||||
shutdownChan chan bool
|
|
||||||
// DB interface
|
|
||||||
//db *ethdb.LDBDatabase
|
|
||||||
db *ethdb.MemDatabase
|
|
||||||
// Block manager for processing new blocks and managing the block chain
|
|
||||||
blockManager *BlockManager
|
|
||||||
// The transaction pool. Transaction can be pushed on this pool
|
|
||||||
// for later including in the blocks
|
|
||||||
txPool *TxPool
|
|
||||||
// Peers (NYI)
|
|
||||||
peers *list.List
|
|
||||||
// Nonce
|
|
||||||
Nonce uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewServer() (*Server, error) {
|
|
||||||
//db, err := ethdb.NewLDBDatabase()
|
|
||||||
db, err := ethdb.NewMemDatabase()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ethutil.Config.Db = db
|
|
||||||
|
|
||||||
nonce, _ := ethutil.RandomUint64()
|
|
||||||
server := &Server{
|
|
||||||
shutdownChan: make(chan bool),
|
|
||||||
db: db,
|
|
||||||
peers: list.New(),
|
|
||||||
Nonce: nonce,
|
|
||||||
}
|
|
||||||
server.txPool = NewTxPool(server)
|
|
||||||
server.blockManager = NewBlockManager(server)
|
|
||||||
|
|
||||||
return server, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) AddPeer(conn net.Conn) {
|
|
||||||
peer := NewPeer(conn, s, true)
|
|
||||||
|
|
||||||
if peer != nil {
|
|
||||||
s.peers.PushBack(peer)
|
|
||||||
peer.Start()
|
|
||||||
|
|
||||||
log.Println("Peer connected ::", conn.RemoteAddr())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) ProcessPeerList(addrs []string) {
|
|
||||||
for _, addr := range addrs {
|
|
||||||
// TODO Probably requires some sanity checks
|
|
||||||
s.ConnectToPeer(addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) ConnectToPeer(addr string) error {
|
|
||||||
peer := NewOutboundPeer(addr, s)
|
|
||||||
|
|
||||||
s.peers.PushBack(peer)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) OutboundPeers() []*Peer {
|
|
||||||
// Create a new peer slice with at least the length of the total peers
|
|
||||||
outboundPeers := make([]*Peer, s.peers.Len())
|
|
||||||
length := 0
|
|
||||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
|
||||||
if !p.inbound {
|
|
||||||
outboundPeers[length] = p
|
|
||||||
length++
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return outboundPeers[:length]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) InboundPeers() []*Peer {
|
|
||||||
// Create a new peer slice with at least the length of the total peers
|
|
||||||
inboundPeers := make([]*Peer, s.peers.Len())
|
|
||||||
length := 0
|
|
||||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
|
||||||
if p.inbound {
|
|
||||||
inboundPeers[length] = p
|
|
||||||
length++
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return inboundPeers[:length]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) Broadcast(msgType ethwire.MsgType, data []byte) {
|
|
||||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
|
||||||
p.QueueMessage(ethwire.NewMessage(msgType, data))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) ReapDeadPeers() {
|
|
||||||
for {
|
|
||||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
|
||||||
if atomic.LoadInt32(&p.disconnect) == 1 || (p.inbound && (time.Now().Unix()-p.lastPong) > int64(5*time.Minute)) {
|
|
||||||
log.Println("Dead peer found .. reaping")
|
|
||||||
|
|
||||||
s.peers.Remove(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
time.Sleep(processReapingTimeout * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the server
|
|
||||||
func (s *Server) Start() {
|
|
||||||
// For now this function just blocks the main thread
|
|
||||||
ln, err := net.Listen("tcp", ":12345")
|
|
||||||
if err != nil {
|
|
||||||
// This is mainly for testing to create a "network"
|
|
||||||
if Debug {
|
|
||||||
log.Println("Connection listening disabled. Acting as client")
|
|
||||||
|
|
||||||
err = s.ConnectToPeer("localhost:12345")
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Error starting server", err)
|
|
||||||
|
|
||||||
s.Stop()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Starting accepting connections
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
conn, err := ln.Accept()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
go s.AddPeer(conn)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the reaping processes
|
|
||||||
go s.ReapDeadPeers()
|
|
||||||
|
|
||||||
// Start the tx pool
|
|
||||||
s.txPool.Start()
|
|
||||||
|
|
||||||
// TMP
|
|
||||||
/*
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
s.Broadcast("block", s.blockManager.bc.GenesisBlock().RlpEncode())
|
|
||||||
|
|
||||||
time.Sleep(1000 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) Stop() {
|
|
||||||
// Close the database
|
|
||||||
defer s.db.Close()
|
|
||||||
|
|
||||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
|
||||||
p.Stop()
|
|
||||||
})
|
|
||||||
|
|
||||||
s.shutdownChan <- true
|
|
||||||
|
|
||||||
s.txPool.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function will wait for a shutdown and resumes main thread execution
|
|
||||||
func (s *Server) WaitForShutdown() {
|
|
||||||
<-s.shutdownChan
|
|
||||||
}
|
|
167
stack.go
167
stack.go
@ -1,167 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
)
|
|
||||||
|
|
||||||
type OpCode int
|
|
||||||
|
|
||||||
// Op codes
|
|
||||||
const (
|
|
||||||
oSTOP OpCode = iota
|
|
||||||
oADD
|
|
||||||
oMUL
|
|
||||||
oSUB
|
|
||||||
oDIV
|
|
||||||
oSDIV
|
|
||||||
oMOD
|
|
||||||
oSMOD
|
|
||||||
oEXP
|
|
||||||
oNEG
|
|
||||||
oLT
|
|
||||||
oLE
|
|
||||||
oGT
|
|
||||||
oGE
|
|
||||||
oEQ
|
|
||||||
oNOT
|
|
||||||
oMYADDRESS
|
|
||||||
oTXSENDER
|
|
||||||
oTXVALUE
|
|
||||||
oTXFEE
|
|
||||||
oTXDATAN
|
|
||||||
oTXDATA
|
|
||||||
oBLK_PREVHASH
|
|
||||||
oBLK_COINBASE
|
|
||||||
oBLK_TIMESTAMP
|
|
||||||
oBLK_NUMBER
|
|
||||||
oBLK_DIFFICULTY
|
|
||||||
oBASEFEE
|
|
||||||
oSHA256 OpCode = 32
|
|
||||||
oRIPEMD160 OpCode = 33
|
|
||||||
oECMUL OpCode = 34
|
|
||||||
oECADD OpCode = 35
|
|
||||||
oECSIGN OpCode = 36
|
|
||||||
oECRECOVER OpCode = 37
|
|
||||||
oECVALID OpCode = 38
|
|
||||||
oSHA3 OpCode = 39
|
|
||||||
oPUSH OpCode = 48
|
|
||||||
oPOP OpCode = 49
|
|
||||||
oDUP OpCode = 50
|
|
||||||
oSWAP OpCode = 51
|
|
||||||
oMLOAD OpCode = 52
|
|
||||||
oMSTORE OpCode = 53
|
|
||||||
oSLOAD OpCode = 54
|
|
||||||
oSSTORE OpCode = 55
|
|
||||||
oJMP OpCode = 56
|
|
||||||
oJMPI OpCode = 57
|
|
||||||
oIND OpCode = 58
|
|
||||||
oEXTRO OpCode = 59
|
|
||||||
oBALANCE OpCode = 60
|
|
||||||
oMKTX OpCode = 61
|
|
||||||
oSUICIDE OpCode = 62
|
|
||||||
)
|
|
||||||
|
|
||||||
// Since the opcodes aren't all in order we can't use a regular slice
|
|
||||||
var opCodeToString = map[OpCode]string{
|
|
||||||
oSTOP: "STOP",
|
|
||||||
oADD: "ADD",
|
|
||||||
oMUL: "MUL",
|
|
||||||
oSUB: "SUB",
|
|
||||||
oDIV: "DIV",
|
|
||||||
oSDIV: "SDIV",
|
|
||||||
oMOD: "MOD",
|
|
||||||
oSMOD: "SMOD",
|
|
||||||
oEXP: "EXP",
|
|
||||||
oNEG: "NEG",
|
|
||||||
oLT: "LT",
|
|
||||||
oLE: "LE",
|
|
||||||
oGT: "GT",
|
|
||||||
oGE: "GE",
|
|
||||||
oEQ: "EQ",
|
|
||||||
oNOT: "NOT",
|
|
||||||
oMYADDRESS: "MYADDRESS",
|
|
||||||
oTXSENDER: "TXSENDER",
|
|
||||||
oTXVALUE: "TXVALUE",
|
|
||||||
oTXFEE: "TXFEE",
|
|
||||||
oTXDATAN: "TXDATAN",
|
|
||||||
oTXDATA: "TXDATA",
|
|
||||||
oBLK_PREVHASH: "BLK_PREVHASH",
|
|
||||||
oBLK_COINBASE: "BLK_COINBASE",
|
|
||||||
oBLK_TIMESTAMP: "BLK_TIMESTAMP",
|
|
||||||
oBLK_NUMBER: "BLK_NUMBER",
|
|
||||||
oBLK_DIFFICULTY: "BLK_DIFFICULTY",
|
|
||||||
oBASEFEE: "BASEFEE",
|
|
||||||
oSHA256: "SHA256",
|
|
||||||
oRIPEMD160: "RIPEMD160",
|
|
||||||
oECMUL: "ECMUL",
|
|
||||||
oECADD: "ECADD",
|
|
||||||
oECSIGN: "ECSIGN",
|
|
||||||
oECRECOVER: "ECRECOVER",
|
|
||||||
oECVALID: "ECVALID",
|
|
||||||
oSHA3: "SHA3",
|
|
||||||
oPUSH: "PUSH",
|
|
||||||
oPOP: "POP",
|
|
||||||
oDUP: "DUP",
|
|
||||||
oSWAP: "SWAP",
|
|
||||||
oMLOAD: "MLOAD",
|
|
||||||
oMSTORE: "MSTORE",
|
|
||||||
oSLOAD: "SLOAD",
|
|
||||||
oSSTORE: "SSTORE",
|
|
||||||
oJMP: "JMP",
|
|
||||||
oJMPI: "JMPI",
|
|
||||||
oIND: "IND",
|
|
||||||
oEXTRO: "EXTRO",
|
|
||||||
oBALANCE: "BALANCE",
|
|
||||||
oMKTX: "MKTX",
|
|
||||||
oSUICIDE: "SUICIDE",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o OpCode) String() string {
|
|
||||||
return opCodeToString[o]
|
|
||||||
}
|
|
||||||
|
|
||||||
type OpType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
tNorm = iota
|
|
||||||
tData
|
|
||||||
tExtro
|
|
||||||
tCrypto
|
|
||||||
)
|
|
||||||
|
|
||||||
type TxCallback func(opType OpType) bool
|
|
||||||
|
|
||||||
// Simple push/pop stack mechanism
|
|
||||||
type Stack struct {
|
|
||||||
data []*big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStack() *Stack {
|
|
||||||
return &Stack{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *Stack) Pop() *big.Int {
|
|
||||||
s := len(st.data)
|
|
||||||
|
|
||||||
str := st.data[s-1]
|
|
||||||
st.data = st.data[:s-1]
|
|
||||||
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *Stack) Popn() (*big.Int, *big.Int) {
|
|
||||||
s := len(st.data)
|
|
||||||
|
|
||||||
ints := st.data[s-2:]
|
|
||||||
st.data = st.data[:s-2]
|
|
||||||
|
|
||||||
return ints[0], ints[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *Stack) Push(d *big.Int) {
|
|
||||||
st.data = append(st.data, d)
|
|
||||||
}
|
|
||||||
func (st *Stack) Print() {
|
|
||||||
fmt.Println(st.data)
|
|
||||||
}
|
|
@ -1,177 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"container/list"
|
|
||||||
"errors"
|
|
||||||
"github.com/ethereum/ethutil-go"
|
|
||||||
"github.com/ethereum/ethwire-go"
|
|
||||||
"log"
|
|
||||||
"math/big"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
txPoolQueueSize = 50
|
|
||||||
)
|
|
||||||
|
|
||||||
func FindTx(pool *list.List, finder func(*ethutil.Transaction, *list.Element) bool) *ethutil.Transaction {
|
|
||||||
for e := pool.Front(); e != nil; e = e.Next() {
|
|
||||||
if tx, ok := e.Value.(*ethutil.Transaction); ok {
|
|
||||||
if finder(tx, e) {
|
|
||||||
return tx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
server *Server
|
|
||||||
// The mutex for accessing the Tx pool.
|
|
||||||
mutex sync.Mutex
|
|
||||||
// Queueing channel for reading and writing incoming
|
|
||||||
// transactions to
|
|
||||||
queueChan chan *ethutil.Transaction
|
|
||||||
// Quiting channel
|
|
||||||
quit chan bool
|
|
||||||
|
|
||||||
pool *list.List
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTxPool(s *Server) *TxPool {
|
|
||||||
return &TxPool{
|
|
||||||
server: s,
|
|
||||||
mutex: sync.Mutex{},
|
|
||||||
pool: list.New(),
|
|
||||||
queueChan: make(chan *ethutil.Transaction, txPoolQueueSize),
|
|
||||||
quit: make(chan bool),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blocking function. Don't use directly. Use QueueTransaction instead
|
|
||||||
func (pool *TxPool) addTransaction(tx *ethutil.Transaction) {
|
|
||||||
pool.mutex.Lock()
|
|
||||||
pool.pool.PushBack(tx)
|
|
||||||
pool.mutex.Unlock()
|
|
||||||
|
|
||||||
// Broadcast the transaction to the rest of the peers
|
|
||||||
pool.server.Broadcast(ethwire.MsgTxTy, tx.RlpEncode())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process transaction validates the Tx and processes funds from the
|
|
||||||
// sender to the recipient.
|
|
||||||
func (pool *TxPool) processTransaction(tx *ethutil.Transaction) error {
|
|
||||||
// Get the last block so we can retrieve the sender and receiver from
|
|
||||||
// the merkle trie
|
|
||||||
block := pool.server.blockManager.bc.LastBlock
|
|
||||||
// Something has gone horribly wrong if this happens
|
|
||||||
if block == nil {
|
|
||||||
return errors.New("No last block on the block chain")
|
|
||||||
}
|
|
||||||
|
|
||||||
var sender, receiver *ethutil.Ether
|
|
||||||
|
|
||||||
// Get the sender
|
|
||||||
data := block.State().Get(string(tx.Sender()))
|
|
||||||
// If it doesn't exist create a new account. Of course trying to send funds
|
|
||||||
// from this account will fail since it will hold 0 Wei
|
|
||||||
if data == "" {
|
|
||||||
sender = ethutil.NewEther(big.NewInt(0))
|
|
||||||
} else {
|
|
||||||
sender = ethutil.NewEtherFromData([]byte(data))
|
|
||||||
}
|
|
||||||
// Defer the update. Whatever happens it should be persisted
|
|
||||||
defer block.State().Update(string(tx.Sender()), string(sender.RlpEncode()))
|
|
||||||
|
|
||||||
// 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(tx.Value) < 0 {
|
|
||||||
if Debug {
|
|
||||||
log.Println("Insufficient amount in sender's account. Adding 1 ETH for debug")
|
|
||||||
sender.Amount = ethutil.BigPow(10, 18)
|
|
||||||
} else {
|
|
||||||
return errors.New("Insufficient amount in sender's account")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subtract the amount from the senders account
|
|
||||||
sender.Amount.Sub(sender.Amount, tx.Value)
|
|
||||||
// Increment the nonce making each tx valid only once to prevent replay
|
|
||||||
// attacks
|
|
||||||
sender.Nonce += 1
|
|
||||||
|
|
||||||
// Get the receiver
|
|
||||||
data = block.State().Get(tx.Recipient)
|
|
||||||
// If the receiver doesn't exist yet, create a new account to which the
|
|
||||||
// funds will be send.
|
|
||||||
if data == "" {
|
|
||||||
receiver = ethutil.NewEther(big.NewInt(0))
|
|
||||||
} else {
|
|
||||||
receiver = ethutil.NewEtherFromData([]byte(data))
|
|
||||||
}
|
|
||||||
// Defer the update
|
|
||||||
defer block.State().Update(tx.Recipient, string(receiver.RlpEncode()))
|
|
||||||
|
|
||||||
// Add the amount to receivers account which should conclude this transaction
|
|
||||||
receiver.Amount.Add(receiver.Amount, tx.Value)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pool *TxPool) queueHandler() {
|
|
||||||
out:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case tx := <-pool.queueChan:
|
|
||||||
hash := tx.Hash()
|
|
||||||
foundTx := FindTx(pool.pool, func(tx *ethutil.Transaction, e *list.Element) bool {
|
|
||||||
return bytes.Compare(tx.Hash(), hash) == 0
|
|
||||||
})
|
|
||||||
|
|
||||||
if foundTx != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the transaction
|
|
||||||
err := pool.processTransaction(tx)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Error processing Tx", err)
|
|
||||||
} else {
|
|
||||||
// Call blocking version. At this point it
|
|
||||||
// doesn't matter since this is a goroutine
|
|
||||||
pool.addTransaction(tx)
|
|
||||||
}
|
|
||||||
case <-pool.quit:
|
|
||||||
break out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pool *TxPool) QueueTransaction(tx *ethutil.Transaction) {
|
|
||||||
pool.queueChan <- tx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pool *TxPool) Flush() {
|
|
||||||
pool.mutex.Lock()
|
|
||||||
|
|
||||||
defer pool.mutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pool *TxPool) Start() {
|
|
||||||
go pool.queueHandler()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pool *TxPool) Stop() {
|
|
||||||
log.Println("[TXP] Stopping...")
|
|
||||||
|
|
||||||
close(pool.quit)
|
|
||||||
|
|
||||||
pool.Flush()
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user