diff options
-rw-r--r-- | block_manager.go | 591 | ||||
-rw-r--r-- | block_manager_test.go | 73 | ||||
-rw-r--r-- | dagger.go | 149 | ||||
-rw-r--r-- | dagger_test.go | 18 | ||||
-rw-r--r-- | dev_console.go | 16 | ||||
-rw-r--r-- | ethereum.go | 26 | ||||
-rw-r--r-- | peer.go | 303 | ||||
-rw-r--r-- | server.go | 208 | ||||
-rw-r--r-- | stack.go | 167 | ||||
-rw-r--r-- | transaction_pool.go | 177 |
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 := ðchain.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(ð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}) -} 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() -} |