aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md18
-rw-r--r--block_manager.go382
-rw-r--r--block_manager_test.go (renamed from vm_test.go)3
-rw-r--r--dagger_test.go3
-rw-r--r--ethereum.go3
-rw-r--r--peer.go127
-rw-r--r--server.go62
-rw-r--r--stack.go167
-rw-r--r--test_runner_test.go22
-rw-r--r--testing.go4
-rw-r--r--vm.go284
11 files changed, 721 insertions, 354 deletions
diff --git a/README.md b/README.md
index 23fdda2e5..05e0156b9 100644
--- a/README.md
+++ b/README.md
@@ -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()))
}
}()
}
diff --git a/peer.go b/peer.go
index 8f68a9bec..158541028 100644
--- a/peer.go
+++ b/peer.go
@@ -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(&ethwire.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(&ethwire.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})
+}
diff --git a/server.go b/server.go
index f658750a9..7a29d1bd9 100644
--- a/server.go
+++ b/server.go
@@ -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()
-}