aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--block_manager.go591
-rw-r--r--block_manager_test.go73
-rw-r--r--dagger.go149
-rw-r--r--dagger_test.go18
-rw-r--r--dev_console.go16
-rw-r--r--ethereum.go26
-rw-r--r--peer.go303
-rw-r--r--server.go208
-rw-r--r--stack.go167
-rw-r--r--transaction_pool.go177
10 files changed, 22 insertions, 1706 deletions
diff --git a/block_manager.go b/block_manager.go
deleted file mode 100644
index 87af9f293..000000000
--- a/block_manager.go
+++ /dev/null
@@ -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()
-}
diff --git a/block_manager_test.go b/block_manager_test.go
deleted file mode 100644
index 5f200f3e7..000000000
--- a/block_manager_test.go
+++ /dev/null
@@ -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)
-}
diff --git a/dagger.go b/dagger.go
deleted file mode 100644
index 966bfa461..000000000
--- a/dagger.go
+++ /dev/null
@@ -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))
-}
-
diff --git a/dagger_test.go b/dagger_test.go
deleted file mode 100644
index 616577a39..000000000
--- a/dagger_test.go
+++ /dev/null
@@ -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)
-}
diff --git a/dev_console.go b/dev_console.go
index d14f019e5..b3e0d73f9 100644
--- a/dev_console.go
+++ b/dev_console.go
@@ -5,6 +5,8 @@ import (
"encoding/hex"
"errors"
"fmt"
+ "github.com/ethereum/eth-go"
+ "github.com/ethereum/ethchain-go"
"github.com/ethereum/ethdb-go"
"github.com/ethereum/ethutil-go"
"os"
@@ -12,16 +14,16 @@ import (
)
type Console struct {
- db *ethdb.MemDatabase
- trie *ethutil.Trie
- server *Server
+ db *ethdb.MemDatabase
+ trie *ethutil.Trie
+ ethereum *eth.Ethereum
}
-func NewConsole(s *Server) *Console {
+func NewConsole(s *eth.Ethereum) *Console {
db, _ := ethdb.NewMemDatabase()
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 {
@@ -101,7 +103,7 @@ func (i *Console) ParseInput(input string) bool {
case "print":
i.db.Print()
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.Big(tokens[2]))) // nonce
case "decode":
@@ -112,7 +114,7 @@ func (i *Console) ParseInput(input string) bool {
case "tx":
tx := ethutil.NewTransaction(tokens[1], ethutil.Big(tokens[2]), []string{""})
- i.server.txPool.QueueTransaction(tx)
+ i.ethereum.TxPool.QueueTransaction(tx)
case "exit", "quit", "q":
return false
case "help":
diff --git a/ethereum.go b/ethereum.go
index b7f059a02..7988f8418 100644
--- a/ethereum.go
+++ b/ethereum.go
@@ -3,6 +3,8 @@ package main
import (
"flag"
"fmt"
+ "github.com/ethereum/eth-go"
+ "github.com/ethereum/ethchain-go"
"github.com/ethereum/ethutil-go"
"log"
"os"
@@ -23,8 +25,8 @@ func Init() {
flag.Parse()
}
-// Register interrupt handlers so we can stop the server
-func RegisterInterupts(s *Server) {
+// Register interrupt handlers so we can stop the ethereum
+func RegisterInterupts(s *eth.Ethereum) {
// Buffered chan of one is enough
c := make(chan os.Signal, 1)
// Notify about interrupts for now
@@ -40,15 +42,13 @@ func RegisterInterupts(s *Server) {
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
-
- ethutil.InitFees()
-
Init()
+ ethutil.InitFees()
ethutil.ReadConfig()
- server, err := NewServer()
-
+ // Instantiated a eth stack
+ ethereum, err := eth.New()
if err != nil {
log.Println(err)
return
@@ -70,29 +70,29 @@ func main() {
ethutil.Config.Log = log.New(file, "", 0)
- console := NewConsole(server)
+ console := NewConsole(ethereum)
go console.Start()
}
log.Println("Starting Ethereum")
- RegisterInterupts(server)
+ RegisterInterupts(ethereum)
if StartMining {
log.Println("Mining started")
- dagger := &Dagger{}
+ dagger := &ethchain.Dagger{}
go func() {
for {
res := dagger.Search(ethutil.Big("01001"), ethutil.BigPow(2, 36))
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
- server.WaitForShutdown()
+ ethereum.WaitForShutdown()
}
diff --git a/peer.go b/peer.go
deleted file mode 100644
index 207f9e59f..000000000
--- a/peer.go
+++ /dev/null
@@ -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(&ethwire.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(&ethwire.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})
-}
diff --git a/server.go b/server.go
deleted file mode 100644
index 2927f023a..000000000
--- a/server.go
+++ /dev/null
@@ -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
-}
diff --git a/stack.go b/stack.go
deleted file mode 100644
index 9d595d85b..000000000
--- a/stack.go
+++ /dev/null
@@ -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)
-}
diff --git a/transaction_pool.go b/transaction_pool.go
deleted file mode 100644
index b302931de..000000000
--- a/transaction_pool.go
+++ /dev/null
@@ -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()
-}