diff options
-rw-r--r-- | README.md | 18 | ||||
-rw-r--r-- | block_manager.go | 382 | ||||
-rw-r--r-- | block_manager_test.go (renamed from vm_test.go) | 3 | ||||
-rw-r--r-- | dagger_test.go | 3 | ||||
-rw-r--r-- | ethereum.go | 3 | ||||
-rw-r--r-- | peer.go | 127 | ||||
-rw-r--r-- | server.go | 62 | ||||
-rw-r--r-- | stack.go | 167 | ||||
-rw-r--r-- | test_runner_test.go | 22 | ||||
-rw-r--r-- | testing.go | 4 | ||||
-rw-r--r-- | vm.go | 284 |
11 files changed, 721 insertions, 354 deletions
@@ -5,6 +5,9 @@ Ethereum Ethereum Go (c) [0255c7881](https://github.com/ethereum/go-ethereum#copy) +A fair warning; Ethereum is not yet to be used in production. There's no +test-net and you aren't mining real blocks (just one which is the genesis block). + Ethereum Go is split up in several sub packages. Please refer to each individual package for more information. @@ -26,8 +29,10 @@ Install Command line options ==================== +``` -c launch the developer console -m start mining fake blocks and broadcast fake messages to the net +``` Contribution ============ @@ -45,8 +50,8 @@ Style](http://golang.org/doc/effective_go.html#formatting). Unless structs fields are supposed to be directly accesible, provide Getters and hide the fields through Go's exporting facility. -Don't "overcomment", meaning that your and my mom doesn't have to read -the source code. +When you comment put meaningfull comments. Describe in detail what you +want to achieve. *wrong* @@ -57,6 +62,15 @@ if x > y { } ``` +Everyone reading the source probably know what you wanted to achieve +with above code. Those are **not** meaningful comments. + +While the project isn't 100% tested I want you to write tests non the +less. I haven't got time to evaluate everyone's code in detail so I +expect you to write tests for me so I don't have to test your code +manually. (If you want to contribute by just writing tests that's fine +too!) + ### Copy 69bce990a619e747b4f57483724b0e8a1732bb3b44ccf70b0dd6abd272af94550fc9d8b21232d33ebf30d38a148612f68e936094b4daeb9ea7174088a439070401 0255c78815d4f056f84c96de438ed9e38c69c0f8af24f5032248be5a79fe9071c3 diff --git a/block_manager.go b/block_manager.go index 7e16cdb3a..9322d0d3a 100644 --- a/block_manager.go +++ b/block_manager.go @@ -1,18 +1,23 @@ package main import ( + "bytes" "errors" "fmt" "github.com/ethereum/ethutil-go" + "github.com/obscuren/secp256k1-go" "log" + "math" "math/big" + "strconv" ) 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 } @@ -21,11 +26,10 @@ func NewBlockChain() *BlockChain { bc.genesisBlock = ethutil.NewBlock(ethutil.Encode(ethutil.Genesis)) // Set the last know difficulty (might be 0x0 as initial value, Genesis) - bc.TD = new(big.Int) - bc.TD.SetBytes(ethutil.Config.Db.LastKnownTD()) + bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD()) // TODO get last block from the database - //bc.LastBlock = bc.genesisBlock + bc.LastBlock = bc.genesisBlock return bc } @@ -40,18 +44,29 @@ func (bc *BlockChain) GenesisBlock() *ethutil.Block { } type BlockManager struct { - // Ethereum virtual machine for processing contracts - vm *Vm // 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() *BlockManager { bm := &BlockManager{ - vm: NewVm(), - bc: NewBlockChain(), + 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 } @@ -89,14 +104,31 @@ func (bm *BlockManager) ProcessBlock(block *ethutil.Block) error { <-lockChan } + // Calculate the new total difficulty and sync back to the db if bm.CalculateTD(block) { - ethutil.Config.Db.Put(block.Hash(), block.MarshalRlp()) + 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 { @@ -171,7 +203,7 @@ func (bm *BlockManager) AccumelateRewards(block *ethutil.Block) error { // Reward amount of ether to the coinbase address ether.AddFee(ethutil.CalculateBlockReward(block, len(block.Uncles))) - block.State().Update(block.Coinbase, string(ether.MarshalRlp())) + block.State().Update(block.Coinbase, string(ether.RlpEncode())) // TODO Reward each uncle @@ -189,7 +221,7 @@ func (bm *BlockManager) ProcessContract(tx *ethutil.Transaction, block *ethutil. }() // Process contract - bm.vm.ProcContract(tx, block, func(opType OpType) bool { + 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 @@ -201,3 +233,329 @@ func (bm *BlockManager) ProcessContract(tx *ethutil.Transaction, block *ethutil. // 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, 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 { + 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 oSHA3: + 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: + case oSUICIDE: + } + 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/vm_test.go b/block_manager_test.go index cb70220e1..3dcf572fd 100644 --- a/vm_test.go +++ b/block_manager_test.go @@ -7,6 +7,7 @@ import ( ) +/* func TestVm(t *testing.T) { InitFees() @@ -68,7 +69,7 @@ func TestVm(t *testing.T) { tx := NewTransaction("1e8a42ea8cce13", 100, []string{}) block := CreateBlock("", 0, "", "c014ba53", 0, 0, "", []*Transaction{ctrct, tx}) - db.Put(block.Hash(), block.MarshalRlp()) + db.Put(block.Hash(), block.RlpEncode()) bm := NewBlockManager() bm.ProcessBlock( block ) diff --git a/dagger_test.go b/dagger_test.go index 58cdd6afd..616577a39 100644 --- a/dagger_test.go +++ b/dagger_test.go @@ -1,13 +1,14 @@ package main import ( + "github.com/ethereum/ethutil-go" "math/big" "testing" ) func BenchmarkDaggerSearch(b *testing.B) { hash := big.NewInt(0) - diff := BigPow(2, 36) + 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 diff --git a/ethereum.go b/ethereum.go index 83f656fe2..d74cb4ff2 100644 --- a/ethereum.go +++ b/ethereum.go @@ -65,7 +65,8 @@ func main() { go func() { for { res := dagger.Search(ethutil.Big("01001"), ethutil.BigPow(2, 36)) - server.Broadcast("blockmine", ethutil.Encode(res.String())) + log.Println("Res dagger", res) + //server.Broadcast("blockmine", ethutil.Encode(res.String())) } }() } @@ -5,13 +5,14 @@ import ( "github.com/ethereum/ethwire-go" "log" "net" + "strconv" "sync/atomic" "time" ) const ( // The size of the output buffer for writing messages - outputBufferSize = 50 + outputBufferSize = 50 ) type Peer struct { @@ -20,13 +21,13 @@ type Peer struct { // Net connection conn net.Conn // Output queue which is used to communicate and handle messages - outputQueue chan *ethwire.InOutMsg + 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 + connected int32 disconnect int32 // Last known message send lastSend time.Time @@ -34,11 +35,17 @@ type Peer struct { // 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.InOutMsg, outputBufferSize), + outputQueue: make(chan *ethwire.Msg, outputBufferSize), quit: make(chan bool), server: server, conn: conn, @@ -50,7 +57,7 @@ func NewPeer(conn net.Conn, server *Server, inbound bool) *Peer { func NewOutboundPeer(addr string, server *Server) *Peer { p := &Peer{ - outputQueue: make(chan *ethwire.InOutMsg, outputBufferSize), + outputQueue: make(chan *ethwire.Msg, outputBufferSize), quit: make(chan bool), server: server, inbound: false, @@ -79,19 +86,19 @@ func NewOutboundPeer(addr string, server *Server) *Peer { } // Outputs any RLP encoded data to the peer -func (p *Peer) QueueMessage(msg *ethwire.InOutMsg) { +func (p *Peer) QueueMessage(msg *ethwire.Msg) { p.outputQueue <- msg } -func (p *Peer) writeMessage(msg *ethwire.InOutMsg) { +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.MsgType { - case "verack": // Ok + switch msg.Type { + case ethwire.MsgHandshakeTy: // Ok default: // Anything but ack is allowed return } @@ -108,6 +115,8 @@ func (p *Peer) writeMessage(msg *ethwire.InOutMsg) { // 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 { @@ -116,6 +125,10 @@ out: 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 @@ -126,7 +139,7 @@ clean: // This loop is for draining the output queue and anybody waiting for us for { select { - case <- p.outputQueue: + case <-p.outputQueue: // TODO default: break clean @@ -148,23 +161,47 @@ out: } if Debug { - log.Printf("Received %s\n", msg.MsgType) + log.Printf("Received %s\n", msg.Type.String()) } - // TODO Hash data and check if for existence (= ignore) - - switch msg.MsgType { - case "verack": + switch msg.Type { + case ethwire.MsgHandshakeTy: // Version message - p.handleVersionAck(msg) - case "block": - err := p.server.blockManager.ProcessBlock(ethutil.NewBlock(msg.Data)) + p.handleHandshake(msg) + case ethwire.MsgBlockTy: + err := p.server.blockManager.ProcessBlock(ethutil.NewBlock(ethutil.Encode(msg.Data))) if err != nil { log.Println(err) } - case "blockmine": - d, _ := ethutil.Decode(msg.Data, 0) - log.Printf("block mined %s\n", d) + case ethwire.MsgTxTy: + //p.server.blockManager.AddToTransactionPool(ethutil.NewTransactionFromData(ethutil.Encode(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() } } @@ -173,7 +210,7 @@ out: func (p *Peer) Start() { if !p.inbound { - err := p.pushVersionAck() + err := p.pushHandshake() if err != nil { log.Printf("Peer can't send outbound version ack", err) @@ -200,17 +237,35 @@ func (p *Peer) Stop() { log.Println("Peer shutdown") } -func (p *Peer) pushVersionAck() error { - msg := ethwire.NewMessage("verack", p.server.Nonce, []byte("01")) +func (p *Peer) pushHandshake() error { + msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, ethutil.Encode([]interface{}{ + 1, 0, p.server.Nonce, + })) p.QueueMessage(msg) return nil } -func (p *Peer) handleVersionAck(msg *ethwire.InOutMsg) { - // Detect self connect - if msg.Nonce == p.server.Nonce { +// 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() @@ -222,7 +277,7 @@ func (p *Peer) handleVersionAck(msg *ethwire.InOutMsg) { // If this is an inbound connection send an ack back if p.inbound { - err := p.pushVersionAck() + err := p.pushHandshake() if err != nil { log.Println("Peer can't send ack back") @@ -230,3 +285,19 @@ func (p *Peer) handleVersionAck(msg *ethwire.InOutMsg) { } } } + +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}) +} @@ -7,8 +7,8 @@ import ( "github.com/ethereum/ethwire-go" "log" "net" - "time" "sync/atomic" + "time" ) func eachPeer(peers *list.List, callback func(*Peer, *list.Element)) { @@ -20,6 +20,9 @@ func eachPeer(peers *list.List, callback func(*Peer, *list.Element)) { } } +const ( + processReapingTimeout = 60 // TODO increase +) type Server struct { // Channel for shutting down the server @@ -67,6 +70,13 @@ func (s *Server) AddPeer(conn net.Conn) { } } +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) @@ -75,20 +85,44 @@ func (s *Server) ConnectToPeer(addr string) error { return nil } -func (s *Server) Broadcast(msgType string, data []byte) { +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) { - p.QueueMessage(ethwire.NewMessage(msgType, 0, data)) + if !p.inbound { + outboundPeers[length] = p + length++ + } }) + + return outboundPeers[:length] } -const ( - processReapingTimeout = 10 // TODO increase -) +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 { + 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) @@ -139,13 +173,13 @@ func (s *Server) Start() { // TMP /* - go func() { - for { - s.Broadcast("block", s.blockManager.bc.GenesisBlock().MarshalRlp()) + go func() { + for { + s.Broadcast("block", s.blockManager.bc.GenesisBlock().RlpEncode()) - time.Sleep(1000 * time.Millisecond) - } - }() + time.Sleep(1000 * time.Millisecond) + } + }() */ } @@ -154,7 +188,7 @@ func (s *Server) Stop() { defer s.db.Close() eachPeer(s.peers, func(p *Peer, e *list.Element) { - p.Stop() + p.Stop() }) s.shutdownChan <- true diff --git a/stack.go b/stack.go new file mode 100644 index 000000000..9d595d85b --- /dev/null +++ b/stack.go @@ -0,0 +1,167 @@ +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/test_runner_test.go b/test_runner_test.go index 5abe20002..0a0b0d69c 100644 --- a/test_runner_test.go +++ b/test_runner_test.go @@ -3,20 +3,24 @@ package main import ( "encoding/hex" _ "fmt" + "github.com/ethereum/ethdb-go" + "github.com/ethereum/ethutil-go" "testing" ) -var testsource = `{"Inputs":{ - "doe": "reindeer", - "dog": "puppy", - "dogglesworth": "cat" - }, - "Expectation":"e378927bfc1bd4f01a2e8d9f59bd18db8a208bb493ac0b00f93ce51d4d2af76c" +var testsource = ` +{ + "inputs":{ + "doe": "reindeer", + "dog": "puppy", + "dogglesworth": "cat" + }, + "expectation":"e378927bfc1bd4f01a2e8d9f59bd18db8a208bb493ac0b00f93ce51d4d2af76c" }` func TestTestRunner(t *testing.T) { - db, _ := NewMemDatabase() - trie := NewTrie(db, "") + db, _ := ethdb.NewMemDatabase() + trie := ethutil.NewTrie(db, "") runner := NewTestRunner(t) runner.RunFromString(testsource, func(source *TestSource) { @@ -24,7 +28,7 @@ func TestTestRunner(t *testing.T) { trie.Update(key, value) } - if hex.EncodeToString([]byte(trie.root)) != source.Expectation { + if hex.EncodeToString([]byte(trie.Root)) != source.Expectation { t.Error("trie root did not match") } }) diff --git a/testing.go b/testing.go index 5e2aec02b..849089a5d 100644 --- a/testing.go +++ b/testing.go @@ -16,11 +16,11 @@ func Testing() { bm := NewBlockManager() tx := NewTransaction("\x00", 20, []string{"PUSH"}) - txData := tx.MarshalRlp() + txData := tx.RlpEncode() //fmt.Printf("%q\n", txData) copyTx := &Transaction{} - copyTx.UnmarshalRlp(txData) + copyTx.RlpDecode(txData) //fmt.Println(tx) //fmt.Println(copyTx) diff --git a/vm.go b/vm.go deleted file mode 100644 index 96a3dfa05..000000000 --- a/vm.go +++ /dev/null @@ -1,284 +0,0 @@ -package main - -import ( - "fmt" - "github.com/ethereum/ethutil-go" - "math/big" - "strconv" -) - -// Op codes -const ( - oSTOP int = 0x00 - oADD int = 0x01 - oMUL int = 0x02 - oSUB int = 0x03 - oDIV int = 0x04 - oSDIV int = 0x05 - oMOD int = 0x06 - oSMOD int = 0x07 - oEXP int = 0x08 - oNEG int = 0x09 - oLT int = 0x0a - oLE int = 0x0b - oGT int = 0x0c - oGE int = 0x0d - oEQ int = 0x0e - oNOT int = 0x0f - oMYADDRESS int = 0x10 - oTXSENDER int = 0x11 - oTXVALUE int = 0x12 - oTXFEE int = 0x13 - oTXDATAN int = 0x14 - oTXDATA int = 0x15 - oBLK_PREVHASH int = 0x16 - oBLK_COINBASE int = 0x17 - oBLK_TIMESTAMP int = 0x18 - oBLK_NUMBER int = 0x19 - oBLK_DIFFICULTY int = 0x1a - oSHA256 int = 0x20 - oRIPEMD160 int = 0x21 - oECMUL int = 0x22 - oECADD int = 0x23 - oECSIGN int = 0x24 - oECRECOVER int = 0x25 - oECVALID int = 0x26 - oPUSH int = 0x30 - oPOP int = 0x31 - oDUP int = 0x32 - oDUPN int = 0x33 - oSWAP int = 0x34 - oSWAPN int = 0x35 - oLOAD int = 0x36 - oSTORE int = 0x37 - oJMP int = 0x40 - oJMPI int = 0x41 - oIND int = 0x42 - oEXTRO int = 0x50 - oBALANCE int = 0x51 - oMKTX int = 0x60 - oSUICIDE int = 0xff -) - -type OpType int - -const ( - tNorm = iota - tData - tExtro - tCrypto -) - -type TxCallback func(opType OpType) bool - -// Simple push/pop stack mechanism -type Stack struct { - data []string -} - -func NewStack() *Stack { - return &Stack{} -} -func (st *Stack) Pop() string { - 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) - - strs := st.data[s-2:] - st.data = st.data[:s-2] - - return ethutil.Big(strs[0]), ethutil.Big(strs[1]) -} - -func (st *Stack) Push(d string) { - st.data = append(st.data, d) -} -func (st *Stack) Print() { - fmt.Println(st.data) -} - -type Vm struct { - // Stack - stack *Stack -} - -func NewVm() *Vm { - return &Vm{ - stack: NewStack(), - } -} - -func (vm *Vm) ProcContract(tx *ethutil.Transaction, - block *ethutil.Block, cb TxCallback) { - // Instruction pointer - pc := 0 - - 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) - op, _, _ := ethutil.Instr(contract.State().Get(string(nb))) - - if !cb(0) { - break - } - - if Debug { - //fmt.Printf("%-3d %-4d\n", pc, op) - } - - switch op { - case oADD: - x, y := vm.stack.Popn() - // (x + y) % 2 ** 256 - base.Add(x, y) - base.Mod(base, Pow256) - // Pop result back on the stack - vm.stack.Push(base.String()) - case oSUB: - x, y := vm.stack.Popn() - // (x - y) % 2 ** 256 - base.Sub(x, y) - base.Mod(base, Pow256) - // Pop result back on the stack - vm.stack.Push(base.String()) - case oMUL: - x, y := vm.stack.Popn() - // (x * y) % 2 ** 256 - base.Mul(x, y) - base.Mod(base, Pow256) - // Pop result back on the stack - vm.stack.Push(base.String()) - case oDIV: - x, y := vm.stack.Popn() - // floor(x / y) - base.Div(x, y) - // Pop result back on the stack - vm.stack.Push(base.String()) - case oSDIV: - x, y := vm.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 - vm.stack.Push(z.String()) - case oMOD: - x, y := vm.stack.Popn() - base.Mod(x, y) - vm.stack.Push(base.String()) - case oSMOD: - x, y := vm.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 - vm.stack.Push(z.String()) - case oEXP: - x, y := vm.stack.Popn() - base.Exp(x, y, Pow256) - - vm.stack.Push(base.String()) - case oNEG: - base.Sub(Pow256, ethutil.Big(vm.stack.Pop())) - vm.stack.Push(base.String()) - case oLT: - x, y := vm.stack.Popn() - // x < y - if x.Cmp(y) < 0 { - vm.stack.Push("1") - } else { - vm.stack.Push("0") - } - case oLE: - x, y := vm.stack.Popn() - // x <= y - if x.Cmp(y) < 1 { - vm.stack.Push("1") - } else { - vm.stack.Push("0") - } - case oGT: - x, y := vm.stack.Popn() - // x > y - if x.Cmp(y) > 0 { - vm.stack.Push("1") - } else { - vm.stack.Push("0") - } - case oGE: - x, y := vm.stack.Popn() - // x >= y - if x.Cmp(y) > -1 { - vm.stack.Push("1") - } else { - vm.stack.Push("0") - } - case oNOT: - x, y := vm.stack.Popn() - // x != y - if x.Cmp(y) != 0 { - vm.stack.Push("1") - } else { - vm.stack.Push("0") - } - case oMYADDRESS: - vm.stack.Push(string(tx.Hash())) - case oTXSENDER: - vm.stack.Push(string(tx.Sender())) - case oPUSH: - // Get the next entry and pushes the value on the stack - pc++ - vm.stack.Push(contract.State().Get(string(ethutil.NumberToBytes(uint64(pc), 32)))) - case oPOP: - // Pop current value of the stack - vm.stack.Pop() - case oLOAD: - // Load instruction X on the stack - i, _ := strconv.Atoi(vm.stack.Pop()) - vm.stack.Push(contract.State().Get(string(ethutil.NumberToBytes(uint64(i), 32)))) - case oSTOP: - break out - } - pc++ - } - - vm.stack.Print() -} |