gofmt no coding standards

This commit is contained in:
obscuren 2014-01-11 15:27:08 +01:00
parent 8bbf879cb3
commit 9571a51286
14 changed files with 922 additions and 887 deletions

View File

@ -21,3 +21,8 @@ Command line options
-c launch the developer console -c launch the developer console
-m start mining fake blocks and broadcast fake messages to the net -m start mining fake blocks and broadcast fake messages to the net
Contribution
============
See CONTRIB.md

View File

@ -1,90 +1,90 @@
package main package main
import ( import (
"fmt" "fmt"
"github.com/ethereum/ethutil-go" "github.com/ethereum/ethutil-go"
) )
type BlockChain struct { type BlockChain struct {
lastBlock *ethutil.Block lastBlock *ethutil.Block
genesisBlock *ethutil.Block genesisBlock *ethutil.Block
} }
func NewBlockChain() *BlockChain { func NewBlockChain() *BlockChain {
bc := &BlockChain{} bc := &BlockChain{}
bc.genesisBlock = ethutil.NewBlock( ethutil.Encode(ethutil.Genesis) ) bc.genesisBlock = ethutil.NewBlock(ethutil.Encode(ethutil.Genesis))
return bc return bc
} }
type BlockManager struct { type BlockManager struct {
vm *Vm vm *Vm
blockChain *BlockChain blockChain *BlockChain
} }
func NewBlockManager() *BlockManager { func NewBlockManager() *BlockManager {
bm := &BlockManager{vm: NewVm()} bm := &BlockManager{vm: NewVm()}
return bm return bm
} }
// Process a block. // Process a block.
func (bm *BlockManager) ProcessBlock(block *ethutil.Block) error { func (bm *BlockManager) ProcessBlock(block *ethutil.Block) error {
// TODO Validation (Or move to other part of the application) // TODO Validation (Or move to other part of the application)
if err := bm.ValidateBlock(block); err != nil { if err := bm.ValidateBlock(block); err != nil {
return err return err
} }
// Get the tx count. Used to create enough channels to 'join' the go routines // Get the tx count. Used to create enough channels to 'join' the go routines
txCount := len(block.Transactions()) txCount := len(block.Transactions())
// Locking channel. When it has been fully buffered this method will return // Locking channel. When it has been fully buffered this method will return
lockChan := make(chan bool, txCount) lockChan := make(chan bool, txCount)
// Process each transaction/contract // Process each transaction/contract
for _, tx := range block.Transactions() { for _, tx := range block.Transactions() {
// If there's no recipient, it's a contract // If there's no recipient, it's a contract
if tx.IsContract() { if tx.IsContract() {
go bm.ProcessContract(tx, block, lockChan) go bm.ProcessContract(tx, block, lockChan)
} else { } else {
// "finish" tx which isn't a contract // "finish" tx which isn't a contract
lockChan <- true lockChan <- true
} }
} }
// Wait for all Tx to finish processing // Wait for all Tx to finish processing
for i := 0; i < txCount; i++ { for i := 0; i < txCount; i++ {
<- lockChan <-lockChan
} }
return nil return nil
} }
func (bm *BlockManager) ValidateBlock(block *ethutil.Block) error { func (bm *BlockManager) ValidateBlock(block *ethutil.Block) error {
return nil return nil
} }
func (bm *BlockManager) ProcessContract(tx *ethutil.Transaction, block *ethutil.Block, lockChan chan bool) { func (bm *BlockManager) ProcessContract(tx *ethutil.Transaction, block *ethutil.Block, lockChan chan bool) {
// Recovering function in case the VM had any errors // Recovering function in case the VM had any errors
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
fmt.Println("Recovered from VM execution with err =", r) 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) // Let the channel know where done even though it failed (so the execution may resume normally)
lockChan <- true lockChan <- true
} }
}() }()
// Process contract // Process contract
bm.vm.ProcContract(tx, block, func(opType OpType) bool { bm.vm.ProcContract(tx, block, func(opType OpType) bool {
// TODO turn on once big ints are in place // TODO turn on once big ints are in place
//if !block.PayFee(tx.Hash(), StepFee.Uint64()) { //if !block.PayFee(tx.Hash(), StepFee.Uint64()) {
// return false // return false
//} //}
return true // Continue return true // Continue
}) })
// Broadcast we're done // Broadcast we're done
lockChan <- true lockChan <- true
} }

187
dagger.go
View File

@ -1,142 +1,145 @@
package main package main
import ( import (
"math/big" "github.com/ethereum/ethutil-go"
"math/rand" "github.com/obscuren/sha3"
"time" "hash"
"github.com/obscuren/sha3" "math/big"
"hash" "math/rand"
"github.com/ethereum/ethutil-go" "time"
) )
type Dagger struct { type Dagger struct {
hash *big.Int hash *big.Int
xn *big.Int xn *big.Int
} }
var Found bool var Found bool
func (dag *Dagger) Find(obj *big.Int, resChan chan int64) { func (dag *Dagger) Find(obj *big.Int, resChan chan int64) {
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
rnd := r.Int63() rnd := r.Int63()
if dag.Eval(big.NewInt(rnd)).Cmp(obj) < 0 { if dag.Eval(big.NewInt(rnd)).Cmp(obj) < 0 {
// Post back result on the channel // Post back result on the channel
resChan <- rnd resChan <- rnd
// Notify other threads we've found a valid nonce // Notify other threads we've found a valid nonce
Found = true Found = true
} }
// Break out if found // Break out if found
if Found { break } if Found {
} break
}
}
resChan <- 0 resChan <- 0
} }
func (dag *Dagger) Search(hash, diff *big.Int) *big.Int { func (dag *Dagger) Search(hash, diff *big.Int) *big.Int {
// TODO fix multi threading. Somehow it results in the wrong nonce // TODO fix multi threading. Somehow it results in the wrong nonce
amountOfRoutines := 1 amountOfRoutines := 1
dag.hash = hash dag.hash = hash
obj := ethutil.BigPow(2, 256) obj := ethutil.BigPow(2, 256)
obj = obj.Div(obj, diff) obj = obj.Div(obj, diff)
Found = false Found = false
resChan := make(chan int64, 3) resChan := make(chan int64, 3)
var res int64 var res int64
for k := 0; k < amountOfRoutines; k++ { for k := 0; k < amountOfRoutines; k++ {
go dag.Find(obj, resChan) go dag.Find(obj, resChan)
} }
// Wait for each go routine to finish // Wait for each go routine to finish
for k := 0; k < amountOfRoutines; k++ { for k := 0; k < amountOfRoutines; k++ {
// Get the result from the channel. 0 = quit // Get the result from the channel. 0 = quit
if r := <- resChan; r != 0 { if r := <-resChan; r != 0 {
res = r res = r
} }
} }
return big.NewInt(res) return big.NewInt(res)
} }
func DaggerVerify(hash, diff, nonce *big.Int) bool { func DaggerVerify(hash, diff, nonce *big.Int) bool {
dagger := &Dagger{} dagger := &Dagger{}
dagger.hash = hash dagger.hash = hash
obj := ethutil.BigPow(2, 256) obj := ethutil.BigPow(2, 256)
obj = obj.Div(obj, diff) obj = obj.Div(obj, diff)
return dagger.Eval(nonce).Cmp(obj) < 0 return dagger.Eval(nonce).Cmp(obj) < 0
} }
func (dag *Dagger) Node(L uint64, i uint64) *big.Int { func (dag *Dagger) Node(L uint64, i uint64) *big.Int {
if L == i { if L == i {
return dag.hash return dag.hash
} }
var m *big.Int var m *big.Int
if L == 9 { if L == 9 {
m = big.NewInt(16) m = big.NewInt(16)
} else { } else {
m = big.NewInt(3) m = big.NewInt(3)
} }
sha := sha3.NewKeccak256() sha := sha3.NewKeccak256()
sha.Reset() sha.Reset()
d := sha3.NewKeccak256() d := sha3.NewKeccak256()
b := new(big.Int) b := new(big.Int)
ret := new(big.Int) ret := new(big.Int)
for k := 0; k < int(m.Uint64()); k++ { for k := 0; k < int(m.Uint64()); k++ {
d.Reset() d.Reset()
d.Write(dag.hash.Bytes()) d.Write(dag.hash.Bytes())
d.Write(dag.xn.Bytes()) d.Write(dag.xn.Bytes())
d.Write(big.NewInt(int64(L)).Bytes()) d.Write(big.NewInt(int64(L)).Bytes())
d.Write(big.NewInt(int64(i)).Bytes()) d.Write(big.NewInt(int64(i)).Bytes())
d.Write(big.NewInt(int64(k)).Bytes()) d.Write(big.NewInt(int64(k)).Bytes())
b.SetBytes(Sum(d)) b.SetBytes(Sum(d))
pk := b.Uint64() & ((1 << ((L - 1) * 3)) - 1) pk := b.Uint64() & ((1 << ((L - 1) * 3)) - 1)
sha.Write(dag.Node(L - 1, pk).Bytes()) sha.Write(dag.Node(L-1, pk).Bytes())
} }
ret.SetBytes(Sum(sha)) ret.SetBytes(Sum(sha))
return ret return ret
} }
func Sum(sha hash.Hash) []byte { func Sum(sha hash.Hash) []byte {
in := make([]byte, 32) in := make([]byte, 32)
return sha.Sum(in) return sha.Sum(in)
} }
func (dag *Dagger) Eval(N *big.Int) *big.Int { func (dag *Dagger) Eval(N *big.Int) *big.Int {
pow := ethutil.BigPow(2, 26) pow := ethutil.BigPow(2, 26)
dag.xn = N.Div(N, pow) dag.xn = N.Div(N, pow)
sha := sha3.NewKeccak256() sha := sha3.NewKeccak256()
sha.Reset() sha.Reset()
ret := new(big.Int) ret := new(big.Int)
for k := 0; k < 4; k++ { for k := 0; k < 4; k++ {
d := sha3.NewKeccak256() d := sha3.NewKeccak256()
b := new(big.Int) b := new(big.Int)
d.Reset() d.Reset()
d.Write(dag.hash.Bytes()) d.Write(dag.hash.Bytes())
d.Write(dag.xn.Bytes()) d.Write(dag.xn.Bytes())
d.Write(N.Bytes()) d.Write(N.Bytes())
d.Write(big.NewInt(int64(k)).Bytes()) d.Write(big.NewInt(int64(k)).Bytes())
b.SetBytes(Sum(d)) b.SetBytes(Sum(d))
pk := (b.Uint64() & 0x1ffffff) pk := (b.Uint64() & 0x1ffffff)
sha.Write(dag.Node(9, pk).Bytes()) sha.Write(dag.Node(9, pk).Bytes())
} }
return ret.SetBytes(Sum(sha)) return ret.SetBytes(Sum(sha))
} }

View File

@ -1,17 +1,17 @@
package main package main
import ( import (
"testing" "math/big"
"math/big" "testing"
) )
func BenchmarkDaggerSearch(b *testing.B) { func BenchmarkDaggerSearch(b *testing.B) {
hash := big.NewInt(0) hash := big.NewInt(0)
diff := BigPow(2, 36) diff := BigPow(2, 36)
o := big.NewInt(0) // nonce doesn't matter. We're only testing against speed, not validity 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 // Reset timer so the big generation isn't included in the benchmark
b.ResetTimer() b.ResetTimer()
// Validate // Validate
DaggerVerify(hash, diff, o) DaggerVerify(hash, diff, o)
} }

View File

@ -1,119 +1,121 @@
package main package main
import ( import (
"fmt" "bufio"
"bufio" "encoding/hex"
"strings" "errors"
"os" "fmt"
"errors" "github.com/ethereum/ethdb-go"
"encoding/hex" "github.com/ethereum/ethutil-go"
"github.com/ethereum/ethdb-go" "os"
"github.com/ethereum/ethutil-go" "strings"
) )
type Console struct { type Console struct {
db *ethdb.MemDatabase db *ethdb.MemDatabase
trie *ethutil.Trie trie *ethutil.Trie
} }
func NewConsole() *Console { func NewConsole() *Console {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
trie := ethutil.NewTrie(db, "") trie := ethutil.NewTrie(db, "")
return &Console{db: db, trie: trie} return &Console{db: db, trie: trie}
} }
func (i *Console) ValidateInput(action string, argumentLength int) error { func (i *Console) ValidateInput(action string, argumentLength int) error {
err := false err := false
var expArgCount int var expArgCount int
switch { switch {
case action == "update" && argumentLength != 2: case action == "update" && argumentLength != 2:
err = true err = true
expArgCount = 2 expArgCount = 2
case action == "get" && argumentLength != 1: case action == "get" && argumentLength != 1:
err = true err = true
expArgCount = 1 expArgCount = 1
case action == "dag" && argumentLength != 2: case action == "dag" && argumentLength != 2:
err = true err = true
expArgCount = 2 expArgCount = 2
} }
if err { if err {
return errors.New(fmt.Sprintf("'%s' requires %d args, got %d", action, expArgCount, argumentLength)) return errors.New(fmt.Sprintf("'%s' requires %d args, got %d", action, expArgCount, argumentLength))
} else { } else {
return nil return nil
} }
} }
func (i *Console) ParseInput(input string) bool { func (i *Console) ParseInput(input string) bool {
scanner := bufio.NewScanner(strings.NewReader(input)) scanner := bufio.NewScanner(strings.NewReader(input))
scanner.Split(bufio.ScanWords) scanner.Split(bufio.ScanWords)
count := 0 count := 0
var tokens []string var tokens []string
for scanner.Scan() { for scanner.Scan() {
count++ count++
tokens = append(tokens, scanner.Text()) tokens = append(tokens, scanner.Text())
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading input:", err) fmt.Fprintln(os.Stderr, "reading input:", err)
} }
if len(tokens) == 0 { return true } if len(tokens) == 0 {
return true
}
err := i.ValidateInput(tokens[0], count-1) err := i.ValidateInput(tokens[0], count-1)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} else { } else {
switch tokens[0] { switch tokens[0] {
case "update": case "update":
i.trie.Update(tokens[1], tokens[2]) i.trie.Update(tokens[1], tokens[2])
fmt.Println(hex.EncodeToString([]byte(i.trie.Root))) fmt.Println(hex.EncodeToString([]byte(i.trie.Root)))
case "get": case "get":
fmt.Println(i.trie.Get(tokens[1])) fmt.Println(i.trie.Get(tokens[1]))
case "root": case "root":
fmt.Println(hex.EncodeToString([]byte(i.trie.Root))) fmt.Println(hex.EncodeToString([]byte(i.trie.Root)))
case "rawroot": case "rawroot":
fmt.Println(i.trie.Root) fmt.Println(i.trie.Root)
case "print": case "print":
i.db.Print() i.db.Print()
case "dag": case "dag":
fmt.Println(DaggerVerify( ethutil.Big(tokens[1]), // hash fmt.Println(DaggerVerify(ethutil.Big(tokens[1]), // hash
ethutil.BigPow(2, 36), // diff ethutil.BigPow(2, 36), // diff
ethutil.Big(tokens[2])))// nonce ethutil.Big(tokens[2]))) // nonce
case "exit", "quit", "q": case "exit", "quit", "q":
return false return false
case "help": case "help":
fmt.Printf( "COMMANDS:\n"+ fmt.Printf("COMMANDS:\n" +
"\033[1m= DB =\033[0m\n"+ "\033[1m= DB =\033[0m\n" +
"update KEY VALUE - Updates/Creates a new value for the given key\n"+ "update KEY VALUE - Updates/Creates a new value for the given key\n" +
"get KEY - Retrieves the given key\n"+ "get KEY - Retrieves the given key\n" +
"root - Prints the hex encoded merkle root\n"+ "root - Prints the hex encoded merkle root\n" +
"rawroot - Prints the raw merkle root\n"+ "rawroot - Prints the raw merkle root\n" +
"\033[1m= Dagger =\033[0m\n"+ "\033[1m= Dagger =\033[0m\n" +
"dag HASH NONCE - Verifies a nonce with the given hash with dagger\n") "dag HASH NONCE - Verifies a nonce with the given hash with dagger\n")
default: default:
fmt.Println("Unknown command:", tokens[0]) fmt.Println("Unknown command:", tokens[0])
} }
} }
return true return true
} }
func (i *Console) Start() { func (i *Console) Start() {
fmt.Printf("Eth Console. Type (help) for help\n") fmt.Printf("Eth Console. Type (help) for help\n")
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
for { for {
fmt.Printf("eth >>> ") fmt.Printf("eth >>> ")
str, _, err := reader.ReadLine() str, _, err := reader.ReadLine()
if err != nil { if err != nil {
fmt.Println("Error reading input", err) fmt.Println("Error reading input", err)
} else { } else {
if !i.ParseInput(string(str)) { if !i.ParseInput(string(str)) {
return return
} }
} }
} }
} }

View File

@ -1,85 +1,85 @@
package main package main
import ( import (
"fmt" "flag"
"os" "fmt"
"os/signal" "github.com/ethereum/ethutil-go"
"flag" "log"
"runtime" "os"
"log" "os/signal"
"github.com/ethereum/ethutil-go" "runtime"
) )
const Debug = true const Debug = true
var StartConsole bool var StartConsole bool
var StartMining bool var StartMining bool
func Init() {
flag.BoolVar(&StartConsole, "c", false, "debug and testing console")
flag.BoolVar(&StartMining, "m", false, "start dagger mining")
flag.Parse() func Init() {
flag.BoolVar(&StartConsole, "c", false, "debug and testing console")
flag.BoolVar(&StartMining, "m", false, "start dagger mining")
flag.Parse()
} }
// Register interrupt handlers so we can stop the server // Register interrupt handlers so we can stop the server
func RegisterInterupts(s *Server) { func RegisterInterupts(s *Server) {
// Buffered chan of one is enough // Buffered chan of one is enough
c := make(chan os.Signal, 1) c := make(chan os.Signal, 1)
// Notify about interrupts for now // Notify about interrupts for now
signal.Notify(c, os.Interrupt) signal.Notify(c, os.Interrupt)
go func() { go func() {
for sig := range c { for sig := range c {
fmt.Printf("Shutting down (%v) ... \n", sig) fmt.Printf("Shutting down (%v) ... \n", sig)
s.Stop() s.Stop()
} }
}() }()
} }
func main() { func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
ethutil.InitFees() ethutil.InitFees()
Init() Init()
if StartConsole { if StartConsole {
console := NewConsole() console := NewConsole()
console.Start() console.Start()
} else{ } else {
log.Println("Starting Ethereum") log.Println("Starting Ethereum")
server, err := NewServer() server, err := NewServer()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
} }
RegisterInterupts(server) RegisterInterupts(server)
if StartMining { if StartMining {
log.Println("Mining started") log.Println("Mining started")
dagger := &Dagger{} dagger := &Dagger{}
go func() { go func() {
for { for {
res := dagger.Search(ethutil.Big("0"), ethutil.BigPow(2, 36)) res := dagger.Search(ethutil.Big("0"), ethutil.BigPow(2, 36))
server.Broadcast("block", Encode(res.String())) server.Broadcast("block", Encode(res.String()))
} }
}() }()
} }
server.Start() server.Start()
err = server.ConnectToPeer("localhost:12345") err = server.ConnectToPeer("localhost:12345")
if err != nil { if err != nil {
log.Println(err) log.Println(err)
server.Stop() server.Stop()
return return
} }
// Wait for shutdown
// Wait for shutdown server.WaitForShutdown()
server.WaitForShutdown() }
}
} }

108
peer.go
View File

@ -1,92 +1,92 @@
package main package main
import ( import (
"net" "github.com/ethereum/ethwire-go"
"log" "log"
"github.com/ethereum/ethwire-go" "net"
) )
type Peer struct { type Peer struct {
// Server interface // Server interface
server *Server server *Server
// Net connection // Net connection
conn net.Conn conn net.Conn
// Output queue which is used to communicate and handle messages // Output queue which is used to communicate and handle messages
outputQueue chan ethwire.InOutMsg outputQueue chan ethwire.InOutMsg
// Quit channel // Quit channel
quit chan bool quit chan bool
} }
func NewPeer(conn net.Conn, server *Server) *Peer { func NewPeer(conn net.Conn, server *Server) *Peer {
return &Peer{ return &Peer{
outputQueue: make(chan ethwire.InOutMsg, 1), // Buffered chan of 1 is enough outputQueue: make(chan ethwire.InOutMsg, 1), // Buffered chan of 1 is enough
quit: make(chan bool), quit: make(chan bool),
server: server, server: server,
conn: conn, conn: conn,
} }
} }
// Outputs any RLP encoded data to the peer // Outputs any RLP encoded data to the peer
func (p *Peer) QueueMessage(msgType string, data []byte) { func (p *Peer) QueueMessage(msgType string, data []byte) {
p.outputQueue <- ethwire.InOutMsg{MsgType: msgType, Data: data} p.outputQueue <- ethwire.InOutMsg{MsgType: msgType, Data: data}
} }
// Outbound message handler. Outbound messages are handled here // Outbound message handler. Outbound messages are handled here
func (p *Peer) HandleOutbound() { func (p *Peer) HandleOutbound() {
out: out:
for { for {
select { select {
// Main message queue. All outbound messages are processed through here // Main message queue. All outbound messages are processed through here
case msg := <-p.outputQueue: case msg := <-p.outputQueue:
// TODO Message checking and handle accordingly // TODO Message checking and handle accordingly
err := ethwire.WriteMessage(p.conn, msg) err := ethwire.WriteMessage(p.conn, msg)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
// Stop the client if there was an error writing to it // Stop the client if there was an error writing to it
p.Stop() p.Stop()
} }
// Break out of the for loop if a quit message is posted // Break out of the for loop if a quit message is posted
case <- p.quit: case <-p.quit:
break out break out
} }
} }
} }
// Inbound handler. Inbound messages are received here and passed to the appropriate methods // Inbound handler. Inbound messages are received here and passed to the appropriate methods
func (p *Peer) HandleInbound() { func (p *Peer) HandleInbound() {
defer p.Stop() defer p.Stop()
out: out:
for { for {
// Wait for a message from the peer // Wait for a message from the peer
msg, err := ethwire.ReadMessage(p.conn) msg, err := ethwire.ReadMessage(p.conn)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
break out break out
} }
// TODO // TODO
data, _ := Decode(msg.Data, 0) data, _ := Decode(msg.Data, 0)
log.Printf("%s, %s\n", msg.MsgType, data) log.Printf("%s, %s\n", msg.MsgType, data)
} }
// Notify the out handler we're quiting // Notify the out handler we're quiting
p.quit <- true p.quit <- true
} }
func (p *Peer) Start() { func (p *Peer) Start() {
// Run the outbound handler in a new goroutine // Run the outbound handler in a new goroutine
go p.HandleOutbound() go p.HandleOutbound()
// Run the inbound handler in a new goroutine // Run the inbound handler in a new goroutine
go p.HandleInbound() go p.HandleInbound()
} }
func (p *Peer) Stop() { func (p *Peer) Stop() {
p.conn.Close() p.conn.Close()
p.quit <- true p.quit <- true
} }

352
rlp.go
View File

@ -1,270 +1,278 @@
package main package main
import ( import (
"fmt" "bytes"
"bytes" "fmt"
"math" "github.com/ethereum/ethutil-go"
"math/big" "math"
"github.com/ethereum/ethutil-go" "math/big"
) )
type RlpEncoder struct { type RlpEncoder struct {
rlpData []byte rlpData []byte
} }
func NewRlpEncoder() *RlpEncoder {
encoder := &RlpEncoder{}
return encoder func NewRlpEncoder() *RlpEncoder {
encoder := &RlpEncoder{}
return encoder
} }
func (coder *RlpEncoder) EncodeData(rlpData []interface{}) []byte { func (coder *RlpEncoder) EncodeData(rlpData []interface{}) []byte {
return nil return nil
} }
// Data attributes are returned by the rlp decoder. The data attributes represents // Data attributes are returned by the rlp decoder. The data attributes represents
// one item within the rlp data structure. It's responsible for all the casting // one item within the rlp data structure. It's responsible for all the casting
// It always returns something valid // It always returns something valid
type RlpDataAttribute struct { type RlpDataAttribute struct {
dataAttrib interface{} dataAttrib interface{}
} }
func NewRlpDataAttribute(attrib interface{}) *RlpDataAttribute { func NewRlpDataAttribute(attrib interface{}) *RlpDataAttribute {
return &RlpDataAttribute{dataAttrib: attrib} return &RlpDataAttribute{dataAttrib: attrib}
} }
func (attr *RlpDataAttribute) Length() int { func (attr *RlpDataAttribute) Length() int {
if data, ok := attr.dataAttrib.([]interface{}); ok { if data, ok := attr.dataAttrib.([]interface{}); ok {
return len(data) return len(data)
} }
return 0 return 0
} }
func (attr *RlpDataAttribute) AsUint() uint64 { func (attr *RlpDataAttribute) AsUint() uint64 {
if value, ok := attr.dataAttrib.(uint8); ok { if value, ok := attr.dataAttrib.(uint8); ok {
return uint64(value) return uint64(value)
} else if value, ok := attr.dataAttrib.(uint16); ok { } else if value, ok := attr.dataAttrib.(uint16); ok {
return uint64(value) return uint64(value)
} else if value, ok := attr.dataAttrib.(uint32); ok { } else if value, ok := attr.dataAttrib.(uint32); ok {
return uint64(value) return uint64(value)
} else if value, ok := attr.dataAttrib.(uint64); ok { } else if value, ok := attr.dataAttrib.(uint64); ok {
return value return value
} }
return 0 return 0
} }
func (attr *RlpDataAttribute) AsBigInt() *big.Int { func (attr *RlpDataAttribute) AsBigInt() *big.Int {
if a, ok := attr.dataAttrib.([]byte); ok { if a, ok := attr.dataAttrib.([]byte); ok {
return ethutil.Big(string(a)) return ethutil.Big(string(a))
} }
return big.NewInt(0) return big.NewInt(0)
} }
func (attr *RlpDataAttribute) AsString() string { func (attr *RlpDataAttribute) AsString() string {
if a, ok := attr.dataAttrib.([]byte); ok { if a, ok := attr.dataAttrib.([]byte); ok {
return string(a) return string(a)
} }
return "" return ""
} }
func (attr *RlpDataAttribute) AsBytes() []byte { func (attr *RlpDataAttribute) AsBytes() []byte {
if a, ok := attr.dataAttrib.([]byte); ok { if a, ok := attr.dataAttrib.([]byte); ok {
return a return a
} }
return make([]byte, 0) return make([]byte, 0)
} }
// Threat the attribute as a slice // Threat the attribute as a slice
func (attr *RlpDataAttribute) Get(idx int) *RlpDataAttribute { func (attr *RlpDataAttribute) Get(idx int) *RlpDataAttribute {
if d, ok := attr.dataAttrib.([]interface{}); ok { if d, ok := attr.dataAttrib.([]interface{}); ok {
// Guard for oob // Guard for oob
if len(d) < idx { if len(d) < idx {
return NewRlpDataAttribute(nil) return NewRlpDataAttribute(nil)
} }
return NewRlpDataAttribute(d[idx]) return NewRlpDataAttribute(d[idx])
} }
// If this wasn't a slice you probably shouldn't be using this function // If this wasn't a slice you probably shouldn't be using this function
return NewRlpDataAttribute(nil) return NewRlpDataAttribute(nil)
} }
type RlpDecoder struct { type RlpDecoder struct {
rlpData interface{} rlpData interface{}
} }
func NewRlpDecoder(rlpData []byte) *RlpDecoder {
decoder := &RlpDecoder{}
// Decode the data
data, _ := Decode(rlpData,0)
decoder.rlpData = data
return decoder func NewRlpDecoder(rlpData []byte) *RlpDecoder {
decoder := &RlpDecoder{}
// Decode the data
data, _ := Decode(rlpData, 0)
decoder.rlpData = data
return decoder
} }
func (dec *RlpDecoder) Get(idx int) *RlpDataAttribute { func (dec *RlpDecoder) Get(idx int) *RlpDataAttribute {
return NewRlpDataAttribute(dec.rlpData).Get(idx) return NewRlpDataAttribute(dec.rlpData).Get(idx)
} }
/// Raw methods /// Raw methods
func BinaryLength(n uint64) uint64 { func BinaryLength(n uint64) uint64 {
if n == 0 { return 0 } if n == 0 {
return 0
}
return 1 + BinaryLength(n / 256) return 1 + BinaryLength(n/256)
} }
func ToBinarySlice(n uint64, length uint64) []uint64 { func ToBinarySlice(n uint64, length uint64) []uint64 {
if length == 0 { if length == 0 {
length = BinaryLength(n) length = BinaryLength(n)
} }
if n == 0 { return make([]uint64, 1) } if n == 0 {
return make([]uint64, 1)
}
slice := ToBinarySlice(n / 256, 0) slice := ToBinarySlice(n/256, 0)
slice = append(slice, n % 256) slice = append(slice, n%256)
return slice return slice
} }
func ToBin(n uint64, length uint64) string { func ToBin(n uint64, length uint64) string {
var buf bytes.Buffer var buf bytes.Buffer
for _, val := range ToBinarySlice(n, length) { for _, val := range ToBinarySlice(n, length) {
buf.WriteString(string(val)) buf.WriteString(string(val))
} }
return buf.String() return buf.String()
} }
func FromBin(data []byte) uint64 { func FromBin(data []byte) uint64 {
if len(data) == 0 { return 0 } if len(data) == 0 {
return 0
}
return FromBin(data[:len(data)-1]) * 256 + uint64(data[len(data)-1]) return FromBin(data[:len(data)-1])*256 + uint64(data[len(data)-1])
} }
func Decode(data []byte, pos int) (interface{}, int) { func Decode(data []byte, pos int) (interface{}, int) {
if pos > len(data)-1 { if pos > len(data)-1 {
panic(fmt.Sprintf("index out of range %d for data %q, l = %d", pos, data, len(data))) panic(fmt.Sprintf("index out of range %d for data %q, l = %d", pos, data, len(data)))
} }
char := int(data[pos]) char := int(data[pos])
slice := make([]interface{}, 0) slice := make([]interface{}, 0)
switch { switch {
case char < 24: case char < 24:
return data[pos], pos + 1 return data[pos], pos + 1
case char < 56: case char < 56:
b := int(data[pos]) - 23 b := int(data[pos]) - 23
return FromBin(data[pos+1 : pos+1+b]), pos + 1 + b return FromBin(data[pos+1 : pos+1+b]), pos + 1 + b
case char < 64: case char < 64:
b := int(data[pos]) - 55 b := int(data[pos]) - 55
b2 := int(FromBin(data[pos+1 : pos+1+b])) b2 := int(FromBin(data[pos+1 : pos+1+b]))
return FromBin(data[pos+1+b : pos+1+b+b2]), pos+1+b+b2 return FromBin(data[pos+1+b : pos+1+b+b2]), pos + 1 + b + b2
case char < 120: case char < 120:
b := int(data[pos]) - 64 b := int(data[pos]) - 64
return data[pos+1:pos+1+b], pos+1+b return data[pos+1 : pos+1+b], pos + 1 + b
case char < 128: case char < 128:
b := int(data[pos]) - 119 b := int(data[pos]) - 119
b2 := int(FromBin(data[pos+1 : pos+1+b])) b2 := int(FromBin(data[pos+1 : pos+1+b]))
return data[pos+1+b : pos+1+b+b2], pos+1+b+b2 return data[pos+1+b : pos+1+b+b2], pos + 1 + b + b2
case char < 184: case char < 184:
b := int(data[pos]) - 128 b := int(data[pos]) - 128
pos++ pos++
for i := 0; i < b; i++ { for i := 0; i < b; i++ {
var obj interface{} var obj interface{}
obj, pos = Decode(data, pos) obj, pos = Decode(data, pos)
slice = append(slice, obj) slice = append(slice, obj)
} }
return slice, pos return slice, pos
case char < 192: case char < 192:
b := int(data[pos]) - 183 b := int(data[pos]) - 183
//b2 := int(FromBin(data[pos+1 : pos+1+b])) (ref implementation has an unused variable) //b2 := int(FromBin(data[pos+1 : pos+1+b])) (ref implementation has an unused variable)
pos = pos+1+b pos = pos + 1 + b
for i := 0; i < b; i++ { for i := 0; i < b; i++ {
var obj interface{} var obj interface{}
obj, pos = Decode(data, pos) obj, pos = Decode(data, pos)
slice = append(slice, obj) slice = append(slice, obj)
} }
return slice, pos return slice, pos
default: default:
panic(fmt.Sprintf("byte not supported: %q", char)) panic(fmt.Sprintf("byte not supported: %q", char))
} }
return slice, 0 return slice, 0
} }
func Encode(object interface{}) []byte { func Encode(object interface{}) []byte {
var buff bytes.Buffer var buff bytes.Buffer
switch t := object.(type) { switch t := object.(type) {
case uint32, uint64: case uint32, uint64:
var num uint64 var num uint64
if _num, ok := t.(uint64); ok { if _num, ok := t.(uint64); ok {
num = _num num = _num
} else if _num, ok := t.(uint32); ok { } else if _num, ok := t.(uint32); ok {
num = uint64(_num) num = uint64(_num)
} }
if num >= 0 && num < 24 { if num >= 0 && num < 24 {
buff.WriteString(string(num)) buff.WriteString(string(num))
} else if num <= uint64(math.Pow(2, 256)) { } else if num <= uint64(math.Pow(2, 256)) {
b := ToBin(num, 0) b := ToBin(num, 0)
buff.WriteString(string(len(b) + 23) + b) buff.WriteString(string(len(b)+23) + b)
} else { } else {
b := ToBin(num, 0) b := ToBin(num, 0)
b2 := ToBin(uint64(len(b)), 0) b2 := ToBin(uint64(len(b)), 0)
buff.WriteString(string(len(b2) + 55) + b2 + b) buff.WriteString(string(len(b2)+55) + b2 + b)
} }
case *big.Int: case *big.Int:
buff.Write(Encode(t.String())) buff.Write(Encode(t.String()))
case string: case string:
if len(t) < 56 { if len(t) < 56 {
buff.WriteString(string(len(t) + 64) + t) buff.WriteString(string(len(t)+64) + t)
} else { } else {
b2 := ToBin(uint64(len(t)), 0) b2 := ToBin(uint64(len(t)), 0)
buff.WriteString(string(len(b2) + 119) + b2 + t) buff.WriteString(string(len(b2)+119) + b2 + t)
} }
case []byte: case []byte:
// Cast the byte slice to a string // Cast the byte slice to a string
buff.Write(Encode(string(t))) buff.Write(Encode(string(t)))
case []interface{}, []string: case []interface{}, []string:
// Inline function for writing the slice header // Inline function for writing the slice header
WriteSliceHeader := func(length int) { WriteSliceHeader := func(length int) {
if length < 56 { if length < 56 {
buff.WriteByte(byte(length + 128)) buff.WriteByte(byte(length + 128))
} else { } else {
b2 := ToBin(uint64(length), 0) b2 := ToBin(uint64(length), 0)
buff.WriteByte(byte(len(b2) + 183)) buff.WriteByte(byte(len(b2) + 183))
buff.WriteString(b2) buff.WriteString(b2)
} }
} }
// FIXME How can I do this "better"? // FIXME How can I do this "better"?
if interSlice, ok := t.([]interface{}); ok { if interSlice, ok := t.([]interface{}); ok {
WriteSliceHeader(len(interSlice)) WriteSliceHeader(len(interSlice))
for _, val := range interSlice { for _, val := range interSlice {
buff.Write(Encode(val)) buff.Write(Encode(val))
} }
} else if stringSlice, ok := t.([]string); ok { } else if stringSlice, ok := t.([]string); ok {
WriteSliceHeader(len(stringSlice)) WriteSliceHeader(len(stringSlice))
for _, val := range stringSlice { for _, val := range stringSlice {
buff.Write(Encode(val)) buff.Write(Encode(val))
} }
} }
} }
return buff.Bytes() return buff.Bytes()
} }

View File

@ -1,54 +1,54 @@
package main package main
import ( import (
"testing" "fmt"
"fmt" "testing"
) )
func TestEncode(t *testing.T) { func TestEncode(t *testing.T) {
strRes := "Cdog" strRes := "Cdog"
bytes := Encode("dog") bytes := Encode("dog")
str := string(bytes) str := string(bytes)
if str != strRes { if str != strRes {
t.Error(fmt.Sprintf("Expected %q, got %q", strRes, str)) t.Error(fmt.Sprintf("Expected %q, got %q", strRes, str))
} }
//dec,_ := Decode(bytes, 0) //dec,_ := Decode(bytes, 0)
sliceRes := "\x83CdogCgodCcat" sliceRes := "\x83CdogCgodCcat"
strs := []string{"dog", "god", "cat"} strs := []string{"dog", "god", "cat"}
bytes = Encode(strs) bytes = Encode(strs)
slice := string(bytes) slice := string(bytes)
if slice != sliceRes { if slice != sliceRes {
t.Error(fmt.Sprintf("Expected %q, got %q", sliceRes, slice)) t.Error(fmt.Sprintf("Expected %q, got %q", sliceRes, slice))
} }
//dec,_ = Decode(bytes, 0) //dec,_ = Decode(bytes, 0)
} }
func TestMultiEncode(t *testing.T) { func TestMultiEncode(t *testing.T) {
inter := []interface{}{ inter := []interface{}{
[]interface{}{ []interface{}{
"1","2","3", "1", "2", "3",
}, },
[]string{ []string{
"string", "string",
"string2", "string2",
"\x86A0J1234567890A\x00B20A0\x82F395843F657986", "\x86A0J1234567890A\x00B20A0\x82F395843F657986",
"\x86A0J1234567890A\x00B20A0\x8cF395843F657986I335612448F524099H16716881A0H13114947G2039362G1507139H16719697G1048387E65360", "\x86A0J1234567890A\x00B20A0\x8cF395843F657986I335612448F524099H16716881A0H13114947G2039362G1507139H16719697G1048387E65360",
}, },
"test", "test",
} }
bytes := Encode(inter) bytes := Encode(inter)
Decode(bytes, 0) Decode(bytes, 0)
} }
func BenchmarkEncodeDecode(b *testing.B) { func BenchmarkEncodeDecode(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
bytes := Encode([]string{"dog", "god", "cat"}) bytes := Encode([]string{"dog", "god", "cat"})
Decode(bytes, 0) Decode(bytes, 0)
} }
} }

155
server.go
View File

@ -1,121 +1,120 @@
package main package main
import ( import (
"container/list" "container/list"
"net" "github.com/ethereum/ethdb-go"
"log" "github.com/ethereum/ethutil-go"
_"time" "log"
"github.com/ethereum/ethdb-go" "net"
"github.com/ethereum/ethutil-go" _ "time"
) )
type Server struct { type Server struct {
// Channel for shutting down the server // Channel for shutting down the server
shutdownChan chan bool shutdownChan chan bool
// DB interface // DB interface
db *ethdb.LDBDatabase db *ethdb.LDBDatabase
// Block manager for processing new blocks and managing the block chain // Block manager for processing new blocks and managing the block chain
blockManager *BlockManager blockManager *BlockManager
// Peers (NYI) // Peers (NYI)
peers *list.List peers *list.List
} }
func NewServer() (*Server, error) { func NewServer() (*Server, error) {
db, err := ethdb.NewLDBDatabase() db, err := ethdb.NewLDBDatabase()
if err != nil { if err != nil {
return nil, err return nil, err
} }
ethutil.SetConfig(db) ethutil.SetConfig(db)
server := &Server{ server := &Server{
shutdownChan: make(chan bool), shutdownChan: make(chan bool),
blockManager: NewBlockManager(), blockManager: NewBlockManager(),
db: db, db: db,
peers: list.New(), peers: list.New(),
} }
return server, nil return server, nil
} }
func (s *Server) AddPeer(conn net.Conn) { func (s *Server) AddPeer(conn net.Conn) {
peer := NewPeer(conn, s) peer := NewPeer(conn, s)
s.peers.PushBack(peer) s.peers.PushBack(peer)
peer.Start() peer.Start()
log.Println("Peer connected ::", conn.RemoteAddr()) log.Println("Peer connected ::", conn.RemoteAddr())
} }
func (s *Server) ConnectToPeer(addr string) error { func (s *Server) ConnectToPeer(addr string) error {
conn, err := net.Dial("tcp", addr) conn, err := net.Dial("tcp", addr)
if err != nil { if err != nil {
return err return err
} }
peer := NewPeer(conn, s) peer := NewPeer(conn, s)
s.peers.PushBack(peer) s.peers.PushBack(peer)
peer.Start() peer.Start()
log.Println("Connected to peer ::", conn.RemoteAddr())
log.Println("Connected to peer ::", conn.RemoteAddr()) return nil
return nil
} }
func (s *Server) Broadcast(msgType string, data []byte) { func (s *Server) Broadcast(msgType string, data []byte) {
for e := s.peers.Front(); e != nil; e = e.Next() { for e := s.peers.Front(); e != nil; e = e.Next() {
if peer, ok := e.Value.(*Peer); ok { if peer, ok := e.Value.(*Peer); ok {
peer.QueueMessage(msgType, data) peer.QueueMessage(msgType, data)
} }
} }
} }
// Start the server // Start the server
func (s *Server) Start() { func (s *Server) Start() {
// For now this function just blocks the main thread // For now this function just blocks the main thread
ln, err := net.Listen("tcp", ":12345") ln, err := net.Listen("tcp", ":12345")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
go func() { go func() {
for { for {
conn, err := ln.Accept() conn, err := ln.Accept()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
continue continue
} }
go s.AddPeer(conn) go s.AddPeer(conn)
} }
}() }()
// TMP // TMP
//go func() { //go func() {
// for { // for {
// s.Broadcast("block", Encode("blockdata")) // s.Broadcast("block", Encode("blockdata"))
// //
// time.Sleep(100 * time.Millisecond) // time.Sleep(100 * time.Millisecond)
// } // }
// }() // }()
} }
func (s *Server) Stop() { func (s *Server) Stop() {
// Close the database // Close the database
defer s.db.Close() defer s.db.Close()
// Loop thru the peers and close them (if we had them) // Loop thru the peers and close them (if we had them)
for e := s.peers.Front(); e != nil; e = e.Next() { for e := s.peers.Front(); e != nil; e = e.Next() {
if peer, ok := e.Value.(*Peer); ok { if peer, ok := e.Value.(*Peer); ok {
peer.Stop() peer.Stop()
} }
} }
s.shutdownChan <- true s.shutdownChan <- true
} }
// This function will wait for a shutdown and resumes main thread execution // This function will wait for a shutdown and resumes main thread execution
func (s *Server) WaitForShutdown() { func (s *Server) WaitForShutdown() {
<- s.shutdownChan <-s.shutdownChan
} }

View File

@ -1,35 +1,35 @@
package main package main
import ( import (
"fmt" "encoding/json"
"testing" "fmt"
"encoding/json" "testing"
) )
type TestSource struct { type TestSource struct {
Inputs map[string]string Inputs map[string]string
Expectation string Expectation string
} }
func NewTestSource(source string) *TestSource { func NewTestSource(source string) *TestSource {
s := &TestSource{} s := &TestSource{}
err := json.Unmarshal([]byte(source), s) err := json.Unmarshal([]byte(source), s)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
return s return s
} }
type TestRunner struct { type TestRunner struct {
source *TestSource source *TestSource
} }
func NewTestRunner(t *testing.T) *TestRunner { func NewTestRunner(t *testing.T) *TestRunner {
return &TestRunner{} return &TestRunner{}
} }
func (runner *TestRunner) RunFromString(input string, Cb func(*TestSource)) { func (runner *TestRunner) RunFromString(input string, Cb func(*TestSource)) {
source := NewTestSource(input) source := NewTestSource(input)
Cb(source) Cb(source)
} }

View File

@ -1,9 +1,9 @@
package main package main
import ( import (
_"fmt" "encoding/hex"
"testing" _ "fmt"
"encoding/hex" "testing"
) )
var testsource = `{"Inputs":{ var testsource = `{"Inputs":{
@ -15,17 +15,17 @@ var testsource = `{"Inputs":{
}` }`
func TestTestRunner(t *testing.T) { func TestTestRunner(t *testing.T) {
db, _ := NewMemDatabase() db, _ := NewMemDatabase()
trie := NewTrie(db, "") trie := NewTrie(db, "")
runner := NewTestRunner(t) runner := NewTestRunner(t)
runner.RunFromString(testsource, func(source *TestSource) { runner.RunFromString(testsource, func(source *TestSource) {
for key, value := range source.Inputs { for key, value := range source.Inputs {
trie.Update(key, value) 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") t.Error("trie root did not match")
} }
}) })
} }

View File

@ -1,4 +1,5 @@
package main package main
/* /*
import ( import (

451
vm.go
View File

@ -1,267 +1,284 @@
package main package main
import ( import (
"math/big" "fmt"
"fmt" "github.com/ethereum/ethutil-go"
"strconv" "math/big"
"github.com/ethereum/ethutil-go" "strconv"
) )
// Op codes // Op codes
const ( const (
oSTOP int = 0x00 oSTOP int = 0x00
oADD int = 0x01 oADD int = 0x01
oMUL int = 0x02 oMUL int = 0x02
oSUB int = 0x03 oSUB int = 0x03
oDIV int = 0x04 oDIV int = 0x04
oSDIV int = 0x05 oSDIV int = 0x05
oMOD int = 0x06 oMOD int = 0x06
oSMOD int = 0x07 oSMOD int = 0x07
oEXP int = 0x08 oEXP int = 0x08
oNEG int = 0x09 oNEG int = 0x09
oLT int = 0x0a oLT int = 0x0a
oLE int = 0x0b oLE int = 0x0b
oGT int = 0x0c oGT int = 0x0c
oGE int = 0x0d oGE int = 0x0d
oEQ int = 0x0e oEQ int = 0x0e
oNOT int = 0x0f oNOT int = 0x0f
oMYADDRESS int = 0x10 oMYADDRESS int = 0x10
oTXSENDER int = 0x11 oTXSENDER int = 0x11
oTXVALUE int = 0x12 oTXVALUE int = 0x12
oTXFEE int = 0x13 oTXFEE int = 0x13
oTXDATAN int = 0x14 oTXDATAN int = 0x14
oTXDATA int = 0x15 oTXDATA int = 0x15
oBLK_PREVHASH int = 0x16 oBLK_PREVHASH int = 0x16
oBLK_COINBASE int = 0x17 oBLK_COINBASE int = 0x17
oBLK_TIMESTAMP int = 0x18 oBLK_TIMESTAMP int = 0x18
oBLK_NUMBER int = 0x19 oBLK_NUMBER int = 0x19
oBLK_DIFFICULTY int = 0x1a oBLK_DIFFICULTY int = 0x1a
oSHA256 int = 0x20 oSHA256 int = 0x20
oRIPEMD160 int = 0x21 oRIPEMD160 int = 0x21
oECMUL int = 0x22 oECMUL int = 0x22
oECADD int = 0x23 oECADD int = 0x23
oECSIGN int = 0x24 oECSIGN int = 0x24
oECRECOVER int = 0x25 oECRECOVER int = 0x25
oECVALID int = 0x26 oECVALID int = 0x26
oPUSH int = 0x30 oPUSH int = 0x30
oPOP int = 0x31 oPOP int = 0x31
oDUP int = 0x32 oDUP int = 0x32
oDUPN int = 0x33 oDUPN int = 0x33
oSWAP int = 0x34 oSWAP int = 0x34
oSWAPN int = 0x35 oSWAPN int = 0x35
oLOAD int = 0x36 oLOAD int = 0x36
oSTORE int = 0x37 oSTORE int = 0x37
oJMP int = 0x40 oJMP int = 0x40
oJMPI int = 0x41 oJMPI int = 0x41
oIND int = 0x42 oIND int = 0x42
oEXTRO int = 0x50 oEXTRO int = 0x50
oBALANCE int = 0x51 oBALANCE int = 0x51
oMKTX int = 0x60 oMKTX int = 0x60
oSUICIDE int = 0xff oSUICIDE int = 0xff
) )
type OpType int type OpType int
const ( const (
tNorm = iota tNorm = iota
tData tData
tExtro tExtro
tCrypto tCrypto
) )
type TxCallback func(opType OpType) bool type TxCallback func(opType OpType) bool
// Simple push/pop stack mechanism // Simple push/pop stack mechanism
type Stack struct { type Stack struct {
data []string data []string
} }
func NewStack() *Stack { func NewStack() *Stack {
return &Stack{} return &Stack{}
} }
func (st *Stack) Pop() string { func (st *Stack) Pop() string {
s := len(st.data) s := len(st.data)
str := st.data[s-1] str := st.data[s-1]
st.data = st.data[:s-1] st.data = st.data[:s-1]
return str return str
} }
func (st *Stack) Popn() (*big.Int, *big.Int) { func (st *Stack) Popn() (*big.Int, *big.Int) {
s := len(st.data) s := len(st.data)
strs := st.data[s-2:] strs := st.data[s-2:]
st.data = st.data[:s-2] st.data = st.data[:s-2]
return ethutil.Big(strs[0]), ethutil.Big(strs[1]) return ethutil.Big(strs[0]), ethutil.Big(strs[1])
} }
func (st *Stack) Push(d string) { func (st *Stack) Push(d string) {
st.data = append(st.data, d) st.data = append(st.data, d)
} }
func (st *Stack) Print() { func (st *Stack) Print() {
fmt.Println(st.data) fmt.Println(st.data)
} }
type Vm struct { type Vm struct {
// Stack // Stack
stack *Stack stack *Stack
} }
func NewVm() *Vm { func NewVm() *Vm {
return &Vm{ return &Vm{
stack: NewStack(), stack: NewStack(),
} }
} }
func (vm *Vm) ProcContract( tx *ethutil.Transaction, func (vm *Vm) ProcContract(tx *ethutil.Transaction,
block *ethutil.Block, cb TxCallback) { block *ethutil.Block, cb TxCallback) {
// Instruction pointer // Instruction pointer
pc := 0 pc := 0
contract := block.GetContract(tx.Hash()) contract := block.GetContract(tx.Hash())
if contract == nil { if contract == nil {
fmt.Println("Contract not found") fmt.Println("Contract not found")
return return
} }
Pow256 := ethutil.BigPow(2, 256) Pow256 := ethutil.BigPow(2, 256)
//fmt.Printf("# op arg\n") //fmt.Printf("# op arg\n")
out: out:
for { for {
// The base big int for all calculations. Use this for any results. // The base big int for all calculations. Use this for any results.
base := new(big.Int) base := new(big.Int)
// XXX Should Instr return big int slice instead of string slice? // XXX Should Instr return big int slice instead of string slice?
// Get the next instruction from the contract // Get the next instruction from the contract
//op, _, _ := Instr(contract.state.Get(string(Encode(uint32(pc))))) //op, _, _ := Instr(contract.state.Get(string(Encode(uint32(pc)))))
nb := ethutil.NumberToBytes(uint64(pc), 32) nb := ethutil.NumberToBytes(uint64(pc), 32)
op, _, _ := ethutil.Instr(contract.State().Get(string(nb))) op, _, _ := ethutil.Instr(contract.State().Get(string(nb)))
if !cb(0) { break } if !cb(0) {
break
}
if Debug { if Debug {
//fmt.Printf("%-3d %-4d\n", pc, op) //fmt.Printf("%-3d %-4d\n", pc, op)
} }
switch op { switch op {
case oADD: case oADD:
x, y := vm.stack.Popn() x, y := vm.stack.Popn()
// (x + y) % 2 ** 256 // (x + y) % 2 ** 256
base.Add(x, y) base.Add(x, y)
base.Mod(base, Pow256) base.Mod(base, Pow256)
// Pop result back on the stack // Pop result back on the stack
vm.stack.Push(base.String()) vm.stack.Push(base.String())
case oSUB: case oSUB:
x, y := vm.stack.Popn() x, y := vm.stack.Popn()
// (x - y) % 2 ** 256 // (x - y) % 2 ** 256
base.Sub(x, y) base.Sub(x, y)
base.Mod(base, Pow256) base.Mod(base, Pow256)
// Pop result back on the stack // Pop result back on the stack
vm.stack.Push(base.String()) vm.stack.Push(base.String())
case oMUL: case oMUL:
x, y := vm.stack.Popn() x, y := vm.stack.Popn()
// (x * y) % 2 ** 256 // (x * y) % 2 ** 256
base.Mul(x, y) base.Mul(x, y)
base.Mod(base, Pow256) base.Mod(base, Pow256)
// Pop result back on the stack // Pop result back on the stack
vm.stack.Push(base.String()) vm.stack.Push(base.String())
case oDIV: case oDIV:
x, y := vm.stack.Popn() x, y := vm.stack.Popn()
// floor(x / y) // floor(x / y)
base.Div(x, y) base.Div(x, y)
// Pop result back on the stack // Pop result back on the stack
vm.stack.Push(base.String()) vm.stack.Push(base.String())
case oSDIV: case oSDIV:
x, y := vm.stack.Popn() x, y := vm.stack.Popn()
// n > 2**255 // n > 2**255
if x.Cmp(Pow256) > 0 { x.Sub(Pow256, x) } if x.Cmp(Pow256) > 0 {
if y.Cmp(Pow256) > 0 { y.Sub(Pow256, y) } x.Sub(Pow256, x)
z := new(big.Int) }
z.Div(x, y) if y.Cmp(Pow256) > 0 {
if z.Cmp(Pow256) > 0 { z.Sub(Pow256, z) } y.Sub(Pow256, y)
// Push result on to the stack }
vm.stack.Push(z.String()) z := new(big.Int)
case oMOD: z.Div(x, y)
x, y := vm.stack.Popn() if z.Cmp(Pow256) > 0 {
base.Mod(x, y) z.Sub(Pow256, z)
vm.stack.Push(base.String()) }
case oSMOD: // Push result on to the stack
x, y := vm.stack.Popn() vm.stack.Push(z.String())
// n > 2**255 case oMOD:
if x.Cmp(Pow256) > 0 { x.Sub(Pow256, x) } x, y := vm.stack.Popn()
if y.Cmp(Pow256) > 0 { y.Sub(Pow256, y) } base.Mod(x, y)
z := new(big.Int) vm.stack.Push(base.String())
z.Mod(x, y) case oSMOD:
if z.Cmp(Pow256) > 0 { z.Sub(Pow256, z) } x, y := vm.stack.Popn()
// Push result on to the stack // n > 2**255
vm.stack.Push(z.String()) if x.Cmp(Pow256) > 0 {
case oEXP: x.Sub(Pow256, x)
x, y := vm.stack.Popn() }
base.Exp(x, y, Pow256) 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()) vm.stack.Push(base.String())
case oNEG: case oNEG:
base.Sub(Pow256, ethutil.Big(vm.stack.Pop())) base.Sub(Pow256, ethutil.Big(vm.stack.Pop()))
vm.stack.Push(base.String()) vm.stack.Push(base.String())
case oLT: case oLT:
x, y := vm.stack.Popn() x, y := vm.stack.Popn()
// x < y // x < y
if x.Cmp(y) < 0 { if x.Cmp(y) < 0 {
vm.stack.Push("1") vm.stack.Push("1")
} else { } else {
vm.stack.Push("0") vm.stack.Push("0")
} }
case oLE: case oLE:
x, y := vm.stack.Popn() x, y := vm.stack.Popn()
// x <= y // x <= y
if x.Cmp(y) < 1 { if x.Cmp(y) < 1 {
vm.stack.Push("1") vm.stack.Push("1")
} else { } else {
vm.stack.Push("0") vm.stack.Push("0")
} }
case oGT: case oGT:
x, y := vm.stack.Popn() x, y := vm.stack.Popn()
// x > y // x > y
if x.Cmp(y) > 0 { if x.Cmp(y) > 0 {
vm.stack.Push("1") vm.stack.Push("1")
} else { } else {
vm.stack.Push("0") vm.stack.Push("0")
} }
case oGE: case oGE:
x, y := vm.stack.Popn() x, y := vm.stack.Popn()
// x >= y // x >= y
if x.Cmp(y) > -1 { if x.Cmp(y) > -1 {
vm.stack.Push("1") vm.stack.Push("1")
} else { } else {
vm.stack.Push("0") vm.stack.Push("0")
} }
case oNOT: case oNOT:
x, y := vm.stack.Popn() x, y := vm.stack.Popn()
// x != y // x != y
if x.Cmp(y) != 0 { if x.Cmp(y) != 0 {
vm.stack.Push("1") vm.stack.Push("1")
} else { } else {
vm.stack.Push("0") vm.stack.Push("0")
} }
case oMYADDRESS: case oMYADDRESS:
vm.stack.Push(string(tx.Hash())) vm.stack.Push(string(tx.Hash()))
case oTXSENDER: case oTXSENDER:
vm.stack.Push(string(tx.Sender())) vm.stack.Push(string(tx.Sender()))
case oPUSH: case oPUSH:
// Get the next entry and pushes the value on the stack // Get the next entry and pushes the value on the stack
pc++ pc++
vm.stack.Push(contract.State().Get(string(ethutil.NumberToBytes(uint64(pc), 32)))) vm.stack.Push(contract.State().Get(string(ethutil.NumberToBytes(uint64(pc), 32))))
case oPOP: case oPOP:
// Pop current value of the stack // Pop current value of the stack
vm.stack.Pop() vm.stack.Pop()
case oLOAD: case oLOAD:
// Load instruction X on the stack // Load instruction X on the stack
i, _ := strconv.Atoi(vm.stack.Pop()) i, _ := strconv.Atoi(vm.stack.Pop())
vm.stack.Push(contract.State().Get(string(ethutil.NumberToBytes(uint64(i), 32)))) vm.stack.Push(contract.State().Get(string(ethutil.NumberToBytes(uint64(i), 32))))
case oSTOP: case oSTOP:
break out break out
} }
pc++ pc++
} }
vm.stack.Print() vm.stack.Print()
} }