mirror of https://github.com/ethereum/go-ethereum
parent
3616080db4
commit
477e8a7a73
@ -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) |
||||
} |
@ -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) |
||||
} |
@ -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}) |
||||
} |
@ -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 |
||||
} |
@ -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