Commit 477e8a7a authored by obscuren's avatar obscuren

Rearrange packages

parent 3616080d
This diff is collapsed.
package main
import (
_ "fmt"
"testing"
)
func TestVm(t *testing.T) {
InitFees()
db, _ := NewMemDatabase()
Db = db
ctrct := NewTransaction("", 200000000, []string{
"PUSH", "1a2f2e",
"PUSH", "hallo",
"POP", // POP hallo
"PUSH", "3",
"LOAD", // Load hallo back on the stack
"PUSH", "1",
"PUSH", "2",
"ADD",
"PUSH", "2",
"PUSH", "1",
"SUB",
"PUSH", "100000000000000000000000",
"PUSH", "10000000000000",
"SDIV",
"PUSH", "105",
"PUSH", "200",
"MOD",
"PUSH", "100000000000000000000000",
"PUSH", "10000000000000",
"SMOD",
"PUSH", "5",
"PUSH", "10",
"LT",
"PUSH", "5",
"PUSH", "5",
"LE",
"PUSH", "50",
"PUSH", "5",
"GT",
"PUSH", "5",
"PUSH", "5",
"GE",
"PUSH", "10",
"PUSH", "10",
"NOT",
"MYADDRESS",
"TXSENDER",
"STOP",
})
tx := NewTransaction("1e8a42ea8cce13", 100, []string{})
block := CreateBlock("", 0, "", "c014ba53", 0, 0, "", []*Transaction{ctrct, tx})
db.Put(block.Hash(), block.RlpEncode())
bm := NewBlockManager()
bm.ProcessBlock(block)
}
package main
import (
"github.com/ethereum/ethutil-go"
"github.com/obscuren/sha3"
"hash"
"math/big"
"math/rand"
"time"
"log"
)
type Dagger struct {
hash *big.Int
xn *big.Int
}
var Found bool
func (dag *Dagger) Find(obj *big.Int, resChan chan int64) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < 1000; i++ {
rnd := r.Int63()
res := dag.Eval(big.NewInt(rnd))
log.Printf("rnd %v\nres %v\nobj %v\n", rnd, res, obj)
if res.Cmp(obj) < 0 {
// Post back result on the channel
resChan <- rnd
// Notify other threads we've found a valid nonce
Found = true
}
// Break out if found
if Found {
break
}
}
resChan <- 0
}
func (dag *Dagger) Search(hash, diff *big.Int) *big.Int {
// TODO fix multi threading. Somehow it results in the wrong nonce
amountOfRoutines := 1
dag.hash = hash
obj := ethutil.BigPow(2, 256)
obj = obj.Div(obj, diff)
Found = false
resChan := make(chan int64, 3)
var res int64
for k := 0; k < amountOfRoutines; k++ {
go dag.Find(obj, resChan)
}
// Wait for each go routine to finish
for k := 0; k < amountOfRoutines; k++ {
// Get the result from the channel. 0 = quit
if r := <-resChan; r != 0 {
res = r
}
}
return big.NewInt(res)
}
func DaggerVerify(hash, diff, nonce *big.Int) bool {
dagger := &Dagger{}
dagger.hash = hash
obj := ethutil.BigPow(2, 256)
obj = obj.Div(obj, diff)
return dagger.Eval(nonce).Cmp(obj) < 0
}
func (dag *Dagger) Node(L uint64, i uint64) *big.Int {
if L == i {
return dag.hash
}
var m *big.Int
if L == 9 {
m = big.NewInt(16)
} else {
m = big.NewInt(3)
}
sha := sha3.NewKeccak256()
sha.Reset()
d := sha3.NewKeccak256()
b := new(big.Int)
ret := new(big.Int)
for k := 0; k < int(m.Uint64()); k++ {
d.Reset()
d.Write(dag.hash.Bytes())
d.Write(dag.xn.Bytes())
d.Write(big.NewInt(int64(L)).Bytes())
d.Write(big.NewInt(int64(i)).Bytes())
d.Write(big.NewInt(int64(k)).Bytes())
b.SetBytes(Sum(d))
pk := b.Uint64() & ((1 << ((L - 1) * 3)) - 1)
sha.Write(dag.Node(L-1, pk).Bytes())
}
ret.SetBytes(Sum(sha))
return ret
}
func Sum(sha hash.Hash) []byte {
//in := make([]byte, 32)
return sha.Sum(nil)
}
func (dag *Dagger) Eval(N *big.Int) *big.Int {
pow := ethutil.BigPow(2, 26)
dag.xn = pow.Div(N, pow)
sha := sha3.NewKeccak256()
sha.Reset()
ret := new(big.Int)
for k := 0; k < 4; k++ {
d := sha3.NewKeccak256()
b := new(big.Int)
d.Reset()
d.Write(dag.hash.Bytes())
d.Write(dag.xn.Bytes())
d.Write(N.Bytes())
d.Write(big.NewInt(int64(k)).Bytes())
b.SetBytes(Sum(d))
pk := (b.Uint64() & 0x1ffffff)
sha.Write(dag.Node(9, pk).Bytes())
}
return ret.SetBytes(Sum(sha))
}
package main
import (
"github.com/ethereum/ethutil-go"
"math/big"
"testing"
)
func BenchmarkDaggerSearch(b *testing.B) {
hash := big.NewInt(0)
diff := ethutil.BigPow(2, 36)
o := big.NewInt(0) // nonce doesn't matter. We're only testing against speed, not validity
// Reset timer so the big generation isn't included in the benchmark
b.ResetTimer()
// Validate
DaggerVerify(hash, diff, o)
}
...@@ -5,6 +5,8 @@ import ( ...@@ -5,6 +5,8 @@ import (
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/ethchain-go"
"github.com/ethereum/ethdb-go" "github.com/ethereum/ethdb-go"
"github.com/ethereum/ethutil-go" "github.com/ethereum/ethutil-go"
"os" "os"
...@@ -14,14 +16,14 @@ import ( ...@@ -14,14 +16,14 @@ import (
type Console struct { type Console struct {
db *ethdb.MemDatabase db *ethdb.MemDatabase
trie *ethutil.Trie trie *ethutil.Trie
server *Server ethereum *eth.Ethereum
} }
func NewConsole(s *Server) *Console { func NewConsole(s *eth.Ethereum) *Console {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
trie := ethutil.NewTrie(db, "") trie := ethutil.NewTrie(db, "")
return &Console{db: db, trie: trie, server: s} return &Console{db: db, trie: trie, ethereum: s}
} }
func (i *Console) ValidateInput(action string, argumentLength int) error { func (i *Console) ValidateInput(action string, argumentLength int) error {
...@@ -101,7 +103,7 @@ func (i *Console) ParseInput(input string) bool { ...@@ -101,7 +103,7 @@ func (i *Console) ParseInput(input string) bool {
case "print": case "print":
i.db.Print() i.db.Print()
case "dag": case "dag":
fmt.Println(DaggerVerify(ethutil.Big(tokens[1]), // hash fmt.Println(ethchain.DaggerVerify(ethutil.Big(tokens[1]), // hash
ethutil.BigPow(2, 36), // diff ethutil.BigPow(2, 36), // diff
ethutil.Big(tokens[2]))) // nonce ethutil.Big(tokens[2]))) // nonce
case "decode": case "decode":
...@@ -112,7 +114,7 @@ func (i *Console) ParseInput(input string) bool { ...@@ -112,7 +114,7 @@ func (i *Console) ParseInput(input string) bool {
case "tx": case "tx":
tx := ethutil.NewTransaction(tokens[1], ethutil.Big(tokens[2]), []string{""}) tx := ethutil.NewTransaction(tokens[1], ethutil.Big(tokens[2]), []string{""})
i.server.txPool.QueueTransaction(tx) i.ethereum.TxPool.QueueTransaction(tx)
case "exit", "quit", "q": case "exit", "quit", "q":
return false return false
case "help": case "help":
......
...@@ -3,6 +3,8 @@ package main ...@@ -3,6 +3,8 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/ethchain-go"
"github.com/ethereum/ethutil-go" "github.com/ethereum/ethutil-go"
"log" "log"
"os" "os"
...@@ -23,8 +25,8 @@ func Init() { ...@@ -23,8 +25,8 @@ func Init() {
flag.Parse() flag.Parse()
} }
// Register interrupt handlers so we can stop the server // Register interrupt handlers so we can stop the ethereum
func RegisterInterupts(s *Server) { func RegisterInterupts(s *eth.Ethereum) {
// 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
...@@ -40,15 +42,13 @@ func RegisterInterupts(s *Server) { ...@@ -40,15 +42,13 @@ func RegisterInterupts(s *Server) {
func main() { func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
ethutil.InitFees()
Init() Init()
ethutil.InitFees()
ethutil.ReadConfig() ethutil.ReadConfig()
server, err := NewServer() // Instantiated a eth stack
ethereum, err := eth.New()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
...@@ -70,29 +70,29 @@ func main() { ...@@ -70,29 +70,29 @@ func main() {
ethutil.Config.Log = log.New(file, "", 0) ethutil.Config.Log = log.New(file, "", 0)
console := NewConsole(server) console := NewConsole(ethereum)
go console.Start() go console.Start()
} }
log.Println("Starting Ethereum") log.Println("Starting Ethereum")
RegisterInterupts(server) RegisterInterupts(ethereum)
if StartMining { if StartMining {
log.Println("Mining started") log.Println("Mining started")
dagger := &Dagger{} dagger := &ethchain.Dagger{}
go func() { go func() {
for { for {
res := dagger.Search(ethutil.Big("01001"), ethutil.BigPow(2, 36)) res := dagger.Search(ethutil.Big("01001"), ethutil.BigPow(2, 36))
log.Println("Res dagger", res) log.Println("Res dagger", res)
//server.Broadcast("blockmine", ethutil.Encode(res.String())) //ethereum.Broadcast("blockmine", ethutil.Encode(res.String()))
} }
}() }()
} }
server.Start() ethereum.Start()
// Wait for shutdown // Wait for shutdown
server.WaitForShutdown() ethereum.WaitForShutdown()
} }
package main
import (
"github.com/ethereum/ethutil-go"
"github.com/ethereum/ethwire-go"
"log"
"net"
"strconv"
"sync/atomic"
"time"
)
const (
// The size of the output buffer for writing messages
outputBufferSize = 50
)
type Peer struct {
// Server interface
server *Server
// Net connection
conn net.Conn
// Output queue which is used to communicate and handle messages
outputQueue chan *ethwire.Msg
// Quit channel
quit chan bool
// Determines whether it's an inbound or outbound peer
inbound bool
// Flag for checking the peer's connectivity state
connected int32
disconnect int32
// Last known message send
lastSend time.Time
// Indicated whether a verack has been send or not
// This flag is used by writeMessage to check if messages are allowed
// to be send or not. If no version is known all messages are ignored.
versionKnown bool
// Last received pong message
lastPong int64
// Indicates whether a MsgGetPeersTy was requested of the peer
// this to prevent receiving false peers.
requestedPeerList bool
}
func NewPeer(conn net.Conn, server *Server, inbound bool) *Peer {
return &Peer{
outputQueue: make(chan *ethwire.Msg, outputBufferSize),
quit: make(chan bool),
server: server,
conn: conn,
inbound: inbound,
disconnect: 0,
connected: 1,
}
}
func NewOutboundPeer(addr string, server *Server) *Peer {
p := &Peer{
outputQueue: make(chan *ethwire.Msg, outputBufferSize),
quit: make(chan bool),
server: server,
inbound: false,
connected: 0,
disconnect: 0,
}
// Set up the connection in another goroutine so we don't block the main thread
go func() {
conn, err := net.Dial("tcp", addr)
if err != nil {
p.Stop()
}
p.conn = conn
// Atomically set the connection state
atomic.StoreInt32(&p.connected, 1)
atomic.StoreInt32(&p.disconnect, 0)
log.Println("Connected to peer ::", conn.RemoteAddr())
p.Start()
}()
return p
}
// Outputs any RLP encoded data to the peer
func (p *Peer) QueueMessage(msg *ethwire.Msg) {
p.outputQueue <- msg
}
func (p *Peer) writeMessage(msg *ethwire.Msg) {
// Ignore the write if we're not connected
if atomic.LoadInt32(&p.connected) != 1 {
return
}
if !p.versionKnown {
switch msg.Type {
case ethwire.MsgHandshakeTy: // Ok
default: // Anything but ack is allowed
return
}
}
err := ethwire.WriteMessage(p.conn, msg)
if err != nil {
log.Println("Can't send message:", err)
// Stop the client if there was an error writing to it
p.Stop()
return
}
}
// Outbound message handler. Outbound messages are handled here
func (p *Peer) HandleOutbound() {
// The ping timer. Makes sure that every 2 minutes a ping is send to the peer
tickleTimer := time.NewTicker(2 * time.Minute)
out:
for {
select {
// Main message queue. All outbound messages are processed through here
case msg := <-p.outputQueue:
p.writeMessage(msg)
p.lastSend = time.Now()
case <-tickleTimer.C:
p.writeMessage(&ethwire.Msg{Type: ethwire.MsgPingTy})
// Break out of the for loop if a quit message is posted
case <-p.quit:
break out
}
}
clean:
// This loop is for draining the output queue and anybody waiting for us
for {
select {
case <-p.outputQueue:
// TODO
default:
break clean
}
}
}
// Inbound handler. Inbound messages are received here and passed to the appropriate methods
func (p *Peer) HandleInbound() {
out:
for atomic.LoadInt32(&p.disconnect) == 0 {
// Wait for a message from the peer
msg, err := ethwire.ReadMessage(p.conn)
if err != nil {
log.Println(err)
break out
}
if Debug {
log.Printf("Received %s\n", msg.Type.String())
}
switch msg.Type {
case ethwire.MsgHandshakeTy:
// Version message
p.handleHandshake(msg)
case ethwire.MsgBlockTy:
err := p.server.blockManager.ProcessBlock(ethutil.NewBlock(msg.Data))
if err != nil {
log.Println(err)
}
case ethwire.MsgTxTy:
p.server.txPool.QueueTransaction(ethutil.NewTransactionFromData(msg.Data))
case ethwire.MsgInvTy:
case ethwire.MsgGetPeersTy:
p.requestedPeerList = true
// Peer asked for list of connected peers
p.pushPeers()
case ethwire.MsgPeersTy:
// Received a list of peers (probably because MsgGetPeersTy was send)
// Only act on message if we actually requested for a peers list
if p.requestedPeerList {
data := ethutil.Conv(msg.Data)
// Create new list of possible peers for the server to process
peers := make([]string, data.Length())
// Parse each possible peer
for i := 0; i < data.Length(); i++ {
peers[i] = data.Get(i).AsString() + strconv.Itoa(int(data.Get(i).AsUint()))
}
// Connect to the list of peers
p.server.ProcessPeerList(peers)
// Mark unrequested again
p.requestedPeerList = false
}
case ethwire.MsgPingTy:
// Respond back with pong
p.QueueMessage(&ethwire.Msg{Type: ethwire.MsgPongTy})
case ethwire.MsgPongTy:
p.lastPong = time.Now().Unix()
}
}
p.Stop()
}
func (p *Peer) Start() {
if !p.inbound {
err := p.pushHandshake()
if err != nil {
log.Printf("Peer can't send outbound version ack", err)
p.Stop()
}
}
// Run the outbound handler in a new goroutine
go p.HandleOutbound()
// Run the inbound handler in a new goroutine
go p.HandleInbound()
}
func (p *Peer) Stop() {
if atomic.AddInt32(&p.disconnect, 1) != 1 {
return
}
close(p.quit)
if atomic.LoadInt32(&p.connected) != 0 {
p.conn.Close()
}
log.Println("Peer shutdown")
}
func (p *Peer) pushHandshake() error {
msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, ethutil.Encode([]interface{}{
1, 0, p.server.Nonce,
}))
p.QueueMessage(msg)
return nil
}
// Pushes the list of outbound peers to the client when requested
func (p *Peer) pushPeers() {
outPeers := make([]interface{}, len(p.server.OutboundPeers()))
// Serialise each peer
for i, peer := range p.server.OutboundPeers() {
outPeers[i] = peer.RlpEncode()
}
// Send message to the peer with the known list of connected clients
msg := ethwire.NewMessage(ethwire.MsgPeersTy, ethutil.Encode(outPeers))
p.QueueMessage(msg)
}
func (p *Peer) handleHandshake(msg *ethwire.Msg) {
c := ethutil.Conv(msg.Data)
// [PROTOCOL_VERSION, NETWORK_ID, CLIENT_ID]
if c.Get(2).AsUint() == p.server.Nonce {
//if msg.Nonce == p.server.Nonce {
log.Println("Peer connected to self, disconnecting")
p.Stop()
return
}
p.versionKnown = true
// If this is an inbound connection send an ack back
if p.inbound {
err := p.pushHandshake()
if err != nil {
log.Println("Peer can't send ack back")
p.Stop()
}
}
}
func (p *Peer) RlpEncode() []byte {
host, prt, err := net.SplitHostPort(p.conn.RemoteAddr().String())
if err != nil {
return nil
}
i, err := strconv.Atoi(prt)
if err != nil {
return nil
}
port := ethutil.NumberToBytes(uint16(i), 16)
return ethutil.Encode([]interface{}{host, port})
}
package main
import (
"container/list"
"github.com/ethereum/ethdb-go"
"github.com/ethereum/ethutil-go"
"github.com/ethereum/ethwire-go"
"log"
"net"
"sync/atomic"
"time"
)
func eachPeer(peers *list.List, callback func(*Peer, *list.Element)) {
// Loop thru the peers and close them (if we had them)
for e := peers.Front(); e != nil; e = e.Next() {
if peer, ok := e.Value.(*Peer); ok {
callback(peer, e)
}
}
}
const (
processReapingTimeout = 60 // TODO increase
)
type Server struct {
// Channel for shutting down the server
shutdownChan chan bool
// DB interface
//db *ethdb.LDBDatabase
db *ethdb.MemDatabase
// Block manager for processing new blocks and managing the block chain
blockManager *BlockManager
// The transaction pool. Transaction can be pushed on this pool
// for later including in the blocks
txPool *TxPool
// Peers (NYI)
peers *list.List
// Nonce
Nonce uint64
}
func NewServer() (*Server, error) {
//db, err := ethdb.NewLDBDatabase()
db, err := ethdb.NewMemDatabase()
if err != nil {
return nil, err
}
ethutil.Config.Db = db
nonce, _ := ethutil.RandomUint64()
server := &Server{
shutdownChan: make(chan bool),
db: db,
peers: list.New(),
Nonce: nonce,
}
server.txPool = NewTxPool(server)
server.blockManager = NewBlockManager(server)
return server, nil
}
func (s *Server) AddPeer(conn net.Conn) {
peer := NewPeer(conn, s, true)
if peer != nil {
s.peers.PushBack(peer)
peer.Start()
log.Println("Peer connected ::", conn.RemoteAddr())
}
}
func (s *Server) ProcessPeerList(addrs []string) {
for _, addr := range addrs {
// TODO Probably requires some sanity checks
s.ConnectToPeer(addr)
}
}
func (s *Server) ConnectToPeer(addr string) error {
peer := NewOutboundPeer(addr, s)
s.peers.PushBack(peer)
return nil
}
func (s *Server) OutboundPeers() []*Peer {
// Create a new peer slice with at least the length of the total peers
outboundPeers := make([]*Peer, s.peers.Len())
length := 0
eachPeer(s.peers, func(p *Peer, e *list.Element) {
if !p.inbound {
outboundPeers[length] = p
length++
}
})
return outboundPeers[:length]
}
func (s *Server) InboundPeers() []*Peer {
// Create a new peer slice with at least the length of the total peers
inboundPeers := make([]*Peer, s.peers.Len())
length := 0
eachPeer(s.peers, func(p *Peer, e *list.Element) {
if p.inbound {
inboundPeers[length] = p
length++
}
})
return inboundPeers[:length]
}
func (s *Server) Broadcast(msgType ethwire.MsgType, data []byte) {
eachPeer(s.peers, func(p *Peer, e *list.Element) {
p.QueueMessage(ethwire.NewMessage(msgType, data))
})
}
func (s *Server) ReapDeadPeers() {
for {
eachPeer(s.peers, func(p *Peer, e *list.Element) {
if atomic.LoadInt32(&p.disconnect) == 1 || (p.inbound && (time.Now().Unix()-p.lastPong) > int64(5*time.Minute)) {
log.Println("Dead peer found .. reaping")
s.peers.Remove(e)
}
})
time.Sleep(processReapingTimeout * time.Second)
}
}
// Start the server
func (s *Server) Start() {
// For now this function just blocks the main thread
ln, err := net.Listen("tcp", ":12345")
if err != nil {
// This is mainly for testing to create a "network"
if Debug {
log.Println("Connection listening disabled. Acting as client")
err = s.ConnectToPeer("localhost:12345")
if err != nil {
log.Println("Error starting server", err)
s.Stop()
}
} else {
log.Fatal(err)
}
} else {
// Starting accepting connections
go func() {
for {
conn, err := ln.Accept()
if err != nil {
log.Println(err)
continue
}
go s.AddPeer(conn)
}
}()
}
// Start the reaping processes
go s.ReapDeadPeers()
// Start the tx pool
s.txPool.Start()
// TMP
/*
go func() {
for {
s.Broadcast("block", s.blockManager.bc.GenesisBlock().RlpEncode())
time.Sleep(1000 * time.Millisecond)
}
}()
*/
}
func (s *Server) Stop() {
// Close the database
defer s.db.Close()
eachPeer(s.peers, func(p *Peer, e *list.Element) {
p.Stop()
})
s.shutdownChan <- true
s.txPool.Stop()
}
// This function will wait for a shutdown and resumes main thread execution
func (s *Server) WaitForShutdown() {
<-s.shutdownChan
}
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)
}
package main
import (
"bytes"
"container/list"
"errors"
"github.com/ethereum/ethutil-go"
"github.com/ethereum/ethwire-go"
"log"
"math/big"
"sync"
)
const (
txPoolQueueSize = 50
)
func FindTx(pool *list.List, finder func(*ethutil.Transaction, *list.Element) bool) *ethutil.Transaction {
for e := pool.Front(); e != nil; e = e.Next() {
if tx, ok := e.Value.(*ethutil.Transaction); ok {
if finder(tx, e) {
return tx
}
}
}
return nil
}
// The tx pool a thread safe transaction pool handler. In order to
// guarantee a non blocking pool we use a queue channel which can be
// independently read without needing access to the actual pool. If the
// pool is being drained or synced for whatever reason the transactions
// will simple queue up and handled when the mutex is freed.
type TxPool struct {
server *Server
// The mutex for accessing the Tx pool.
mutex sync.Mutex
// Queueing channel for reading and writing incoming
// transactions to
queueChan chan *ethutil.Transaction
// Quiting channel
quit chan bool
pool *list.List
}
func NewTxPool(s *Server) *TxPool {
return &TxPool{
server: s,
mutex: sync.Mutex{},
pool: list.New(),
queueChan: make(chan *ethutil.Transaction, txPoolQueueSize),
quit: make(chan bool),
}
}
// Blocking function. Don't use directly. Use QueueTransaction instead
func (pool *TxPool) addTransaction(tx *ethutil.Transaction) {
pool.mutex.Lock()
pool.pool.PushBack(tx)
pool.mutex.Unlock()
// Broadcast the transaction to the rest of the peers
pool.server.Broadcast(ethwire.MsgTxTy, tx.RlpEncode())
}
// Process transaction validates the Tx and processes funds from the
// sender to the recipient.
func (pool *TxPool) processTransaction(tx *ethutil.Transaction) error {
// Get the last block so we can retrieve the sender and receiver from
// the merkle trie
block := pool.server.blockManager.bc.LastBlock
// Something has gone horribly wrong if this happens
if block == nil {
return errors.New("No last block on the block chain")
}
var sender, receiver *ethutil.Ether
// Get the sender
data := block.State().Get(string(tx.Sender()))
// If it doesn't exist create a new account. Of course trying to send funds
// from this account will fail since it will hold 0 Wei
if data == "" {
sender = ethutil.NewEther(big.NewInt(0))
} else {
sender = ethutil.NewEtherFromData([]byte(data))
}
// Defer the update. Whatever happens it should be persisted
defer block.State().Update(string(tx.Sender()), string(sender.RlpEncode()))
// Make sure there's enough in the sender's account. Having insufficient
// funds won't invalidate this transaction but simple ignores it.
if sender.Amount.Cmp(tx.Value) < 0 {
if Debug {
log.Println("Insufficient amount in sender's account. Adding 1 ETH for debug")
sender.Amount = ethutil.BigPow(10, 18)
} else {
return errors.New("Insufficient amount in sender's account")
}
}
// Subtract the amount from the senders account
sender.Amount.Sub(sender.Amount, tx.Value)
// Increment the nonce making each tx valid only once to prevent replay
// attacks
sender.Nonce += 1
// Get the receiver
data = block.State().Get(tx.Recipient)
// If the receiver doesn't exist yet, create a new account to which the
// funds will be send.
if data == "" {
receiver = ethutil.NewEther(big.NewInt(0))
} else {
receiver = ethutil.NewEtherFromData([]byte(data))
}
// Defer the update
defer block.State().Update(tx.Recipient, string(receiver.RlpEncode()))
// Add the amount to receivers account which should conclude this transaction
receiver.Amount.Add(receiver.Amount, tx.Value)
return nil
}
func (pool *TxPool) queueHandler() {
out:
for {
select {
case tx := <-pool.queueChan:
hash := tx.Hash()
foundTx := FindTx(pool.pool, func(tx *ethutil.Transaction, e *list.Element) bool {
return bytes.Compare(tx.Hash(), hash) == 0
})
if foundTx != nil {
break
}
// Process the transaction
err := pool.processTransaction(tx)
if err != nil {
log.Println("Error processing Tx", err)
} else {
// Call blocking version. At this point it
// doesn't matter since this is a goroutine
pool.addTransaction(tx)
}
case <-pool.quit:
break out
}
}
}
func (pool *TxPool) QueueTransaction(tx *ethutil.Transaction) {
pool.queueChan <- tx
}
func (pool *TxPool) Flush() {
pool.mutex.Lock()
defer pool.mutex.Unlock()
}
func (pool *TxPool) Start() {
go pool.queueHandler()
}
func (pool *TxPool) Stop() {
log.Println("[TXP] Stopping...")
close(pool.quit)
pool.Flush()
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment