forked from cerc-io/plugeth
Merge branch 'poc8' into docbranch
This commit is contained in:
commit
0fb1bcd321
10
.travis.yml
10
.travis.yml
@ -1,6 +1,6 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.3
|
||||
- tip
|
||||
before_install:
|
||||
- sudo add-apt-repository ppa:ubuntu-sdk-team/ppa -y
|
||||
- sudo apt-get update -qq
|
||||
@ -8,10 +8,10 @@ before_install:
|
||||
install:
|
||||
- go get code.google.com/p/go.tools/cmd/goimports
|
||||
- go get github.com/golang/lint/golint
|
||||
# - go get code.google.com/p/go.tools/cmd/vet
|
||||
- go get code.google.com/p/go.tools/cmd/cover
|
||||
# - go get golang.org/x/tools/cmd/vet
|
||||
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
|
||||
- go get github.com/mattn/goveralls
|
||||
- ./install_deps.sh
|
||||
- ETH_DEPS=$(go list -f '{{.Imports}} {{.TestImports}} {{.XTestImports}}' github.com/ethereum/go-ethereum/... | sed -e 's/\[//g' | sed -e 's/\]//g' | sed -e 's/C //g'); if [ "$ETH_DEPS" ]; then go get $ETH_DEPS; fi
|
||||
before_script:
|
||||
- gofmt -l -w .
|
||||
- goimports -l -w .
|
||||
@ -19,7 +19,7 @@ before_script:
|
||||
# - go vet ./...
|
||||
# - go test -race ./...
|
||||
script:
|
||||
- ./gocoverage.sh && goveralls -coverprofile=profile.cov -service=travis-ci -repotoken $COVERALLS_TOKEN
|
||||
- ./gocoverage.sh
|
||||
env:
|
||||
- secure: "U2U1AmkU4NJBgKR/uUAebQY87cNL0+1JHjnLOmmXwxYYyj5ralWb1aSuSH3qSXiT93qLBmtaUkuv9fberHVqrbAeVlztVdUsKAq7JMQH+M99iFkC9UiRMqHmtjWJ0ok4COD1sRYixxi21wb/JrMe3M1iL4QJVS61iltjHhVdM64="
|
||||
|
||||
|
@ -25,14 +25,14 @@ RUN apt-get install -y qtbase5-private-dev qtdeclarative5-private-dev libqt5open
|
||||
## Fetch and install serpent-go
|
||||
RUN go get -v -d github.com/ethereum/serpent-go
|
||||
WORKDIR $GOPATH/src/github.com/ethereum/serpent-go
|
||||
RUN git checkout master
|
||||
# RUN git checkout master
|
||||
RUN git submodule update --init
|
||||
RUN go install -v
|
||||
|
||||
# Fetch and install go-ethereum
|
||||
RUN go get -v -d github.com/ethereum/go-ethereum/...
|
||||
WORKDIR $GOPATH/src/github.com/ethereum/go-ethereum
|
||||
RUN git checkout poc8
|
||||
# RUN git checkout develop
|
||||
RUN ETH_DEPS=$(go list -f '{{.Imports}} {{.TestImports}} {{.XTestImports}}' github.com/ethereum/go-ethereum/... | sed -e 's/\[//g' | sed -e 's/\]//g' | sed -e 's/C //g'); if [ "$ETH_DEPS" ]; then go get $ETH_DEPS; fi
|
||||
RUN go install -v ./cmd/ethereum
|
||||
|
||||
|
@ -9,6 +9,7 @@ Ethereum
|
||||
[![Build
|
||||
Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20master%20branch)](http://build.ethdev.com:8010/builders/Linux%20Go%20master%20branch/builds/-1) master [![Build
|
||||
Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](http://build.ethdev.com:8010/builders/Linux%20Go%20develop%20branch/builds/-1) develop
|
||||
[![Coverage Status](https://coveralls.io/repos/ethereum/go-ethereum/badge.png?branch=tests)](https://coveralls.io/r/ethereum/go-ethereum?branch=tests) tests
|
||||
|
||||
Ethereum Go Client © 2014 Jeffrey Wilcke.
|
||||
|
||||
|
BIN
_data/invalid1
Executable file
BIN
_data/invalid1
Executable file
Binary file not shown.
BIN
_data/valid1
Executable file
BIN
_data/valid1
Executable file
Binary file not shown.
BIN
_data/valid2
Executable file
BIN
_data/valid2
Executable file
Binary file not shown.
BIN
_data/valid3
Executable file
BIN
_data/valid3
Executable file
Binary file not shown.
BIN
_data/valid4
Executable file
BIN
_data/valid4
Executable file
Binary file not shown.
351
block_pool.go
351
block_pool.go
@ -1,351 +0,0 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
)
|
||||
|
||||
var poollogger = logger.NewLogger("BPOOL")
|
||||
|
||||
type block struct {
|
||||
from *Peer
|
||||
peer *Peer
|
||||
block *types.Block
|
||||
reqAt time.Time
|
||||
requested int
|
||||
}
|
||||
|
||||
type BlockPool struct {
|
||||
mut sync.Mutex
|
||||
|
||||
eth *Ethereum
|
||||
|
||||
hashes [][]byte
|
||||
pool map[string]*block
|
||||
|
||||
td *big.Int
|
||||
quit chan bool
|
||||
|
||||
fetchingHashes bool
|
||||
downloadStartedAt time.Time
|
||||
|
||||
ChainLength, BlocksProcessed int
|
||||
|
||||
peer *Peer
|
||||
}
|
||||
|
||||
func NewBlockPool(eth *Ethereum) *BlockPool {
|
||||
return &BlockPool{
|
||||
eth: eth,
|
||||
pool: make(map[string]*block),
|
||||
td: ethutil.Big0,
|
||||
quit: make(chan bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BlockPool) Len() int {
|
||||
return len(self.hashes)
|
||||
}
|
||||
|
||||
func (self *BlockPool) Reset() {
|
||||
self.pool = make(map[string]*block)
|
||||
self.hashes = nil
|
||||
}
|
||||
|
||||
func (self *BlockPool) HasLatestHash() bool {
|
||||
self.mut.Lock()
|
||||
defer self.mut.Unlock()
|
||||
|
||||
return self.pool[string(self.eth.ChainManager().CurrentBlock.Hash())] != nil
|
||||
}
|
||||
|
||||
func (self *BlockPool) HasCommonHash(hash []byte) bool {
|
||||
return self.eth.ChainManager().GetBlock(hash) != nil
|
||||
}
|
||||
|
||||
func (self *BlockPool) Blocks() (blocks types.Blocks) {
|
||||
for _, item := range self.pool {
|
||||
if item.block != nil {
|
||||
blocks = append(blocks, item.block)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *BlockPool) FetchHashes(peer *Peer) bool {
|
||||
highestTd := self.eth.HighestTDPeer()
|
||||
|
||||
if (self.peer == nil && peer.td.Cmp(highestTd) >= 0) || (self.peer != nil && peer.td.Cmp(self.peer.td) > 0) || self.peer == peer {
|
||||
if self.peer != peer {
|
||||
poollogger.Infof("Found better suitable peer (%v vs %v)\n", self.td, peer.td)
|
||||
|
||||
if self.peer != nil {
|
||||
self.peer.doneFetchingHashes = true
|
||||
}
|
||||
}
|
||||
|
||||
self.peer = peer
|
||||
self.td = peer.td
|
||||
|
||||
if !self.HasLatestHash() {
|
||||
self.fetchHashes()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *BlockPool) fetchHashes() {
|
||||
peer := self.peer
|
||||
|
||||
peer.doneFetchingHashes = false
|
||||
|
||||
const amount = 256
|
||||
peerlogger.Debugf("Fetching hashes (%d) %x...\n", amount, peer.lastReceivedHash[0:4])
|
||||
peer.QueueMessage(wire.NewMessage(wire.MsgGetBlockHashesTy, []interface{}{peer.lastReceivedHash, uint32(amount)}))
|
||||
}
|
||||
|
||||
func (self *BlockPool) AddHash(hash []byte, peer *Peer) {
|
||||
self.mut.Lock()
|
||||
defer self.mut.Unlock()
|
||||
|
||||
if self.pool[string(hash)] == nil {
|
||||
self.pool[string(hash)] = &block{peer, nil, nil, time.Now(), 0}
|
||||
|
||||
self.hashes = append([][]byte{hash}, self.hashes...)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BlockPool) Add(b *types.Block, peer *Peer) {
|
||||
self.addBlock(b, peer, false)
|
||||
}
|
||||
|
||||
func (self *BlockPool) AddNew(b *types.Block, peer *Peer) {
|
||||
self.addBlock(b, peer, true)
|
||||
}
|
||||
|
||||
func (self *BlockPool) addBlock(b *types.Block, peer *Peer, newBlock bool) {
|
||||
self.mut.Lock()
|
||||
defer self.mut.Unlock()
|
||||
|
||||
hash := string(b.Hash())
|
||||
|
||||
if self.pool[hash] == nil && !self.eth.ChainManager().HasBlock(b.Hash()) {
|
||||
poollogger.Infof("Got unrequested block (%x...)\n", hash[0:4])
|
||||
|
||||
self.hashes = append(self.hashes, b.Hash())
|
||||
self.pool[hash] = &block{peer, peer, b, time.Now(), 0}
|
||||
|
||||
// The following is only performed on an unrequested new block
|
||||
if newBlock {
|
||||
fmt.Println("1.", !self.eth.ChainManager().HasBlock(b.PrevHash), ethutil.Bytes2Hex(b.Hash()[0:4]), ethutil.Bytes2Hex(b.PrevHash[0:4]))
|
||||
fmt.Println("2.", self.pool[string(b.PrevHash)] == nil)
|
||||
fmt.Println("3.", !self.fetchingHashes)
|
||||
if !self.eth.ChainManager().HasBlock(b.PrevHash) /*&& self.pool[string(b.PrevHash)] == nil*/ && !self.fetchingHashes {
|
||||
poollogger.Infof("Unknown chain, requesting (%x...)\n", b.PrevHash[0:4])
|
||||
peer.QueueMessage(wire.NewMessage(wire.MsgGetBlockHashesTy, []interface{}{b.Hash(), uint32(256)}))
|
||||
}
|
||||
}
|
||||
} else if self.pool[hash] != nil {
|
||||
self.pool[hash].block = b
|
||||
}
|
||||
|
||||
self.BlocksProcessed++
|
||||
}
|
||||
|
||||
func (self *BlockPool) Remove(hash []byte) {
|
||||
self.mut.Lock()
|
||||
defer self.mut.Unlock()
|
||||
|
||||
self.hashes = ethutil.DeleteFromByteSlice(self.hashes, hash)
|
||||
delete(self.pool, string(hash))
|
||||
}
|
||||
|
||||
func (self *BlockPool) DistributeHashes() {
|
||||
self.mut.Lock()
|
||||
defer self.mut.Unlock()
|
||||
|
||||
var (
|
||||
peerLen = self.eth.peers.Len()
|
||||
amount = 256 * peerLen
|
||||
dist = make(map[*Peer][][]byte)
|
||||
)
|
||||
|
||||
num := int(math.Min(float64(amount), float64(len(self.pool))))
|
||||
for i, j := 0, 0; i < len(self.hashes) && j < num; i++ {
|
||||
hash := self.hashes[i]
|
||||
item := self.pool[string(hash)]
|
||||
|
||||
if item != nil && item.block == nil {
|
||||
var peer *Peer
|
||||
lastFetchFailed := time.Since(item.reqAt) > 5*time.Second
|
||||
|
||||
// Handle failed requests
|
||||
if lastFetchFailed && item.requested > 5 && item.peer != nil {
|
||||
if item.requested < 100 {
|
||||
// Select peer the hash was retrieved off
|
||||
peer = item.from
|
||||
} else {
|
||||
// Remove it
|
||||
self.hashes = ethutil.DeleteFromByteSlice(self.hashes, hash)
|
||||
delete(self.pool, string(hash))
|
||||
}
|
||||
} else if lastFetchFailed || item.peer == nil {
|
||||
// Find a suitable, available peer
|
||||
eachPeer(self.eth.peers, func(p *Peer, v *list.Element) {
|
||||
if peer == nil && len(dist[p]) < amount/peerLen && p.statusKnown {
|
||||
peer = p
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if peer != nil {
|
||||
item.reqAt = time.Now()
|
||||
item.peer = peer
|
||||
item.requested++
|
||||
|
||||
dist[peer] = append(dist[peer], hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for peer, hashes := range dist {
|
||||
peer.FetchBlocks(hashes)
|
||||
}
|
||||
|
||||
if len(dist) > 0 {
|
||||
self.downloadStartedAt = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BlockPool) Start() {
|
||||
go self.downloadThread()
|
||||
go self.chainThread()
|
||||
}
|
||||
|
||||
func (self *BlockPool) Stop() {
|
||||
close(self.quit)
|
||||
}
|
||||
|
||||
func (self *BlockPool) downloadThread() {
|
||||
serviceTimer := time.NewTicker(100 * time.Millisecond)
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-self.quit:
|
||||
break out
|
||||
case <-serviceTimer.C:
|
||||
// Check if we're catching up. If not distribute the hashes to
|
||||
// the peers and download the blockchain
|
||||
self.fetchingHashes = false
|
||||
eachPeer(self.eth.peers, func(p *Peer, v *list.Element) {
|
||||
if p.statusKnown && p.FetchingHashes() {
|
||||
self.fetchingHashes = true
|
||||
}
|
||||
})
|
||||
|
||||
if len(self.hashes) > 0 {
|
||||
self.DistributeHashes()
|
||||
}
|
||||
|
||||
if self.ChainLength < len(self.hashes) {
|
||||
self.ChainLength = len(self.hashes)
|
||||
}
|
||||
|
||||
if self.peer != nil &&
|
||||
!self.peer.doneFetchingHashes &&
|
||||
time.Since(self.peer.lastHashAt) > 10*time.Second &&
|
||||
time.Since(self.peer.lastHashRequestedAt) > 5*time.Second {
|
||||
self.fetchHashes()
|
||||
}
|
||||
|
||||
/*
|
||||
if !self.fetchingHashes {
|
||||
blocks := self.Blocks()
|
||||
chain.BlockBy(chain.Number).Sort(blocks)
|
||||
|
||||
if len(blocks) > 0 {
|
||||
if !self.eth.ChainManager().HasBlock(b.PrevHash) && self.pool[string(b.PrevHash)] == nil && !self.fetchingHashes {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BlockPool) chainThread() {
|
||||
procTimer := time.NewTicker(500 * time.Millisecond)
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-self.quit:
|
||||
break out
|
||||
case <-procTimer.C:
|
||||
blocks := self.Blocks()
|
||||
types.BlockBy(types.Number).Sort(blocks)
|
||||
|
||||
// Find common block
|
||||
for i, block := range blocks {
|
||||
if self.eth.ChainManager().HasBlock(block.PrevHash) {
|
||||
blocks = blocks[i:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(blocks) > 0 {
|
||||
if self.eth.ChainManager().HasBlock(blocks[0].PrevHash) {
|
||||
for i, block := range blocks[1:] {
|
||||
// NOTE: The Ith element in this loop refers to the previous block in
|
||||
// outer "blocks"
|
||||
if bytes.Compare(block.PrevHash, blocks[i].Hash()) != 0 {
|
||||
blocks = blocks[:i]
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
blocks = nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(blocks) > 0 {
|
||||
chainman := self.eth.ChainManager()
|
||||
|
||||
err := chainman.InsertChain(blocks)
|
||||
if err != nil {
|
||||
poollogger.Debugln(err)
|
||||
|
||||
self.Reset()
|
||||
|
||||
if self.peer != nil && self.peer.conn != nil {
|
||||
poollogger.Debugf("Punishing peer for supplying bad chain (%v)\n", self.peer.conn.RemoteAddr())
|
||||
}
|
||||
|
||||
// This peer gave us bad hashes and made us fetch a bad chain, therefor he shall be punished.
|
||||
self.eth.BlacklistPeer(self.peer)
|
||||
self.peer.StopWithReason(DiscBadPeer)
|
||||
self.td = ethutil.Big0
|
||||
self.peer = nil
|
||||
}
|
||||
|
||||
for _, block := range blocks {
|
||||
self.Remove(block.Hash())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -21,9 +21,9 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/cmd/ethereum/repl"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/javascript"
|
||||
)
|
||||
|
||||
|
@ -38,7 +38,8 @@ var (
|
||||
StartRpc bool
|
||||
StartWebSockets bool
|
||||
RpcPort int
|
||||
UseUPnP bool
|
||||
NatType string
|
||||
PMPGateway string
|
||||
OutboundPort string
|
||||
ShowGenesis bool
|
||||
AddPeer string
|
||||
@ -57,6 +58,7 @@ var (
|
||||
DumpHash string
|
||||
DumpNumber int
|
||||
VmType int
|
||||
ImportChain string
|
||||
)
|
||||
|
||||
// flags specific to cli client
|
||||
@ -84,8 +86,9 @@ func Init() {
|
||||
flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
|
||||
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
|
||||
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
||||
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
|
||||
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers")
|
||||
flag.StringVar(&NatType, "nat", "", "NAT support (UPNP|PMP) (none)")
|
||||
flag.StringVar(&PMPGateway, "pmp", "", "Gateway IP for PMP")
|
||||
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
|
||||
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
|
||||
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
|
||||
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server")
|
||||
@ -102,6 +105,7 @@ func Init() {
|
||||
flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0")
|
||||
flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false")
|
||||
flag.BoolVar(&ShowGenesis, "genesis", false, "Dump the genesis block")
|
||||
flag.StringVar(&ImportChain, "chain", "", "Imports fiven chain")
|
||||
|
||||
flag.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]")
|
||||
flag.StringVar(&DumpHash, "hash", "", "specify arg in hex")
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
@ -30,7 +31,7 @@ import (
|
||||
|
||||
const (
|
||||
ClientIdentifier = "Ethereum(G)"
|
||||
Version = "0.7.9"
|
||||
Version = "0.7.11"
|
||||
)
|
||||
|
||||
var clilogger = logger.NewLogger("CLI")
|
||||
@ -38,6 +39,10 @@ var clilogger = logger.NewLogger("CLI")
|
||||
func main() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
defer func() {
|
||||
logger.Flush()
|
||||
}()
|
||||
|
||||
utils.HandleInterrupt()
|
||||
|
||||
// precedence: code-internal flag default < config file < environment variables < command line
|
||||
@ -69,15 +74,15 @@ func main() {
|
||||
// create, import, export keys
|
||||
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
|
||||
|
||||
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier)
|
||||
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier, string(keyManager.PublicKey()))
|
||||
|
||||
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
|
||||
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, utils.NatType(NatType, PMPGateway), OutboundPort, MaxPeer)
|
||||
|
||||
if Dump {
|
||||
var block *types.Block
|
||||
|
||||
if len(DumpHash) == 0 && DumpNumber == -1 {
|
||||
block = ethereum.ChainManager().CurrentBlock
|
||||
block = ethereum.ChainManager().CurrentBlock()
|
||||
} else if len(DumpHash) > 0 {
|
||||
block = ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(DumpHash))
|
||||
} else {
|
||||
@ -93,9 +98,6 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// block.GetRoot() does not exist
|
||||
//fmt.Printf("RLP: %x\nstate: %x\nhash: %x\n", ethutil.Rlp(block), block.GetRoot(), block.Hash())
|
||||
|
||||
// Leave the Println. This needs clean output for piping
|
||||
fmt.Printf("%s\n", block.State().Dump())
|
||||
|
||||
@ -112,6 +114,16 @@ func main() {
|
||||
utils.StartMining(ethereum)
|
||||
}
|
||||
|
||||
if len(ImportChain) > 0 {
|
||||
start := time.Now()
|
||||
err := utils.ImportChain(ethereum, ImportChain)
|
||||
if err != nil {
|
||||
clilogger.Infoln(err)
|
||||
}
|
||||
clilogger.Infoln("import done in", time.Since(start))
|
||||
return
|
||||
}
|
||||
|
||||
// better reworked as cases
|
||||
if StartJsConsole {
|
||||
InitJsConsole(ethereum)
|
||||
@ -131,5 +143,4 @@ func main() {
|
||||
|
||||
// this blocks the thread
|
||||
ethereum.WaitForShutdown()
|
||||
logger.Flush()
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/javascript"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
@ -86,12 +86,6 @@ func (self *JSRepl) Stop() {
|
||||
}
|
||||
|
||||
func (self *JSRepl) parseInput(code string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("[native] error", r)
|
||||
}
|
||||
}()
|
||||
|
||||
value, err := self.re.Run(code)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
5
cmd/ethtest/.bowerrc
Normal file
5
cmd/ethtest/.bowerrc
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"directory": "example/js/",
|
||||
"cwd": "./",
|
||||
"analytics": false
|
||||
}
|
12
cmd/ethtest/.editorconfig
Normal file
12
cmd/ethtest/.editorconfig
Normal file
@ -0,0 +1,12 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
8
wire/.gitignore → cmd/ethtest/.gitignore
vendored
8
wire/.gitignore → cmd/ethtest/.gitignore
vendored
@ -4,9 +4,15 @@
|
||||
# or operating system, you probably want to add a global ignore instead:
|
||||
# git config --global core.excludesfile ~/.gitignore_global
|
||||
|
||||
*.swp
|
||||
/tmp
|
||||
*/**/*un~
|
||||
*un~
|
||||
.DS_Store
|
||||
*/**/.DS_Store
|
||||
|
||||
ethereum/ethereum
|
||||
ethereal/ethereal
|
||||
example/js
|
||||
node_modules
|
||||
bower_components
|
||||
npm-debug.log
|
50
cmd/ethtest/.jshintrc
Normal file
50
cmd/ethtest/.jshintrc
Normal file
@ -0,0 +1,50 @@
|
||||
{
|
||||
"predef": [
|
||||
"console",
|
||||
"require",
|
||||
"equal",
|
||||
"test",
|
||||
"testBoth",
|
||||
"testWithDefault",
|
||||
"raises",
|
||||
"deepEqual",
|
||||
"start",
|
||||
"stop",
|
||||
"ok",
|
||||
"strictEqual",
|
||||
"module",
|
||||
"expect",
|
||||
"reject",
|
||||
"impl"
|
||||
],
|
||||
|
||||
"esnext": true,
|
||||
"proto": true,
|
||||
"node" : true,
|
||||
"browser" : true,
|
||||
"browserify" : true,
|
||||
|
||||
"boss" : true,
|
||||
"curly": false,
|
||||
"debug": true,
|
||||
"devel": true,
|
||||
"eqeqeq": true,
|
||||
"evil": true,
|
||||
"forin": false,
|
||||
"immed": false,
|
||||
"laxbreak": false,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": false,
|
||||
"nonew": false,
|
||||
"nomen": false,
|
||||
"onevar": false,
|
||||
"plusplus": false,
|
||||
"regexp": false,
|
||||
"undef": true,
|
||||
"sub": true,
|
||||
"strict": false,
|
||||
"white": false,
|
||||
"shadow": true,
|
||||
"eqnull": true
|
||||
}
|
9
cmd/ethtest/.npmignore
Normal file
9
cmd/ethtest/.npmignore
Normal file
@ -0,0 +1,9 @@
|
||||
example/js
|
||||
node_modules
|
||||
test
|
||||
.gitignore
|
||||
.editorconfig
|
||||
.travis.yml
|
||||
.npmignore
|
||||
component.json
|
||||
testling.html
|
11
cmd/ethtest/.travis.yml
Normal file
11
cmd/ethtest/.travis.yml
Normal file
@ -0,0 +1,11 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.11"
|
||||
- "0.10"
|
||||
before_script:
|
||||
- npm install
|
||||
- npm install jshint
|
||||
script:
|
||||
- "jshint *.js lib"
|
||||
after_script:
|
||||
- npm run-script gulp
|
14
cmd/ethtest/LICENSE
Normal file
14
cmd/ethtest/LICENSE
Normal file
@ -0,0 +1,14 @@
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
79
cmd/ethtest/README.md
Normal file
79
cmd/ethtest/README.md
Normal file
@ -0,0 +1,79 @@
|
||||
# Ethereum JavaScript API
|
||||
|
||||
This is the Ethereum compatible JavaScript API using `Promise`s
|
||||
which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC) spec. It's available on npm as a node module and also for bower and component as an embeddable js
|
||||
|
||||
[![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![dependency status][dep-image]][dep-url] [![dev dependency status][dep-dev-image]][dep-dev-url]
|
||||
|
||||
<!-- [![browser support](https://ci.testling.com/ethereum/ethereum.js.png)](https://ci.testling.com/ethereum/ethereum.js) -->
|
||||
|
||||
## Installation
|
||||
|
||||
### Node.js
|
||||
|
||||
npm install ethereum.js
|
||||
|
||||
### For browser
|
||||
Bower
|
||||
|
||||
bower install ethereum.js
|
||||
|
||||
Component
|
||||
|
||||
component install ethereum/ethereum.js
|
||||
|
||||
* Include `ethereum.min.js` in your html file.
|
||||
* Include [es6-promise](https://github.com/jakearchibald/es6-promise) or another ES6-Shim if your browser doesn't support ECMAScript 6.
|
||||
|
||||
## Usage
|
||||
Require the library:
|
||||
|
||||
var web3 = require('web3');
|
||||
|
||||
Set a provider (QtProvider, WebSocketProvider, HttpRpcProvider)
|
||||
|
||||
var web3.setProvider(new web3.providers.WebSocketProvider('ws://localhost:40404/eth'));
|
||||
|
||||
There you go, now you can use it:
|
||||
|
||||
```
|
||||
web3.eth.coinbase.then(function(result){
|
||||
console.log(result);
|
||||
return web3.eth.balanceAt(result);
|
||||
}).then(function(balance){
|
||||
console.log(web3.toDecimal(balance));
|
||||
}).catch(function(err){
|
||||
console.log(err);
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
For another example see `example/index.html`.
|
||||
|
||||
## Building
|
||||
|
||||
* `gulp build`
|
||||
|
||||
|
||||
### Testing
|
||||
|
||||
**Please note this repo is in it's early stage.**
|
||||
|
||||
If you'd like to run a WebSocket ethereum node check out
|
||||
[go-ethereum](https://github.com/ethereum/go-ethereum).
|
||||
|
||||
To install ethereum and spawn a node:
|
||||
|
||||
```
|
||||
go get github.com/ethereum/go-ethereum/ethereum
|
||||
ethereum -ws -loglevel=4
|
||||
```
|
||||
|
||||
[npm-image]: https://badge.fury.io/js/ethereum.js.png
|
||||
[npm-url]: https://npmjs.org/package/ethereum.js
|
||||
[travis-image]: https://travis-ci.org/ethereum/ethereum.js.svg
|
||||
[travis-url]: https://travis-ci.org/ethereum/ethereum.js
|
||||
[dep-image]: https://david-dm.org/ethereum/ethereum.js.svg
|
||||
[dep-url]: https://david-dm.org/ethereum/ethereum.js
|
||||
[dep-dev-image]: https://david-dm.org/ethereum/ethereum.js/dev-status.svg
|
||||
[dep-dev-url]: https://david-dm.org/ethereum/ethereum.js#info=devDependencies
|
51
cmd/ethtest/bower.json
Normal file
51
cmd/ethtest/bower.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "ethereum.js",
|
||||
"namespace": "ethereum",
|
||||
"version": "0.0.3",
|
||||
"description": "Ethereum Compatible JavaScript API",
|
||||
"main": ["./dist/ethereum.js", "./dist/ethereum.min.js"],
|
||||
"dependencies": {
|
||||
"es6-promise": "#master"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ethereum/ethereum.js.git"
|
||||
},
|
||||
"homepage": "https://github.com/ethereum/ethereum.js",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ethereum/ethereum.js/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"ethereum",
|
||||
"javascript",
|
||||
"API"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Marek Kotewicz",
|
||||
"email": "marek@ethdev.com",
|
||||
"homepage": "https://github.com/debris"
|
||||
},
|
||||
{
|
||||
"name": "Marian Oancea",
|
||||
"email": "marian@ethdev.com",
|
||||
"homepage": "https://github.com/cubedro"
|
||||
}
|
||||
],
|
||||
"license": "LGPL-3.0",
|
||||
"ignore": [
|
||||
"example",
|
||||
"lib",
|
||||
"node_modules",
|
||||
"package.json",
|
||||
".bowerrc",
|
||||
".editorconfig",
|
||||
".gitignore",
|
||||
".jshintrc",
|
||||
".npmignore",
|
||||
".travis.yml",
|
||||
"gulpfile.js",
|
||||
"index.js",
|
||||
"**/*.txt"
|
||||
]
|
||||
}
|
20
cmd/ethtest/dist/ethereum.js
vendored
Normal file
20
cmd/ethtest/dist/ethereum.js
vendored
Normal file
File diff suppressed because one or more lines are too long
29
cmd/ethtest/dist/ethereum.js.map
vendored
Normal file
29
cmd/ethtest/dist/ethereum.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
cmd/ethtest/dist/ethereum.min.js
vendored
Normal file
1
cmd/ethtest/dist/ethereum.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
75
cmd/ethtest/example/contract.html
Normal file
75
cmd/ethtest/example/contract.html
Normal file
@ -0,0 +1,75 @@
|
||||
<!doctype>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
|
||||
<script type="text/javascript" src="../dist/ethereum.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var web3 = require('web3');
|
||||
web3.setProvider(new web3.providers.AutoProvider());
|
||||
|
||||
// solidity source code
|
||||
var source = "" +
|
||||
"contract test {\n" +
|
||||
" function multiply(uint a) returns(uint d) {\n" +
|
||||
" return a * 7;\n" +
|
||||
" }\n" +
|
||||
"}\n";
|
||||
|
||||
// contract description, this will be autogenerated somehow
|
||||
var desc = [{
|
||||
"name": "multiply",
|
||||
"inputs": [
|
||||
{
|
||||
"name": "a",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "d",
|
||||
"type": "uint256"
|
||||
}
|
||||
]
|
||||
}];
|
||||
|
||||
var contract;
|
||||
|
||||
function createExampleContract() {
|
||||
// hide create button
|
||||
document.getElementById('create').style.visibility = 'hidden';
|
||||
document.getElementById('source').innerText = source;
|
||||
|
||||
// create contract
|
||||
web3.eth.transact({code: web3.eth.solidity(source)}).then(function (address) {
|
||||
contract = web3.contract(address, desc);
|
||||
document.getElementById('call').style.visibility = 'visible';
|
||||
});
|
||||
}
|
||||
|
||||
function callExampleContract() {
|
||||
// this should be generated by ethereum
|
||||
var param = document.getElementById('value').value;
|
||||
|
||||
// call the contract
|
||||
contract.multiply(param).call().then(function(res) {
|
||||
document.getElementById('result').innerText = res[0];
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>contract</h1>
|
||||
<div id="source"></div>
|
||||
<div id='create'>
|
||||
<button type="button" onClick="createExampleContract();">create example contract</button>
|
||||
</div>
|
||||
<div id='call' style='visibility: hidden;'>
|
||||
<input type="number" id="value" onkeyup='callExampleContract()'></input>
|
||||
</div>
|
||||
<div id="result"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
41
cmd/ethtest/example/index.html
Normal file
41
cmd/ethtest/example/index.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!doctype>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<script type="text/javascript" src="js/es6-promise/promise.min.js"></script>
|
||||
<script type="text/javascript" src="../dist/ethereum.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var web3 = require('web3');
|
||||
web3.setProvider(new web3.providers.AutoProvider());
|
||||
|
||||
function watchBalance() {
|
||||
var coinbase = web3.eth.coinbase;
|
||||
var originalBalance = 0;
|
||||
|
||||
web3.eth.balanceAt(coinbase).then(function (balance) {
|
||||
originalBalance = web3.toDecimal(balance);
|
||||
document.getElementById('original').innerText = 'original balance: ' + originalBalance + ' watching...';
|
||||
});
|
||||
|
||||
web3.eth.watch({altered: coinbase}).changed(function() {
|
||||
web3.eth.balanceAt(coinbase).then(function (balance) {
|
||||
var currentBalance = web3.toDecimal(balance);
|
||||
document.getElementById("current").innerText = 'current: ' + currentBalance;
|
||||
document.getElementById("diff").innerText = 'diff: ' + (currentBalance - originalBalance);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>coinbase balance</h1>
|
||||
<button type="button" onClick="watchBalance();">watch balance</button>
|
||||
<div></div>
|
||||
<div id="original"></div>
|
||||
<div id="current"></div>
|
||||
<div id="diff"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
16
cmd/ethtest/example/node-app.js
Normal file
16
cmd/ethtest/example/node-app.js
Normal file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require('es6-promise').polyfill();
|
||||
|
||||
var web3 = require("../index.js");
|
||||
|
||||
web3.setProvider(new web3.providers.HttpRpcProvider('http://localhost:8080'));
|
||||
|
||||
web3.eth.coinbase.then(function(result){
|
||||
console.log(result);
|
||||
return web3.eth.balanceAt(result);
|
||||
}).then(function(balance){
|
||||
console.log(web3.toDecimal(balance));
|
||||
}).catch(function(err){
|
||||
console.log(err);
|
||||
});
|
123
cmd/ethtest/gulpfile.js
Normal file
123
cmd/ethtest/gulpfile.js
Normal file
@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
|
||||
var del = require('del');
|
||||
var gulp = require('gulp');
|
||||
var browserify = require('browserify');
|
||||
var jshint = require('gulp-jshint');
|
||||
var uglify = require('gulp-uglify');
|
||||
var rename = require('gulp-rename');
|
||||
var envify = require('envify/custom');
|
||||
var unreach = require('unreachable-branch-transform');
|
||||
var source = require('vinyl-source-stream');
|
||||
var exorcist = require('exorcist');
|
||||
var bower = require('bower');
|
||||
|
||||
var DEST = './dist/';
|
||||
|
||||
var build = function(src, dst) {
|
||||
return browserify({
|
||||
debug: true,
|
||||
insert_global_vars: false,
|
||||
detectGlobals: false,
|
||||
bundleExternal: false
|
||||
})
|
||||
.require('./' + src + '.js', {expose: 'web3'})
|
||||
.add('./' + src + '.js')
|
||||
.transform('envify', {
|
||||
NODE_ENV: 'build'
|
||||
})
|
||||
.transform('unreachable-branch-transform')
|
||||
.transform('uglifyify', {
|
||||
mangle: false,
|
||||
compress: {
|
||||
dead_code: false,
|
||||
conditionals: true,
|
||||
unused: false,
|
||||
hoist_funs: true,
|
||||
hoist_vars: true,
|
||||
negate_iife: false
|
||||
},
|
||||
beautify: true,
|
||||
warnings: true
|
||||
})
|
||||
.bundle()
|
||||
.pipe(exorcist(path.join( DEST, dst + '.js.map')))
|
||||
.pipe(source(dst + '.js'))
|
||||
.pipe(gulp.dest( DEST ));
|
||||
};
|
||||
|
||||
var buildDev = function(src, dst) {
|
||||
return browserify({
|
||||
debug: true,
|
||||
insert_global_vars: false,
|
||||
detectGlobals: false,
|
||||
bundleExternal: false
|
||||
})
|
||||
.require('./' + src + '.js', {expose: 'web3'})
|
||||
.add('./' + src + '.js')
|
||||
.transform('envify', {
|
||||
NODE_ENV: 'build'
|
||||
})
|
||||
.transform('unreachable-branch-transform')
|
||||
.bundle()
|
||||
.pipe(exorcist(path.join( DEST, dst + '.js.map')))
|
||||
.pipe(source(dst + '.js'))
|
||||
.pipe(gulp.dest( DEST ));
|
||||
};
|
||||
|
||||
var uglifyFile = function(file) {
|
||||
return gulp.src( DEST + file + '.js')
|
||||
.pipe(uglify())
|
||||
.pipe(rename(file + '.min.js'))
|
||||
.pipe(gulp.dest( DEST ));
|
||||
};
|
||||
|
||||
gulp.task('bower', function(cb){
|
||||
bower.commands.install().on('end', function (installed){
|
||||
console.log(installed);
|
||||
cb();
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('lint', function(){
|
||||
return gulp.src(['./*.js', './lib/*.js'])
|
||||
.pipe(jshint())
|
||||
.pipe(jshint.reporter('default'));
|
||||
});
|
||||
|
||||
gulp.task('clean', ['lint'], function(cb) {
|
||||
del([ DEST ], cb);
|
||||
});
|
||||
|
||||
gulp.task('build', ['clean'], function () {
|
||||
return build('index', 'ethereum');
|
||||
});
|
||||
|
||||
gulp.task('buildQt', ['clean'], function () {
|
||||
return build('index_qt', 'ethereum');
|
||||
});
|
||||
|
||||
gulp.task('buildDev', ['clean'], function () {
|
||||
return buildDev('index', 'ethereum');
|
||||
});
|
||||
|
||||
gulp.task('uglify', ['build'], function(){
|
||||
return uglifyFile('ethereum');
|
||||
});
|
||||
|
||||
gulp.task('uglifyQt', ['buildQt'], function () {
|
||||
return uglifyFile('ethereum');
|
||||
});
|
||||
|
||||
gulp.task('watch', function() {
|
||||
gulp.watch(['./lib/*.js'], ['lint', 'prepare', 'build']);
|
||||
});
|
||||
|
||||
gulp.task('default', ['bower', 'lint', 'build', 'uglify']);
|
||||
gulp.task('qt', ['bower', 'lint', 'buildQt', 'uglifyQt']);
|
||||
gulp.task('dev', ['bower', 'lint', 'buildDev']);
|
||||
|
8
cmd/ethtest/index.js
Normal file
8
cmd/ethtest/index.js
Normal file
@ -0,0 +1,8 @@
|
||||
var web3 = require('./lib/main');
|
||||
web3.providers.WebSocketProvider = require('./lib/websocket');
|
||||
web3.providers.HttpRpcProvider = require('./lib/httprpc');
|
||||
web3.providers.QtProvider = require('./lib/qt');
|
||||
web3.providers.AutoProvider = require('./lib/autoprovider');
|
||||
web3.contract = require('./lib/contract');
|
||||
|
||||
module.exports = web3;
|
5
cmd/ethtest/index_qt.js
Normal file
5
cmd/ethtest/index_qt.js
Normal file
@ -0,0 +1,5 @@
|
||||
var web3 = require('./lib/main');
|
||||
web3.providers.QtProvider = require('./lib/qt');
|
||||
web3.contract = require('./lib/contract');
|
||||
|
||||
module.exports = web3;
|
218
cmd/ethtest/lib/abi.js
Normal file
218
cmd/ethtest/lib/abi.js
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file abi.js
|
||||
* @authors:
|
||||
* Marek Kotewicz <marek@ethdev.com>
|
||||
* Gav Wood <g@ethdev.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
// TODO: make these be actually accurate instead of falling back onto JS's doubles.
|
||||
var hexToDec = function (hex) {
|
||||
return parseInt(hex, 16).toString();
|
||||
};
|
||||
|
||||
var decToHex = function (dec) {
|
||||
return parseInt(dec).toString(16);
|
||||
};
|
||||
|
||||
var findIndex = function (array, callback) {
|
||||
var end = false;
|
||||
var i = 0;
|
||||
for (; i < array.length && !end; i++) {
|
||||
end = callback(array[i]);
|
||||
}
|
||||
return end ? i - 1 : -1;
|
||||
};
|
||||
|
||||
var findMethodIndex = function (json, methodName) {
|
||||
return findIndex(json, function (method) {
|
||||
return method.name === methodName;
|
||||
});
|
||||
};
|
||||
|
||||
var padLeft = function (string, chars) {
|
||||
return Array(chars - string.length + 1).join("0") + string;
|
||||
};
|
||||
|
||||
var setupInputTypes = function () {
|
||||
var prefixedType = function (prefix) {
|
||||
return function (type, value) {
|
||||
var expected = prefix;
|
||||
if (type.indexOf(expected) !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var padding = parseInt(type.slice(expected.length)) / 8;
|
||||
if (typeof value === "number")
|
||||
value = value.toString(16);
|
||||
else if (value.indexOf('0x') === 0)
|
||||
value = value.substr(2);
|
||||
else
|
||||
value = (+value).toString(16);
|
||||
return padLeft(value, padding * 2);
|
||||
};
|
||||
};
|
||||
|
||||
var namedType = function (name, padding, formatter) {
|
||||
return function (type, value) {
|
||||
if (type !== name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return padLeft(formatter ? formatter(value) : value, padding * 2);
|
||||
};
|
||||
};
|
||||
|
||||
var formatBool = function (value) {
|
||||
return value ? '0x1' : '0x0';
|
||||
};
|
||||
|
||||
return [
|
||||
prefixedType('uint'),
|
||||
prefixedType('int'),
|
||||
prefixedType('hash'),
|
||||
namedType('address', 20),
|
||||
namedType('bool', 1, formatBool),
|
||||
];
|
||||
};
|
||||
|
||||
var inputTypes = setupInputTypes();
|
||||
|
||||
var toAbiInput = function (json, methodName, params) {
|
||||
var bytes = "";
|
||||
var index = findMethodIndex(json, methodName);
|
||||
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
bytes = "0x" + padLeft(index.toString(16), 2);
|
||||
var method = json[index];
|
||||
|
||||
for (var i = 0; i < method.inputs.length; i++) {
|
||||
var found = false;
|
||||
for (var j = 0; j < inputTypes.length && !found; j++) {
|
||||
found = inputTypes[j](method.inputs[i].type, params[i]);
|
||||
}
|
||||
if (!found) {
|
||||
console.error('unsupported json type: ' + method.inputs[i].type);
|
||||
}
|
||||
bytes += found;
|
||||
}
|
||||
return bytes;
|
||||
};
|
||||
|
||||
var setupOutputTypes = function () {
|
||||
var prefixedType = function (prefix) {
|
||||
return function (type) {
|
||||
var expected = prefix;
|
||||
if (type.indexOf(expected) !== 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var padding = parseInt(type.slice(expected.length)) / 8;
|
||||
return padding * 2;
|
||||
};
|
||||
};
|
||||
|
||||
var namedType = function (name, padding) {
|
||||
return function (type) {
|
||||
return name === type ? padding * 2 : -1;
|
||||
};
|
||||
};
|
||||
|
||||
var formatInt = function (value) {
|
||||
return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value);
|
||||
};
|
||||
|
||||
var formatHash = function (value) {
|
||||
return "0x" + value;
|
||||
};
|
||||
|
||||
var formatBool = function (value) {
|
||||
return value === '1' ? true : false;
|
||||
};
|
||||
|
||||
return [
|
||||
{ padding: prefixedType('uint'), format: formatInt },
|
||||
{ padding: prefixedType('int'), format: formatInt },
|
||||
{ padding: prefixedType('hash'), format: formatHash },
|
||||
{ padding: namedType('address', 20) },
|
||||
{ padding: namedType('bool', 1), format: formatBool }
|
||||
];
|
||||
};
|
||||
|
||||
var outputTypes = setupOutputTypes();
|
||||
|
||||
var fromAbiOutput = function (json, methodName, output) {
|
||||
var index = findMethodIndex(json, methodName);
|
||||
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
output = output.slice(2);
|
||||
|
||||
var result = [];
|
||||
var method = json[index];
|
||||
for (var i = 0; i < method.outputs.length; i++) {
|
||||
var padding = -1;
|
||||
for (var j = 0; j < outputTypes.length && padding === -1; j++) {
|
||||
padding = outputTypes[j].padding(method.outputs[i].type);
|
||||
}
|
||||
|
||||
if (padding === -1) {
|
||||
// not found output parsing
|
||||
continue;
|
||||
}
|
||||
var res = output.slice(0, padding);
|
||||
var formatter = outputTypes[j - 1].format;
|
||||
result.push(formatter ? formatter(res) : ("0x" + res));
|
||||
output = output.slice(padding);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
var inputParser = function (json) {
|
||||
var parser = {};
|
||||
json.forEach(function (method) {
|
||||
parser[method.name] = function () {
|
||||
var params = Array.prototype.slice.call(arguments);
|
||||
return toAbiInput(json, method.name, params);
|
||||
};
|
||||
});
|
||||
|
||||
return parser;
|
||||
};
|
||||
|
||||
var outputParser = function (json) {
|
||||
var parser = {};
|
||||
json.forEach(function (method) {
|
||||
parser[method.name] = function (output) {
|
||||
return fromAbiOutput(json, method.name, output);
|
||||
};
|
||||
});
|
||||
|
||||
return parser;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
inputParser: inputParser,
|
||||
outputParser: outputParser
|
||||
};
|
103
cmd/ethtest/lib/autoprovider.js
Normal file
103
cmd/ethtest/lib/autoprovider.js
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file autoprovider.js
|
||||
* @authors:
|
||||
* Marek Kotewicz <marek@ethdev.com>
|
||||
* Marian Oancea <marian@ethdev.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
/*
|
||||
* @brief if qt object is available, uses QtProvider,
|
||||
* if not tries to connect over websockets
|
||||
* if it fails, it uses HttpRpcProvider
|
||||
*/
|
||||
|
||||
// TODO: work out which of the following two lines it is supposed to be...
|
||||
//if (process.env.NODE_ENV !== 'build') {
|
||||
if ("build" !== 'build') {/*
|
||||
var WebSocket = require('ws'); // jshint ignore:line
|
||||
var web3 = require('./main.js'); // jshint ignore:line
|
||||
*/}
|
||||
|
||||
var AutoProvider = function (userOptions) {
|
||||
if (web3.haveProvider()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// before we determine what provider we are, we have to cache request
|
||||
this.sendQueue = [];
|
||||
this.onmessageQueue = [];
|
||||
|
||||
if (navigator.qt) {
|
||||
this.provider = new web3.providers.QtProvider();
|
||||
return;
|
||||
}
|
||||
|
||||
userOptions = userOptions || {};
|
||||
var options = {
|
||||
httprpc: userOptions.httprpc || 'http://localhost:8080',
|
||||
websockets: userOptions.websockets || 'ws://localhost:40404/eth'
|
||||
};
|
||||
|
||||
var self = this;
|
||||
var closeWithSuccess = function (success) {
|
||||
ws.close();
|
||||
if (success) {
|
||||
self.provider = new web3.providers.WebSocketProvider(options.websockets);
|
||||
} else {
|
||||
self.provider = new web3.providers.HttpRpcProvider(options.httprpc);
|
||||
self.poll = self.provider.poll.bind(self.provider);
|
||||
}
|
||||
self.sendQueue.forEach(function (payload) {
|
||||
self.provider(payload);
|
||||
});
|
||||
self.onmessageQueue.forEach(function (handler) {
|
||||
self.provider.onmessage = handler;
|
||||
});
|
||||
};
|
||||
|
||||
var ws = new WebSocket(options.websockets);
|
||||
|
||||
ws.onopen = function() {
|
||||
closeWithSuccess(true);
|
||||
};
|
||||
|
||||
ws.onerror = function() {
|
||||
closeWithSuccess(false);
|
||||
};
|
||||
};
|
||||
|
||||
AutoProvider.prototype.send = function (payload) {
|
||||
if (this.provider) {
|
||||
this.provider.send(payload);
|
||||
return;
|
||||
}
|
||||
this.sendQueue.push(payload);
|
||||
};
|
||||
|
||||
Object.defineProperty(AutoProvider.prototype, 'onmessage', {
|
||||
set: function (handler) {
|
||||
if (this.provider) {
|
||||
this.provider.onmessage = handler;
|
||||
return;
|
||||
}
|
||||
this.onmessageQueue.push(handler);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = AutoProvider;
|
65
cmd/ethtest/lib/contract.js
Normal file
65
cmd/ethtest/lib/contract.js
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file contract.js
|
||||
* @authors:
|
||||
* Marek Kotewicz <marek@ethdev.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
// TODO: work out which of the following two lines it is supposed to be...
|
||||
//if (process.env.NODE_ENV !== 'build') {
|
||||
if ("build" !== 'build') {/*
|
||||
var web3 = require('./web3'); // jshint ignore:line
|
||||
*/}
|
||||
var abi = require('./abi');
|
||||
|
||||
var contract = function (address, desc) {
|
||||
var inputParser = abi.inputParser(desc);
|
||||
var outputParser = abi.outputParser(desc);
|
||||
|
||||
var contract = {};
|
||||
|
||||
desc.forEach(function (method) {
|
||||
contract[method.name] = function () {
|
||||
var params = Array.prototype.slice.call(arguments);
|
||||
var parsed = inputParser[method.name].apply(null, params);
|
||||
|
||||
var onSuccess = function (result) {
|
||||
return outputParser[method.name](result);
|
||||
};
|
||||
|
||||
return {
|
||||
call: function (extra) {
|
||||
extra = extra || {};
|
||||
extra.to = address;
|
||||
extra.data = parsed;
|
||||
return web3.eth.call(extra).then(onSuccess);
|
||||
},
|
||||
transact: function (extra) {
|
||||
extra = extra || {};
|
||||
extra.to = address;
|
||||
extra.data = parsed;
|
||||
return web3.eth.transact(extra).then(onSuccess);
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
return contract;
|
||||
};
|
||||
|
||||
module.exports = contract;
|
95
cmd/ethtest/lib/httprpc.js
Normal file
95
cmd/ethtest/lib/httprpc.js
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file httprpc.js
|
||||
* @authors:
|
||||
* Marek Kotewicz <marek@ethdev.com>
|
||||
* Marian Oancea <marian@ethdev.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
// TODO: work out which of the following two lines it is supposed to be...
|
||||
//if (process.env.NODE_ENV !== 'build') {
|
||||
if ("build" !== "build") {/*
|
||||
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
|
||||
*/}
|
||||
|
||||
var HttpRpcProvider = function (host) {
|
||||
this.handlers = [];
|
||||
this.host = host;
|
||||
};
|
||||
|
||||
function formatJsonRpcObject(object) {
|
||||
return {
|
||||
jsonrpc: '2.0',
|
||||
method: object.call,
|
||||
params: object.args,
|
||||
id: object._id
|
||||
};
|
||||
}
|
||||
|
||||
function formatJsonRpcMessage(message) {
|
||||
var object = JSON.parse(message);
|
||||
|
||||
return {
|
||||
_id: object.id,
|
||||
data: object.result,
|
||||
error: object.error
|
||||
};
|
||||
}
|
||||
|
||||
HttpRpcProvider.prototype.sendRequest = function (payload, cb) {
|
||||
var data = formatJsonRpcObject(payload);
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.open("POST", this.host, true);
|
||||
request.send(JSON.stringify(data));
|
||||
request.onreadystatechange = function () {
|
||||
if (request.readyState === 4 && cb) {
|
||||
cb(request);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
HttpRpcProvider.prototype.send = function (payload) {
|
||||
var self = this;
|
||||
this.sendRequest(payload, function (request) {
|
||||
self.handlers.forEach(function (handler) {
|
||||
handler.call(self, formatJsonRpcMessage(request.responseText));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
HttpRpcProvider.prototype.poll = function (payload, id) {
|
||||
var self = this;
|
||||
this.sendRequest(payload, function (request) {
|
||||
var parsed = JSON.parse(request.responseText);
|
||||
if (parsed.error || (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result)) {
|
||||
return;
|
||||
}
|
||||
self.handlers.forEach(function (handler) {
|
||||
handler.call(self, {_event: payload.call, _id: id, data: parsed.result});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Object.defineProperty(HttpRpcProvider.prototype, "onmessage", {
|
||||
set: function (handler) {
|
||||
this.handlers.push(handler);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = HttpRpcProvider;
|
494
cmd/ethtest/lib/main.js
Normal file
494
cmd/ethtest/lib/main.js
Normal file
@ -0,0 +1,494 @@
|
||||
/*
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file main.js
|
||||
* @authors:
|
||||
* Jeffrey Wilcke <jeff@ethdev.com>
|
||||
* Marek Kotewicz <marek@ethdev.com>
|
||||
* Marian Oancea <marian@ethdev.com>
|
||||
* Gav Wood <g@ethdev.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
function flattenPromise (obj) {
|
||||
if (obj instanceof Promise) {
|
||||
return Promise.resolve(obj);
|
||||
}
|
||||
|
||||
if (obj instanceof Array) {
|
||||
return new Promise(function (resolve) {
|
||||
var promises = obj.map(function (o) {
|
||||
return flattenPromise(o);
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(function (res) {
|
||||
for (var i = 0; i < obj.length; i++) {
|
||||
obj[i] = res[i];
|
||||
}
|
||||
resolve(obj);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (obj instanceof Object) {
|
||||
return new Promise(function (resolve) {
|
||||
var keys = Object.keys(obj);
|
||||
var promises = keys.map(function (key) {
|
||||
return flattenPromise(obj[key]);
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(function (res) {
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
obj[keys[i]] = res[i];
|
||||
}
|
||||
resolve(obj);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(obj);
|
||||
}
|
||||
|
||||
var web3Methods = function () {
|
||||
return [
|
||||
{ name: 'sha3', call: 'web3_sha3' }
|
||||
];
|
||||
};
|
||||
|
||||
var ethMethods = function () {
|
||||
var blockCall = function (args) {
|
||||
return typeof args[0] === "string" ? "eth_blockByHash" : "eth_blockByNumber";
|
||||
};
|
||||
|
||||
var transactionCall = function (args) {
|
||||
return typeof args[0] === "string" ? 'eth_transactionByHash' : 'eth_transactionByNumber';
|
||||
};
|
||||
|
||||
var uncleCall = function (args) {
|
||||
return typeof args[0] === "string" ? 'eth_uncleByHash' : 'eth_uncleByNumber';
|
||||
};
|
||||
|
||||
var methods = [
|
||||
{ name: 'balanceAt', call: 'eth_balanceAt' },
|
||||
{ name: 'stateAt', call: 'eth_stateAt' },
|
||||
{ name: 'storageAt', call: 'eth_storageAt' },
|
||||
{ name: 'countAt', call: 'eth_countAt'},
|
||||
{ name: 'codeAt', call: 'eth_codeAt' },
|
||||
{ name: 'transact', call: 'eth_transact' },
|
||||
{ name: 'call', call: 'eth_call' },
|
||||
{ name: 'block', call: blockCall },
|
||||
{ name: 'transaction', call: transactionCall },
|
||||
{ name: 'uncle', call: uncleCall },
|
||||
{ name: 'compilers', call: 'eth_compilers' },
|
||||
{ name: 'lll', call: 'eth_lll' },
|
||||
{ name: 'solidity', call: 'eth_solidity' },
|
||||
{ name: 'serpent', call: 'eth_serpent' },
|
||||
{ name: 'logs', call: 'eth_logs' }
|
||||
];
|
||||
return methods;
|
||||
};
|
||||
|
||||
var ethProperties = function () {
|
||||
return [
|
||||
{ name: 'coinbase', getter: 'eth_coinbase', setter: 'eth_setCoinbase' },
|
||||
{ name: 'listening', getter: 'eth_listening', setter: 'eth_setListening' },
|
||||
{ name: 'mining', getter: 'eth_mining', setter: 'eth_setMining' },
|
||||
{ name: 'gasPrice', getter: 'eth_gasPrice' },
|
||||
{ name: 'account', getter: 'eth_account' },
|
||||
{ name: 'accounts', getter: 'eth_accounts' },
|
||||
{ name: 'peerCount', getter: 'eth_peerCount' },
|
||||
{ name: 'defaultBlock', getter: 'eth_defaultBlock', setter: 'eth_setDefaultBlock' },
|
||||
{ name: 'number', getter: 'eth_number'}
|
||||
];
|
||||
};
|
||||
|
||||
var dbMethods = function () {
|
||||
return [
|
||||
{ name: 'put', call: 'db_put' },
|
||||
{ name: 'get', call: 'db_get' },
|
||||
{ name: 'putString', call: 'db_putString' },
|
||||
{ name: 'getString', call: 'db_getString' }
|
||||
];
|
||||
};
|
||||
|
||||
var shhMethods = function () {
|
||||
return [
|
||||
{ name: 'post', call: 'shh_post' },
|
||||
{ name: 'newIdentity', call: 'shh_newIdentity' },
|
||||
{ name: 'haveIdentity', call: 'shh_haveIdentity' },
|
||||
{ name: 'newGroup', call: 'shh_newGroup' },
|
||||
{ name: 'addToGroup', call: 'shh_addToGroup' }
|
||||
];
|
||||
};
|
||||
|
||||
var ethWatchMethods = function () {
|
||||
var newFilter = function (args) {
|
||||
return typeof args[0] === 'string' ? 'eth_newFilterString' : 'eth_newFilter';
|
||||
};
|
||||
|
||||
return [
|
||||
{ name: 'newFilter', call: newFilter },
|
||||
{ name: 'uninstallFilter', call: 'eth_uninstallFilter' },
|
||||
{ name: 'getMessages', call: 'eth_filterLogs' }
|
||||
];
|
||||
};
|
||||
|
||||
var shhWatchMethods = function () {
|
||||
return [
|
||||
{ name: 'newFilter', call: 'shh_newFilter' },
|
||||
{ name: 'uninstallFilter', call: 'shh_uninstallFilter' },
|
||||
{ name: 'getMessage', call: 'shh_getMessages' }
|
||||
];
|
||||
};
|
||||
|
||||
var setupMethods = function (obj, methods) {
|
||||
methods.forEach(function (method) {
|
||||
obj[method.name] = function () {
|
||||
return flattenPromise(Array.prototype.slice.call(arguments)).then(function (args) {
|
||||
var call = typeof method.call === "function" ? method.call(args) : method.call;
|
||||
return {call: call, args: args};
|
||||
}).then(function (request) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
web3.provider.send(request, function (err, result) {
|
||||
if (!err) {
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}).catch(function(err) {
|
||||
console.error(err);
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
var setupProperties = function (obj, properties) {
|
||||
properties.forEach(function (property) {
|
||||
var proto = {};
|
||||
proto.get = function () {
|
||||
return new Promise(function(resolve, reject) {
|
||||
web3.provider.send({call: property.getter}, function(err, result) {
|
||||
if (!err) {
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
if (property.setter) {
|
||||
proto.set = function (val) {
|
||||
return flattenPromise([val]).then(function (args) {
|
||||
return new Promise(function (resolve) {
|
||||
web3.provider.send({call: property.setter, args: args}, function (err, result) {
|
||||
if (!err) {
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}).catch(function (err) {
|
||||
console.error(err);
|
||||
});
|
||||
};
|
||||
}
|
||||
Object.defineProperty(obj, property.name, proto);
|
||||
});
|
||||
};
|
||||
|
||||
// TODO: import from a dependency, don't duplicate.
|
||||
var hexToDec = function (hex) {
|
||||
return parseInt(hex, 16).toString();
|
||||
};
|
||||
|
||||
var decToHex = function (dec) {
|
||||
return parseInt(dec).toString(16);
|
||||
};
|
||||
|
||||
|
||||
var web3 = {
|
||||
_callbacks: {},
|
||||
_events: {},
|
||||
providers: {},
|
||||
|
||||
toAscii: function(hex) {
|
||||
// Find termination
|
||||
var str = "";
|
||||
var i = 0, l = hex.length;
|
||||
if (hex.substring(0, 2) === '0x')
|
||||
i = 2;
|
||||
for(; i < l; i+=2) {
|
||||
var code = hex.charCodeAt(i);
|
||||
if(code === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||
}
|
||||
|
||||
return str;
|
||||
},
|
||||
|
||||
fromAscii: function(str, pad) {
|
||||
pad = pad === undefined ? 32 : pad;
|
||||
var hex = this.toHex(str);
|
||||
while(hex.length < pad*2)
|
||||
hex += "00";
|
||||
return "0x" + hex;
|
||||
},
|
||||
|
||||
toDecimal: function (val) {
|
||||
return hexToDec(val.substring(2));
|
||||
},
|
||||
|
||||
fromDecimal: function (val) {
|
||||
return "0x" + decToHex(val);
|
||||
},
|
||||
|
||||
toEth: function(str) {
|
||||
var val = typeof str === "string" ? str.indexOf('0x') == 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str;
|
||||
var unit = 0;
|
||||
var units = [ 'wei', 'Kwei', 'Mwei', 'Gwei', 'szabo', 'finney', 'ether', 'grand', 'Mether', 'Gether', 'Tether', 'Pether', 'Eether', 'Zether', 'Yether', 'Nether', 'Dether', 'Vether', 'Uether' ];
|
||||
while (val > 3000 && unit < units.length - 1)
|
||||
{
|
||||
val /= 1000;
|
||||
unit++;
|
||||
}
|
||||
var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2);
|
||||
while (true) {
|
||||
var o = s;
|
||||
s = s.replace(/(\d)(\d\d\d[\.\,])/, function($0, $1, $2) { return $1 + ',' + $2; });
|
||||
if (o == s)
|
||||
break;
|
||||
}
|
||||
return s + ' ' + units[unit];
|
||||
},
|
||||
|
||||
eth: {
|
||||
prototype: Object(), // jshint ignore:line
|
||||
watch: function (params) {
|
||||
return new Filter(params, ethWatch);
|
||||
}
|
||||
},
|
||||
|
||||
db: {
|
||||
prototype: Object() // jshint ignore:line
|
||||
},
|
||||
|
||||
shh: {
|
||||
prototype: Object(), // jshint ignore:line
|
||||
watch: function (params) {
|
||||
return new Filter(params, shhWatch);
|
||||
}
|
||||
},
|
||||
|
||||
on: function(event, id, cb) {
|
||||
if(web3._events[event] === undefined) {
|
||||
web3._events[event] = {};
|
||||
}
|
||||
|
||||
web3._events[event][id] = cb;
|
||||
return this;
|
||||
},
|
||||
|
||||
off: function(event, id) {
|
||||
if(web3._events[event] !== undefined) {
|
||||
delete web3._events[event][id];
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
trigger: function(event, id, data) {
|
||||
var callbacks = web3._events[event];
|
||||
if (!callbacks || !callbacks[id]) {
|
||||
return;
|
||||
}
|
||||
var cb = callbacks[id];
|
||||
cb(data);
|
||||
}
|
||||
};
|
||||
|
||||
setupMethods(web3, web3Methods());
|
||||
setupMethods(web3.eth, ethMethods());
|
||||
setupProperties(web3.eth, ethProperties());
|
||||
setupMethods(web3.db, dbMethods());
|
||||
setupMethods(web3.shh, shhMethods());
|
||||
|
||||
var ethWatch = {
|
||||
changed: 'eth_changed'
|
||||
};
|
||||
setupMethods(ethWatch, ethWatchMethods());
|
||||
var shhWatch = {
|
||||
changed: 'shh_changed'
|
||||
};
|
||||
setupMethods(shhWatch, shhWatchMethods());
|
||||
|
||||
var ProviderManager = function() {
|
||||
this.queued = [];
|
||||
this.polls = [];
|
||||
this.ready = false;
|
||||
this.provider = undefined;
|
||||
this.id = 1;
|
||||
|
||||
var self = this;
|
||||
var poll = function () {
|
||||
if (self.provider && self.provider.poll) {
|
||||
self.polls.forEach(function (data) {
|
||||
data.data._id = self.id;
|
||||
self.id++;
|
||||
self.provider.poll(data.data, data.id);
|
||||
});
|
||||
}
|
||||
setTimeout(poll, 12000);
|
||||
};
|
||||
poll();
|
||||
};
|
||||
|
||||
ProviderManager.prototype.send = function(data, cb) {
|
||||
data._id = this.id;
|
||||
if (cb) {
|
||||
web3._callbacks[data._id] = cb;
|
||||
}
|
||||
|
||||
data.args = data.args || [];
|
||||
this.id++;
|
||||
|
||||
if(this.provider !== undefined) {
|
||||
this.provider.send(data);
|
||||
} else {
|
||||
console.warn("provider is not set");
|
||||
this.queued.push(data);
|
||||
}
|
||||
};
|
||||
|
||||
ProviderManager.prototype.set = function(provider) {
|
||||
if(this.provider !== undefined && this.provider.unload !== undefined) {
|
||||
this.provider.unload();
|
||||
}
|
||||
|
||||
this.provider = provider;
|
||||
this.ready = true;
|
||||
};
|
||||
|
||||
ProviderManager.prototype.sendQueued = function() {
|
||||
for(var i = 0; this.queued.length; i++) {
|
||||
// Resend
|
||||
this.send(this.queued[i]);
|
||||
}
|
||||
};
|
||||
|
||||
ProviderManager.prototype.installed = function() {
|
||||
return this.provider !== undefined;
|
||||
};
|
||||
|
||||
ProviderManager.prototype.startPolling = function (data, pollId) {
|
||||
if (!this.provider || !this.provider.poll) {
|
||||
return;
|
||||
}
|
||||
this.polls.push({data: data, id: pollId});
|
||||
};
|
||||
|
||||
ProviderManager.prototype.stopPolling = function (pollId) {
|
||||
for (var i = this.polls.length; i--;) {
|
||||
var poll = this.polls[i];
|
||||
if (poll.id === pollId) {
|
||||
this.polls.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
web3.provider = new ProviderManager();
|
||||
|
||||
web3.setProvider = function(provider) {
|
||||
provider.onmessage = messageHandler;
|
||||
web3.provider.set(provider);
|
||||
web3.provider.sendQueued();
|
||||
};
|
||||
|
||||
web3.haveProvider = function() {
|
||||
return !!web3.provider.provider;
|
||||
};
|
||||
|
||||
var Filter = function(options, impl) {
|
||||
this.impl = impl;
|
||||
this.callbacks = [];
|
||||
|
||||
var self = this;
|
||||
this.promise = impl.newFilter(options);
|
||||
this.promise.then(function (id) {
|
||||
self.id = id;
|
||||
web3.on(impl.changed, id, self.trigger.bind(self));
|
||||
web3.provider.startPolling({call: impl.changed, args: [id]}, id);
|
||||
});
|
||||
};
|
||||
|
||||
Filter.prototype.arrived = function(callback) {
|
||||
this.changed(callback);
|
||||
};
|
||||
|
||||
Filter.prototype.changed = function(callback) {
|
||||
var self = this;
|
||||
this.promise.then(function(id) {
|
||||
self.callbacks.push(callback);
|
||||
});
|
||||
};
|
||||
|
||||
Filter.prototype.trigger = function(messages) {
|
||||
for(var i = 0; i < this.callbacks.length; i++) {
|
||||
this.callbacks[i].call(this, messages);
|
||||
}
|
||||
};
|
||||
|
||||
Filter.prototype.uninstall = function() {
|
||||
var self = this;
|
||||
this.promise.then(function (id) {
|
||||
self.impl.uninstallFilter(id);
|
||||
web3.provider.stopPolling(id);
|
||||
web3.off(impl.changed, id);
|
||||
});
|
||||
};
|
||||
|
||||
Filter.prototype.messages = function() {
|
||||
var self = this;
|
||||
return this.promise.then(function (id) {
|
||||
return self.impl.getMessages(id);
|
||||
});
|
||||
};
|
||||
|
||||
Filter.prototype.logs = function () {
|
||||
return this.messages();
|
||||
};
|
||||
|
||||
function messageHandler(data) {
|
||||
if(data._event !== undefined) {
|
||||
web3.trigger(data._event, data._id, data.data);
|
||||
return;
|
||||
}
|
||||
|
||||
if(data._id) {
|
||||
var cb = web3._callbacks[data._id];
|
||||
if (cb) {
|
||||
cb.call(this, data.error, data.data);
|
||||
delete web3._callbacks[data._id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = web3;
|
45
cmd/ethtest/lib/qt.js
Normal file
45
cmd/ethtest/lib/qt.js
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file qt.js
|
||||
* @authors:
|
||||
* Jeffrey Wilcke <jeff@ethdev.com>
|
||||
* Marek Kotewicz <marek@ethdev.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
var QtProvider = function() {
|
||||
this.handlers = [];
|
||||
|
||||
var self = this;
|
||||
navigator.qt.onmessage = function (message) {
|
||||
self.handlers.forEach(function (handler) {
|
||||
handler.call(self, JSON.parse(message.data));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
QtProvider.prototype.send = function(payload) {
|
||||
navigator.qt.postMessage(JSON.stringify(payload));
|
||||
};
|
||||
|
||||
Object.defineProperty(QtProvider.prototype, "onmessage", {
|
||||
set: function(handler) {
|
||||
this.handlers.push(handler);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = QtProvider;
|
78
cmd/ethtest/lib/websocket.js
Normal file
78
cmd/ethtest/lib/websocket.js
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
This file is part of ethereum.js.
|
||||
|
||||
ethereum.js is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
ethereum.js is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file websocket.js
|
||||
* @authors:
|
||||
* Jeffrey Wilcke <jeff@ethdev.com>
|
||||
* Marek Kotewicz <marek@ethdev.com>
|
||||
* Marian Oancea <marian@ethdev.com>
|
||||
* @date 2014
|
||||
*/
|
||||
|
||||
// TODO: work out which of the following two lines it is supposed to be...
|
||||
//if (process.env.NODE_ENV !== 'build') {
|
||||
if ("build" !== "build") {/*
|
||||
var WebSocket = require('ws'); // jshint ignore:line
|
||||
*/}
|
||||
|
||||
var WebSocketProvider = function(host) {
|
||||
// onmessage handlers
|
||||
this.handlers = [];
|
||||
// queue will be filled with messages if send is invoked before the ws is ready
|
||||
this.queued = [];
|
||||
this.ready = false;
|
||||
|
||||
this.ws = new WebSocket(host);
|
||||
|
||||
var self = this;
|
||||
this.ws.onmessage = function(event) {
|
||||
for(var i = 0; i < self.handlers.length; i++) {
|
||||
self.handlers[i].call(self, JSON.parse(event.data), event);
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onopen = function() {
|
||||
self.ready = true;
|
||||
|
||||
for(var i = 0; i < self.queued.length; i++) {
|
||||
// Resend
|
||||
self.send(self.queued[i]);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
WebSocketProvider.prototype.send = function(payload) {
|
||||
if(this.ready) {
|
||||
var data = JSON.stringify(payload);
|
||||
|
||||
this.ws.send(data);
|
||||
} else {
|
||||
this.queued.push(payload);
|
||||
}
|
||||
};
|
||||
|
||||
WebSocketProvider.prototype.onMessage = function(handler) {
|
||||
this.handlers.push(handler);
|
||||
};
|
||||
|
||||
WebSocketProvider.prototype.unload = function() {
|
||||
this.ws.close();
|
||||
};
|
||||
Object.defineProperty(WebSocketProvider.prototype, "onmessage", {
|
||||
set: function(provider) { this.onMessage(provider); }
|
||||
});
|
||||
|
||||
module.exports = WebSocketProvider;
|
@ -95,11 +95,16 @@ func RunVmTest(js string) (failed int) {
|
||||
failed = 1
|
||||
}
|
||||
|
||||
if len(test.Gas) == 0 && err == nil {
|
||||
log.Printf("0 gas indicates error but no error given by VM")
|
||||
failed = 1
|
||||
} else {
|
||||
gexp := ethutil.Big(test.Gas)
|
||||
if gexp.Cmp(gas) != 0 {
|
||||
log.Printf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas)
|
||||
failed = 1
|
||||
}
|
||||
}
|
||||
|
||||
for addr, account := range test.Post {
|
||||
obj := state.GetStateObject(helper.FromHex(addr))
|
||||
|
67
cmd/ethtest/package.json
Normal file
67
cmd/ethtest/package.json
Normal file
@ -0,0 +1,67 @@
|
||||
{
|
||||
"name": "ethereum.js",
|
||||
"namespace": "ethereum",
|
||||
"version": "0.0.5",
|
||||
"description": "Ethereum Compatible JavaScript API",
|
||||
"main": "./index.js",
|
||||
"directories": {
|
||||
"lib": "./lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"es6-promise": "*",
|
||||
"ws": "*",
|
||||
"xmlhttprequest": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bower": ">=1.3.0",
|
||||
"browserify": ">=6.0",
|
||||
"del": ">=0.1.1",
|
||||
"envify": "^3.0.0",
|
||||
"exorcist": "^0.1.6",
|
||||
"gulp": ">=3.4.0",
|
||||
"gulp-jshint": ">=1.5.0",
|
||||
"gulp-rename": ">=1.2.0",
|
||||
"gulp-uglify": ">=1.0.0",
|
||||
"jshint": ">=2.5.0",
|
||||
"uglifyify": "^2.6.0",
|
||||
"unreachable-branch-transform": "^0.1.0",
|
||||
"vinyl-source-stream": "^1.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp",
|
||||
"watch": "gulp watch",
|
||||
"lint": "gulp lint"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ethereum/ethereum.js.git"
|
||||
},
|
||||
"homepage": "https://github.com/ethereum/ethereum.js",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ethereum/ethereum.js/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"ethereum",
|
||||
"javascript",
|
||||
"API"
|
||||
],
|
||||
"author": "ethdev.com",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jeffery Wilcke",
|
||||
"email": "jeff@ethdev.com",
|
||||
"url": "https://github.com/obscuren"
|
||||
},
|
||||
{
|
||||
"name": "Marek Kotewicz",
|
||||
"email": "marek@ethdev.com",
|
||||
"url": "https://github.com/debris"
|
||||
},
|
||||
{
|
||||
"name": "Marian Oancea",
|
||||
"email": "marian@ethdev.com",
|
||||
"url": "https://github.com/cubedro"
|
||||
}
|
||||
],
|
||||
"license": "LGPL-3.0"
|
||||
}
|
@ -37,8 +37,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/ptrie"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/vm"
|
||||
)
|
||||
|
||||
@ -65,7 +65,7 @@ func main() {
|
||||
ethutil.ReadConfig("/tmp/evmtest", "/tmp/evm", "")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb := state.New(trie.New(db, ""))
|
||||
statedb := state.New(ptrie.New(nil, db))
|
||||
sender := statedb.NewStateObject([]byte("sender"))
|
||||
receiver := statedb.NewStateObject([]byte("receiver"))
|
||||
//receiver.SetCode([]byte(*code))
|
||||
@ -141,9 +141,7 @@ func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
|
||||
}
|
||||
|
||||
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execution {
|
||||
evm := vm.New(self, vm.DebugVmTy)
|
||||
|
||||
return core.NewExecution(evm, addr, data, gas, price, value)
|
||||
return core.NewExecution(self, addr, data, gas, price, value)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
|
@ -66,7 +66,11 @@ Rectangle {
|
||||
onMessages: {
|
||||
// Bit of a cheat to get proper JSON
|
||||
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
|
||||
webview.postEvent("messages", [m, id]);
|
||||
webview.postEvent("messages", id, m);
|
||||
}
|
||||
|
||||
function onShhMessage(message, id) {
|
||||
webview.postEvent("shhChanged", id, message)
|
||||
}
|
||||
|
||||
Item {
|
||||
@ -327,6 +331,33 @@ Rectangle {
|
||||
require(1);
|
||||
eth.uninstallFilter(data.args[0])
|
||||
break;
|
||||
|
||||
|
||||
case "shhNewFilter":
|
||||
require(1);
|
||||
var id = shh.watch(data.args[0], window);
|
||||
postData(data._id, id);
|
||||
break;
|
||||
|
||||
case "newIdentity":
|
||||
postData(data._id, shh.newIdentity())
|
||||
break
|
||||
|
||||
case "post":
|
||||
require(1);
|
||||
var params = data.args[0];
|
||||
var fields = ["payload", "to", "from"];
|
||||
for(var i = 0; i < fields.length; i++) {
|
||||
params[fields[i]] = params[fields[i]] || "";
|
||||
}
|
||||
if(typeof params.payload !== "object") { params.payload = [params.payload]; } //params.payload = params.payload.join(""); }
|
||||
params.topics = params.topics || [];
|
||||
params.priority = params.priority || 1000;
|
||||
params.ttl = params.ttl || 100;
|
||||
|
||||
console.log(JSON.stringify(params))
|
||||
shh.post(params.payload, params.to, params.from, params.topics, params.priority, params.ttl);
|
||||
break;
|
||||
}
|
||||
} catch(e) {
|
||||
console.log(data.call + ": " + e)
|
||||
@ -348,8 +379,8 @@ Rectangle {
|
||||
function postData(seed, data) {
|
||||
webview.experimental.postMessage(JSON.stringify({data: data, _id: seed}))
|
||||
}
|
||||
function postEvent(event, data) {
|
||||
webview.experimental.postMessage(JSON.stringify({data: data, _event: event}))
|
||||
function postEvent(event, id, data) {
|
||||
webview.experimental.postMessage(JSON.stringify({data: data, _id: id, _event: event}))
|
||||
}
|
||||
|
||||
function onWatchedCb(data, id) {
|
@ -45,11 +45,12 @@ ApplicationWindow {
|
||||
// Takes care of loading all default plugins
|
||||
Component.onCompleted: {
|
||||
var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||
var browser = addPlugin("./webapp.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||
var browser = addPlugin("./browser.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||
root.browser = browser;
|
||||
addPlugin("./views/miner.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||
|
||||
addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
addPlugin("./views/whisper.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
@ -786,8 +787,8 @@ ApplicationWindow {
|
||||
title: "About"
|
||||
minimumWidth: 350
|
||||
maximumWidth: 350
|
||||
maximumHeight: 200
|
||||
minimumHeight: 200
|
||||
maximumHeight: 280
|
||||
minimumHeight: 280
|
||||
|
||||
Image {
|
||||
id: aboutIcon
|
||||
@ -797,7 +798,7 @@ ApplicationWindow {
|
||||
smooth: true
|
||||
source: "../facet.png"
|
||||
x: 10
|
||||
y: 10
|
||||
y: 30
|
||||
}
|
||||
|
||||
Text {
|
||||
@ -806,7 +807,7 @@ ApplicationWindow {
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 30
|
||||
font.pointSize: 12
|
||||
text: "<h2>Mist (0.6.5)</h2><h4>Amalthea</h4><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor Trón<br><h3>Building</h3>Maran Hidskes"
|
||||
text: "<h2>Mist (0.7.10)</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor Trón<br>Felix Lange<br>Taylor Gerring<br>Daniel Nagy<br><h3>UX</h3>Alex van de Sande<br>"
|
||||
}
|
||||
}
|
||||
|
||||
|
76
cmd/mist/assets/qml/views/whisper.qml
Normal file
76
cmd/mist/assets/qml/views/whisper.qml
Normal file
@ -0,0 +1,76 @@
|
||||
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
import QtQuick.Dialogs 1.0;
|
||||
import QtQuick.Window 2.1;
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import Ethereum 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property var title: "Whisper Traffic"
|
||||
property var iconSource: "../facet.png"
|
||||
property var menuItem
|
||||
|
||||
objectName: "whisperView"
|
||||
anchors.fill: parent
|
||||
|
||||
property var identity: ""
|
||||
Component.onCompleted: {
|
||||
identity = shh.newIdentity()
|
||||
console.log("New identity:", identity)
|
||||
|
||||
var t = shh.watch({}, root)
|
||||
}
|
||||
|
||||
function onShhMessage(message, i) {
|
||||
whisperModel.insert(0, {from: message.from, payload: eth.toAscii(message.payload)})
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: input
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 20
|
||||
top: parent.top
|
||||
topMargin: 20
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: to
|
||||
placeholderText: "To"
|
||||
}
|
||||
TextField {
|
||||
id: data
|
||||
placeholderText: "Data"
|
||||
}
|
||||
TextField {
|
||||
id: topics
|
||||
placeholderText: "topic1, topic2, topic3, ..."
|
||||
}
|
||||
Button {
|
||||
text: "Send"
|
||||
onClicked: {
|
||||
shh.post([eth.toHex(data.text)], "", identity, topics.text.split(","), 500, 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TableView {
|
||||
id: txTableView
|
||||
anchors {
|
||||
top: input.bottom
|
||||
topMargin: 10
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
TableViewColumn{ id: fromRole; role: "from" ; title: "From"; width: 300 }
|
||||
TableViewColumn{ role: "payload" ; title: "Payload" ; width: parent.width - fromRole.width - 2 }
|
||||
|
||||
model: ListModel {
|
||||
id: whisperModel
|
||||
}
|
||||
}
|
||||
}
|
@ -149,7 +149,7 @@ func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, data
|
||||
|
||||
self.SetAsm(script)
|
||||
|
||||
block := self.lib.eth.ChainManager().CurrentBlock
|
||||
block := self.lib.eth.ChainManager().CurrentBlock()
|
||||
|
||||
env := utils.NewEnv(statedb, block, account.Address(), value)
|
||||
|
||||
|
@ -36,10 +36,12 @@ var (
|
||||
Identifier string
|
||||
KeyRing string
|
||||
KeyStore string
|
||||
PMPGateway string
|
||||
StartRpc bool
|
||||
StartWebSockets bool
|
||||
RpcPort int
|
||||
UseUPnP bool
|
||||
NatType string
|
||||
OutboundPort string
|
||||
ShowGenesis bool
|
||||
AddPeer string
|
||||
@ -104,17 +106,19 @@ func Init() {
|
||||
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
|
||||
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
||||
flag.BoolVar(&UseUPnP, "upnp", true, "enable UPnP support")
|
||||
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers")
|
||||
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
|
||||
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
|
||||
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
|
||||
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server")
|
||||
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
|
||||
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
||||
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
|
||||
flag.StringVar(&NatType, "nat", "", "NAT support (UPNP|PMP) (none)")
|
||||
flag.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)")
|
||||
flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given")
|
||||
flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
|
||||
flag.StringVar(&Datadir, "datadir", defaultDataDir(), "specifies the datadir to use")
|
||||
flag.StringVar(&PMPGateway, "pmp", "", "Gateway IP for PMP")
|
||||
flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file")
|
||||
flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)")
|
||||
flag.IntVar(&LogLevel, "loglevel", int(logger.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)")
|
||||
|
124
cmd/mist/gui.go
124
cmd/mist/gui.go
@ -30,50 +30,19 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/ui/qt/qwhisper"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
"gopkg.in/qml.v1"
|
||||
)
|
||||
|
||||
/*
|
||||
func LoadExtension(path string) (uintptr, error) {
|
||||
lib, err := ffi.NewLibrary(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
so, err := lib.Fct("sharedObject", ffi.Pointer, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ptr := so()
|
||||
|
||||
err = lib.Close()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return ptr.Interface().(uintptr), nil
|
||||
}
|
||||
*/
|
||||
/*
|
||||
vec, errr := LoadExtension("/Users/jeffrey/Desktop/build-libqmltest-Desktop_Qt_5_2_1_clang_64bit-Debug/liblibqmltest_debug.dylib")
|
||||
fmt.Printf("Fetched vec with addr: %#x\n", vec)
|
||||
if errr != nil {
|
||||
fmt.Println(errr)
|
||||
} else {
|
||||
context.SetVar("vec", (unsafe.Pointer)(vec))
|
||||
}
|
||||
*/
|
||||
|
||||
var guilogger = logger.NewLogger("GUI")
|
||||
|
||||
type Gui struct {
|
||||
@ -88,6 +57,7 @@ type Gui struct {
|
||||
|
||||
// The public Ethereum library
|
||||
uiLib *UiLib
|
||||
whisper *qwhisper.Whisper
|
||||
|
||||
txDb *ethdb.LDBDatabase
|
||||
|
||||
@ -97,7 +67,7 @@ type Gui struct {
|
||||
pipe *xeth.JSXEth
|
||||
|
||||
Session string
|
||||
clientIdentity *wire.SimpleClientIdentity
|
||||
clientIdentity *p2p.SimpleClientIdentity
|
||||
config *ethutil.ConfigManager
|
||||
|
||||
plugins map[string]plugin
|
||||
@ -107,7 +77,7 @@ type Gui struct {
|
||||
}
|
||||
|
||||
// Create GUI, but doesn't start it
|
||||
func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIdentity *wire.SimpleClientIdentity, session string, logLevel int) *Gui {
|
||||
func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIdentity *p2p.SimpleClientIdentity, session string, logLevel int) *Gui {
|
||||
db, err := ethdb.NewLDBDatabase("tx_database")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -138,10 +108,12 @@ func (gui *Gui) Start(assetPath string) {
|
||||
gui.engine = qml.NewEngine()
|
||||
context := gui.engine.Context()
|
||||
gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath)
|
||||
gui.whisper = qwhisper.New(gui.eth.Whisper())
|
||||
|
||||
// Expose the eth library and the ui library to QML
|
||||
context.SetVar("gui", gui)
|
||||
context.SetVar("eth", gui.uiLib)
|
||||
context.SetVar("shh", gui.whisper)
|
||||
|
||||
// Load the main QML interface
|
||||
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
||||
@ -246,10 +218,10 @@ func (gui *Gui) CreateAndSetPrivKey() (string, string, string, string) {
|
||||
}
|
||||
|
||||
func (gui *Gui) setInitialChain(ancientBlocks bool) {
|
||||
sBlk := gui.eth.ChainManager().LastBlockHash
|
||||
sBlk := gui.eth.ChainManager().LastBlockHash()
|
||||
blk := gui.eth.ChainManager().GetBlock(sBlk)
|
||||
for ; blk != nil; blk = gui.eth.ChainManager().GetBlock(sBlk) {
|
||||
sBlk = blk.PrevHash
|
||||
sBlk = blk.ParentHash()
|
||||
|
||||
gui.processBlock(blk, true)
|
||||
}
|
||||
@ -297,7 +269,7 @@ func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
|
||||
addr := gui.address()
|
||||
|
||||
var inout string
|
||||
if bytes.Compare(tx.Sender(), addr) == 0 {
|
||||
if bytes.Compare(tx.From(), addr) == 0 {
|
||||
inout = "send"
|
||||
} else {
|
||||
inout = "recv"
|
||||
@ -305,27 +277,27 @@ func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
|
||||
|
||||
var (
|
||||
ptx = xeth.NewJSTx(tx, pipe.World().State())
|
||||
send = nameReg.Storage(tx.Sender())
|
||||
rec = nameReg.Storage(tx.Recipient)
|
||||
send = nameReg.Storage(tx.From())
|
||||
rec = nameReg.Storage(tx.To())
|
||||
s, r string
|
||||
)
|
||||
|
||||
if tx.CreatesContract() {
|
||||
rec = nameReg.Storage(tx.CreationAddress(pipe.World().State()))
|
||||
if core.MessageCreatesContract(tx) {
|
||||
rec = nameReg.Storage(core.AddressFromMessage(tx))
|
||||
}
|
||||
|
||||
if send.Len() != 0 {
|
||||
s = strings.Trim(send.Str(), "\x00")
|
||||
} else {
|
||||
s = ethutil.Bytes2Hex(tx.Sender())
|
||||
s = ethutil.Bytes2Hex(tx.From())
|
||||
}
|
||||
if rec.Len() != 0 {
|
||||
r = strings.Trim(rec.Str(), "\x00")
|
||||
} else {
|
||||
if tx.CreatesContract() {
|
||||
r = ethutil.Bytes2Hex(tx.CreationAddress(pipe.World().State()))
|
||||
if core.MessageCreatesContract(tx) {
|
||||
r = ethutil.Bytes2Hex(core.AddressFromMessage(tx))
|
||||
} else {
|
||||
r = ethutil.Bytes2Hex(tx.Recipient)
|
||||
r = ethutil.Bytes2Hex(tx.To())
|
||||
}
|
||||
}
|
||||
ptx.Sender = s
|
||||
@ -350,7 +322,7 @@ func (gui *Gui) readPreviousTransactions() {
|
||||
}
|
||||
|
||||
func (gui *Gui) processBlock(block *types.Block, initial bool) {
|
||||
name := strings.Trim(gui.pipe.World().Config().Get("NameReg").Storage(block.Coinbase).Str(), "\x00")
|
||||
name := strings.Trim(gui.pipe.World().Config().Get("NameReg").Storage(block.Coinbase()).Str(), "\x00")
|
||||
b := xeth.NewJSBlock(block)
|
||||
b.Name = name
|
||||
|
||||
@ -391,6 +363,8 @@ func (gui *Gui) update() {
|
||||
gui.setPeerInfo()
|
||||
}()
|
||||
|
||||
gui.whisper.SetView(gui.win.Root().ObjectByName("whisperView"))
|
||||
|
||||
for _, plugin := range gui.plugins {
|
||||
guilogger.Infoln("Loading plugin ", plugin.Name)
|
||||
|
||||
@ -409,8 +383,7 @@ func (gui *Gui) update() {
|
||||
miningLabel := gui.getObjectByName("miningLabel")
|
||||
|
||||
events := gui.eth.EventMux().Subscribe(
|
||||
eth.ChainSyncEvent{},
|
||||
eth.PeerListEvent{},
|
||||
//eth.PeerListEvent{},
|
||||
core.NewBlockEvent{},
|
||||
core.TxPreEvent{},
|
||||
core.TxPostEvent{},
|
||||
@ -427,7 +400,7 @@ func (gui *Gui) update() {
|
||||
switch ev := ev.(type) {
|
||||
case core.NewBlockEvent:
|
||||
gui.processBlock(ev.Block, false)
|
||||
if bytes.Compare(ev.Block.Coinbase, gui.address()) == 0 {
|
||||
if bytes.Compare(ev.Block.Coinbase(), gui.address()) == 0 {
|
||||
gui.setWalletValue(gui.eth.ChainManager().State().GetBalance(gui.address()), nil)
|
||||
}
|
||||
|
||||
@ -448,30 +421,28 @@ func (gui *Gui) update() {
|
||||
tx := ev.Tx
|
||||
object := state.GetAccount(gui.address())
|
||||
|
||||
if bytes.Compare(tx.Sender(), gui.address()) == 0 {
|
||||
object.SubAmount(tx.Value)
|
||||
if bytes.Compare(tx.From(), gui.address()) == 0 {
|
||||
object.SubAmount(tx.Value())
|
||||
|
||||
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||
} else if bytes.Compare(tx.Recipient, gui.address()) == 0 {
|
||||
object.AddAmount(tx.Value)
|
||||
} else if bytes.Compare(tx.To(), gui.address()) == 0 {
|
||||
object.AddAmount(tx.Value())
|
||||
|
||||
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||
}
|
||||
|
||||
gui.setWalletValue(object.Balance(), nil)
|
||||
state.UpdateStateObject(object)
|
||||
|
||||
case eth.PeerListEvent:
|
||||
gui.setPeerInfo()
|
||||
}
|
||||
|
||||
case <-peerUpdateTicker.C:
|
||||
gui.setPeerInfo()
|
||||
case <-generalUpdateTicker.C:
|
||||
statusText := "#" + gui.eth.ChainManager().CurrentBlock.Number.String()
|
||||
statusText := "#" + gui.eth.ChainManager().CurrentBlock().Number().String()
|
||||
lastBlockLabel.Set("text", statusText)
|
||||
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash")
|
||||
|
||||
/*
|
||||
blockLength := gui.eth.BlockPool().BlocksProcessed
|
||||
chainLength := gui.eth.BlockPool().ChainLength
|
||||
|
||||
@ -482,6 +453,7 @@ func (gui *Gui) update() {
|
||||
)
|
||||
dlWidget.Set("value", pct)
|
||||
dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength))
|
||||
*/
|
||||
|
||||
case <-statsUpdateTicker.C:
|
||||
gui.setStatsPane()
|
||||
@ -509,7 +481,7 @@ Heap Alloc: %d
|
||||
CGNext: %x
|
||||
NumGC: %d
|
||||
`, Version, runtime.Version(),
|
||||
eth.ProtocolVersion, eth.P2PVersion,
|
||||
eth.ProtocolVersion, 2,
|
||||
runtime.NumCPU, runtime.NumGoroutine(), runtime.NumCgoCall(),
|
||||
memStats.Alloc, memStats.HeapAlloc,
|
||||
memStats.NextGC, memStats.NumGC,
|
||||
@ -531,3 +503,35 @@ func (gui *Gui) privateKey() string {
|
||||
func (gui *Gui) address() []byte {
|
||||
return gui.eth.KeyManager().Address()
|
||||
}
|
||||
|
||||
/*
|
||||
func LoadExtension(path string) (uintptr, error) {
|
||||
lib, err := ffi.NewLibrary(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
so, err := lib.Fct("sharedObject", ffi.Pointer, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ptr := so()
|
||||
|
||||
err = lib.Close()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return ptr.Interface().(uintptr), nil
|
||||
}
|
||||
*/
|
||||
/*
|
||||
vec, errr := LoadExtension("/Users/jeffrey/Desktop/build-libqmltest-Desktop_Qt_5_2_1_clang_64bit-Debug/liblibqmltest_debug.dylib")
|
||||
fmt.Printf("Fetched vec with addr: %#x\n", vec)
|
||||
if errr != nil {
|
||||
fmt.Println(errr)
|
||||
} else {
|
||||
context.SetVar("vec", (unsafe.Pointer)(vec))
|
||||
}
|
||||
*/
|
||||
|
@ -139,7 +139,7 @@ func (app *HtmlApplication) Window() *qml.Window {
|
||||
}
|
||||
|
||||
func (app *HtmlApplication) NewBlock(block *types.Block) {
|
||||
b := &xeth.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
|
||||
b := &xeth.JSBlock{Number: int(block.NumberU64()), Hash: ethutil.Bytes2Hex(block.Hash())}
|
||||
app.webView.Call("onNewBlockCb", b)
|
||||
}
|
||||
|
||||
|
@ -23,15 +23,15 @@ import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"gopkg.in/qml.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
ClientIdentifier = "Mist"
|
||||
Version = "0.7.9"
|
||||
Version = "0.7.11"
|
||||
)
|
||||
|
||||
var ethereum *eth.Ethereum
|
||||
@ -58,8 +58,8 @@ func run() error {
|
||||
|
||||
// create, import, export keys
|
||||
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
|
||||
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier)
|
||||
ethereum = utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
|
||||
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier, string(keyManager.PublicKey()))
|
||||
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, utils.NatType(NatType, PMPGateway), OutboundPort, MaxPeer)
|
||||
|
||||
if ShowGenesis {
|
||||
utils.ShowGenesis(ethereum)
|
||||
@ -69,6 +69,10 @@ func run() error {
|
||||
utils.StartRpc(ethereum, RpcPort)
|
||||
}
|
||||
|
||||
if StartWebSockets {
|
||||
utils.StartWebSockets(ethereum)
|
||||
}
|
||||
|
||||
gui := NewWindow(ethereum, config, clientIdentity, KeyRing, LogLevel)
|
||||
gui.stdLog = stdLog
|
||||
|
||||
@ -100,16 +104,10 @@ func main() {
|
||||
|
||||
utils.HandleInterrupt()
|
||||
|
||||
if StartWebSockets {
|
||||
utils.StartWebSockets(ethereum)
|
||||
}
|
||||
|
||||
// we need to run the interrupt callbacks in case gui is closed
|
||||
// this skips if we got here by actual interrupt stopping the GUI
|
||||
if !interrupted {
|
||||
utils.RunInterruptCallbacks(os.Interrupt)
|
||||
}
|
||||
// this blocks the thread
|
||||
ethereum.WaitForShutdown()
|
||||
logger.Flush()
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ func (app *QmlApplication) NewWatcher(quitChan chan bool) {
|
||||
|
||||
// Events
|
||||
func (app *QmlApplication) NewBlock(block *types.Block) {
|
||||
pblock := &xeth.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
|
||||
pblock := &xeth.JSBlock{Number: int(block.NumberU64()), Hash: ethutil.Bytes2Hex(block.Hash())}
|
||||
app.win.Call("onNewBlockCb", pblock)
|
||||
}
|
||||
|
||||
|
@ -24,11 +24,12 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event/filter"
|
||||
"github.com/ethereum/go-ethereum/javascript"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
@ -57,6 +58,7 @@ type UiLib struct {
|
||||
jsEngine *javascript.JSRE
|
||||
|
||||
filterCallbacks map[int][]int
|
||||
filterManager *filter.FilterManager
|
||||
|
||||
miner *miner.Miner
|
||||
}
|
||||
@ -64,6 +66,7 @@ type UiLib struct {
|
||||
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
|
||||
lib := &UiLib{JSXEth: xeth.NewJSXEth(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int)} //, filters: make(map[int]*xeth.JSFilter)}
|
||||
lib.miner = miner.New(eth.KeyManager().Address(), eth)
|
||||
lib.filterManager = filter.NewFilterManager(eth.EventMux())
|
||||
|
||||
return lib
|
||||
}
|
||||
@ -123,7 +126,8 @@ func (self *UiLib) LookupAddress(name string) string {
|
||||
}
|
||||
|
||||
func (self *UiLib) PastPeers() *ethutil.List {
|
||||
return ethutil.NewList(eth.PastPeers())
|
||||
return ethutil.NewList([]string{})
|
||||
//return ethutil.NewList(eth.PastPeers())
|
||||
}
|
||||
|
||||
func (self *UiLib) ImportTx(rlpTx string) {
|
||||
@ -191,7 +195,7 @@ func (ui *UiLib) Connect(button qml.Object) {
|
||||
}
|
||||
|
||||
func (ui *UiLib) ConnectToPeer(addr string) {
|
||||
ui.eth.ConnectToPeer(addr)
|
||||
ui.eth.SuggestPeer(addr)
|
||||
}
|
||||
|
||||
func (ui *UiLib) AssetPath(p string) string {
|
||||
@ -221,94 +225,6 @@ func (self *UiLib) StartDebugger() {
|
||||
dbWindow.Show()
|
||||
}
|
||||
|
||||
func (self *UiLib) NewFilter(object map[string]interface{}) (id int) {
|
||||
filter := qt.NewFilterFromMap(object, self.eth)
|
||||
filter.MessageCallback = func(messages state.Messages) {
|
||||
self.win.Root().Call("invokeFilterCallback", xeth.ToJSMessages(messages), id)
|
||||
}
|
||||
id = self.eth.InstallFilter(filter)
|
||||
return id
|
||||
}
|
||||
|
||||
func (self *UiLib) NewFilterString(typ string) (id int) {
|
||||
filter := core.NewFilter(self.eth)
|
||||
filter.BlockCallback = func(block *types.Block) {
|
||||
if self.win != nil && self.win.Root() != nil {
|
||||
self.win.Root().Call("invokeFilterCallback", "{}", id)
|
||||
} else {
|
||||
fmt.Println("QML is lagging")
|
||||
}
|
||||
}
|
||||
id = self.eth.InstallFilter(filter)
|
||||
return id
|
||||
}
|
||||
|
||||
func (self *UiLib) Messages(id int) *ethutil.List {
|
||||
filter := self.eth.GetFilter(id)
|
||||
if filter != nil {
|
||||
messages := xeth.ToJSMessages(filter.Find())
|
||||
|
||||
return messages
|
||||
}
|
||||
|
||||
return ethutil.EmptyList()
|
||||
}
|
||||
|
||||
func (self *UiLib) UninstallFilter(id int) {
|
||||
self.eth.UninstallFilter(id)
|
||||
}
|
||||
|
||||
func mapToTxParams(object map[string]interface{}) map[string]string {
|
||||
// Default values
|
||||
if object["from"] == nil {
|
||||
object["from"] = ""
|
||||
}
|
||||
if object["to"] == nil {
|
||||
object["to"] = ""
|
||||
}
|
||||
if object["value"] == nil {
|
||||
object["value"] = ""
|
||||
}
|
||||
if object["gas"] == nil {
|
||||
object["gas"] = ""
|
||||
}
|
||||
if object["gasPrice"] == nil {
|
||||
object["gasPrice"] = ""
|
||||
}
|
||||
|
||||
var dataStr string
|
||||
var data []string
|
||||
if list, ok := object["data"].(*qml.List); ok {
|
||||
list.Convert(&data)
|
||||
} else if str, ok := object["data"].(string); ok {
|
||||
data = []string{str}
|
||||
}
|
||||
|
||||
for _, str := range data {
|
||||
if ethutil.IsHex(str) {
|
||||
str = str[2:]
|
||||
|
||||
if len(str) != 64 {
|
||||
str = ethutil.LeftPadString(str, 64)
|
||||
}
|
||||
} else {
|
||||
str = ethutil.Bytes2Hex(ethutil.LeftPadBytes(ethutil.Big(str).Bytes(), 32))
|
||||
}
|
||||
|
||||
dataStr += str
|
||||
}
|
||||
object["data"] = dataStr
|
||||
|
||||
conv := make(map[string]string)
|
||||
for key, value := range object {
|
||||
if v, ok := value.(string); ok {
|
||||
conv[key] = v
|
||||
}
|
||||
}
|
||||
|
||||
return conv
|
||||
}
|
||||
|
||||
func (self *UiLib) Transact(params map[string]interface{}) (string, error) {
|
||||
object := mapToTxParams(params)
|
||||
|
||||
@ -372,3 +288,104 @@ func (self *UiLib) ToggleMining() bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (self *UiLib) ToHex(data string) string {
|
||||
return "0x" + ethutil.Bytes2Hex([]byte(data))
|
||||
}
|
||||
|
||||
func (self *UiLib) ToAscii(data string) string {
|
||||
start := 0
|
||||
if len(data) > 1 && data[0:2] == "0x" {
|
||||
start = 2
|
||||
}
|
||||
return string(ethutil.Hex2Bytes(data[start:]))
|
||||
}
|
||||
|
||||
/// Ethereum filter methods
|
||||
func (self *UiLib) NewFilter(object map[string]interface{}) (id int) {
|
||||
filter := qt.NewFilterFromMap(object, self.eth)
|
||||
filter.MessageCallback = func(messages state.Messages) {
|
||||
self.win.Root().Call("invokeFilterCallback", xeth.ToJSMessages(messages), id)
|
||||
}
|
||||
id = self.filterManager.InstallFilter(filter)
|
||||
return id
|
||||
}
|
||||
|
||||
func (self *UiLib) NewFilterString(typ string) (id int) {
|
||||
filter := core.NewFilter(self.eth)
|
||||
filter.BlockCallback = func(block *types.Block) {
|
||||
if self.win != nil && self.win.Root() != nil {
|
||||
self.win.Root().Call("invokeFilterCallback", "{}", id)
|
||||
} else {
|
||||
fmt.Println("QML is lagging")
|
||||
}
|
||||
}
|
||||
id = self.filterManager.InstallFilter(filter)
|
||||
return id
|
||||
}
|
||||
|
||||
func (self *UiLib) Messages(id int) *ethutil.List {
|
||||
filter := self.filterManager.GetFilter(id)
|
||||
if filter != nil {
|
||||
messages := xeth.ToJSMessages(filter.Find())
|
||||
|
||||
return messages
|
||||
}
|
||||
|
||||
return ethutil.EmptyList()
|
||||
}
|
||||
|
||||
func (self *UiLib) UninstallFilter(id int) {
|
||||
self.filterManager.UninstallFilter(id)
|
||||
}
|
||||
|
||||
func mapToTxParams(object map[string]interface{}) map[string]string {
|
||||
// Default values
|
||||
if object["from"] == nil {
|
||||
object["from"] = ""
|
||||
}
|
||||
if object["to"] == nil {
|
||||
object["to"] = ""
|
||||
}
|
||||
if object["value"] == nil {
|
||||
object["value"] = ""
|
||||
}
|
||||
if object["gas"] == nil {
|
||||
object["gas"] = ""
|
||||
}
|
||||
if object["gasPrice"] == nil {
|
||||
object["gasPrice"] = ""
|
||||
}
|
||||
|
||||
var dataStr string
|
||||
var data []string
|
||||
if list, ok := object["data"].(*qml.List); ok {
|
||||
list.Convert(&data)
|
||||
} else if str, ok := object["data"].(string); ok {
|
||||
data = []string{str}
|
||||
}
|
||||
|
||||
for _, str := range data {
|
||||
if ethutil.IsHex(str) {
|
||||
str = str[2:]
|
||||
|
||||
if len(str) != 64 {
|
||||
str = ethutil.LeftPadString(str, 64)
|
||||
}
|
||||
} else {
|
||||
str = ethutil.Bytes2Hex(ethutil.LeftPadBytes(ethutil.Big(str).Bytes(), 32))
|
||||
}
|
||||
|
||||
dataStr += str
|
||||
}
|
||||
object["data"] = dataStr
|
||||
|
||||
conv := make(map[string]string)
|
||||
for key, value := range object {
|
||||
if v, ok := value.(string); ok {
|
||||
conv[key] = v
|
||||
}
|
||||
}
|
||||
|
||||
return conv
|
||||
}
|
||||
|
@ -4,23 +4,25 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"bitbucket.org/kardianos/osext"
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
|
||||
@ -144,17 +146,32 @@ func NewDatabase() ethutil.Database {
|
||||
return db
|
||||
}
|
||||
|
||||
func NewClientIdentity(clientIdentifier, version, customIdentifier string) *wire.SimpleClientIdentity {
|
||||
return wire.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier)
|
||||
func NewClientIdentity(clientIdentifier, version, customIdentifier string, pubkey string) *p2p.SimpleClientIdentity {
|
||||
return p2p.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier, pubkey)
|
||||
}
|
||||
|
||||
func NewEthereum(db ethutil.Database, clientIdentity wire.ClientIdentity, keyManager *crypto.KeyManager, usePnp bool, OutboundPort string, MaxPeer int) *eth.Ethereum {
|
||||
ethereum, err := eth.New(db, clientIdentity, keyManager, eth.CapDefault, usePnp)
|
||||
func NatType(natType string, gateway string) (nat p2p.NAT) {
|
||||
switch natType {
|
||||
case "UPNP":
|
||||
nat = p2p.UPNP()
|
||||
case "PMP":
|
||||
ip := net.ParseIP(gateway)
|
||||
if ip == nil {
|
||||
clilogger.Fatalf("cannot resolve PMP gateway IP %s", gateway)
|
||||
}
|
||||
nat = p2p.PMP(ip)
|
||||
case "":
|
||||
default:
|
||||
clilogger.Fatalf("unrecognised NAT type '%s'", natType)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NewEthereum(db ethutil.Database, clientIdentity p2p.ClientIdentity, keyManager *crypto.KeyManager, nat p2p.NAT, OutboundPort string, MaxPeer int) *eth.Ethereum {
|
||||
ethereum, err := eth.New(db, clientIdentity, keyManager, nat, OutboundPort, MaxPeer)
|
||||
if err != nil {
|
||||
clilogger.Fatalln("eth start err:", err)
|
||||
}
|
||||
ethereum.Port = OutboundPort
|
||||
ethereum.MaxPeers = MaxPeer
|
||||
return ethereum
|
||||
}
|
||||
|
||||
@ -268,11 +285,6 @@ func StartMining(ethereum *eth.Ethereum) bool {
|
||||
if gminer == nil {
|
||||
gminer = miner.New(addr, ethereum)
|
||||
}
|
||||
// Give it some time to connect with peers
|
||||
time.Sleep(3 * time.Second)
|
||||
for !ethereum.IsUpToDate() {
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
gminer.Start()
|
||||
}()
|
||||
RegisterInterrupt(func(os.Signal) {
|
||||
@ -315,7 +327,7 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
|
||||
return fmt.Errorf("unknown block %x", hash)
|
||||
}
|
||||
|
||||
parent := ethereum.ChainManager().GetBlock(block.PrevHash)
|
||||
parent := ethereum.ChainManager().GetBlock(block.ParentHash())
|
||||
|
||||
_, err := ethereum.BlockManager().TransitionState(parent.State(), parent, block)
|
||||
if err != nil {
|
||||
@ -325,3 +337,25 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func ImportChain(ethereum *eth.Ethereum, fn string) error {
|
||||
clilogger.Infof("importing chain '%s'\n", fn)
|
||||
fh, err := os.OpenFile(fn, os.O_RDONLY, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
var chain types.Blocks
|
||||
if err := rlp.Decode(fh, &chain); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ethereum.ChainManager().Reset()
|
||||
if err := ethereum.ChainManager().InsertChain(chain); err != nil {
|
||||
return err
|
||||
}
|
||||
clilogger.Infof("imported %d blocks\n", len(chain))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -30,15 +30,15 @@ func NewEnv(state *state.StateDB, block *types.Block, transactor []byte, value *
|
||||
}
|
||||
|
||||
func (self *VMEnv) Origin() []byte { return self.transactor }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number }
|
||||
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
|
||||
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
|
||||
func (self *VMEnv) Time() int64 { return self.block.Time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() }
|
||||
func (self *VMEnv) PrevHash() []byte { return self.block.ParentHash() }
|
||||
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase() }
|
||||
func (self *VMEnv) Time() int64 { return self.block.Time() }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() }
|
||||
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit() }
|
||||
func (self *VMEnv) Value() *big.Int { return self.value }
|
||||
func (self *VMEnv) State() *state.StateDB { return self.state }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit }
|
||||
func (self *VMEnv) Depth() int { return self.depth }
|
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||
func (self *VMEnv) AddLog(log state.Log) {
|
||||
@ -49,9 +49,7 @@ func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
|
||||
}
|
||||
|
||||
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execution {
|
||||
evm := vm.New(self, vm.DebugVmTy)
|
||||
|
||||
return core.NewExecution(evm, addr, data, gas, price, value)
|
||||
return core.NewExecution(self, addr, data, gas, price, value)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
|
@ -1,12 +1,15 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/websocket"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
|
||||
var wslogger = logger.NewLogger("WS")
|
||||
|
||||
func args(v ...interface{}) []interface{} {
|
||||
return v
|
||||
}
|
||||
@ -106,6 +109,8 @@ func (self *WebSocketServer) Serv() {
|
||||
}
|
||||
|
||||
func StartWebSockets(eth *eth.Ethereum) {
|
||||
wslogger.Infoln("Starting WebSockets")
|
||||
|
||||
sock := NewWebSocketServer(eth)
|
||||
go sock.Serv()
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
@ -14,10 +13,11 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/pow/ezp"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
var statelogger = logger.NewLogger("BLOCK")
|
||||
@ -38,13 +38,12 @@ type EthManager interface {
|
||||
BlockManager() *BlockManager
|
||||
ChainManager() *ChainManager
|
||||
TxPool() *TxPool
|
||||
Broadcast(msgType wire.MsgType, data []interface{})
|
||||
PeerCount() int
|
||||
IsMining() bool
|
||||
IsListening() bool
|
||||
Peers() *list.List
|
||||
Peers() []*p2p.Peer
|
||||
KeyManager() *crypto.KeyManager
|
||||
ClientIdentity() wire.ClientIdentity
|
||||
ClientIdentity() p2p.ClientIdentity
|
||||
Db() ethutil.Database
|
||||
EventMux() *event.TypeMux
|
||||
}
|
||||
@ -58,8 +57,8 @@ type BlockManager struct {
|
||||
mem map[string]*big.Int
|
||||
// Proof of work used for validating
|
||||
Pow pow.PoW
|
||||
// The ethereum manager interface
|
||||
eth EthManager
|
||||
|
||||
txpool *TxPool
|
||||
|
||||
// The last attempted block is mainly used for debugging purposes
|
||||
// This does not have to be a valid block and will be set during
|
||||
@ -71,21 +70,21 @@ type BlockManager struct {
|
||||
eventMux *event.TypeMux
|
||||
}
|
||||
|
||||
func NewBlockManager(ethereum EthManager) *BlockManager {
|
||||
func NewBlockManager(txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockManager {
|
||||
sm := &BlockManager{
|
||||
mem: make(map[string]*big.Int),
|
||||
Pow: ezp.New(),
|
||||
eth: ethereum,
|
||||
bc: ethereum.ChainManager(),
|
||||
eventMux: ethereum.EventMux(),
|
||||
bc: chainManager,
|
||||
eventMux: eventMux,
|
||||
txpool: txpool,
|
||||
}
|
||||
|
||||
return sm
|
||||
}
|
||||
|
||||
func (sm *BlockManager) TransitionState(statedb *state.StateDB, parent, block *types.Block) (receipts types.Receipts, err error) {
|
||||
coinbase := statedb.GetOrNewStateObject(block.Coinbase)
|
||||
coinbase.SetGasPool(block.CalcGasLimit(parent))
|
||||
coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase)
|
||||
coinbase.SetGasPool(CalcGasLimit(parent, block))
|
||||
|
||||
// Process the transactions on to current block
|
||||
receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), false)
|
||||
@ -111,11 +110,11 @@ done:
|
||||
// If we are mining this block and validating we want to set the logs back to 0
|
||||
state.EmptyLogs()
|
||||
|
||||
txGas := new(big.Int).Set(tx.Gas)
|
||||
txGas := new(big.Int).Set(tx.Gas())
|
||||
|
||||
cb := state.GetStateObject(coinbase.Address())
|
||||
st := NewStateTransition(cb, tx, state, block)
|
||||
err = st.TransitionState()
|
||||
_, err = st.TransitionState()
|
||||
if err != nil {
|
||||
switch {
|
||||
case IsNonceErr(err):
|
||||
@ -129,12 +128,11 @@ done:
|
||||
statelogger.Infoln(err)
|
||||
erroneous = append(erroneous, tx)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
txGas.Sub(txGas, st.gas)
|
||||
cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice))
|
||||
cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice()))
|
||||
|
||||
// Update the state with pending changes
|
||||
state.Update(txGas)
|
||||
@ -143,6 +141,7 @@ done:
|
||||
receipt := types.NewReceipt(state.Root(), cumulative)
|
||||
receipt.SetLogs(state.Logs())
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||
chainlogger.Debugln(receipt)
|
||||
|
||||
// Notify all subscribers
|
||||
if !transientProcess {
|
||||
@ -158,7 +157,7 @@ done:
|
||||
}
|
||||
|
||||
block.Reward = cumulativeSum
|
||||
block.GasUsed = totalUsedGas
|
||||
block.Header().GasUsed = totalUsedGas
|
||||
|
||||
return receipts, handled, unhandled, erroneous, err
|
||||
}
|
||||
@ -168,14 +167,15 @@ func (sm *BlockManager) Process(block *types.Block) (td *big.Int, msgs state.Mes
|
||||
sm.mutex.Lock()
|
||||
defer sm.mutex.Unlock()
|
||||
|
||||
if sm.bc.HasBlock(block.Hash()) {
|
||||
return nil, nil, &KnownBlockError{block.Number, block.Hash()}
|
||||
header := block.Header()
|
||||
if sm.bc.HasBlock(header.Hash()) {
|
||||
return nil, nil, &KnownBlockError{header.Number, header.Hash()}
|
||||
}
|
||||
|
||||
if !sm.bc.HasBlock(block.PrevHash) {
|
||||
return nil, nil, ParentError(block.PrevHash)
|
||||
if !sm.bc.HasBlock(header.ParentHash) {
|
||||
return nil, nil, ParentError(header.ParentHash)
|
||||
}
|
||||
parent := sm.bc.GetBlock(block.PrevHash)
|
||||
parent := sm.bc.GetBlock(header.ParentHash)
|
||||
|
||||
return sm.ProcessWithParent(block, parent)
|
||||
}
|
||||
@ -183,13 +183,7 @@ func (sm *BlockManager) Process(block *types.Block) (td *big.Int, msgs state.Mes
|
||||
func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) {
|
||||
sm.lastAttemptedBlock = block
|
||||
|
||||
state := parent.State().Copy()
|
||||
|
||||
// Defer the Undo on the Trie. If the block processing happened
|
||||
// we don't want to undo but since undo only happens on dirty
|
||||
// nodes this won't happen because Commit would have been called
|
||||
// before that.
|
||||
defer state.Reset()
|
||||
state := state.New(parent.Trie().Copy())
|
||||
|
||||
// Block validation
|
||||
if err = sm.ValidateBlock(block, parent); err != nil {
|
||||
@ -201,21 +195,24 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
|
||||
return
|
||||
}
|
||||
|
||||
header := block.Header()
|
||||
|
||||
rbloom := types.CreateBloom(receipts)
|
||||
if bytes.Compare(rbloom, block.LogsBloom) != 0 {
|
||||
if bytes.Compare(rbloom, header.Bloom) != 0 {
|
||||
err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
|
||||
return
|
||||
}
|
||||
|
||||
txSha := types.DeriveSha(block.Transactions())
|
||||
if bytes.Compare(txSha, block.TxSha) != 0 {
|
||||
err = fmt.Errorf("validating transaction root. received=%x got=%x", block.TxSha, txSha)
|
||||
if bytes.Compare(txSha, header.TxHash) != 0 {
|
||||
err = fmt.Errorf("validating transaction root. received=%x got=%x", header.TxHash, txSha)
|
||||
return
|
||||
}
|
||||
|
||||
receiptSha := types.DeriveSha(receipts)
|
||||
if bytes.Compare(receiptSha, block.ReceiptSha) != 0 {
|
||||
err = fmt.Errorf("validating receipt root. received=%x got=%x", block.ReceiptSha, receiptSha)
|
||||
if bytes.Compare(receiptSha, header.ReceiptHash) != 0 {
|
||||
fmt.Println("receipts", receipts)
|
||||
err = fmt.Errorf("validating receipt root. received=%x got=%x", header.ReceiptHash, receiptSha)
|
||||
return
|
||||
}
|
||||
|
||||
@ -225,8 +222,8 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
|
||||
|
||||
state.Update(ethutil.Big0)
|
||||
|
||||
if !block.State().Cmp(state) {
|
||||
err = fmt.Errorf("invalid merkle root. received=%x got=%x", block.Root(), state.Root())
|
||||
if !bytes.Equal(header.Root, state.Root()) {
|
||||
err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root())
|
||||
return
|
||||
}
|
||||
|
||||
@ -238,9 +235,9 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
|
||||
messages := state.Manifest().Messages
|
||||
state.Manifest().Reset()
|
||||
|
||||
chainlogger.Infof("Processed block #%d (%x...)\n", block.Number, block.Hash()[0:4])
|
||||
chainlogger.Infof("Processed block #%d (%x...)\n", header.Number, block.Hash()[0:4])
|
||||
|
||||
sm.eth.TxPool().RemoveSet(block.Transactions())
|
||||
sm.txpool.RemoveSet(block.Transactions())
|
||||
|
||||
return td, messages, nil
|
||||
} else {
|
||||
@ -250,18 +247,18 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
|
||||
|
||||
func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) {
|
||||
uncleDiff := new(big.Int)
|
||||
for _, uncle := range block.Uncles {
|
||||
for _, uncle := range block.Uncles() {
|
||||
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
|
||||
}
|
||||
|
||||
// TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
|
||||
td := new(big.Int)
|
||||
td = td.Add(sm.bc.TD, uncleDiff)
|
||||
td = td.Add(td, block.Difficulty)
|
||||
td = td.Add(sm.bc.Td(), uncleDiff)
|
||||
td = td.Add(td, block.Header().Difficulty)
|
||||
|
||||
// The new TD will only be accepted if the new difficulty is
|
||||
// is greater than the previous.
|
||||
if td.Cmp(sm.bc.TD) > 0 {
|
||||
if td.Cmp(sm.bc.Td()) > 0 {
|
||||
return td, true
|
||||
}
|
||||
|
||||
@ -273,13 +270,13 @@ func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) {
|
||||
// Validation validates easy over difficult (dagger takes longer time = difficult)
|
||||
func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
|
||||
expd := CalcDifficulty(block, parent)
|
||||
if expd.Cmp(block.Difficulty) < 0 {
|
||||
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
|
||||
if expd.Cmp(block.Header().Difficulty) < 0 {
|
||||
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Header().Difficulty, expd)
|
||||
}
|
||||
|
||||
diff := block.Time - parent.Time
|
||||
diff := block.Header().Time - parent.Header().Time
|
||||
if diff < 0 {
|
||||
return ValidationError("Block timestamp less then prev block %v (%v - %v)", diff, block.Time, sm.bc.CurrentBlock.Time)
|
||||
return ValidationError("Block timestamp less then prev block %v (%v - %v)", diff, block.Header().Time, sm.bc.CurrentBlock().Header().Time)
|
||||
}
|
||||
|
||||
/* XXX
|
||||
@ -291,7 +288,7 @@ func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
|
||||
|
||||
// Verify the nonce of the block. Return an error if it's not valid
|
||||
if !sm.Pow.Verify(block /*block.HashNoNonce(), block.Difficulty, block.Nonce*/) {
|
||||
return ValidationError("Block's nonce is invalid (= %v)", ethutil.Bytes2Hex(block.Nonce))
|
||||
return ValidationError("Block's nonce is invalid (= %v)", ethutil.Bytes2Hex(block.Header().Nonce))
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -300,24 +297,28 @@ func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
|
||||
func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent *types.Block) error {
|
||||
reward := new(big.Int).Set(BlockReward)
|
||||
|
||||
knownUncles := ethutil.Set(parent.Uncles)
|
||||
nonces := ethutil.NewSet(block.Nonce)
|
||||
for _, uncle := range block.Uncles {
|
||||
knownUncles := set.New()
|
||||
for _, uncle := range parent.Uncles() {
|
||||
knownUncles.Add(string(uncle.Hash()))
|
||||
}
|
||||
|
||||
nonces := ethutil.NewSet(block.Header().Nonce)
|
||||
for _, uncle := range block.Uncles() {
|
||||
if nonces.Include(uncle.Nonce) {
|
||||
// Error not unique
|
||||
return UncleError("Uncle not unique")
|
||||
}
|
||||
|
||||
uncleParent := sm.bc.GetBlock(uncle.PrevHash)
|
||||
uncleParent := sm.bc.GetBlock(uncle.ParentHash)
|
||||
if uncleParent == nil {
|
||||
return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.PrevHash[0:4]))
|
||||
return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
|
||||
}
|
||||
|
||||
if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 {
|
||||
if uncleParent.Header().Number.Cmp(new(big.Int).Sub(parent.Header().Number, big.NewInt(6))) < 0 {
|
||||
return UncleError("Uncle too old")
|
||||
}
|
||||
|
||||
if knownUncles.Include(uncle.Hash()) {
|
||||
if knownUncles.Has(string(uncle.Hash())) {
|
||||
return UncleError("Uncle in chain")
|
||||
}
|
||||
|
||||
@ -333,15 +334,15 @@ func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent
|
||||
}
|
||||
|
||||
// Get the account associated with the coinbase
|
||||
account := statedb.GetAccount(block.Coinbase)
|
||||
account := statedb.GetAccount(block.Header().Coinbase)
|
||||
// Reward amount of ether to the coinbase address
|
||||
account.AddAmount(reward)
|
||||
|
||||
statedb.Manifest().AddMessage(&state.Message{
|
||||
To: block.Coinbase,
|
||||
To: block.Header().Coinbase,
|
||||
Input: nil,
|
||||
Origin: nil,
|
||||
Block: block.Hash(), Timestamp: block.Time, Coinbase: block.Coinbase, Number: block.Number,
|
||||
Block: block.Hash(), Timestamp: int64(block.Header().Time), Coinbase: block.Header().Coinbase, Number: block.Header().Number,
|
||||
Value: new(big.Int).Add(reward, block.Reward),
|
||||
})
|
||||
|
||||
@ -349,15 +350,15 @@ func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent
|
||||
}
|
||||
|
||||
func (sm *BlockManager) GetMessages(block *types.Block) (messages []*state.Message, err error) {
|
||||
if !sm.bc.HasBlock(block.PrevHash) {
|
||||
return nil, ParentError(block.PrevHash)
|
||||
if !sm.bc.HasBlock(block.Header().ParentHash) {
|
||||
return nil, ParentError(block.Header().ParentHash)
|
||||
}
|
||||
|
||||
sm.lastAttemptedBlock = block
|
||||
|
||||
var (
|
||||
parent = sm.bc.GetBlock(block.PrevHash)
|
||||
state = parent.State().Copy()
|
||||
parent = sm.bc.GetBlock(block.Header().ParentHash)
|
||||
state = state.New(parent.Trie().Copy())
|
||||
)
|
||||
|
||||
defer state.Reset()
|
||||
|
@ -1,18 +1,22 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
var chainlogger = logger.NewLogger("CHAIN")
|
||||
|
||||
/*
|
||||
func AddTestNetFunds(block *types.Block) {
|
||||
for _, addr := range []string{
|
||||
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
||||
@ -30,39 +34,87 @@ func AddTestNetFunds(block *types.Block) {
|
||||
block.State().UpdateStateObject(account)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func CalcDifficulty(block, parent *types.Block) *big.Int {
|
||||
diff := new(big.Int)
|
||||
|
||||
adjust := new(big.Int).Rsh(parent.Difficulty, 10)
|
||||
if block.Time >= parent.Time+5 {
|
||||
diff.Sub(parent.Difficulty, adjust)
|
||||
bh, ph := block.Header(), parent.Header()
|
||||
adjust := new(big.Int).Rsh(ph.Difficulty, 10)
|
||||
if bh.Time >= ph.Time+5 {
|
||||
diff.Sub(ph.Difficulty, adjust)
|
||||
} else {
|
||||
diff.Add(parent.Difficulty, adjust)
|
||||
diff.Add(ph.Difficulty, adjust)
|
||||
}
|
||||
|
||||
return diff
|
||||
}
|
||||
|
||||
func CalcGasLimit(parent, block *types.Block) *big.Int {
|
||||
if block.Number().Cmp(big.NewInt(0)) == 0 {
|
||||
return ethutil.BigPow(10, 6)
|
||||
}
|
||||
|
||||
// ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024
|
||||
|
||||
previous := new(big.Int).Mul(big.NewInt(1024-1), parent.GasLimit())
|
||||
current := new(big.Rat).Mul(new(big.Rat).SetInt(parent.GasUsed()), big.NewRat(6, 5))
|
||||
curInt := new(big.Int).Div(current.Num(), current.Denom())
|
||||
|
||||
result := new(big.Int).Add(previous, curInt)
|
||||
result.Div(result, big.NewInt(1024))
|
||||
|
||||
min := big.NewInt(125000)
|
||||
|
||||
return ethutil.BigMax(min, result)
|
||||
}
|
||||
|
||||
type ChainManager struct {
|
||||
//eth EthManager
|
||||
processor types.BlockProcessor
|
||||
eventMux *event.TypeMux
|
||||
genesisBlock *types.Block
|
||||
// Last known total difficulty
|
||||
TD *big.Int
|
||||
|
||||
LastBlockNumber uint64
|
||||
|
||||
CurrentBlock *types.Block
|
||||
LastBlockHash []byte
|
||||
mu sync.RWMutex
|
||||
td *big.Int
|
||||
lastBlockNumber uint64
|
||||
currentBlock *types.Block
|
||||
lastBlockHash []byte
|
||||
|
||||
transState *state.StateDB
|
||||
}
|
||||
|
||||
func (self *ChainManager) Td() *big.Int {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return self.td
|
||||
}
|
||||
|
||||
func (self *ChainManager) LastBlockNumber() uint64 {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return self.lastBlockNumber
|
||||
}
|
||||
|
||||
func (self *ChainManager) LastBlockHash() []byte {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return self.lastBlockHash
|
||||
}
|
||||
|
||||
func (self *ChainManager) CurrentBlock() *types.Block {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return self.currentBlock
|
||||
}
|
||||
|
||||
func NewChainManager(mux *event.TypeMux) *ChainManager {
|
||||
bc := &ChainManager{}
|
||||
bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis))
|
||||
bc.genesisBlock = GenesisBlock()
|
||||
bc.eventMux = mux
|
||||
|
||||
bc.setLastBlock()
|
||||
@ -72,12 +124,19 @@ func NewChainManager(mux *event.TypeMux) *ChainManager {
|
||||
return bc
|
||||
}
|
||||
|
||||
func (self *ChainManager) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return self.td, self.currentBlock.Hash(), self.Genesis().Hash()
|
||||
}
|
||||
|
||||
func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
|
||||
self.processor = proc
|
||||
}
|
||||
|
||||
func (self *ChainManager) State() *state.StateDB {
|
||||
return self.CurrentBlock.State()
|
||||
return state.New(self.CurrentBlock().Trie())
|
||||
}
|
||||
|
||||
func (self *ChainManager) TransState() *state.StateDB {
|
||||
@ -87,46 +146,48 @@ func (self *ChainManager) TransState() *state.StateDB {
|
||||
func (bc *ChainManager) setLastBlock() {
|
||||
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
|
||||
if len(data) != 0 {
|
||||
// Prep genesis
|
||||
AddTestNetFunds(bc.genesisBlock)
|
||||
|
||||
block := types.NewBlockFromBytes(data)
|
||||
bc.CurrentBlock = block
|
||||
bc.LastBlockHash = block.Hash()
|
||||
bc.LastBlockNumber = block.Number.Uint64()
|
||||
var block types.Block
|
||||
rlp.Decode(bytes.NewReader(data), &block)
|
||||
bc.currentBlock = &block
|
||||
bc.lastBlockHash = block.Hash()
|
||||
bc.lastBlockNumber = block.Header().Number.Uint64()
|
||||
|
||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
||||
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
|
||||
bc.td = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
|
||||
} else {
|
||||
bc.Reset()
|
||||
}
|
||||
|
||||
chainlogger.Infof("Last block (#%d) %x\n", bc.LastBlockNumber, bc.CurrentBlock.Hash())
|
||||
chainlogger.Infof("Last block (#%d) %x\n", bc.lastBlockNumber, bc.currentBlock.Hash())
|
||||
}
|
||||
|
||||
// Block creation & chain handling
|
||||
func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
|
||||
var root interface{}
|
||||
hash := ZeroHash256
|
||||
bc.mu.RLock()
|
||||
defer bc.mu.RUnlock()
|
||||
|
||||
var root []byte
|
||||
parentHash := ZeroHash256
|
||||
|
||||
if bc.CurrentBlock != nil {
|
||||
root = bc.CurrentBlock.Root()
|
||||
hash = bc.LastBlockHash
|
||||
root = bc.currentBlock.Header().Root
|
||||
parentHash = bc.lastBlockHash
|
||||
}
|
||||
|
||||
block := types.CreateBlock(
|
||||
root,
|
||||
hash,
|
||||
block := types.NewBlock(
|
||||
parentHash,
|
||||
coinbase,
|
||||
root,
|
||||
ethutil.BigPow(2, 32),
|
||||
nil,
|
||||
"")
|
||||
|
||||
parent := bc.CurrentBlock
|
||||
parent := bc.currentBlock
|
||||
if parent != nil {
|
||||
block.Difficulty = CalcDifficulty(block, parent)
|
||||
block.Number = new(big.Int).Add(bc.CurrentBlock.Number, ethutil.Big1)
|
||||
block.GasLimit = block.CalcGasLimit(bc.CurrentBlock)
|
||||
header := block.Header()
|
||||
header.Difficulty = CalcDifficulty(block, parent)
|
||||
header.Number = new(big.Int).Add(parent.Header().Number, ethutil.Big1)
|
||||
header.GasLimit = CalcGasLimit(parent, block)
|
||||
|
||||
}
|
||||
|
||||
@ -134,41 +195,46 @@ func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
|
||||
}
|
||||
|
||||
func (bc *ChainManager) Reset() {
|
||||
AddTestNetFunds(bc.genesisBlock)
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) {
|
||||
ethutil.Config.Db.Delete(block.Hash())
|
||||
}
|
||||
|
||||
bc.genesisBlock.Trie().Sync()
|
||||
// Prepare the genesis block
|
||||
bc.write(bc.genesisBlock)
|
||||
bc.insert(bc.genesisBlock)
|
||||
bc.CurrentBlock = bc.genesisBlock
|
||||
bc.currentBlock = bc.genesisBlock
|
||||
|
||||
bc.SetTotalDifficulty(ethutil.Big("0"))
|
||||
|
||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
||||
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
|
||||
bc.setTotalDifficulty(ethutil.Big("0"))
|
||||
}
|
||||
|
||||
func (self *ChainManager) Export() []byte {
|
||||
chainlogger.Infoln("exporting", self.CurrentBlock.Number, "blocks")
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
blocks := make(types.Blocks, int(self.CurrentBlock.Number.Int64())+1)
|
||||
for block := self.CurrentBlock; block != nil; block = self.GetBlock(block.PrevHash) {
|
||||
blocks[block.Number.Int64()] = block
|
||||
chainlogger.Infof("exporting %v blocks...\n", self.currentBlock.Header().Number)
|
||||
|
||||
blocks := make([]*types.Block, int(self.currentBlock.NumberU64())+1)
|
||||
for block := self.currentBlock; block != nil; block = self.GetBlock(block.Header().ParentHash) {
|
||||
blocks[block.NumberU64()] = block
|
||||
}
|
||||
|
||||
return ethutil.Encode(blocks)
|
||||
}
|
||||
|
||||
func (bc *ChainManager) insert(block *types.Block) {
|
||||
encodedBlock := block.RlpEncode()
|
||||
encodedBlock := ethutil.Encode(block)
|
||||
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
|
||||
bc.CurrentBlock = block
|
||||
bc.LastBlockHash = block.Hash()
|
||||
bc.currentBlock = block
|
||||
bc.lastBlockHash = block.Hash()
|
||||
}
|
||||
|
||||
func (bc *ChainManager) write(block *types.Block) {
|
||||
bc.writeBlockInfo(block)
|
||||
|
||||
encodedBlock := block.RlpEncode()
|
||||
encodedBlock := ethutil.Encode(block)
|
||||
ethutil.Config.Db.Put(block.Hash(), encodedBlock)
|
||||
}
|
||||
|
||||
@ -183,7 +249,7 @@ func (bc *ChainManager) HasBlock(hash []byte) bool {
|
||||
return len(data) != 0
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain [][]byte) {
|
||||
func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain [][]byte) {
|
||||
block := self.GetBlock(hash)
|
||||
if block == nil {
|
||||
return
|
||||
@ -193,11 +259,11 @@ func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain
|
||||
for i := uint64(0); i < max; i++ {
|
||||
chain = append(chain, block.Hash())
|
||||
|
||||
if block.Number.Cmp(ethutil.Big0) <= 0 {
|
||||
if block.Header().Number.Cmp(ethutil.Big0) <= 0 {
|
||||
break
|
||||
}
|
||||
|
||||
block = self.GetBlock(block.PrevHash)
|
||||
block = self.GetBlock(block.Header().ParentHash)
|
||||
}
|
||||
|
||||
return
|
||||
@ -208,65 +274,61 @@ func (self *ChainManager) GetBlock(hash []byte) *types.Block {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
var block types.Block
|
||||
if err := rlp.Decode(bytes.NewReader(data), &block); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return types.NewBlockFromBytes(data)
|
||||
return &block
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
|
||||
block := self.CurrentBlock
|
||||
for ; block != nil; block = self.GetBlock(block.PrevHash) {
|
||||
if block.Number.Uint64() == num {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
block := self.currentBlock
|
||||
for ; block != nil; block = self.GetBlock(block.Header().ParentHash) {
|
||||
if block.Header().Number.Uint64() == num {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if block != nil && block.Number.Uint64() == 0 && num != 0 {
|
||||
if block != nil && block.Header().Number.Uint64() == 0 && num != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func (bc *ChainManager) SetTotalDifficulty(td *big.Int) {
|
||||
func (bc *ChainManager) setTotalDifficulty(td *big.Int) {
|
||||
ethutil.Config.Db.Put([]byte("LTD"), td.Bytes())
|
||||
bc.TD = td
|
||||
bc.td = td
|
||||
}
|
||||
|
||||
func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
|
||||
parent := self.GetBlock(block.PrevHash)
|
||||
parent := self.GetBlock(block.Header().ParentHash)
|
||||
if parent == nil {
|
||||
return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.PrevHash)
|
||||
return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.Header().ParentHash)
|
||||
}
|
||||
|
||||
parentTd := parent.BlockInfo().TD
|
||||
parentTd := parent.Td
|
||||
|
||||
uncleDiff := new(big.Int)
|
||||
for _, uncle := range block.Uncles {
|
||||
for _, uncle := range block.Uncles() {
|
||||
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
|
||||
}
|
||||
|
||||
td := new(big.Int)
|
||||
td = td.Add(parentTd, uncleDiff)
|
||||
td = td.Add(td, block.Difficulty)
|
||||
td = td.Add(td, block.Header().Difficulty)
|
||||
|
||||
return td, nil
|
||||
}
|
||||
|
||||
func (bc *ChainManager) BlockInfo(block *types.Block) types.BlockInfo {
|
||||
bi := types.BlockInfo{}
|
||||
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
|
||||
bi.RlpDecode(data)
|
||||
|
||||
return bi
|
||||
}
|
||||
|
||||
// Unexported method for writing extra non-essential block info to the db
|
||||
func (bc *ChainManager) writeBlockInfo(block *types.Block) {
|
||||
bc.LastBlockNumber++
|
||||
bi := types.BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash, TD: bc.TD}
|
||||
|
||||
// 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())
|
||||
bc.lastBlockNumber++
|
||||
}
|
||||
|
||||
func (bc *ChainManager) Stop() {
|
||||
@ -283,24 +345,30 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
|
||||
continue
|
||||
}
|
||||
|
||||
chainlogger.Infof("block #%v process failed (%x)\n", block.Number, block.Hash()[:4])
|
||||
h := block.Header()
|
||||
chainlogger.Infof("block #%v process failed (%x)\n", h.Number, h.Hash()[:4])
|
||||
chainlogger.Infoln(block)
|
||||
chainlogger.Infoln(err)
|
||||
return err
|
||||
}
|
||||
|
||||
self.mu.Lock()
|
||||
{
|
||||
self.write(block)
|
||||
if td.Cmp(self.TD) > 0 {
|
||||
if block.Number.Cmp(new(big.Int).Add(self.CurrentBlock.Number, ethutil.Big1)) < 0 {
|
||||
chainlogger.Infof("Split detected. New head #%v (%x), was #%v (%x)\n", block.Number, block.Hash()[:4], self.CurrentBlock.Number, self.CurrentBlock.Hash()[:4])
|
||||
cblock := self.currentBlock
|
||||
if td.Cmp(self.td) > 0 {
|
||||
if block.Header().Number.Cmp(new(big.Int).Add(cblock.Header().Number, ethutil.Big1)) < 0 {
|
||||
chainlogger.Infof("Split detected. New head #%v (%x), was #%v (%x)\n", block.Header().Number, block.Hash()[:4], cblock.Header().Number, cblock.Hash()[:4])
|
||||
}
|
||||
|
||||
self.SetTotalDifficulty(td)
|
||||
self.setTotalDifficulty(td)
|
||||
self.insert(block)
|
||||
self.transState = self.State().Copy()
|
||||
//sm.eth.TxPool().RemoveSet(block.Transactions())
|
||||
self.transState = state.New(cblock.Trie().Copy())
|
||||
}
|
||||
|
||||
}
|
||||
self.mu.Unlock()
|
||||
|
||||
self.eventMux.Post(NewBlockEvent{block})
|
||||
self.eventMux.Post(messages)
|
||||
}
|
||||
|
@ -2,18 +2,138 @@ package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
func TestChainInsertions(t *testing.T) {
|
||||
c1, err := ethutil.ReadAllFile(path.Join("..", "_data", "chain1"))
|
||||
//var Logger logpkg.LogSystem
|
||||
|
||||
//var Log = logpkg.NewLogger("TEST")
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
//Logger = logpkg.NewStdLogSystem(os.Stdout, log.LstdFlags, logpkg.DebugLevel)
|
||||
//logpkg.AddLogSystem(Logger)
|
||||
|
||||
ethutil.ReadConfig("/tmp/ethtest", "/tmp/ethtest", "ETH")
|
||||
|
||||
db, err := ethdb.NewMemDatabase()
|
||||
if err != nil {
|
||||
panic("Could not create mem-db, failing")
|
||||
}
|
||||
ethutil.Config.Db = db
|
||||
}
|
||||
|
||||
func loadChain(fn string, t *testing.T) (types.Blocks, error) {
|
||||
fh, err := os.OpenFile(path.Join("..", "_data", fn), os.O_RDONLY, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
var chain types.Blocks
|
||||
if err := rlp.Decode(fh, &chain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return chain, nil
|
||||
}
|
||||
|
||||
func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *testing.T) {
|
||||
err := chainMan.InsertChain(chain)
|
||||
done <- true
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
data1, _ := ethutil.Decode([]byte(c1), 0)
|
||||
fmt.Println(data1)
|
||||
}
|
||||
|
||||
func TestChainInsertions(t *testing.T) {
|
||||
chain1, err := loadChain("valid1", t)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
fmt.Println(len(chain1))
|
||||
|
||||
chain2, err := loadChain("valid2", t)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
var eventMux event.TypeMux
|
||||
chainMan := NewChainManager(&eventMux)
|
||||
txPool := NewTxPool(chainMan, &eventMux)
|
||||
blockMan := NewBlockManager(txPool, chainMan, &eventMux)
|
||||
chainMan.SetProcessor(blockMan)
|
||||
|
||||
const max = 2
|
||||
done := make(chan bool, max)
|
||||
|
||||
go insertChain(done, chainMan, chain1, t)
|
||||
go insertChain(done, chainMan, chain2, t)
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(chain2[len(chain2)-1], chainMan.CurrentBlock()) {
|
||||
t.Error("chain2 is canonical and shouldn't be")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(chain1[len(chain1)-1], chainMan.CurrentBlock()) {
|
||||
t.Error("chain1 isn't canonical and should be")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainMultipleInsertions(t *testing.T) {
|
||||
const max = 4
|
||||
chains := make([]types.Blocks, max)
|
||||
var longest int
|
||||
for i := 0; i < max; i++ {
|
||||
var err error
|
||||
name := "valid" + strconv.Itoa(i+1)
|
||||
chains[i], err = loadChain(name, t)
|
||||
if len(chains[i]) >= len(chains[longest]) {
|
||||
longest = i
|
||||
}
|
||||
fmt.Println("loaded", name, "with a length of", len(chains[i]))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
var eventMux event.TypeMux
|
||||
chainMan := NewChainManager(&eventMux)
|
||||
txPool := NewTxPool(chainMan, &eventMux)
|
||||
blockMan := NewBlockManager(txPool, chainMan, &eventMux)
|
||||
chainMan.SetProcessor(blockMan)
|
||||
done := make(chan bool, max)
|
||||
for i, chain := range chains {
|
||||
var i int = i
|
||||
go func() {
|
||||
insertChain(done, chainMan, chain, t)
|
||||
fmt.Println(i, "done")
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(chains[longest][len(chains[longest])-1], chainMan.CurrentBlock()) {
|
||||
t.Error("Invalid canonical chain")
|
||||
}
|
||||
}
|
||||
|
@ -10,3 +10,6 @@ type TxPostEvent struct{ Tx *types.Transaction }
|
||||
|
||||
// NewBlockEvent is posted when a block has been imported.
|
||||
type NewBlockEvent struct{ Block *types.Block }
|
||||
|
||||
// NewMinedBlockEvent is posted when a block has been imported.
|
||||
type NewMinedBlockEvent struct{ Block *types.Block }
|
||||
|
@ -3,22 +3,21 @@ package core
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/vm"
|
||||
)
|
||||
|
||||
type Execution struct {
|
||||
vm vm.VirtualMachine
|
||||
env vm.Environment
|
||||
address, input []byte
|
||||
Gas, price, value *big.Int
|
||||
object *state.StateObject
|
||||
SkipTransfer bool
|
||||
}
|
||||
|
||||
func NewExecution(vm vm.VirtualMachine, address, input []byte, gas, gasPrice, value *big.Int) *Execution {
|
||||
return &Execution{vm: vm, address: address, input: input, Gas: gas, price: gasPrice, value: value}
|
||||
func NewExecution(env vm.Environment, address, input []byte, gas, gasPrice, value *big.Int) *Execution {
|
||||
return &Execution{env: env, address: address, input: input, Gas: gas, price: gasPrice, value: value}
|
||||
}
|
||||
|
||||
func (self *Execution) Addr() []byte {
|
||||
@ -27,14 +26,19 @@ func (self *Execution) Addr() []byte {
|
||||
|
||||
func (self *Execution) Call(codeAddr []byte, caller vm.ClosureRef) ([]byte, error) {
|
||||
// Retrieve the executing code
|
||||
code := self.vm.Env().State().GetCode(codeAddr)
|
||||
code := self.env.State().GetCode(codeAddr)
|
||||
|
||||
return self.exec(code, codeAddr, caller)
|
||||
}
|
||||
|
||||
func (self *Execution) exec(code, contextAddr []byte, caller vm.ClosureRef) (ret []byte, err error) {
|
||||
env := self.vm.Env()
|
||||
chainlogger.Debugf("pre state %x\n", env.State().Root())
|
||||
env := self.env
|
||||
evm := vm.New(env, vm.DebugVmTy)
|
||||
|
||||
if env.Depth() == vm.MaxCallDepth {
|
||||
// Consume all gas (by not returning it) and return a depth error
|
||||
return nil, vm.DepthError{}
|
||||
}
|
||||
|
||||
from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address)
|
||||
// Skipping transfer is used on testing for the initial call
|
||||
@ -49,32 +53,19 @@ func (self *Execution) exec(code, contextAddr []byte, caller vm.ClosureRef) (ret
|
||||
}
|
||||
|
||||
snapshot := env.State().Copy()
|
||||
defer func() {
|
||||
if vm.IsDepthErr(err) || vm.IsOOGErr(err) {
|
||||
start := time.Now()
|
||||
ret, err = evm.Run(to, caller, code, self.value, self.Gas, self.price, self.input)
|
||||
if err != nil {
|
||||
env.State().Set(snapshot)
|
||||
}
|
||||
chainlogger.Debugf("post state %x\n", env.State().Root())
|
||||
}()
|
||||
|
||||
self.object = to
|
||||
// Pre-compiled contracts (address.go) 1, 2 & 3.
|
||||
naddr := ethutil.BigD(contextAddr).Uint64()
|
||||
if p := vm.Precompiled[naddr]; p != nil {
|
||||
if self.Gas.Cmp(p.Gas(len(self.input))) >= 0 {
|
||||
ret = p.Call(self.input)
|
||||
self.vm.Printf("NATIVE_FUNC(%x) => %x", naddr, ret)
|
||||
self.vm.Endl()
|
||||
}
|
||||
} else {
|
||||
ret, err = self.vm.Run(to, caller, code, self.value, self.Gas, self.price, self.input)
|
||||
}
|
||||
chainlogger.Debugf("vm took %v\n", time.Since(start))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Execution) Create(caller vm.ClosureRef) (ret []byte, err error, account *state.StateObject) {
|
||||
ret, err = self.exec(self.input, nil, caller)
|
||||
account = self.vm.Env().State().GetStateObject(self.address)
|
||||
account = self.env.State().GetStateObject(self.address)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -76,13 +76,14 @@ func (self *Filter) SetSkip(skip int) {
|
||||
|
||||
// Run filters messages with the current parameters set
|
||||
func (self *Filter) Find() []*state.Message {
|
||||
earliestBlock := self.eth.ChainManager().CurrentBlock()
|
||||
var earliestBlockNo uint64 = uint64(self.earliest)
|
||||
if self.earliest == -1 {
|
||||
earliestBlockNo = self.eth.ChainManager().CurrentBlock.Number.Uint64()
|
||||
earliestBlockNo = earliestBlock.NumberU64()
|
||||
}
|
||||
var latestBlockNo uint64 = uint64(self.latest)
|
||||
if self.latest == -1 {
|
||||
latestBlockNo = self.eth.ChainManager().CurrentBlock.Number.Uint64()
|
||||
latestBlockNo = earliestBlock.NumberU64()
|
||||
}
|
||||
|
||||
var (
|
||||
@ -93,7 +94,7 @@ func (self *Filter) Find() []*state.Message {
|
||||
for i := 0; !quit && block != nil; i++ {
|
||||
// Quit on latest
|
||||
switch {
|
||||
case block.Number.Uint64() == earliestBlockNo, block.Number.Uint64() == 0:
|
||||
case block.NumberU64() == earliestBlockNo, block.NumberU64() == 0:
|
||||
quit = true
|
||||
case self.max <= len(messages):
|
||||
break
|
||||
@ -113,7 +114,7 @@ func (self *Filter) Find() []*state.Message {
|
||||
messages = append(messages, self.FilterMessages(msgs)...)
|
||||
}
|
||||
|
||||
block = self.eth.ChainManager().GetBlock(block.PrevHash)
|
||||
block = self.eth.ChainManager().GetBlock(block.ParentHash())
|
||||
}
|
||||
|
||||
skip := int(math.Min(float64(len(messages)), float64(self.skip)))
|
||||
@ -176,7 +177,7 @@ func (self *Filter) bloomFilter(block *types.Block) bool {
|
||||
var fromIncluded, toIncluded bool
|
||||
if len(self.from) > 0 {
|
||||
for _, from := range self.from {
|
||||
if types.BloomLookup(block.LogsBloom, from) || bytes.Equal(block.Coinbase, from) {
|
||||
if types.BloomLookup(block.Bloom(), from) || bytes.Equal(block.Coinbase(), from) {
|
||||
fromIncluded = true
|
||||
break
|
||||
}
|
||||
@ -187,7 +188,7 @@ func (self *Filter) bloomFilter(block *types.Block) bool {
|
||||
|
||||
if len(self.to) > 0 {
|
||||
for _, to := range self.to {
|
||||
if types.BloomLookup(block.LogsBloom, ethutil.U256(new(big.Int).Add(ethutil.Big1, ethutil.BigD(to))).Bytes()) || bytes.Equal(block.Coinbase, to) {
|
||||
if types.BloomLookup(block.Bloom(), ethutil.U256(new(big.Int).Add(ethutil.Big1, ethutil.BigD(to))).Bytes()) || bytes.Equal(block.Coinbase(), to) {
|
||||
toIncluded = true
|
||||
break
|
||||
}
|
||||
|
@ -1,7 +1 @@
|
||||
package core
|
||||
|
||||
// import "testing"
|
||||
|
||||
// func TestFilter(t *testing.T) {
|
||||
// NewFilter(NewTestManager())
|
||||
// }
|
||||
|
@ -3,8 +3,10 @@ package core
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
/*
|
||||
@ -17,36 +19,35 @@ var ZeroHash512 = make([]byte, 64)
|
||||
var EmptyShaList = crypto.Sha3(ethutil.Encode([]interface{}{}))
|
||||
var EmptyListRoot = crypto.Sha3(ethutil.Encode(""))
|
||||
|
||||
var GenesisHeader = []interface{}{
|
||||
// Previous hash (none)
|
||||
ZeroHash256,
|
||||
// Empty uncles
|
||||
EmptyShaList,
|
||||
// Coinbase
|
||||
ZeroHash160,
|
||||
// Root state
|
||||
EmptyShaList,
|
||||
// tx root
|
||||
EmptyListRoot,
|
||||
// receipt root
|
||||
EmptyListRoot,
|
||||
// bloom
|
||||
ZeroHash512,
|
||||
// Difficulty
|
||||
//ethutil.BigPow(2, 22),
|
||||
big.NewInt(131072),
|
||||
// Number
|
||||
ethutil.Big0,
|
||||
// Block upper gas bound
|
||||
big.NewInt(1000000),
|
||||
// Block gas used
|
||||
ethutil.Big0,
|
||||
// Time
|
||||
ethutil.Big0,
|
||||
// Extra
|
||||
nil,
|
||||
// Nonce
|
||||
crypto.Sha3(big.NewInt(42).Bytes()),
|
||||
}
|
||||
func GenesisBlock() *types.Block {
|
||||
genesis := types.NewBlock(ZeroHash256, ZeroHash160, nil, big.NewInt(131072), crypto.Sha3(big.NewInt(42).Bytes()), "")
|
||||
genesis.Header().Number = ethutil.Big0
|
||||
genesis.Header().GasLimit = big.NewInt(1000000)
|
||||
genesis.Header().GasUsed = ethutil.Big0
|
||||
genesis.Header().Time = 0
|
||||
|
||||
var Genesis = []interface{}{GenesisHeader, []interface{}{}, []interface{}{}}
|
||||
genesis.SetUncles([]*types.Header{})
|
||||
genesis.SetTransactions(types.Transactions{})
|
||||
genesis.SetReceipts(types.Receipts{})
|
||||
|
||||
statedb := state.New(genesis.Trie())
|
||||
for _, addr := range []string{
|
||||
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
||||
"e4157b34ea9615cfbde6b4fda419828124b70c78",
|
||||
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
|
||||
"6c386a4b26f73c802f34673f7248bb118f97424a",
|
||||
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
|
||||
"2ef47100e0787b915105fd5e3f4ff6752079d5cb",
|
||||
"e6716f9544a56c530d868e4bfbacb172315bdead",
|
||||
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
|
||||
} {
|
||||
codedAddr := ethutil.Hex2Bytes(addr)
|
||||
account := statedb.GetAccount(codedAddr)
|
||||
account.SetBalance(ethutil.Big("1606938044258990275541962092341162602522202993782792835301376")) //ethutil.BigPow(2, 200)
|
||||
statedb.UpdateStateObject(account)
|
||||
}
|
||||
statedb.Sync()
|
||||
genesis.Header().Root = statedb.Root()
|
||||
|
||||
return genesis
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
)
|
||||
|
||||
// Implement our EthTest Manager
|
||||
@ -54,11 +54,11 @@ func (tm *TestManager) TxPool() *TxPool {
|
||||
func (tm *TestManager) EventMux() *event.TypeMux {
|
||||
return tm.eventMux
|
||||
}
|
||||
func (tm *TestManager) Broadcast(msgType wire.MsgType, data []interface{}) {
|
||||
func (tm *TestManager) Broadcast(msgType p2p.Msg, data []interface{}) {
|
||||
fmt.Println("Broadcast not implemented")
|
||||
}
|
||||
|
||||
func (tm *TestManager) ClientIdentity() wire.ClientIdentity {
|
||||
func (tm *TestManager) ClientIdentity() p2p.ClientIdentity {
|
||||
return nil
|
||||
}
|
||||
func (tm *TestManager) KeyManager() *crypto.KeyManager {
|
||||
|
@ -5,6 +5,8 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/vm"
|
||||
)
|
||||
@ -27,48 +29,69 @@ import (
|
||||
*/
|
||||
type StateTransition struct {
|
||||
coinbase, receiver []byte
|
||||
tx *types.Transaction
|
||||
msg Message
|
||||
gas, gasPrice *big.Int
|
||||
initialGas *big.Int
|
||||
value *big.Int
|
||||
data []byte
|
||||
state *state.StateDB
|
||||
block *types.Block
|
||||
|
||||
cb, rec, sen *state.StateObject
|
||||
|
||||
Env vm.Environment
|
||||
}
|
||||
|
||||
func NewStateTransition(coinbase *state.StateObject, tx *types.Transaction, state *state.StateDB, block *types.Block) *StateTransition {
|
||||
return &StateTransition{coinbase.Address(), tx.Recipient, tx, new(big.Int), new(big.Int).Set(tx.GasPrice), tx.Value, tx.Data, state, block, coinbase, nil, nil}
|
||||
type Message interface {
|
||||
Hash() []byte
|
||||
|
||||
From() []byte
|
||||
To() []byte
|
||||
|
||||
GasPrice() *big.Int
|
||||
Gas() *big.Int
|
||||
Value() *big.Int
|
||||
|
||||
Nonce() uint64
|
||||
Data() []byte
|
||||
}
|
||||
|
||||
func AddressFromMessage(msg Message) []byte {
|
||||
// Generate a new address
|
||||
return crypto.Sha3(ethutil.NewValue([]interface{}{msg.From(), msg.Nonce()}).Encode())[12:]
|
||||
}
|
||||
|
||||
func MessageCreatesContract(msg Message) bool {
|
||||
return len(msg.To()) == 0
|
||||
}
|
||||
|
||||
func MessageGasValue(msg Message) *big.Int {
|
||||
return new(big.Int).Mul(msg.Gas(), msg.GasPrice())
|
||||
}
|
||||
|
||||
func NewStateTransition(coinbase *state.StateObject, msg Message, state *state.StateDB, block *types.Block) *StateTransition {
|
||||
return &StateTransition{coinbase.Address(), msg.To(), msg, new(big.Int), new(big.Int).Set(msg.GasPrice()), new(big.Int), msg.Value(), msg.Data(), state, block, coinbase, nil, nil, nil}
|
||||
}
|
||||
|
||||
func (self *StateTransition) VmEnv() vm.Environment {
|
||||
if self.Env == nil {
|
||||
self.Env = NewEnv(self.state, self.msg, self.block)
|
||||
}
|
||||
|
||||
return self.Env
|
||||
}
|
||||
|
||||
func (self *StateTransition) Coinbase() *state.StateObject {
|
||||
if self.cb != nil {
|
||||
return self.cb
|
||||
}
|
||||
|
||||
self.cb = self.state.GetOrNewStateObject(self.coinbase)
|
||||
return self.cb
|
||||
return self.state.GetOrNewStateObject(self.coinbase)
|
||||
}
|
||||
func (self *StateTransition) Sender() *state.StateObject {
|
||||
if self.sen != nil {
|
||||
return self.sen
|
||||
}
|
||||
|
||||
self.sen = self.state.GetOrNewStateObject(self.tx.Sender())
|
||||
|
||||
return self.sen
|
||||
func (self *StateTransition) From() *state.StateObject {
|
||||
return self.state.GetOrNewStateObject(self.msg.From())
|
||||
}
|
||||
func (self *StateTransition) Receiver() *state.StateObject {
|
||||
if self.tx != nil && self.tx.CreatesContract() {
|
||||
func (self *StateTransition) To() *state.StateObject {
|
||||
if self.msg != nil && MessageCreatesContract(self.msg) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if self.rec != nil {
|
||||
return self.rec
|
||||
}
|
||||
|
||||
self.rec = self.state.GetOrNewStateObject(self.tx.Recipient)
|
||||
return self.rec
|
||||
return self.state.GetOrNewStateObject(self.msg.To())
|
||||
}
|
||||
|
||||
func (self *StateTransition) UseGas(amount *big.Int) error {
|
||||
@ -87,41 +110,33 @@ func (self *StateTransition) AddGas(amount *big.Int) {
|
||||
func (self *StateTransition) BuyGas() error {
|
||||
var err error
|
||||
|
||||
sender := self.Sender()
|
||||
if sender.Balance().Cmp(self.tx.GasValue()) < 0 {
|
||||
return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Balance())
|
||||
sender := self.From()
|
||||
if sender.Balance().Cmp(MessageGasValue(self.msg)) < 0 {
|
||||
return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address()[:4], MessageGasValue(self.msg), sender.Balance())
|
||||
}
|
||||
|
||||
coinbase := self.Coinbase()
|
||||
err = coinbase.BuyGas(self.tx.Gas, self.tx.GasPrice)
|
||||
err = coinbase.BuyGas(self.msg.Gas(), self.msg.GasPrice())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.AddGas(self.tx.Gas)
|
||||
sender.SubAmount(self.tx.GasValue())
|
||||
self.AddGas(self.msg.Gas())
|
||||
self.initialGas.Set(self.msg.Gas())
|
||||
sender.SubAmount(MessageGasValue(self.msg))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *StateTransition) RefundGas() {
|
||||
coinbase, sender := self.Coinbase(), self.Sender()
|
||||
coinbase.RefundGas(self.gas, self.tx.GasPrice)
|
||||
|
||||
// Return remaining gas
|
||||
remaining := new(big.Int).Mul(self.gas, self.tx.GasPrice)
|
||||
sender.AddAmount(remaining)
|
||||
}
|
||||
|
||||
func (self *StateTransition) preCheck() (err error) {
|
||||
var (
|
||||
tx = self.tx
|
||||
sender = self.Sender()
|
||||
msg = self.msg
|
||||
sender = self.From()
|
||||
)
|
||||
|
||||
// Make sure this transaction's nonce is correct
|
||||
if sender.Nonce != tx.Nonce {
|
||||
return NonceError(tx.Nonce, sender.Nonce)
|
||||
if sender.Nonce != msg.Nonce() {
|
||||
return NonceError(msg.Nonce(), sender.Nonce)
|
||||
}
|
||||
|
||||
// Pre-pay gas / Buy gas of the coinbase account
|
||||
@ -132,8 +147,8 @@ func (self *StateTransition) preCheck() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *StateTransition) TransitionState() (err error) {
|
||||
statelogger.Debugf("(~) %x\n", self.tx.Hash())
|
||||
func (self *StateTransition) TransitionState() (ret []byte, err error) {
|
||||
statelogger.Debugf("(~) %x\n", self.msg.Hash())
|
||||
|
||||
// XXX Transactions after this point are considered valid.
|
||||
if err = self.preCheck(); err != nil {
|
||||
@ -141,8 +156,8 @@ func (self *StateTransition) TransitionState() (err error) {
|
||||
}
|
||||
|
||||
var (
|
||||
tx = self.tx
|
||||
sender = self.Sender()
|
||||
msg = self.msg
|
||||
sender = self.From()
|
||||
)
|
||||
|
||||
defer self.RefundGas()
|
||||
@ -168,30 +183,56 @@ func (self *StateTransition) TransitionState() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
var ret []byte
|
||||
vmenv := NewEnv(self.state, self.tx, self.block)
|
||||
vmenv := self.VmEnv()
|
||||
var ref vm.ClosureRef
|
||||
if tx.CreatesContract() {
|
||||
self.rec = MakeContract(tx, self.state)
|
||||
|
||||
ret, err, ref = vmenv.Create(sender, self.rec.Address(), self.tx.Data, self.gas, self.gasPrice, self.value)
|
||||
if MessageCreatesContract(msg) {
|
||||
contract := MakeContract(msg, self.state)
|
||||
ret, err, ref = vmenv.Create(sender, contract.Address(), self.msg.Data(), self.gas, self.gasPrice, self.value)
|
||||
if err == nil {
|
||||
dataGas := big.NewInt(int64(len(ret)))
|
||||
dataGas.Mul(dataGas, vm.GasCreateByte)
|
||||
if err = self.UseGas(dataGas); err == nil {
|
||||
//self.state.SetCode(ref.Address(), ret)
|
||||
ref.SetCode(ret)
|
||||
} else {
|
||||
ret, err = vmenv.Call(self.Sender(), self.Receiver().Address(), self.tx.Data, self.gas, self.gasPrice, self.value)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret, err = vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
statelogger.Debugln(err)
|
||||
self.UseGas(self.gas)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Converts an transaction in to a state object
|
||||
func MakeContract(tx *types.Transaction, state *state.StateDB) *state.StateObject {
|
||||
addr := tx.CreationAddress(state)
|
||||
func MakeContract(msg Message, state *state.StateDB) *state.StateObject {
|
||||
addr := AddressFromMessage(msg)
|
||||
|
||||
contract := state.GetOrNewStateObject(addr)
|
||||
contract.InitCode = tx.Data
|
||||
contract.InitCode = msg.Data()
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
func (self *StateTransition) RefundGas() {
|
||||
coinbase, sender := self.Coinbase(), self.From()
|
||||
// Return remaining gas
|
||||
remaining := new(big.Int).Mul(self.gas, self.msg.GasPrice())
|
||||
sender.AddAmount(remaining)
|
||||
|
||||
uhalf := new(big.Int).Div(self.GasUsed(), ethutil.Big2)
|
||||
for addr, ref := range self.state.Refunds() {
|
||||
refund := ethutil.BigMin(uhalf, ref)
|
||||
self.gas.Add(self.gas, refund)
|
||||
self.state.AddBalance([]byte(addr), refund.Mul(refund, self.msg.GasPrice()))
|
||||
}
|
||||
|
||||
coinbase.RefundGas(self.gas, self.msg.GasPrice())
|
||||
}
|
||||
|
||||
func (self *StateTransition) GasUsed() *big.Int {
|
||||
return new(big.Int).Sub(self.initialGas, self.gas)
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
)
|
||||
|
||||
var txplogger = logger.NewLogger("TXP")
|
||||
@ -18,7 +18,9 @@ var txplogger = logger.NewLogger("TXP")
|
||||
const txPoolQueueSize = 50
|
||||
|
||||
type TxPoolHook chan *types.Transaction
|
||||
type TxMsgTy byte
|
||||
type TxMsg struct {
|
||||
Tx *types.Transaction
|
||||
}
|
||||
|
||||
const (
|
||||
minGasPrice = 1000000
|
||||
@ -26,11 +28,6 @@ const (
|
||||
|
||||
var MinGasPrice = big.NewInt(10000000000000)
|
||||
|
||||
type TxMsg struct {
|
||||
Tx *types.Transaction
|
||||
Type TxMsgTy
|
||||
}
|
||||
|
||||
func EachTx(pool *list.List, it func(*types.Transaction, *list.Element) bool) {
|
||||
for e := pool.Front(); e != nil; e = e.Next() {
|
||||
if it(e.Value.(*types.Transaction), e) {
|
||||
@ -61,7 +58,6 @@ type TxProcessor interface {
|
||||
// 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 {
|
||||
Ethereum EthManager
|
||||
// The mutex for accessing the Tx pool.
|
||||
mutex sync.Mutex
|
||||
// Queueing channel for reading and writing incoming
|
||||
@ -75,14 +71,18 @@ type TxPool struct {
|
||||
SecondaryProcessor TxProcessor
|
||||
|
||||
subscribers []chan TxMsg
|
||||
|
||||
chainManager *ChainManager
|
||||
eventMux *event.TypeMux
|
||||
}
|
||||
|
||||
func NewTxPool(ethereum EthManager) *TxPool {
|
||||
func NewTxPool(chainManager *ChainManager, eventMux *event.TypeMux) *TxPool {
|
||||
return &TxPool{
|
||||
pool: list.New(),
|
||||
queueChan: make(chan *types.Transaction, txPoolQueueSize),
|
||||
quit: make(chan bool),
|
||||
Ethereum: ethereum,
|
||||
chainManager: chainManager,
|
||||
eventMux: eventMux,
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,20 +94,20 @@ func (pool *TxPool) addTransaction(tx *types.Transaction) {
|
||||
pool.pool.PushBack(tx)
|
||||
|
||||
// Broadcast the transaction to the rest of the peers
|
||||
pool.Ethereum.Broadcast(wire.MsgTxTy, []interface{}{tx.RlpData()})
|
||||
pool.eventMux.Post(TxPreEvent{tx})
|
||||
}
|
||||
|
||||
func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
|
||||
// Get the last block so we can retrieve the sender and receiver from
|
||||
// the merkle trie
|
||||
block := pool.Ethereum.ChainManager().CurrentBlock
|
||||
block := pool.chainManager.CurrentBlock
|
||||
// Something has gone horribly wrong if this happens
|
||||
if block == nil {
|
||||
return fmt.Errorf("No last block on the block chain")
|
||||
}
|
||||
|
||||
if len(tx.Recipient) != 0 && len(tx.Recipient) != 20 {
|
||||
return fmt.Errorf("Invalid recipient. len = %d", len(tx.Recipient))
|
||||
if len(tx.To()) != 0 && len(tx.To()) != 20 {
|
||||
return fmt.Errorf("Invalid recipient. len = %d", len(tx.To()))
|
||||
}
|
||||
|
||||
v, _, _ := tx.Curve()
|
||||
@ -116,19 +116,17 @@ func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
|
||||
}
|
||||
|
||||
// Get the sender
|
||||
sender := pool.Ethereum.ChainManager().State().GetAccount(tx.Sender())
|
||||
senderAddr := tx.From()
|
||||
if senderAddr == nil {
|
||||
return fmt.Errorf("invalid sender")
|
||||
}
|
||||
sender := pool.chainManager.State().GetAccount(senderAddr)
|
||||
|
||||
totAmount := new(big.Int).Set(tx.Value)
|
||||
totAmount := new(big.Int).Set(tx.Value())
|
||||
// Make sure there's enough in the sender's account. Having insufficient
|
||||
// funds won't invalidate this transaction but simple ignores it.
|
||||
if sender.Balance().Cmp(totAmount) < 0 {
|
||||
return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.Sender())
|
||||
}
|
||||
|
||||
if tx.IsContract() {
|
||||
if tx.GasPrice.Cmp(big.NewInt(minGasPrice)) < 0 {
|
||||
return fmt.Errorf("Gasprice too low, %s given should be at least %d.", tx.GasPrice, minGasPrice)
|
||||
}
|
||||
return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.From())
|
||||
}
|
||||
|
||||
// Increment the nonce making each tx valid only once to prevent replay
|
||||
@ -154,13 +152,10 @@ func (self *TxPool) Add(tx *types.Transaction) error {
|
||||
|
||||
self.addTransaction(tx)
|
||||
|
||||
tmp := make([]byte, 4)
|
||||
copy(tmp, tx.Recipient)
|
||||
|
||||
txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.Sender()[:4], tmp, tx.Value, tx.Hash())
|
||||
txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.From()[:4], tx.To()[:4], tx.Value, tx.Hash())
|
||||
|
||||
// Notify the subscribers
|
||||
go self.Ethereum.EventMux().Post(TxPreEvent{tx})
|
||||
go self.eventMux.Post(TxPreEvent{tx})
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -169,7 +164,17 @@ func (self *TxPool) Size() int {
|
||||
return self.pool.Len()
|
||||
}
|
||||
|
||||
func (pool *TxPool) CurrentTransactions() []*types.Transaction {
|
||||
func (self *TxPool) AddTransactions(txs []*types.Transaction) {
|
||||
for _, tx := range txs {
|
||||
if err := self.Add(tx); err != nil {
|
||||
txplogger.Infoln(err)
|
||||
} else {
|
||||
txplogger.Infof("tx %x\n", tx.Hash()[0:4])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pool *TxPool) GetTransactions() []*types.Transaction {
|
||||
pool.mutex.Lock()
|
||||
defer pool.mutex.Unlock()
|
||||
|
||||
@ -192,9 +197,9 @@ func (pool *TxPool) RemoveInvalid(state *state.StateDB) {
|
||||
|
||||
for e := pool.pool.Front(); e != nil; e = e.Next() {
|
||||
tx := e.Value.(*types.Transaction)
|
||||
sender := state.GetAccount(tx.Sender())
|
||||
sender := state.GetAccount(tx.From())
|
||||
err := pool.ValidateTransaction(tx)
|
||||
if err != nil || sender.Nonce >= tx.Nonce {
|
||||
if err != nil || sender.Nonce >= tx.Nonce() {
|
||||
pool.pool.Remove(e)
|
||||
}
|
||||
}
|
||||
@ -216,7 +221,7 @@ func (self *TxPool) RemoveSet(txs types.Transactions) {
|
||||
}
|
||||
|
||||
func (pool *TxPool) Flush() []*types.Transaction {
|
||||
txList := pool.CurrentTransactions()
|
||||
txList := pool.GetTransactions()
|
||||
|
||||
// Recreate a new list all together
|
||||
// XXX Is this the fastest way?
|
||||
|
@ -9,41 +9,240 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/ptrie"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
type BlockInfo struct {
|
||||
Number uint64
|
||||
Hash []byte
|
||||
Parent []byte
|
||||
TD *big.Int
|
||||
type Header struct {
|
||||
// Hash to the previous block
|
||||
ParentHash ethutil.Bytes
|
||||
// Uncles of this block
|
||||
UncleHash []byte
|
||||
// The coin base address
|
||||
Coinbase []byte
|
||||
// Block Trie state
|
||||
Root []byte
|
||||
// Tx sha
|
||||
TxHash []byte
|
||||
// Receipt sha
|
||||
ReceiptHash []byte
|
||||
// Bloom
|
||||
Bloom []byte
|
||||
// Difficulty for the current block
|
||||
Difficulty *big.Int
|
||||
// The block number
|
||||
Number *big.Int
|
||||
// Gas limit
|
||||
GasLimit *big.Int
|
||||
// Gas used
|
||||
GasUsed *big.Int
|
||||
// Creation time
|
||||
Time uint64
|
||||
// Extra data
|
||||
Extra string
|
||||
// Block Nonce for verification
|
||||
Nonce ethutil.Bytes
|
||||
}
|
||||
|
||||
func (bi *BlockInfo) RlpDecode(data []byte) {
|
||||
decoder := ethutil.NewValueFromBytes(data)
|
||||
func (self *Header) rlpData(withNonce bool) []interface{} {
|
||||
fields := []interface{}{self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra}
|
||||
if withNonce {
|
||||
fields = append(fields, self.Nonce)
|
||||
}
|
||||
|
||||
bi.Number = decoder.Get(0).Uint()
|
||||
bi.Hash = decoder.Get(1).Bytes()
|
||||
bi.Parent = decoder.Get(2).Bytes()
|
||||
bi.TD = decoder.Get(3).BigInt()
|
||||
return fields
|
||||
}
|
||||
|
||||
func (bi *BlockInfo) RlpEncode() []byte {
|
||||
return ethutil.Encode([]interface{}{bi.Number, bi.Hash, bi.Parent, bi.TD})
|
||||
func (self *Header) RlpData() interface{} {
|
||||
return self.rlpData(true)
|
||||
}
|
||||
|
||||
func (self *Header) Hash() []byte {
|
||||
return crypto.Sha3(ethutil.Encode(self.rlpData(true)))
|
||||
}
|
||||
|
||||
func (self *Header) HashNoNonce() []byte {
|
||||
return crypto.Sha3(ethutil.Encode(self.rlpData(false)))
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
header *Header
|
||||
uncles []*Header
|
||||
transactions Transactions
|
||||
Td *big.Int
|
||||
|
||||
receipts Receipts
|
||||
Reward *big.Int
|
||||
}
|
||||
|
||||
func NewBlock(parentHash []byte, coinbase []byte, root []byte, difficulty *big.Int, nonce []byte, extra string) *Block {
|
||||
header := &Header{
|
||||
Root: root,
|
||||
ParentHash: parentHash,
|
||||
Coinbase: coinbase,
|
||||
Difficulty: difficulty,
|
||||
Nonce: nonce,
|
||||
Time: uint64(time.Now().Unix()),
|
||||
Extra: extra,
|
||||
GasUsed: new(big.Int),
|
||||
GasLimit: new(big.Int),
|
||||
}
|
||||
|
||||
block := &Block{header: header, Reward: new(big.Int)}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func NewBlockWithHeader(header *Header) *Block {
|
||||
return &Block{header: header}
|
||||
}
|
||||
|
||||
func (self *Block) DecodeRLP(s *rlp.Stream) error {
|
||||
if _, err := s.List(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var header Header
|
||||
if err := s.Decode(&header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var transactions []*Transaction
|
||||
if err := s.Decode(&transactions); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var uncleHeaders []*Header
|
||||
if err := s.Decode(&uncleHeaders); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tdBytes []byte
|
||||
if err := s.Decode(&tdBytes); err != nil {
|
||||
// If this block comes from the network that's fine. If loaded from disk it should be there
|
||||
// Blocks don't store their Td when propagated over the network
|
||||
} else {
|
||||
self.Td = ethutil.BigD(tdBytes)
|
||||
}
|
||||
|
||||
if err := s.ListEnd(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.header = &header
|
||||
self.uncles = uncleHeaders
|
||||
self.transactions = transactions
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Block) Header() *Header {
|
||||
return self.header
|
||||
}
|
||||
|
||||
func (self *Block) Uncles() []*Header {
|
||||
return self.uncles
|
||||
}
|
||||
|
||||
func (self *Block) SetUncles(uncleHeaders []*Header) {
|
||||
self.uncles = uncleHeaders
|
||||
self.header.UncleHash = crypto.Sha3(ethutil.Encode(uncleHeaders))
|
||||
}
|
||||
|
||||
func (self *Block) Transactions() Transactions {
|
||||
return self.transactions
|
||||
}
|
||||
|
||||
func (self *Block) Transaction(hash []byte) *Transaction {
|
||||
for _, transaction := range self.transactions {
|
||||
if bytes.Equal(hash, transaction.Hash()) {
|
||||
return transaction
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Block) SetTransactions(transactions Transactions) {
|
||||
self.transactions = transactions
|
||||
self.header.TxHash = DeriveSha(transactions)
|
||||
}
|
||||
|
||||
func (self *Block) Receipts() Receipts {
|
||||
return self.receipts
|
||||
}
|
||||
|
||||
func (self *Block) SetReceipts(receipts Receipts) {
|
||||
self.receipts = receipts
|
||||
self.header.ReceiptHash = DeriveSha(receipts)
|
||||
self.header.Bloom = CreateBloom(receipts)
|
||||
}
|
||||
|
||||
func (self *Block) RlpData() interface{} {
|
||||
return []interface{}{self.header, self.transactions, self.uncles}
|
||||
}
|
||||
|
||||
func (self *Block) RlpDataForStorage() interface{} {
|
||||
return []interface{}{self.header, self.transactions, self.uncles, self.Td /* TODO receipts */}
|
||||
}
|
||||
|
||||
// Header accessors (add as you need them)
|
||||
func (self *Block) Number() *big.Int { return self.header.Number }
|
||||
func (self *Block) NumberU64() uint64 { return self.header.Number.Uint64() }
|
||||
func (self *Block) ParentHash() []byte { return self.header.ParentHash }
|
||||
func (self *Block) Bloom() []byte { return self.header.Bloom }
|
||||
func (self *Block) Coinbase() []byte { return self.header.Coinbase }
|
||||
func (self *Block) Time() int64 { return int64(self.header.Time) }
|
||||
func (self *Block) GasLimit() *big.Int { return self.header.GasLimit }
|
||||
func (self *Block) GasUsed() *big.Int { return self.header.GasUsed }
|
||||
func (self *Block) Hash() []byte { return self.header.Hash() }
|
||||
func (self *Block) Trie() *ptrie.Trie { return ptrie.New(self.header.Root, ethutil.Config.Db) }
|
||||
func (self *Block) State() *state.StateDB { return state.New(self.Trie()) }
|
||||
func (self *Block) Size() ethutil.StorageSize { return ethutil.StorageSize(len(ethutil.Encode(self))) }
|
||||
func (self *Block) SetRoot(root []byte) { self.header.Root = root }
|
||||
|
||||
// Implement block.Pow
|
||||
func (self *Block) Difficulty() *big.Int { return self.header.Difficulty }
|
||||
func (self *Block) N() []byte { return self.header.Nonce }
|
||||
func (self *Block) HashNoNonce() []byte {
|
||||
return crypto.Sha3(ethutil.Encode(self.header.rlpData(false)))
|
||||
}
|
||||
|
||||
func (self *Block) String() string {
|
||||
return fmt.Sprintf(`BLOCK(%x): Size: %v {
|
||||
Header:
|
||||
[
|
||||
%v
|
||||
]
|
||||
Transactions:
|
||||
%v
|
||||
Uncles:
|
||||
%v
|
||||
}
|
||||
`, self.header.Hash(), self.Size(), self.header, self.transactions, self.uncles)
|
||||
}
|
||||
|
||||
func (self *Header) String() string {
|
||||
return fmt.Sprintf(`
|
||||
ParentHash: %x
|
||||
UncleHash: %x
|
||||
Coinbase: %x
|
||||
Root: %x
|
||||
TxSha %x
|
||||
ReceiptSha: %x
|
||||
Bloom: %x
|
||||
Difficulty: %v
|
||||
Number: %v
|
||||
GasLimit: %v
|
||||
GasUsed: %v
|
||||
Time: %v
|
||||
Extra: %v
|
||||
Nonce: %x
|
||||
`, self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra, self.Nonce)
|
||||
}
|
||||
|
||||
type Blocks []*Block
|
||||
|
||||
func (self Blocks) AsSet() ethutil.UniqueSet {
|
||||
set := make(ethutil.UniqueSet)
|
||||
for _, block := range self {
|
||||
set.Insert(block.Hash())
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
type BlockBy func(b1, b2 *Block) bool
|
||||
|
||||
func (self BlockBy) Sort(blocks Blocks) {
|
||||
@ -65,352 +264,4 @@ func (self blockSorter) Swap(i, j int) {
|
||||
}
|
||||
func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) }
|
||||
|
||||
func Number(b1, b2 *Block) bool { return b1.Number.Cmp(b2.Number) < 0 }
|
||||
|
||||
type Block struct {
|
||||
// Hash to the previous block
|
||||
PrevHash ethutil.Bytes
|
||||
// Uncles of this block
|
||||
Uncles Blocks
|
||||
UncleSha []byte
|
||||
// The coin base address
|
||||
Coinbase []byte
|
||||
// Block Trie state
|
||||
//state *ethutil.Trie
|
||||
state *state.StateDB
|
||||
// Difficulty for the current block
|
||||
Difficulty *big.Int
|
||||
// Creation time
|
||||
Time int64
|
||||
// The block number
|
||||
Number *big.Int
|
||||
// Gas limit
|
||||
GasLimit *big.Int
|
||||
// Gas used
|
||||
GasUsed *big.Int
|
||||
// Extra data
|
||||
Extra string
|
||||
// Block Nonce for verification
|
||||
Nonce ethutil.Bytes
|
||||
// List of transactions and/or contracts
|
||||
transactions Transactions
|
||||
receipts Receipts
|
||||
TxSha, ReceiptSha []byte
|
||||
LogsBloom []byte
|
||||
|
||||
Reward *big.Int
|
||||
}
|
||||
|
||||
func NewBlockFromBytes(raw []byte) *Block {
|
||||
block := &Block{}
|
||||
block.RlpDecode(raw)
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
// New block takes a raw encoded string
|
||||
func NewBlockFromRlpValue(rlpValue *ethutil.Value) *Block {
|
||||
block := &Block{}
|
||||
block.RlpValueDecode(rlpValue)
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func CreateBlock(root interface{},
|
||||
prevHash []byte,
|
||||
base []byte,
|
||||
Difficulty *big.Int,
|
||||
Nonce []byte,
|
||||
extra string) *Block {
|
||||
|
||||
block := &Block{
|
||||
PrevHash: prevHash,
|
||||
Coinbase: base,
|
||||
Difficulty: Difficulty,
|
||||
Nonce: Nonce,
|
||||
Time: time.Now().Unix(),
|
||||
Extra: extra,
|
||||
UncleSha: nil,
|
||||
GasUsed: new(big.Int),
|
||||
GasLimit: new(big.Int),
|
||||
}
|
||||
block.SetUncles([]*Block{})
|
||||
|
||||
block.state = state.New(trie.New(ethutil.Config.Db, root))
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
// Returns a hash of the block
|
||||
func (block *Block) Hash() ethutil.Bytes {
|
||||
return crypto.Sha3(ethutil.NewValue(block.header()).Encode())
|
||||
//return crypto.Sha3(block.Value().Encode())
|
||||
}
|
||||
|
||||
func (block *Block) HashNoNonce() []byte {
|
||||
return crypto.Sha3(ethutil.Encode(block.miningHeader()))
|
||||
}
|
||||
|
||||
func (block *Block) State() *state.StateDB {
|
||||
return block.state
|
||||
}
|
||||
|
||||
func (block *Block) Transactions() Transactions {
|
||||
return block.transactions
|
||||
}
|
||||
|
||||
func (block *Block) CalcGasLimit(parent *Block) *big.Int {
|
||||
if block.Number.Cmp(big.NewInt(0)) == 0 {
|
||||
return ethutil.BigPow(10, 6)
|
||||
}
|
||||
|
||||
// ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024
|
||||
|
||||
previous := new(big.Int).Mul(big.NewInt(1024-1), parent.GasLimit)
|
||||
current := new(big.Rat).Mul(new(big.Rat).SetInt(parent.GasUsed), big.NewRat(6, 5))
|
||||
curInt := new(big.Int).Div(current.Num(), current.Denom())
|
||||
|
||||
result := new(big.Int).Add(previous, curInt)
|
||||
result.Div(result, big.NewInt(1024))
|
||||
|
||||
min := big.NewInt(125000)
|
||||
|
||||
return ethutil.BigMax(min, result)
|
||||
}
|
||||
|
||||
func (block *Block) BlockInfo() BlockInfo {
|
||||
bi := BlockInfo{}
|
||||
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
|
||||
bi.RlpDecode(data)
|
||||
|
||||
return bi
|
||||
}
|
||||
|
||||
func (self *Block) GetTransaction(hash []byte) *Transaction {
|
||||
for _, tx := range self.transactions {
|
||||
if bytes.Compare(tx.Hash(), hash) == 0 {
|
||||
return tx
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sync the block's state and contract respectively
|
||||
func (block *Block) Sync() {
|
||||
block.state.Sync()
|
||||
}
|
||||
|
||||
func (block *Block) Undo() {
|
||||
// Sync the block state itself
|
||||
block.state.Reset()
|
||||
}
|
||||
|
||||
/////// Block Encoding
|
||||
func (block *Block) rlpReceipts() interface{} {
|
||||
// Marshal the transactions of this block
|
||||
encR := make([]interface{}, len(block.receipts))
|
||||
for i, r := range block.receipts {
|
||||
// Cast it to a string (safe)
|
||||
encR[i] = r.RlpData()
|
||||
}
|
||||
|
||||
return encR
|
||||
}
|
||||
|
||||
func (block *Block) rlpUncles() interface{} {
|
||||
// Marshal the transactions of this block
|
||||
uncles := make([]interface{}, len(block.Uncles))
|
||||
for i, uncle := range block.Uncles {
|
||||
// Cast it to a string (safe)
|
||||
uncles[i] = uncle.header()
|
||||
}
|
||||
|
||||
return uncles
|
||||
}
|
||||
|
||||
func (block *Block) SetUncles(uncles []*Block) {
|
||||
block.Uncles = uncles
|
||||
block.UncleSha = crypto.Sha3(ethutil.Encode(block.rlpUncles()))
|
||||
}
|
||||
|
||||
func (self *Block) SetReceipts(receipts Receipts) {
|
||||
self.receipts = receipts
|
||||
self.ReceiptSha = DeriveSha(receipts)
|
||||
self.LogsBloom = CreateBloom(receipts)
|
||||
}
|
||||
|
||||
func (self *Block) SetTransactions(txs Transactions) {
|
||||
self.transactions = txs
|
||||
self.TxSha = DeriveSha(txs)
|
||||
}
|
||||
|
||||
func (block *Block) Value() *ethutil.Value {
|
||||
return ethutil.NewValue([]interface{}{block.header(), block.transactions, block.rlpUncles()})
|
||||
}
|
||||
|
||||
func (block *Block) RlpEncode() []byte {
|
||||
// Encode a slice interface which contains the header and the list of
|
||||
// transactions.
|
||||
return block.Value().Encode()
|
||||
}
|
||||
|
||||
func (block *Block) RlpDecode(data []byte) {
|
||||
rlpValue := ethutil.NewValueFromBytes(data)
|
||||
block.RlpValueDecode(rlpValue)
|
||||
}
|
||||
|
||||
func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
|
||||
block.setHeader(decoder.Get(0))
|
||||
|
||||
// Tx list might be empty if this is an uncle. Uncles only have their
|
||||
// header set.
|
||||
if decoder.Get(1).IsNil() == false { // Yes explicitness
|
||||
//receipts := decoder.Get(1)
|
||||
//block.receipts = make([]*Receipt, receipts.Len())
|
||||
txs := decoder.Get(1)
|
||||
block.transactions = make(Transactions, txs.Len())
|
||||
for i := 0; i < txs.Len(); i++ {
|
||||
block.transactions[i] = NewTransactionFromValue(txs.Get(i))
|
||||
//receipt := NewRecieptFromValue(receipts.Get(i))
|
||||
//block.transactions[i] = receipt.Tx
|
||||
//block.receipts[i] = receipt
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if decoder.Get(2).IsNil() == false { // Yes explicitness
|
||||
uncles := decoder.Get(2)
|
||||
block.Uncles = make([]*Block, uncles.Len())
|
||||
for i := 0; i < uncles.Len(); i++ {
|
||||
block.Uncles[i] = NewUncleBlockFromValue(uncles.Get(i))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (self *Block) setHeader(header *ethutil.Value) {
|
||||
self.PrevHash = header.Get(0).Bytes()
|
||||
self.UncleSha = header.Get(1).Bytes()
|
||||
self.Coinbase = header.Get(2).Bytes()
|
||||
self.state = state.New(trie.New(ethutil.Config.Db, header.Get(3).Val))
|
||||
self.TxSha = header.Get(4).Bytes()
|
||||
self.ReceiptSha = header.Get(5).Bytes()
|
||||
self.LogsBloom = header.Get(6).Bytes()
|
||||
self.Difficulty = header.Get(7).BigInt()
|
||||
self.Number = header.Get(8).BigInt()
|
||||
self.GasLimit = header.Get(9).BigInt()
|
||||
self.GasUsed = header.Get(10).BigInt()
|
||||
self.Time = int64(header.Get(11).BigInt().Uint64())
|
||||
self.Extra = header.Get(12).Str()
|
||||
self.Nonce = header.Get(13).Bytes()
|
||||
}
|
||||
|
||||
func NewUncleBlockFromValue(header *ethutil.Value) *Block {
|
||||
block := &Block{}
|
||||
block.setHeader(header)
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func (block *Block) Trie() *trie.Trie {
|
||||
return block.state.Trie
|
||||
}
|
||||
|
||||
func (block *Block) Root() interface{} {
|
||||
return block.state.Root()
|
||||
}
|
||||
|
||||
func (block *Block) Diff() *big.Int {
|
||||
return block.Difficulty
|
||||
}
|
||||
|
||||
func (self *Block) Receipts() []*Receipt {
|
||||
return self.receipts
|
||||
}
|
||||
|
||||
func (block *Block) miningHeader() []interface{} {
|
||||
return []interface{}{
|
||||
// Sha of the previous block
|
||||
block.PrevHash,
|
||||
// Sha of uncles
|
||||
block.UncleSha,
|
||||
// Coinbase address
|
||||
block.Coinbase,
|
||||
// root state
|
||||
block.Root(),
|
||||
// tx root
|
||||
block.TxSha,
|
||||
// Sha of tx
|
||||
block.ReceiptSha,
|
||||
// Bloom
|
||||
block.LogsBloom,
|
||||
// Current block Difficulty
|
||||
block.Difficulty,
|
||||
// The block number
|
||||
block.Number,
|
||||
// Block upper gas bound
|
||||
block.GasLimit,
|
||||
// Block gas used
|
||||
block.GasUsed,
|
||||
// Time the block was found?
|
||||
block.Time,
|
||||
// Extra data
|
||||
block.Extra,
|
||||
}
|
||||
}
|
||||
|
||||
func (block *Block) header() []interface{} {
|
||||
return append(block.miningHeader(), block.Nonce)
|
||||
}
|
||||
|
||||
func (block *Block) String() string {
|
||||
return fmt.Sprintf(`
|
||||
BLOCK(%x): Size: %v
|
||||
PrevHash: %x
|
||||
UncleSha: %x
|
||||
Coinbase: %x
|
||||
Root: %x
|
||||
TxSha %x
|
||||
ReceiptSha: %x
|
||||
Bloom: %x
|
||||
Difficulty: %v
|
||||
Number: %v
|
||||
MaxLimit: %v
|
||||
GasUsed: %v
|
||||
Time: %v
|
||||
Extra: %v
|
||||
Nonce: %x
|
||||
NumTx: %v
|
||||
`,
|
||||
block.Hash(),
|
||||
block.Size(),
|
||||
block.PrevHash,
|
||||
block.UncleSha,
|
||||
block.Coinbase,
|
||||
block.Root(),
|
||||
block.TxSha,
|
||||
block.ReceiptSha,
|
||||
block.LogsBloom,
|
||||
block.Difficulty,
|
||||
block.Number,
|
||||
block.GasLimit,
|
||||
block.GasUsed,
|
||||
block.Time,
|
||||
block.Extra,
|
||||
block.Nonce,
|
||||
len(block.transactions),
|
||||
)
|
||||
}
|
||||
|
||||
func (self *Block) Size() ethutil.StorageSize {
|
||||
return ethutil.StorageSize(len(self.RlpEncode()))
|
||||
}
|
||||
|
||||
// Implement RlpEncodable
|
||||
func (self *Block) RlpData() interface{} {
|
||||
return self.Value().Val
|
||||
}
|
||||
|
||||
// Implement pow.Block
|
||||
func (self *Block) N() []byte { return self.Nonce }
|
||||
func Number(b1, b2 *Block) bool { return b1.Header().Number.Cmp(b2.Header().Number) < 0 }
|
||||
|
23
core/types/block_test.go
Normal file
23
core/types/block_test.go
Normal file
@ -0,0 +1,23 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
|
||||
ethutil.Config.Db, _ = ethdb.NewMemDatabase()
|
||||
}
|
||||
|
||||
func TestNewBlock(t *testing.T) {
|
||||
block := GenesisBlock()
|
||||
data := ethutil.Encode(block)
|
||||
|
||||
var genesis Block
|
||||
err := rlp.Decode(bytes.NewReader(data), &genesis)
|
||||
}
|
@ -2,7 +2,7 @@ package types
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/ptrie"
|
||||
)
|
||||
|
||||
type DerivableList interface {
|
||||
@ -11,10 +11,10 @@ type DerivableList interface {
|
||||
}
|
||||
|
||||
func DeriveSha(list DerivableList) []byte {
|
||||
trie := trie.New(ethutil.Config.Db, "")
|
||||
trie := ptrie.New(nil, ethutil.Config.Db)
|
||||
for i := 0; i < list.Len(); i++ {
|
||||
trie.Update(string(ethutil.NewValue(i).Encode()), string(list.GetRlp(i)))
|
||||
trie.Update(ethutil.Encode(i), list.GetRlp(i))
|
||||
}
|
||||
|
||||
return trie.GetRoot()
|
||||
return trie.Root()
|
||||
}
|
||||
|
@ -1,42 +1,37 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/obscuren/secp256k1-go"
|
||||
)
|
||||
|
||||
var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
func IsContractAddr(addr []byte) bool {
|
||||
return len(addr) == 0
|
||||
//return bytes.Compare(addr, ContractAddr) == 0
|
||||
}
|
||||
|
||||
type Transaction struct {
|
||||
Nonce uint64
|
||||
AccountNonce uint64
|
||||
Price *big.Int
|
||||
GasLimit *big.Int
|
||||
Recipient []byte
|
||||
Value *big.Int
|
||||
Gas *big.Int
|
||||
GasPrice *big.Int
|
||||
Data []byte
|
||||
v byte
|
||||
r, s []byte
|
||||
|
||||
// Indicates whether this tx is a contract creation transaction
|
||||
contractCreation bool
|
||||
Amount *big.Int
|
||||
Payload []byte
|
||||
V uint64
|
||||
R, S []byte
|
||||
}
|
||||
|
||||
func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction {
|
||||
return &Transaction{Recipient: nil, Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true}
|
||||
func NewContractCreationTx(Amount, gasAmount, price *big.Int, data []byte) *Transaction {
|
||||
return NewTransactionMessage(nil, Amount, gasAmount, price, data)
|
||||
}
|
||||
|
||||
func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction {
|
||||
return &Transaction{Recipient: to, Value: value, GasPrice: gasPrice, Gas: gas, Data: data, contractCreation: IsContractAddr(to)}
|
||||
func NewTransactionMessage(to []byte, Amount, gasAmount, price *big.Int, data []byte) *Transaction {
|
||||
return &Transaction{Recipient: to, Amount: Amount, Price: price, GasLimit: gasAmount, Payload: data}
|
||||
}
|
||||
|
||||
func NewTransactionFromBytes(data []byte) *Transaction {
|
||||
@ -46,46 +41,55 @@ func NewTransactionFromBytes(data []byte) *Transaction {
|
||||
return tx
|
||||
}
|
||||
|
||||
func NewTransactionFromValue(val *ethutil.Value) *Transaction {
|
||||
func NewTransactionFromAmount(val *ethutil.Value) *Transaction {
|
||||
tx := &Transaction{}
|
||||
tx.RlpValueDecode(val)
|
||||
|
||||
return tx
|
||||
}
|
||||
|
||||
func (self *Transaction) GasValue() *big.Int {
|
||||
return new(big.Int).Mul(self.Gas, self.GasPrice)
|
||||
}
|
||||
|
||||
func (self *Transaction) TotalValue() *big.Int {
|
||||
v := self.GasValue()
|
||||
return v.Add(v, self.Value)
|
||||
}
|
||||
|
||||
func (tx *Transaction) Hash() []byte {
|
||||
data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data}
|
||||
data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload}
|
||||
|
||||
return crypto.Sha3(ethutil.NewValue(data).Encode())
|
||||
return crypto.Sha3(ethutil.Encode(data))
|
||||
}
|
||||
|
||||
func (tx *Transaction) CreatesContract() bool {
|
||||
return tx.contractCreation
|
||||
func (self *Transaction) Data() []byte {
|
||||
return self.Payload
|
||||
}
|
||||
|
||||
/* Deprecated */
|
||||
func (tx *Transaction) IsContract() bool {
|
||||
return tx.CreatesContract()
|
||||
func (self *Transaction) Gas() *big.Int {
|
||||
return self.GasLimit
|
||||
}
|
||||
|
||||
func (tx *Transaction) CreationAddress(state *state.StateDB) []byte {
|
||||
// Generate a new address
|
||||
return crypto.Sha3(ethutil.NewValue([]interface{}{tx.Sender(), tx.Nonce}).Encode())[12:]
|
||||
func (self *Transaction) GasPrice() *big.Int {
|
||||
return self.Price
|
||||
}
|
||||
|
||||
func (self *Transaction) Value() *big.Int {
|
||||
return self.Amount
|
||||
}
|
||||
|
||||
func (self *Transaction) Nonce() uint64 {
|
||||
return self.AccountNonce
|
||||
}
|
||||
|
||||
func (self *Transaction) SetNonce(AccountNonce uint64) {
|
||||
self.AccountNonce = AccountNonce
|
||||
}
|
||||
|
||||
func (self *Transaction) From() []byte {
|
||||
return self.sender()
|
||||
}
|
||||
|
||||
func (self *Transaction) To() []byte {
|
||||
return self.Recipient
|
||||
}
|
||||
|
||||
func (tx *Transaction) Curve() (v byte, r []byte, s []byte) {
|
||||
v = tx.v
|
||||
r = ethutil.LeftPadBytes(tx.r, 32)
|
||||
s = ethutil.LeftPadBytes(tx.s, 32)
|
||||
v = byte(tx.V)
|
||||
r = ethutil.LeftPadBytes(tx.R, 32)
|
||||
s = ethutil.LeftPadBytes(tx.S, 32)
|
||||
|
||||
return
|
||||
}
|
||||
@ -106,18 +110,18 @@ func (tx *Transaction) PublicKey() []byte {
|
||||
sig := append(r, s...)
|
||||
sig = append(sig, v-27)
|
||||
|
||||
pubkey := crypto.Ecrecover(append(hash, sig...))
|
||||
//pubkey, _ := secp256k1.RecoverPubkey(hash, sig)
|
||||
//pubkey := crypto.Ecrecover(append(hash, sig...))
|
||||
pubkey, _ := secp256k1.RecoverPubkey(hash, sig)
|
||||
|
||||
return pubkey
|
||||
}
|
||||
|
||||
func (tx *Transaction) Sender() []byte {
|
||||
func (tx *Transaction) sender() []byte {
|
||||
pubkey := tx.PublicKey()
|
||||
|
||||
// Validate the returned key.
|
||||
// Return nil if public key isn't in full format
|
||||
if len(pubkey) != 0 && pubkey[0] != 4 {
|
||||
if len(pubkey) == 0 || pubkey[0] != 4 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -128,48 +132,37 @@ func (tx *Transaction) Sign(privk []byte) error {
|
||||
|
||||
sig := tx.Signature(privk)
|
||||
|
||||
tx.r = sig[:32]
|
||||
tx.s = sig[32:64]
|
||||
tx.v = sig[64] + 27
|
||||
tx.R = sig[:32]
|
||||
tx.S = sig[32:64]
|
||||
tx.V = uint64(sig[64] + 27)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpData() interface{} {
|
||||
data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data}
|
||||
data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload}
|
||||
|
||||
// TODO Remove prefixing zero's
|
||||
|
||||
return append(data, tx.v, new(big.Int).SetBytes(tx.r).Bytes(), new(big.Int).SetBytes(tx.s).Bytes())
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpValue() *ethutil.Value {
|
||||
return ethutil.NewValue(tx.RlpData())
|
||||
return append(data, tx.V, new(big.Int).SetBytes(tx.R).Bytes(), new(big.Int).SetBytes(tx.S).Bytes())
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpEncode() []byte {
|
||||
return tx.RlpValue().Encode()
|
||||
return ethutil.Encode(tx)
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpDecode(data []byte) {
|
||||
tx.RlpValueDecode(ethutil.NewValueFromBytes(data))
|
||||
rlp.Decode(bytes.NewReader(data), tx)
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
|
||||
tx.Nonce = decoder.Get(0).Uint()
|
||||
tx.GasPrice = decoder.Get(1).BigInt()
|
||||
tx.Gas = decoder.Get(2).BigInt()
|
||||
tx.AccountNonce = decoder.Get(0).Uint()
|
||||
tx.Price = decoder.Get(1).BigInt()
|
||||
tx.GasLimit = decoder.Get(2).BigInt()
|
||||
tx.Recipient = decoder.Get(3).Bytes()
|
||||
tx.Value = decoder.Get(4).BigInt()
|
||||
tx.Data = decoder.Get(5).Bytes()
|
||||
tx.v = byte(decoder.Get(6).Uint())
|
||||
|
||||
tx.r = decoder.Get(7).Bytes()
|
||||
tx.s = decoder.Get(8).Bytes()
|
||||
|
||||
if IsContractAddr(tx.Recipient) {
|
||||
tx.contractCreation = true
|
||||
}
|
||||
tx.Amount = decoder.Get(4).BigInt()
|
||||
tx.Payload = decoder.Get(5).Bytes()
|
||||
tx.V = decoder.Get(6).Uint()
|
||||
tx.R = decoder.Get(7).Bytes()
|
||||
tx.S = decoder.Get(8).Bytes()
|
||||
}
|
||||
|
||||
func (tx *Transaction) String() string {
|
||||
@ -180,25 +173,28 @@ func (tx *Transaction) String() string {
|
||||
To: %x
|
||||
Nonce: %v
|
||||
GasPrice: %v
|
||||
Gas: %v
|
||||
GasLimit %v
|
||||
Value: %v
|
||||
Data: 0x%x
|
||||
V: 0x%x
|
||||
R: 0x%x
|
||||
S: 0x%x
|
||||
`,
|
||||
Hex: %x
|
||||
`,
|
||||
tx.Hash(),
|
||||
len(tx.Recipient) == 0,
|
||||
tx.Sender(),
|
||||
tx.Recipient,
|
||||
tx.Nonce,
|
||||
tx.GasPrice,
|
||||
tx.Gas,
|
||||
tx.Value,
|
||||
tx.Data,
|
||||
tx.v,
|
||||
tx.r,
|
||||
tx.s)
|
||||
tx.From(),
|
||||
tx.To(),
|
||||
tx.AccountNonce,
|
||||
tx.Price,
|
||||
tx.GasLimit,
|
||||
tx.Amount,
|
||||
tx.Payload,
|
||||
tx.V,
|
||||
tx.R,
|
||||
tx.S,
|
||||
ethutil.Encode(tx),
|
||||
)
|
||||
}
|
||||
|
||||
// Transaction slice type for basic sorting
|
||||
@ -221,5 +217,5 @@ func (s Transactions) GetRlp(i int) []byte { return ethutil.Rlp(s[i]) }
|
||||
type TxByNonce struct{ Transactions }
|
||||
|
||||
func (s TxByNonce) Less(i, j int) bool {
|
||||
return s.Transactions[i].Nonce < s.Transactions[j].Nonce
|
||||
return s.Transactions[i].AccountNonce < s.Transactions[j].AccountNonce
|
||||
}
|
||||
|
@ -11,28 +11,28 @@ import (
|
||||
type VMEnv struct {
|
||||
state *state.StateDB
|
||||
block *types.Block
|
||||
tx *types.Transaction
|
||||
msg Message
|
||||
depth int
|
||||
}
|
||||
|
||||
func NewEnv(state *state.StateDB, tx *types.Transaction, block *types.Block) *VMEnv {
|
||||
func NewEnv(state *state.StateDB, msg Message, block *types.Block) *VMEnv {
|
||||
return &VMEnv{
|
||||
state: state,
|
||||
block: block,
|
||||
tx: tx,
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *VMEnv) Origin() []byte { return self.tx.Sender() }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number }
|
||||
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
|
||||
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
|
||||
func (self *VMEnv) Time() int64 { return self.block.Time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
|
||||
func (self *VMEnv) Origin() []byte { return self.msg.From() }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() }
|
||||
func (self *VMEnv) PrevHash() []byte { return self.block.ParentHash() }
|
||||
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase() }
|
||||
func (self *VMEnv) Time() int64 { return self.block.Time() }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() }
|
||||
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
|
||||
func (self *VMEnv) Value() *big.Int { return self.tx.Value }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit() }
|
||||
func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
|
||||
func (self *VMEnv) State() *state.StateDB { return self.state }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit }
|
||||
func (self *VMEnv) Depth() int { return self.depth }
|
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||
func (self *VMEnv) AddLog(log state.Log) {
|
||||
@ -43,9 +43,7 @@ func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
|
||||
}
|
||||
|
||||
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *Execution {
|
||||
evm := vm.New(self, vm.DebugVmTy)
|
||||
|
||||
return NewExecution(evm, addr, data, gas, price, value)
|
||||
return NewExecution(self, addr, data, gas, price, value)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Call(me vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
|
249
eth/backend.go
Normal file
249
eth/backend.go
Normal file
@ -0,0 +1,249 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
ethlogger "github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/pow/ezp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
)
|
||||
|
||||
const (
|
||||
seedNodeAddress = "poc-7.ethdev.com:30300"
|
||||
)
|
||||
|
||||
var logger = ethlogger.NewLogger("SERV")
|
||||
|
||||
type Ethereum struct {
|
||||
// Channel for shutting down the ethereum
|
||||
shutdownChan chan bool
|
||||
quit chan bool
|
||||
|
||||
// DB interface
|
||||
db ethutil.Database
|
||||
blacklist p2p.Blacklist
|
||||
|
||||
//*** SERVICES ***
|
||||
// State manager for processing new blocks and managing the over all states
|
||||
blockManager *core.BlockManager
|
||||
txPool *core.TxPool
|
||||
chainManager *core.ChainManager
|
||||
blockPool *BlockPool
|
||||
whisper *whisper.Whisper
|
||||
|
||||
server *p2p.Server
|
||||
eventMux *event.TypeMux
|
||||
txSub event.Subscription
|
||||
blockSub event.Subscription
|
||||
|
||||
RpcServer *rpc.JsonRpcServer
|
||||
keyManager *crypto.KeyManager
|
||||
|
||||
clientIdentity p2p.ClientIdentity
|
||||
|
||||
synclock sync.Mutex
|
||||
syncGroup sync.WaitGroup
|
||||
|
||||
Mining bool
|
||||
}
|
||||
|
||||
func New(db ethutil.Database, identity p2p.ClientIdentity, keyManager *crypto.KeyManager, nat p2p.NAT, port string, maxPeers int) (*Ethereum, error) {
|
||||
|
||||
saveProtocolVersion(db)
|
||||
ethutil.Config.Db = db
|
||||
|
||||
eth := &Ethereum{
|
||||
shutdownChan: make(chan bool),
|
||||
quit: make(chan bool),
|
||||
db: db,
|
||||
keyManager: keyManager,
|
||||
clientIdentity: identity,
|
||||
blacklist: p2p.NewBlacklist(),
|
||||
eventMux: &event.TypeMux{},
|
||||
}
|
||||
|
||||
eth.chainManager = core.NewChainManager(eth.EventMux())
|
||||
eth.txPool = core.NewTxPool(eth.chainManager, eth.EventMux())
|
||||
eth.blockManager = core.NewBlockManager(eth.txPool, eth.chainManager, eth.EventMux())
|
||||
eth.chainManager.SetProcessor(eth.blockManager)
|
||||
eth.whisper = whisper.New()
|
||||
|
||||
hasBlock := eth.chainManager.HasBlock
|
||||
insertChain := eth.chainManager.InsertChain
|
||||
eth.blockPool = NewBlockPool(hasBlock, insertChain, ezp.Verify)
|
||||
|
||||
// Start services
|
||||
eth.txPool.Start()
|
||||
|
||||
ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool)
|
||||
protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()}
|
||||
|
||||
server := &p2p.Server{
|
||||
Identity: identity,
|
||||
MaxPeers: maxPeers,
|
||||
Protocols: protocols,
|
||||
ListenAddr: ":" + port,
|
||||
Blacklist: eth.blacklist,
|
||||
NAT: nat,
|
||||
}
|
||||
|
||||
eth.server = server
|
||||
|
||||
return eth, nil
|
||||
}
|
||||
|
||||
func (s *Ethereum) KeyManager() *crypto.KeyManager {
|
||||
return s.keyManager
|
||||
}
|
||||
|
||||
func (s *Ethereum) ClientIdentity() p2p.ClientIdentity {
|
||||
return s.clientIdentity
|
||||
}
|
||||
|
||||
func (s *Ethereum) ChainManager() *core.ChainManager {
|
||||
return s.chainManager
|
||||
}
|
||||
|
||||
func (s *Ethereum) BlockManager() *core.BlockManager {
|
||||
return s.blockManager
|
||||
}
|
||||
|
||||
func (s *Ethereum) TxPool() *core.TxPool {
|
||||
return s.txPool
|
||||
}
|
||||
|
||||
func (s *Ethereum) BlockPool() *BlockPool {
|
||||
return s.blockPool
|
||||
}
|
||||
|
||||
func (s *Ethereum) Whisper() *whisper.Whisper {
|
||||
return s.whisper
|
||||
}
|
||||
|
||||
func (s *Ethereum) EventMux() *event.TypeMux {
|
||||
return s.eventMux
|
||||
}
|
||||
func (self *Ethereum) Db() ethutil.Database {
|
||||
return self.db
|
||||
}
|
||||
|
||||
func (s *Ethereum) IsMining() bool {
|
||||
return s.Mining
|
||||
}
|
||||
|
||||
func (s *Ethereum) IsListening() bool {
|
||||
// XXX TODO
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Ethereum) PeerCount() int {
|
||||
return s.server.PeerCount()
|
||||
}
|
||||
|
||||
func (s *Ethereum) Peers() []*p2p.Peer {
|
||||
return s.server.Peers()
|
||||
}
|
||||
|
||||
func (s *Ethereum) MaxPeers() int {
|
||||
return s.server.MaxPeers
|
||||
}
|
||||
|
||||
// Start the ethereum
|
||||
func (s *Ethereum) Start(seed bool) error {
|
||||
err := s.server.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.blockPool.Start()
|
||||
s.whisper.Start()
|
||||
|
||||
// broadcast transactions
|
||||
s.txSub = s.eventMux.Subscribe(core.TxPreEvent{})
|
||||
go s.txBroadcastLoop()
|
||||
|
||||
// broadcast mined blocks
|
||||
s.blockSub = s.eventMux.Subscribe(core.NewMinedBlockEvent{})
|
||||
go s.blockBroadcastLoop()
|
||||
|
||||
// TODO: read peers here
|
||||
if seed {
|
||||
logger.Infof("Connect to seed node %v", seedNodeAddress)
|
||||
if err := s.SuggestPeer(seedNodeAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logger.Infoln("Server started")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Ethereum) SuggestPeer(addr string) error {
|
||||
netaddr, err := net.ResolveTCPAddr("tcp", addr)
|
||||
if err != nil {
|
||||
logger.Errorf("couldn't resolve %s:", addr, err)
|
||||
return err
|
||||
}
|
||||
|
||||
self.server.SuggestPeer(netaddr.IP, netaddr.Port, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Ethereum) Stop() {
|
||||
// Close the database
|
||||
defer s.db.Close()
|
||||
|
||||
close(s.quit)
|
||||
|
||||
s.txSub.Unsubscribe() // quits txBroadcastLoop
|
||||
s.blockSub.Unsubscribe() // quits blockBroadcastLoop
|
||||
|
||||
if s.RpcServer != nil {
|
||||
s.RpcServer.Stop()
|
||||
}
|
||||
s.txPool.Stop()
|
||||
s.eventMux.Stop()
|
||||
s.blockPool.Stop()
|
||||
s.whisper.Stop()
|
||||
|
||||
logger.Infoln("Server stopped")
|
||||
close(s.shutdownChan)
|
||||
}
|
||||
|
||||
// This function will wait for a shutdown and resumes main thread execution
|
||||
func (s *Ethereum) WaitForShutdown() {
|
||||
<-s.shutdownChan
|
||||
}
|
||||
|
||||
// now tx broadcasting is taken out of txPool
|
||||
// handled here via subscription, efficiency?
|
||||
func (self *Ethereum) txBroadcastLoop() {
|
||||
// automatically stops if unsubscribe
|
||||
for obj := range self.txSub.Chan() {
|
||||
event := obj.(core.TxPreEvent)
|
||||
self.server.Broadcast("eth", TxMsg, []interface{}{event.Tx.RlpData()})
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Ethereum) blockBroadcastLoop() {
|
||||
// automatically stops if unsubscribe
|
||||
for obj := range self.txSub.Chan() {
|
||||
event := obj.(core.NewMinedBlockEvent)
|
||||
self.server.Broadcast("eth", NewBlockMsg, event.Block.RlpData())
|
||||
}
|
||||
}
|
||||
|
||||
func saveProtocolVersion(db ethutil.Database) {
|
||||
d, _ := db.Get([]byte("ProtocolVersion"))
|
||||
protocolVersion := ethutil.NewValue(d).Uint()
|
||||
|
||||
if protocolVersion == 0 {
|
||||
db.Put([]byte("ProtocolVersion"), ethutil.NewValue(ProtocolVersion).Bytes())
|
||||
}
|
||||
}
|
1015
eth/block_pool.go
Normal file
1015
eth/block_pool.go
Normal file
File diff suppressed because it is too large
Load Diff
198
eth/block_pool_test.go
Normal file
198
eth/block_pool_test.go
Normal file
@ -0,0 +1,198 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
ethlogger "github.com/ethereum/go-ethereum/logger"
|
||||
)
|
||||
|
||||
var sys = ethlogger.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlogger.LogLevel(ethlogger.DebugDetailLevel))
|
||||
|
||||
type testChainManager struct {
|
||||
knownBlock func(hash []byte) bool
|
||||
addBlock func(*types.Block) error
|
||||
checkPoW func(*types.Block) bool
|
||||
}
|
||||
|
||||
func (self *testChainManager) KnownBlock(hash []byte) bool {
|
||||
if self.knownBlock != nil {
|
||||
return self.knownBlock(hash)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *testChainManager) AddBlock(block *types.Block) error {
|
||||
if self.addBlock != nil {
|
||||
return self.addBlock(block)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *testChainManager) CheckPoW(block *types.Block) bool {
|
||||
if self.checkPoW != nil {
|
||||
return self.checkPoW(block)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func knownBlock(hashes ...[]byte) (f func([]byte) bool) {
|
||||
f = func(block []byte) bool {
|
||||
for _, hash := range hashes {
|
||||
if bytes.Compare(block, hash) == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func addBlock(hashes ...[]byte) (f func(*types.Block) error) {
|
||||
f = func(block *types.Block) error {
|
||||
for _, hash := range hashes {
|
||||
if bytes.Compare(block.Hash(), hash) == 0 {
|
||||
return fmt.Errorf("invalid by test")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func checkPoW(hashes ...[]byte) (f func(*types.Block) bool) {
|
||||
f = func(block *types.Block) bool {
|
||||
for _, hash := range hashes {
|
||||
if bytes.Compare(block.Hash(), hash) == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func newTestChainManager(knownBlocks [][]byte, invalidBlocks [][]byte, invalidPoW [][]byte) *testChainManager {
|
||||
return &testChainManager{
|
||||
knownBlock: knownBlock(knownBlocks...),
|
||||
addBlock: addBlock(invalidBlocks...),
|
||||
checkPoW: checkPoW(invalidPoW...),
|
||||
}
|
||||
}
|
||||
|
||||
type intToHash map[int][]byte
|
||||
|
||||
type hashToInt map[string]int
|
||||
|
||||
type testHashPool struct {
|
||||
intToHash
|
||||
hashToInt
|
||||
}
|
||||
|
||||
func newHash(i int) []byte {
|
||||
return crypto.Sha3([]byte(string(i)))
|
||||
}
|
||||
|
||||
func newTestBlockPool(knownBlockIndexes []int, invalidBlockIndexes []int, invalidPoWIndexes []int) (hashPool *testHashPool, blockPool *BlockPool) {
|
||||
hashPool = &testHashPool{make(intToHash), make(hashToInt)}
|
||||
knownBlocks := hashPool.indexesToHashes(knownBlockIndexes)
|
||||
invalidBlocks := hashPool.indexesToHashes(invalidBlockIndexes)
|
||||
invalidPoW := hashPool.indexesToHashes(invalidPoWIndexes)
|
||||
blockPool = NewBlockPool(newTestChainManager(knownBlocks, invalidBlocks, invalidPoW))
|
||||
return
|
||||
}
|
||||
|
||||
func (self *testHashPool) indexesToHashes(indexes []int) (hashes [][]byte) {
|
||||
for _, i := range indexes {
|
||||
hash, found := self.intToHash[i]
|
||||
if !found {
|
||||
hash = newHash(i)
|
||||
self.intToHash[i] = hash
|
||||
self.hashToInt[string(hash)] = i
|
||||
}
|
||||
hashes = append(hashes, hash)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *testHashPool) hashesToIndexes(hashes [][]byte) (indexes []int) {
|
||||
for _, hash := range hashes {
|
||||
i, found := self.hashToInt[string(hash)]
|
||||
if !found {
|
||||
i = -1
|
||||
}
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type protocolChecker struct {
|
||||
blockHashesRequests []int
|
||||
blocksRequests [][]int
|
||||
invalidBlocks []error
|
||||
hashPool *testHashPool
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// -1 is special: not found (a hash never seen)
|
||||
func (self *protocolChecker) requestBlockHashesCallBack() (requestBlockHashesCallBack func([]byte) error) {
|
||||
requestBlockHashesCallBack = func(hash []byte) error {
|
||||
indexes := self.hashPool.hashesToIndexes([][]byte{hash})
|
||||
self.lock.Lock()
|
||||
defer self.lock.Unlock()
|
||||
self.blockHashesRequests = append(self.blockHashesRequests, indexes[0])
|
||||
return nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *protocolChecker) requestBlocksCallBack() (requestBlocksCallBack func([][]byte) error) {
|
||||
requestBlocksCallBack = func(hashes [][]byte) error {
|
||||
indexes := self.hashPool.hashesToIndexes(hashes)
|
||||
self.lock.Lock()
|
||||
defer self.lock.Unlock()
|
||||
self.blocksRequests = append(self.blocksRequests, indexes)
|
||||
return nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *protocolChecker) invalidBlockCallBack() (invalidBlockCallBack func(error)) {
|
||||
invalidBlockCallBack = func(err error) {
|
||||
self.invalidBlocks = append(self.invalidBlocks, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestAddPeer(t *testing.T) {
|
||||
ethlogger.AddLogSystem(sys)
|
||||
knownBlockIndexes := []int{0, 1}
|
||||
invalidBlockIndexes := []int{2, 3}
|
||||
invalidPoWIndexes := []int{4, 5}
|
||||
hashPool, blockPool := newTestBlockPool(knownBlockIndexes, invalidBlockIndexes, invalidPoWIndexes)
|
||||
// TODO:
|
||||
// hashPool, blockPool, blockChainChecker = newTestBlockPool(knownBlockIndexes, invalidBlockIndexes, invalidPoWIndexes)
|
||||
peer0 := &protocolChecker{
|
||||
// blockHashesRequests: make([]int),
|
||||
// blocksRequests: make([][]int),
|
||||
// invalidBlocks: make([]error),
|
||||
hashPool: hashPool,
|
||||
}
|
||||
best := blockPool.AddPeer(ethutil.Big1, newHash(100), "0",
|
||||
peer0.requestBlockHashesCallBack(),
|
||||
peer0.requestBlocksCallBack(),
|
||||
peer0.invalidBlockCallBack(),
|
||||
)
|
||||
if !best {
|
||||
t.Errorf("peer not accepted as best")
|
||||
}
|
||||
blockPool.Stop()
|
||||
|
||||
}
|
71
eth/error.go
Normal file
71
eth/error.go
Normal file
@ -0,0 +1,71 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
ErrMsgTooLarge = iota
|
||||
ErrDecode
|
||||
ErrInvalidMsgCode
|
||||
ErrProtocolVersionMismatch
|
||||
ErrNetworkIdMismatch
|
||||
ErrGenesisBlockMismatch
|
||||
ErrNoStatusMsg
|
||||
ErrExtraStatusMsg
|
||||
ErrInvalidBlock
|
||||
ErrInvalidPoW
|
||||
ErrUnrequestedBlock
|
||||
)
|
||||
|
||||
var errorToString = map[int]string{
|
||||
ErrMsgTooLarge: "Message too long",
|
||||
ErrDecode: "Invalid message",
|
||||
ErrInvalidMsgCode: "Invalid message code",
|
||||
ErrProtocolVersionMismatch: "Protocol version mismatch",
|
||||
ErrNetworkIdMismatch: "NetworkId mismatch",
|
||||
ErrGenesisBlockMismatch: "Genesis block mismatch",
|
||||
ErrNoStatusMsg: "No status message",
|
||||
ErrExtraStatusMsg: "Extra status message",
|
||||
ErrInvalidBlock: "Invalid block",
|
||||
ErrInvalidPoW: "Invalid PoW",
|
||||
ErrUnrequestedBlock: "Unrequested block",
|
||||
}
|
||||
|
||||
type protocolError struct {
|
||||
Code int
|
||||
fatal bool
|
||||
message string
|
||||
format string
|
||||
params []interface{}
|
||||
// size int
|
||||
}
|
||||
|
||||
func newProtocolError(code int, format string, params ...interface{}) *protocolError {
|
||||
return &protocolError{Code: code, format: format, params: params}
|
||||
}
|
||||
|
||||
func ProtocolError(code int, format string, params ...interface{}) (err *protocolError) {
|
||||
err = newProtocolError(code, format, params...)
|
||||
// report(err)
|
||||
return
|
||||
}
|
||||
|
||||
func (self protocolError) Error() (message string) {
|
||||
message = self.message
|
||||
if message == "" {
|
||||
message, ok := errorToString[self.Code]
|
||||
if !ok {
|
||||
panic("invalid error code")
|
||||
}
|
||||
if self.format != "" {
|
||||
message += ": " + fmt.Sprintf(self.format, self.params...)
|
||||
}
|
||||
self.message = message
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *protocolError) Fatal() bool {
|
||||
return self.fatal
|
||||
}
|
23
eth/peer_util.go
Normal file
23
eth/peer_util.go
Normal file
@ -0,0 +1,23 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
)
|
||||
|
||||
func WritePeers(path string, addresses []string) {
|
||||
if len(addresses) > 0 {
|
||||
data, _ := json.MarshalIndent(addresses, "", " ")
|
||||
ethutil.WriteFile(path, data)
|
||||
}
|
||||
}
|
||||
|
||||
func ReadPeers(path string) (ips []string, err error) {
|
||||
var data string
|
||||
data, err = ethutil.ReadAllFile(path)
|
||||
if err != nil {
|
||||
json.Unmarshal([]byte(data), &ips)
|
||||
}
|
||||
return
|
||||
}
|
319
eth/protocol.go
Normal file
319
eth/protocol.go
Normal file
@ -0,0 +1,319 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
const (
|
||||
ProtocolVersion = 49
|
||||
NetworkId = 0
|
||||
ProtocolLength = uint64(8)
|
||||
ProtocolMaxMsgSize = 10 * 1024 * 1024
|
||||
)
|
||||
|
||||
// eth protocol message codes
|
||||
const (
|
||||
StatusMsg = iota
|
||||
GetTxMsg // unused
|
||||
TxMsg
|
||||
GetBlockHashesMsg
|
||||
BlockHashesMsg
|
||||
GetBlocksMsg
|
||||
BlocksMsg
|
||||
NewBlockMsg
|
||||
)
|
||||
|
||||
// ethProtocol represents the ethereum wire protocol
|
||||
// instance is running on each peer
|
||||
type ethProtocol struct {
|
||||
txPool txPool
|
||||
chainManager chainManager
|
||||
blockPool blockPool
|
||||
peer *p2p.Peer
|
||||
id string
|
||||
rw p2p.MsgReadWriter
|
||||
}
|
||||
|
||||
// backend is the interface the ethereum protocol backend should implement
|
||||
// used as an argument to EthProtocol
|
||||
type txPool interface {
|
||||
AddTransactions([]*types.Transaction)
|
||||
}
|
||||
|
||||
type chainManager interface {
|
||||
GetBlockHashesFromHash(hash []byte, amount uint64) (hashes [][]byte)
|
||||
GetBlock(hash []byte) (block *types.Block)
|
||||
Status() (td *big.Int, currentBlock []byte, genesisBlock []byte)
|
||||
}
|
||||
|
||||
type blockPool interface {
|
||||
AddBlockHashes(next func() ([]byte, bool), peerId string)
|
||||
AddBlock(block *types.Block, peerId string)
|
||||
AddPeer(td *big.Int, currentBlock []byte, peerId string, requestHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(int, string, ...interface{})) (best bool)
|
||||
RemovePeer(peerId string)
|
||||
}
|
||||
|
||||
// message structs used for rlp decoding
|
||||
type newBlockMsgData struct {
|
||||
Block *types.Block
|
||||
TD *big.Int
|
||||
}
|
||||
|
||||
type getBlockHashesMsgData struct {
|
||||
Hash []byte
|
||||
Amount uint64
|
||||
}
|
||||
|
||||
// main entrypoint, wrappers starting a server running the eth protocol
|
||||
// use this constructor to attach the protocol ("class") to server caps
|
||||
// the Dev p2p layer then runs the protocol instance on each peer
|
||||
func EthProtocol(txPool txPool, chainManager chainManager, blockPool blockPool) p2p.Protocol {
|
||||
return p2p.Protocol{
|
||||
Name: "eth",
|
||||
Version: ProtocolVersion,
|
||||
Length: ProtocolLength,
|
||||
Run: func(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||
return runEthProtocol(txPool, chainManager, blockPool, peer, rw)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// the main loop that handles incoming messages
|
||||
// note RemovePeer in the post-disconnect hook
|
||||
func runEthProtocol(txPool txPool, chainManager chainManager, blockPool blockPool, peer *p2p.Peer, rw p2p.MsgReadWriter) (err error) {
|
||||
self := ðProtocol{
|
||||
txPool: txPool,
|
||||
chainManager: chainManager,
|
||||
blockPool: blockPool,
|
||||
rw: rw,
|
||||
peer: peer,
|
||||
id: (string)(peer.Identity().Pubkey()),
|
||||
}
|
||||
err = self.handleStatus()
|
||||
if err == nil {
|
||||
for {
|
||||
err = self.handle()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
self.blockPool.RemovePeer(self.id)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *ethProtocol) handle() error {
|
||||
msg, err := self.rw.ReadMsg()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if msg.Size > ProtocolMaxMsgSize {
|
||||
return ProtocolError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
|
||||
}
|
||||
// make sure that the payload has been fully consumed
|
||||
defer msg.Discard()
|
||||
|
||||
switch msg.Code {
|
||||
|
||||
case StatusMsg:
|
||||
return ProtocolError(ErrExtraStatusMsg, "")
|
||||
|
||||
case TxMsg:
|
||||
// TODO: rework using lazy RLP stream
|
||||
var txs []*types.Transaction
|
||||
if err := msg.Decode(&txs); err != nil {
|
||||
return ProtocolError(ErrDecode, "%v", err)
|
||||
}
|
||||
self.txPool.AddTransactions(txs)
|
||||
|
||||
case GetBlockHashesMsg:
|
||||
var request getBlockHashesMsgData
|
||||
if err := msg.Decode(&request); err != nil {
|
||||
return ProtocolError(ErrDecode, "%v", err)
|
||||
}
|
||||
hashes := self.chainManager.GetBlockHashesFromHash(request.Hash, request.Amount)
|
||||
return self.rw.EncodeMsg(BlockHashesMsg, ethutil.ByteSliceToInterface(hashes)...)
|
||||
|
||||
case BlockHashesMsg:
|
||||
// TODO: redo using lazy decode , this way very inefficient on known chains
|
||||
msgStream := rlp.NewListStream(msg.Payload, uint64(msg.Size))
|
||||
var err error
|
||||
iter := func() (hash []byte, ok bool) {
|
||||
hash, err = msgStream.Bytes()
|
||||
if err == nil {
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
self.blockPool.AddBlockHashes(iter, self.id)
|
||||
if err != nil && err != rlp.EOL {
|
||||
return ProtocolError(ErrDecode, "%v", err)
|
||||
}
|
||||
|
||||
case GetBlocksMsg:
|
||||
var blockHashes [][]byte
|
||||
if err := msg.Decode(&blockHashes); err != nil {
|
||||
return ProtocolError(ErrDecode, "%v", err)
|
||||
}
|
||||
max := int(math.Min(float64(len(blockHashes)), blockHashesBatchSize))
|
||||
var blocks []interface{}
|
||||
for i, hash := range blockHashes {
|
||||
if i >= max {
|
||||
break
|
||||
}
|
||||
block := self.chainManager.GetBlock(hash)
|
||||
if block != nil {
|
||||
blocks = append(blocks, block.RlpData())
|
||||
}
|
||||
}
|
||||
return self.rw.EncodeMsg(BlocksMsg, blocks...)
|
||||
|
||||
case BlocksMsg:
|
||||
msgStream := rlp.NewListStream(msg.Payload, uint64(msg.Size))
|
||||
for {
|
||||
var block *types.Block
|
||||
if err := msgStream.Decode(&block); err != nil {
|
||||
if err == rlp.EOL {
|
||||
break
|
||||
} else {
|
||||
return ProtocolError(ErrDecode, "%v", err)
|
||||
}
|
||||
}
|
||||
self.blockPool.AddBlock(block, self.id)
|
||||
}
|
||||
|
||||
case NewBlockMsg:
|
||||
var request newBlockMsgData
|
||||
if err := msg.Decode(&request); err != nil {
|
||||
return ProtocolError(ErrDecode, "%v", err)
|
||||
}
|
||||
hash := request.Block.Hash()
|
||||
// to simplify backend interface adding a new block
|
||||
// uses AddPeer followed by AddHashes, AddBlock only if peer is the best peer
|
||||
// (or selected as new best peer)
|
||||
if self.blockPool.AddPeer(request.TD, hash, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect) {
|
||||
called := true
|
||||
iter := func() (hash []byte, ok bool) {
|
||||
if called {
|
||||
called = false
|
||||
return hash, true
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
self.blockPool.AddBlockHashes(iter, self.id)
|
||||
self.blockPool.AddBlock(request.Block, self.id)
|
||||
}
|
||||
|
||||
default:
|
||||
return ProtocolError(ErrInvalidMsgCode, "%v", msg.Code)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type statusMsgData struct {
|
||||
ProtocolVersion uint
|
||||
NetworkId uint
|
||||
TD *big.Int
|
||||
CurrentBlock []byte
|
||||
GenesisBlock []byte
|
||||
}
|
||||
|
||||
func (self *ethProtocol) statusMsg() p2p.Msg {
|
||||
td, currentBlock, genesisBlock := self.chainManager.Status()
|
||||
|
||||
return p2p.NewMsg(StatusMsg,
|
||||
uint32(ProtocolVersion),
|
||||
uint32(NetworkId),
|
||||
td,
|
||||
currentBlock,
|
||||
genesisBlock,
|
||||
)
|
||||
}
|
||||
|
||||
func (self *ethProtocol) handleStatus() error {
|
||||
// send precanned status message
|
||||
if err := self.rw.WriteMsg(self.statusMsg()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// read and handle remote status
|
||||
msg, err := self.rw.ReadMsg()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if msg.Code != StatusMsg {
|
||||
return ProtocolError(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg)
|
||||
}
|
||||
|
||||
if msg.Size > ProtocolMaxMsgSize {
|
||||
return ProtocolError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize)
|
||||
}
|
||||
|
||||
var status statusMsgData
|
||||
if err := msg.Decode(&status); err != nil {
|
||||
return ProtocolError(ErrDecode, "%v", err)
|
||||
}
|
||||
|
||||
_, _, genesisBlock := self.chainManager.Status()
|
||||
|
||||
if bytes.Compare(status.GenesisBlock, genesisBlock) != 0 {
|
||||
return ProtocolError(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesisBlock)
|
||||
}
|
||||
|
||||
if status.NetworkId != NetworkId {
|
||||
return ProtocolError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, NetworkId)
|
||||
}
|
||||
|
||||
if ProtocolVersion != status.ProtocolVersion {
|
||||
return ProtocolError(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, ProtocolVersion)
|
||||
}
|
||||
|
||||
self.peer.Infof("Peer is [eth] capable (%d/%d). TD=%v H=%x\n", status.ProtocolVersion, status.NetworkId, status.TD, status.CurrentBlock[:4])
|
||||
|
||||
//self.blockPool.AddPeer(status.TD, status.CurrentBlock, self.id, self.requestBlockHashes, self.requestBlocks, self.protoErrorDisconnect)
|
||||
self.peer.Infoln("AddPeer(IGNORED)")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ethProtocol) requestBlockHashes(from []byte) error {
|
||||
self.peer.Debugf("fetching hashes (%d) %x...\n", blockHashesBatchSize, from[0:4])
|
||||
return self.rw.EncodeMsg(GetBlockHashesMsg, from, blockHashesBatchSize)
|
||||
}
|
||||
|
||||
func (self *ethProtocol) requestBlocks(hashes [][]byte) error {
|
||||
self.peer.Debugf("fetching %v blocks", len(hashes))
|
||||
return self.rw.EncodeMsg(GetBlocksMsg, ethutil.ByteSliceToInterface(hashes))
|
||||
}
|
||||
|
||||
func (self *ethProtocol) protoError(code int, format string, params ...interface{}) (err *protocolError) {
|
||||
err = ProtocolError(code, format, params...)
|
||||
if err.Fatal() {
|
||||
self.peer.Errorln(err)
|
||||
} else {
|
||||
self.peer.Debugln(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *ethProtocol) protoErrorDisconnect(code int, format string, params ...interface{}) {
|
||||
err := ProtocolError(code, format, params...)
|
||||
if err.Fatal() {
|
||||
self.peer.Errorln(err)
|
||||
// disconnect
|
||||
} else {
|
||||
self.peer.Debugln(err)
|
||||
}
|
||||
|
||||
}
|
232
eth/protocol_test.go
Normal file
232
eth/protocol_test.go
Normal file
@ -0,0 +1,232 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
)
|
||||
|
||||
type testMsgReadWriter struct {
|
||||
in chan p2p.Msg
|
||||
out chan p2p.Msg
|
||||
}
|
||||
|
||||
func (self *testMsgReadWriter) In(msg p2p.Msg) {
|
||||
self.in <- msg
|
||||
}
|
||||
|
||||
func (self *testMsgReadWriter) Out(msg p2p.Msg) {
|
||||
self.in <- msg
|
||||
}
|
||||
|
||||
func (self *testMsgReadWriter) WriteMsg(msg p2p.Msg) error {
|
||||
self.out <- msg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *testMsgReadWriter) EncodeMsg(code uint64, data ...interface{}) error {
|
||||
return self.WriteMsg(p2p.NewMsg(code, data))
|
||||
}
|
||||
|
||||
func (self *testMsgReadWriter) ReadMsg() (p2p.Msg, error) {
|
||||
msg, ok := <-self.in
|
||||
if !ok {
|
||||
return msg, io.EOF
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func errorCheck(t *testing.T, expCode int, err error) {
|
||||
perr, ok := err.(*protocolError)
|
||||
if ok && perr != nil {
|
||||
if code := perr.Code; code != expCode {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
t.Errorf("expected error code %v, got %v", ErrNoStatusMsg, err)
|
||||
}
|
||||
}
|
||||
|
||||
type TestBackend struct {
|
||||
getTransactions func() []*types.Transaction
|
||||
addTransactions func(txs []*types.Transaction)
|
||||
getBlockHashes func(hash []byte, amount uint32) (hashes [][]byte)
|
||||
addBlockHashes func(next func() ([]byte, bool), peerId string)
|
||||
getBlock func(hash []byte) *types.Block
|
||||
addBlock func(block *types.Block, peerId string) (err error)
|
||||
addPeer func(td *big.Int, currentBlock []byte, peerId string, requestHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool)
|
||||
removePeer func(peerId string)
|
||||
status func() (td *big.Int, currentBlock []byte, genesisBlock []byte)
|
||||
}
|
||||
|
||||
func (self *TestBackend) GetTransactions() (txs []*types.Transaction) {
|
||||
if self.getTransactions != nil {
|
||||
txs = self.getTransactions()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *TestBackend) AddTransactions(txs []*types.Transaction) {
|
||||
if self.addTransactions != nil {
|
||||
self.addTransactions(txs)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *TestBackend) GetBlockHashes(hash []byte, amount uint32) (hashes [][]byte) {
|
||||
if self.getBlockHashes != nil {
|
||||
hashes = self.getBlockHashes(hash, amount)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
func (self *TestBackend) AddBlockHashes(next func() ([]byte, bool), peerId string) {
|
||||
if self.addBlockHashes != nil {
|
||||
self.addBlockHashes(next, peerId)
|
||||
}
|
||||
}
|
||||
|
||||
=======
|
||||
func (self *TestBackend) AddHash(hash []byte, peer *p2p.Peer) (more bool) {
|
||||
if self.addHash != nil {
|
||||
more = self.addHash(hash, peer)
|
||||
=======
|
||||
func (self *TestBackend) AddBlockHashes(next func() ([]byte, bool), peerId string) {
|
||||
if self.addBlockHashes != nil {
|
||||
self.addBlockHashes(next, peerId)
|
||||
>>>>>>> eth protocol changes
|
||||
}
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> initial commit for eth-p2p integration
|
||||
=======
|
||||
|
||||
>>>>>>> eth protocol changes
|
||||
func (self *TestBackend) GetBlock(hash []byte) (block *types.Block) {
|
||||
if self.getBlock != nil {
|
||||
block = self.getBlock(hash)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
func (self *TestBackend) AddBlock(block *types.Block, peerId string) (err error) {
|
||||
if self.addBlock != nil {
|
||||
err = self.addBlock(block, peerId)
|
||||
=======
|
||||
func (self *TestBackend) AddBlock(td *big.Int, block *types.Block, peer *p2p.Peer) (fetchHashes bool, err error) {
|
||||
if self.addBlock != nil {
|
||||
fetchHashes, err = self.addBlock(td, block, peer)
|
||||
>>>>>>> initial commit for eth-p2p integration
|
||||
=======
|
||||
func (self *TestBackend) AddBlock(block *types.Block, peerId string) (err error) {
|
||||
if self.addBlock != nil {
|
||||
err = self.addBlock(block, peerId)
|
||||
>>>>>>> eth protocol changes
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool) {
|
||||
if self.addPeer != nil {
|
||||
best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, invalidBlock)
|
||||
=======
|
||||
func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peer *p2p.Peer) (fetchHashes bool) {
|
||||
if self.addPeer != nil {
|
||||
fetchHashes = self.addPeer(td, currentBlock, peer)
|
||||
>>>>>>> initial commit for eth-p2p integration
|
||||
=======
|
||||
func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool) {
|
||||
if self.addPeer != nil {
|
||||
best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, invalidBlock)
|
||||
>>>>>>> eth protocol changes
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
>>>>>>> eth protocol changes
|
||||
func (self *TestBackend) RemovePeer(peerId string) {
|
||||
if self.removePeer != nil {
|
||||
self.removePeer(peerId)
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
>>>>>>> initial commit for eth-p2p integration
|
||||
=======
|
||||
>>>>>>> eth protocol changes
|
||||
func (self *TestBackend) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) {
|
||||
if self.status != nil {
|
||||
td, currentBlock, genesisBlock = self.status()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
>>>>>>> eth protocol changes
|
||||
// TODO: refactor this into p2p/client_identity
|
||||
type peerId struct {
|
||||
pubkey []byte
|
||||
}
|
||||
|
||||
func (self *peerId) String() string {
|
||||
return "test peer"
|
||||
}
|
||||
|
||||
func (self *peerId) Pubkey() (pubkey []byte) {
|
||||
pubkey = self.pubkey
|
||||
if len(pubkey) == 0 {
|
||||
pubkey = crypto.GenerateNewKeyPair().PublicKey
|
||||
self.pubkey = pubkey
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func testPeer() *p2p.Peer {
|
||||
return p2p.NewPeer(&peerId{}, []p2p.Cap{})
|
||||
}
|
||||
|
||||
func TestErrNoStatusMsg(t *testing.T) {
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
func TestEth(t *testing.T) {
|
||||
>>>>>>> initial commit for eth-p2p integration
|
||||
=======
|
||||
>>>>>>> eth protocol changes
|
||||
quit := make(chan bool)
|
||||
rw := &testMsgReadWriter{make(chan p2p.Msg, 10), make(chan p2p.Msg, 10)}
|
||||
testBackend := &TestBackend{}
|
||||
var err error
|
||||
go func() {
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
err = runEthProtocol(testBackend, testPeer(), rw)
|
||||
=======
|
||||
err = runEthProtocol(testBackend, nil, rw)
|
||||
>>>>>>> initial commit for eth-p2p integration
|
||||
=======
|
||||
err = runEthProtocol(testBackend, testPeer(), rw)
|
||||
>>>>>>> eth protocol changes
|
||||
close(quit)
|
||||
}()
|
||||
statusMsg := p2p.NewMsg(4)
|
||||
rw.In(statusMsg)
|
||||
<-quit
|
||||
errorCheck(t, ErrNoStatusMsg, err)
|
||||
// read(t, remote, []byte("hello, world"), nil)
|
||||
}
|
@ -71,6 +71,10 @@ func (self *LDBDatabase) NewIterator() iterator.Iterator {
|
||||
return self.db.NewIterator(nil, nil)
|
||||
}
|
||||
|
||||
func (self *LDBDatabase) Write(batch *leveldb.Batch) error {
|
||||
return self.db.Write(batch, nil)
|
||||
}
|
||||
|
||||
func (self *LDBDatabase) Close() {
|
||||
// Close the leveldb database
|
||||
self.db.Close()
|
||||
|
659
ethereum.go
659
ethereum.go
@ -1,659 +0,0 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"net"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
)
|
||||
|
||||
const (
|
||||
seedTextFileUri string = "http://www.ethereum.org/servers.poc3.txt"
|
||||
seedNodeAddress = "poc-7.ethdev.com:30303"
|
||||
)
|
||||
|
||||
var loggerger = logger.NewLogger("SERV")
|
||||
|
||||
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() {
|
||||
callback(e.Value.(*Peer), e)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
processReapingTimeout = 60 // TODO increase
|
||||
)
|
||||
|
||||
type Ethereum struct {
|
||||
// Channel for shutting down the ethereum
|
||||
shutdownChan chan bool
|
||||
quit chan bool
|
||||
|
||||
// DB interface
|
||||
db ethutil.Database
|
||||
// State manager for processing new blocks and managing the over all states
|
||||
blockManager *core.BlockManager
|
||||
// The transaction pool. Transaction can be pushed on this pool
|
||||
// for later including in the blocks
|
||||
txPool *core.TxPool
|
||||
// The canonical chain
|
||||
blockChain *core.ChainManager
|
||||
// The block pool
|
||||
blockPool *BlockPool
|
||||
// Eventer
|
||||
eventMux event.TypeMux
|
||||
// Peers
|
||||
peers *list.List
|
||||
// Nonce
|
||||
Nonce uint64
|
||||
|
||||
Addr net.Addr
|
||||
Port string
|
||||
|
||||
blacklist [][]byte
|
||||
|
||||
peerMut sync.Mutex
|
||||
|
||||
// Capabilities for outgoing peers
|
||||
serverCaps Caps
|
||||
|
||||
nat NAT
|
||||
|
||||
// Specifies the desired amount of maximum peers
|
||||
MaxPeers int
|
||||
|
||||
Mining bool
|
||||
|
||||
listening bool
|
||||
|
||||
RpcServer *rpc.JsonRpcServer
|
||||
|
||||
keyManager *crypto.KeyManager
|
||||
|
||||
clientIdentity wire.ClientIdentity
|
||||
|
||||
isUpToDate bool
|
||||
|
||||
filterMu sync.RWMutex
|
||||
filterId int
|
||||
filters map[int]*core.Filter
|
||||
}
|
||||
|
||||
func New(db ethutil.Database, clientIdentity wire.ClientIdentity, keyManager *crypto.KeyManager, caps Caps, usePnp bool) (*Ethereum, error) {
|
||||
var err error
|
||||
var nat NAT
|
||||
|
||||
if usePnp {
|
||||
nat, err = Discover()
|
||||
if err != nil {
|
||||
loggerger.Debugln("UPnP failed", err)
|
||||
}
|
||||
}
|
||||
|
||||
bootstrapDb(db)
|
||||
|
||||
ethutil.Config.Db = db
|
||||
|
||||
nonce, _ := ethutil.RandomUint64()
|
||||
ethereum := &Ethereum{
|
||||
shutdownChan: make(chan bool),
|
||||
quit: make(chan bool),
|
||||
db: db,
|
||||
peers: list.New(),
|
||||
Nonce: nonce,
|
||||
serverCaps: caps,
|
||||
nat: nat,
|
||||
keyManager: keyManager,
|
||||
clientIdentity: clientIdentity,
|
||||
isUpToDate: true,
|
||||
filters: make(map[int]*core.Filter),
|
||||
}
|
||||
|
||||
ethereum.blockPool = NewBlockPool(ethereum)
|
||||
ethereum.txPool = core.NewTxPool(ethereum)
|
||||
ethereum.blockChain = core.NewChainManager(ethereum.EventMux())
|
||||
ethereum.blockManager = core.NewBlockManager(ethereum)
|
||||
ethereum.blockChain.SetProcessor(ethereum.blockManager)
|
||||
|
||||
// Start the tx pool
|
||||
ethereum.txPool.Start()
|
||||
|
||||
return ethereum, nil
|
||||
}
|
||||
|
||||
func (s *Ethereum) KeyManager() *crypto.KeyManager {
|
||||
return s.keyManager
|
||||
}
|
||||
|
||||
func (s *Ethereum) ClientIdentity() wire.ClientIdentity {
|
||||
return s.clientIdentity
|
||||
}
|
||||
|
||||
func (s *Ethereum) ChainManager() *core.ChainManager {
|
||||
return s.blockChain
|
||||
}
|
||||
|
||||
func (s *Ethereum) BlockManager() *core.BlockManager {
|
||||
return s.blockManager
|
||||
}
|
||||
|
||||
func (s *Ethereum) TxPool() *core.TxPool {
|
||||
return s.txPool
|
||||
}
|
||||
func (s *Ethereum) BlockPool() *BlockPool {
|
||||
return s.blockPool
|
||||
}
|
||||
func (s *Ethereum) EventMux() *event.TypeMux {
|
||||
return &s.eventMux
|
||||
}
|
||||
func (self *Ethereum) Db() ethutil.Database {
|
||||
return self.db
|
||||
}
|
||||
|
||||
func (s *Ethereum) ServerCaps() Caps {
|
||||
return s.serverCaps
|
||||
}
|
||||
func (s *Ethereum) IsMining() bool {
|
||||
return s.Mining
|
||||
}
|
||||
func (s *Ethereum) PeerCount() int {
|
||||
return s.peers.Len()
|
||||
}
|
||||
func (s *Ethereum) IsUpToDate() bool {
|
||||
upToDate := true
|
||||
eachPeer(s.peers, func(peer *Peer, e *list.Element) {
|
||||
if atomic.LoadInt32(&peer.connected) == 1 {
|
||||
if peer.catchingUp == true && peer.versionKnown {
|
||||
upToDate = false
|
||||
}
|
||||
}
|
||||
})
|
||||
return upToDate
|
||||
}
|
||||
func (s *Ethereum) PushPeer(peer *Peer) {
|
||||
s.peers.PushBack(peer)
|
||||
}
|
||||
func (s *Ethereum) IsListening() bool {
|
||||
return s.listening
|
||||
}
|
||||
|
||||
func (s *Ethereum) HighestTDPeer() (td *big.Int) {
|
||||
td = big.NewInt(0)
|
||||
|
||||
eachPeer(s.peers, func(p *Peer, v *list.Element) {
|
||||
if p.td.Cmp(td) > 0 {
|
||||
td = p.td
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Ethereum) BlacklistPeer(peer *Peer) {
|
||||
self.blacklist = append(self.blacklist, peer.pubkey)
|
||||
}
|
||||
|
||||
func (s *Ethereum) AddPeer(conn net.Conn) {
|
||||
peer := NewPeer(conn, s, true)
|
||||
|
||||
if peer != nil {
|
||||
if s.peers.Len() < s.MaxPeers {
|
||||
peer.Start()
|
||||
} else {
|
||||
loggerger.Debugf("Max connected peers reached. Not adding incoming peer.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Ethereum) ProcessPeerList(addrs []string) {
|
||||
for _, addr := range addrs {
|
||||
// TODO Probably requires some sanity checks
|
||||
s.ConnectToPeer(addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Ethereum) ConnectToPeer(addr string) error {
|
||||
if s.peers.Len() < s.MaxPeers {
|
||||
var alreadyConnected bool
|
||||
|
||||
ahost, aport, _ := net.SplitHostPort(addr)
|
||||
var chost string
|
||||
|
||||
ips, err := net.LookupIP(ahost)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
// If more then one ip is available try stripping away the ipv6 ones
|
||||
if len(ips) > 1 {
|
||||
var ipsv4 []net.IP
|
||||
// For now remove the ipv6 addresses
|
||||
for _, ip := range ips {
|
||||
if strings.Contains(ip.String(), "::") {
|
||||
continue
|
||||
} else {
|
||||
ipsv4 = append(ipsv4, ip)
|
||||
}
|
||||
}
|
||||
if len(ipsv4) == 0 {
|
||||
return fmt.Errorf("[SERV] No IPV4 addresses available for hostname")
|
||||
}
|
||||
|
||||
// Pick a random ipv4 address, simulating round-robin DNS.
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
i := rand.Intn(len(ipsv4))
|
||||
chost = ipsv4[i].String()
|
||||
} else {
|
||||
if len(ips) == 0 {
|
||||
return fmt.Errorf("[SERV] No IPs resolved for the given hostname")
|
||||
return nil
|
||||
}
|
||||
chost = ips[0].String()
|
||||
}
|
||||
}
|
||||
|
||||
eachPeer(s.peers, func(p *Peer, v *list.Element) {
|
||||
if p.conn == nil {
|
||||
return
|
||||
}
|
||||
phost, pport, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
|
||||
|
||||
if phost == chost && pport == aport {
|
||||
alreadyConnected = true
|
||||
//loggerger.Debugf("Peer %s already added.\n", chost)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
if alreadyConnected {
|
||||
return nil
|
||||
}
|
||||
|
||||
NewOutboundPeer(addr, s, s.serverCaps)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Ethereum) 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 && p.conn != nil {
|
||||
outboundPeers[length] = p
|
||||
length++
|
||||
}
|
||||
})
|
||||
|
||||
return outboundPeers[:length]
|
||||
}
|
||||
|
||||
func (s *Ethereum) 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 *Ethereum) InOutPeers() []*Peer {
|
||||
// Reap the dead peers first
|
||||
s.reapPeers()
|
||||
|
||||
// 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) {
|
||||
// Only return peers with an actual ip
|
||||
if len(p.host) > 0 {
|
||||
inboundPeers[length] = p
|
||||
length++
|
||||
}
|
||||
})
|
||||
|
||||
return inboundPeers[:length]
|
||||
}
|
||||
|
||||
func (s *Ethereum) Broadcast(msgType wire.MsgType, data []interface{}) {
|
||||
msg := wire.NewMessage(msgType, data)
|
||||
s.BroadcastMsg(msg)
|
||||
}
|
||||
|
||||
func (s *Ethereum) BroadcastMsg(msg *wire.Msg) {
|
||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
||||
p.QueueMessage(msg)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Ethereum) Peers() *list.List {
|
||||
return s.peers
|
||||
}
|
||||
|
||||
func (s *Ethereum) reapPeers() {
|
||||
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)) {
|
||||
s.removePeerElement(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Ethereum) removePeerElement(e *list.Element) {
|
||||
s.peerMut.Lock()
|
||||
defer s.peerMut.Unlock()
|
||||
|
||||
s.peers.Remove(e)
|
||||
|
||||
s.eventMux.Post(PeerListEvent{s.peers})
|
||||
}
|
||||
|
||||
func (s *Ethereum) RemovePeer(p *Peer) {
|
||||
eachPeer(s.peers, func(peer *Peer, e *list.Element) {
|
||||
if peer == p {
|
||||
s.removePeerElement(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Ethereum) reapDeadPeerHandler() {
|
||||
reapTimer := time.NewTicker(processReapingTimeout * time.Second)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-reapTimer.C:
|
||||
s.reapPeers()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start the ethereum
|
||||
func (s *Ethereum) Start(seed bool) {
|
||||
s.blockPool.Start()
|
||||
|
||||
// Bind to addr and port
|
||||
ln, err := net.Listen("tcp", ":"+s.Port)
|
||||
if err != nil {
|
||||
loggerger.Warnf("Port %s in use. Connection listening disabled. Acting as client", s.Port)
|
||||
s.listening = false
|
||||
} else {
|
||||
s.listening = true
|
||||
// Starting accepting connections
|
||||
loggerger.Infoln("Ready and accepting connections")
|
||||
// Start the peer handler
|
||||
go s.peerHandler(ln)
|
||||
}
|
||||
|
||||
if s.nat != nil {
|
||||
go s.upnpUpdateThread()
|
||||
}
|
||||
|
||||
// Start the reaping processes
|
||||
go s.reapDeadPeerHandler()
|
||||
go s.update()
|
||||
go s.filterLoop()
|
||||
|
||||
if seed {
|
||||
s.Seed()
|
||||
}
|
||||
s.ConnectToPeer("localhost:40404")
|
||||
loggerger.Infoln("Server started")
|
||||
}
|
||||
|
||||
func (s *Ethereum) Seed() {
|
||||
// Sorry Py person. I must blacklist. you perform badly
|
||||
s.blacklist = append(s.blacklist, ethutil.Hex2Bytes("64656330303561383532336435376331616537643864663236623336313863373537353163636634333530626263396330346237336262623931383064393031"))
|
||||
ips := PastPeers()
|
||||
if len(ips) > 0 {
|
||||
for _, ip := range ips {
|
||||
loggerger.Infoln("Connecting to previous peer ", ip)
|
||||
s.ConnectToPeer(ip)
|
||||
}
|
||||
} else {
|
||||
loggerger.Debugln("Retrieving seed nodes")
|
||||
|
||||
// Eth-Go Bootstrapping
|
||||
ips, er := net.LookupIP("seed.bysh.me")
|
||||
if er == nil {
|
||||
peers := []string{}
|
||||
for _, ip := range ips {
|
||||
node := fmt.Sprintf("%s:%d", ip.String(), 30303)
|
||||
loggerger.Debugln("Found DNS Go Peer:", node)
|
||||
peers = append(peers, node)
|
||||
}
|
||||
s.ProcessPeerList(peers)
|
||||
}
|
||||
|
||||
// Official DNS Bootstrapping
|
||||
_, nodes, err := net.LookupSRV("eth", "tcp", "ethereum.org")
|
||||
if err == nil {
|
||||
peers := []string{}
|
||||
// Iterate SRV nodes
|
||||
for _, n := range nodes {
|
||||
target := n.Target
|
||||
port := strconv.Itoa(int(n.Port))
|
||||
// Resolve target to ip (Go returns list, so may resolve to multiple ips?)
|
||||
addr, err := net.LookupHost(target)
|
||||
if err == nil {
|
||||
for _, a := range addr {
|
||||
// Build string out of SRV port and Resolved IP
|
||||
peer := net.JoinHostPort(a, port)
|
||||
loggerger.Debugln("Found DNS Bootstrap Peer:", peer)
|
||||
peers = append(peers, peer)
|
||||
}
|
||||
} else {
|
||||
loggerger.Debugln("Couldn't resolve :", target)
|
||||
}
|
||||
}
|
||||
// Connect to Peer list
|
||||
s.ProcessPeerList(peers)
|
||||
}
|
||||
|
||||
s.ConnectToPeer(seedNodeAddress)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Ethereum) peerHandler(listener net.Listener) {
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
loggerger.Debugln(err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
go s.AddPeer(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Ethereum) Stop() {
|
||||
// Stop eventMux first, it will close all subscriptions.
|
||||
s.eventMux.Stop()
|
||||
|
||||
// Close the database
|
||||
defer s.db.Close()
|
||||
|
||||
var ips []string
|
||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
||||
ips = append(ips, p.conn.RemoteAddr().String())
|
||||
})
|
||||
|
||||
if len(ips) > 0 {
|
||||
d, _ := json.MarshalIndent(ips, "", " ")
|
||||
ethutil.WriteFile(path.Join(ethutil.Config.ExecPath, "known_peers.json"), d)
|
||||
}
|
||||
|
||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
||||
p.Stop()
|
||||
})
|
||||
|
||||
close(s.quit)
|
||||
|
||||
if s.RpcServer != nil {
|
||||
s.RpcServer.Stop()
|
||||
}
|
||||
s.txPool.Stop()
|
||||
s.blockPool.Stop()
|
||||
|
||||
loggerger.Infoln("Server stopped")
|
||||
close(s.shutdownChan)
|
||||
}
|
||||
|
||||
// This function will wait for a shutdown and resumes main thread execution
|
||||
func (s *Ethereum) WaitForShutdown() {
|
||||
<-s.shutdownChan
|
||||
}
|
||||
|
||||
func (s *Ethereum) upnpUpdateThread() {
|
||||
// Go off immediately to prevent code duplication, thereafter we renew
|
||||
// lease every 15 minutes.
|
||||
timer := time.NewTimer(5 * time.Minute)
|
||||
lport, _ := strconv.ParseInt(s.Port, 10, 16)
|
||||
first := true
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
var err error
|
||||
_, err = s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60)
|
||||
if err != nil {
|
||||
loggerger.Debugln("can't add UPnP port mapping:", err)
|
||||
break out
|
||||
}
|
||||
if first && err == nil {
|
||||
_, err = s.nat.GetExternalAddress()
|
||||
if err != nil {
|
||||
loggerger.Debugln("UPnP can't get external address:", err)
|
||||
continue out
|
||||
}
|
||||
first = false
|
||||
}
|
||||
timer.Reset(time.Minute * 15)
|
||||
case <-s.quit:
|
||||
break out
|
||||
}
|
||||
}
|
||||
|
||||
timer.Stop()
|
||||
|
||||
if err := s.nat.DeletePortMapping("TCP", int(lport), int(lport)); err != nil {
|
||||
loggerger.Debugln("unable to remove UPnP port mapping:", err)
|
||||
} else {
|
||||
loggerger.Debugln("succesfully disestablished UPnP port mapping")
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Ethereum) update() {
|
||||
upToDateTimer := time.NewTicker(1 * time.Second)
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-upToDateTimer.C:
|
||||
if self.IsUpToDate() && !self.isUpToDate {
|
||||
self.eventMux.Post(ChainSyncEvent{false})
|
||||
self.isUpToDate = true
|
||||
} else if !self.IsUpToDate() && self.isUpToDate {
|
||||
self.eventMux.Post(ChainSyncEvent{true})
|
||||
self.isUpToDate = false
|
||||
}
|
||||
case <-self.quit:
|
||||
break out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InstallFilter adds filter for blockchain events.
|
||||
// The filter's callbacks will run for matching blocks and messages.
|
||||
// The filter should not be modified after it has been installed.
|
||||
func (self *Ethereum) InstallFilter(filter *core.Filter) (id int) {
|
||||
self.filterMu.Lock()
|
||||
id = self.filterId
|
||||
self.filters[id] = filter
|
||||
self.filterId++
|
||||
self.filterMu.Unlock()
|
||||
return id
|
||||
}
|
||||
|
||||
func (self *Ethereum) UninstallFilter(id int) {
|
||||
self.filterMu.Lock()
|
||||
delete(self.filters, id)
|
||||
self.filterMu.Unlock()
|
||||
}
|
||||
|
||||
// GetFilter retrieves a filter installed using InstallFilter.
|
||||
// The filter may not be modified.
|
||||
func (self *Ethereum) GetFilter(id int) *core.Filter {
|
||||
self.filterMu.RLock()
|
||||
defer self.filterMu.RUnlock()
|
||||
return self.filters[id]
|
||||
}
|
||||
|
||||
func (self *Ethereum) filterLoop() {
|
||||
// Subscribe to events
|
||||
events := self.eventMux.Subscribe(core.NewBlockEvent{}, state.Messages(nil))
|
||||
for event := range events.Chan() {
|
||||
switch event := event.(type) {
|
||||
case core.NewBlockEvent:
|
||||
self.filterMu.RLock()
|
||||
for _, filter := range self.filters {
|
||||
if filter.BlockCallback != nil {
|
||||
filter.BlockCallback(event.Block)
|
||||
}
|
||||
}
|
||||
self.filterMu.RUnlock()
|
||||
|
||||
case state.Messages:
|
||||
self.filterMu.RLock()
|
||||
for _, filter := range self.filters {
|
||||
if filter.MessageCallback != nil {
|
||||
msgs := filter.FilterMessages(event)
|
||||
if len(msgs) > 0 {
|
||||
filter.MessageCallback(msgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.filterMu.RUnlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func bootstrapDb(db ethutil.Database) {
|
||||
d, _ := db.Get([]byte("ProtocolVersion"))
|
||||
protov := ethutil.NewValue(d).Uint()
|
||||
|
||||
if protov == 0 {
|
||||
db.Put([]byte("ProtocolVersion"), ethutil.NewValue(ProtocolVersion).Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func PastPeers() []string {
|
||||
var ips []string
|
||||
data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "known_peers.json"))
|
||||
json.Unmarshal([]byte(data), &ips)
|
||||
|
||||
return ips
|
||||
}
|
@ -2,7 +2,6 @@ package ethutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
@ -193,8 +192,13 @@ func Encode(object interface{}) []byte {
|
||||
if blen < 56 {
|
||||
buff.WriteByte(byte(blen) + 0xc0)
|
||||
} else {
|
||||
buff.WriteByte(byte(intlen(int64(blen))) + 0xf7)
|
||||
binary.Write(&buff, binary.BigEndian, int64(blen))
|
||||
ilen := byte(intlen(int64(blen)))
|
||||
buff.WriteByte(ilen + 0xf7)
|
||||
t := make([]byte, ilen)
|
||||
for i := byte(0); i < ilen; i++ {
|
||||
t[ilen-i-1] = byte(blen >> (i * 8))
|
||||
}
|
||||
buff.Write(t)
|
||||
}
|
||||
buff.ReadFrom(&b)
|
||||
}
|
||||
|
@ -397,5 +397,5 @@ func (it *ValueIterator) Value() *Value {
|
||||
}
|
||||
|
||||
func (it *ValueIterator) Idx() int {
|
||||
return it.idx
|
||||
return it.idx - 1
|
||||
}
|
||||
|
@ -2,19 +2,29 @@ package filter
|
||||
|
||||
type Generic struct {
|
||||
Str1, Str2, Str3 string
|
||||
Data map[string]struct{}
|
||||
|
||||
Fn func(data interface{})
|
||||
}
|
||||
|
||||
// self = registered, f = incoming
|
||||
func (self Generic) Compare(f Filter) bool {
|
||||
var strMatch, dataMatch = true, true
|
||||
|
||||
filter := f.(Generic)
|
||||
if (len(self.Str1) == 0 || filter.Str1 == self.Str1) &&
|
||||
(len(self.Str2) == 0 || filter.Str2 == self.Str2) &&
|
||||
(len(self.Str3) == 0 || filter.Str3 == self.Str3) {
|
||||
return true
|
||||
if (len(self.Str1) > 0 && filter.Str1 != self.Str1) ||
|
||||
(len(self.Str2) > 0 && filter.Str2 != self.Str2) ||
|
||||
(len(self.Str3) > 0 && filter.Str3 != self.Str3) {
|
||||
strMatch = false
|
||||
}
|
||||
|
||||
for k, _ := range self.Data {
|
||||
if _, ok := filter.Data[k]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return strMatch && dataMatch
|
||||
}
|
||||
|
||||
func (self Generic) Trigger(data interface{}) {
|
||||
|
94
event/filter/old_filter.go
Normal file
94
event/filter/old_filter.go
Normal file
@ -0,0 +1,94 @@
|
||||
// XXX This is the old filter system specifically for messages. This is till in used and could use some refactoring
|
||||
package filter
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
type FilterManager struct {
|
||||
eventMux *event.TypeMux
|
||||
|
||||
filterMu sync.RWMutex
|
||||
filterId int
|
||||
filters map[int]*core.Filter
|
||||
|
||||
quit chan struct{}
|
||||
}
|
||||
|
||||
func NewFilterManager(mux *event.TypeMux) *FilterManager {
|
||||
return &FilterManager{
|
||||
eventMux: mux,
|
||||
filters: make(map[int]*core.Filter),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *FilterManager) Start() {
|
||||
go self.filterLoop()
|
||||
}
|
||||
|
||||
func (self *FilterManager) Stop() {
|
||||
close(self.quit)
|
||||
}
|
||||
|
||||
func (self *FilterManager) InstallFilter(filter *core.Filter) (id int) {
|
||||
self.filterMu.Lock()
|
||||
id = self.filterId
|
||||
self.filters[id] = filter
|
||||
self.filterId++
|
||||
self.filterMu.Unlock()
|
||||
return id
|
||||
}
|
||||
|
||||
func (self *FilterManager) UninstallFilter(id int) {
|
||||
self.filterMu.Lock()
|
||||
delete(self.filters, id)
|
||||
self.filterMu.Unlock()
|
||||
}
|
||||
|
||||
// GetFilter retrieves a filter installed using InstallFilter.
|
||||
// The filter may not be modified.
|
||||
func (self *FilterManager) GetFilter(id int) *core.Filter {
|
||||
self.filterMu.RLock()
|
||||
defer self.filterMu.RUnlock()
|
||||
return self.filters[id]
|
||||
}
|
||||
|
||||
func (self *FilterManager) filterLoop() {
|
||||
// Subscribe to events
|
||||
events := self.eventMux.Subscribe(core.NewBlockEvent{}, state.Messages(nil))
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-self.quit:
|
||||
break out
|
||||
case event := <-events.Chan():
|
||||
switch event := event.(type) {
|
||||
case core.NewBlockEvent:
|
||||
self.filterMu.RLock()
|
||||
for _, filter := range self.filters {
|
||||
if filter.BlockCallback != nil {
|
||||
filter.BlockCallback(event.Block)
|
||||
}
|
||||
}
|
||||
self.filterMu.RUnlock()
|
||||
|
||||
case state.Messages:
|
||||
self.filterMu.RLock()
|
||||
for _, filter := range self.filters {
|
||||
if filter.MessageCallback != nil {
|
||||
msgs := filter.FilterMessages(event)
|
||||
if len(msgs) > 0 {
|
||||
filter.MessageCallback(msgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.filterMu.RUnlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
events.go
11
events.go
@ -1,11 +0,0 @@
|
||||
package eth
|
||||
|
||||
import "container/list"
|
||||
|
||||
type PeerListEvent struct {
|
||||
Peers *list.List
|
||||
}
|
||||
|
||||
type ChainSyncEvent struct {
|
||||
InSync bool
|
||||
}
|
@ -13,7 +13,10 @@ for dir in $(find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -type d)
|
||||
do
|
||||
if ls $dir/*.go &> /dev/null; then
|
||||
# echo $dir
|
||||
if [[ $dir != "./tests/vm" ]]
|
||||
then
|
||||
go test -covermode=count -coverprofile=$dir/profile.tmp $dir
|
||||
fi
|
||||
if [ -f $dir/profile.tmp ]
|
||||
then
|
||||
cat $dir/profile.tmp | tail -n +2 >> profile.cov
|
||||
@ -26,4 +29,4 @@ go tool cover -func profile.cov
|
||||
|
||||
# To submit the test coverage result to coveralls.io,
|
||||
# use goveralls (https://github.com/mattn/goveralls)
|
||||
# goveralls -coverprofile=profile.cov -service=travis-ci
|
||||
goveralls -coverprofile=profile.cov -service=travis-ci -repotoken $COVERALLS_TOKEN
|
||||
|
@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
TEST_DEPS=$(go list -f '{{.Imports}} {{.TestImports}} {{.XTestImports}}' github.com/ethereum/go-ethereum/... | sed -e 's/\[//g' | sed -e 's/\]//g' | sed -e 's/C //g')
|
||||
if [ "$TEST_DEPS" ]; then
|
||||
go get -race $TEST_DEPS
|
||||
fi
|
@ -7,10 +7,10 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
@ -203,7 +203,7 @@ func (self *JSRE) addPeer(call otto.FunctionCall) otto.Value {
|
||||
if err != nil {
|
||||
return otto.FalseValue()
|
||||
}
|
||||
self.ethereum.ConnectToPeer(host)
|
||||
self.ethereum.SuggestPeer(host)
|
||||
|
||||
return otto.TrueValue()
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package javascript
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/ui"
|
||||
@ -18,11 +18,11 @@ type JSStateObject struct {
|
||||
|
||||
func (self *JSStateObject) EachStorage(call otto.FunctionCall) otto.Value {
|
||||
cb := call.Argument(0)
|
||||
self.JSObject.EachStorage(func(key string, value *ethutil.Value) {
|
||||
value.Decode()
|
||||
|
||||
cb.Call(self.eth.toVal(self), self.eth.toVal(key), self.eth.toVal(ethutil.Bytes2Hex(value.Bytes())))
|
||||
})
|
||||
it := self.JSObject.Trie().Iterator()
|
||||
for it.Next() {
|
||||
cb.Call(self.eth.toVal(self), self.eth.toVal(ethutil.Bytes2Hex(it.Key)), self.eth.toVal(ethutil.Bytes2Hex(it.Value)))
|
||||
}
|
||||
|
||||
return otto.UndefinedValue()
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import (
|
||||
"math/big"
|
||||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/pow/ezp"
|
||||
@ -36,7 +36,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
)
|
||||
|
||||
type LocalTx struct {
|
||||
@ -57,7 +56,7 @@ type Miner struct {
|
||||
eth *eth.Ethereum
|
||||
events event.Subscription
|
||||
|
||||
uncles types.Blocks
|
||||
uncles []*types.Header
|
||||
localTxs map[int]*LocalTx
|
||||
localTxId int
|
||||
|
||||
@ -185,15 +184,17 @@ func (self *Miner) mine() {
|
||||
block.SetUncles(self.uncles)
|
||||
}
|
||||
|
||||
parent := chainMan.GetBlock(block.PrevHash)
|
||||
coinbase := block.State().GetOrNewStateObject(block.Coinbase)
|
||||
coinbase.SetGasPool(block.CalcGasLimit(parent))
|
||||
parent := chainMan.GetBlock(block.ParentHash())
|
||||
coinbase := block.State().GetOrNewStateObject(block.Coinbase())
|
||||
coinbase.SetGasPool(core.CalcGasLimit(parent, block))
|
||||
|
||||
transactions := self.finiliseTxs()
|
||||
|
||||
state := block.State()
|
||||
|
||||
// Accumulate all valid transactions and apply them to the new state
|
||||
// Error may be ignored. It's not important during mining
|
||||
receipts, txs, _, erroneous, err := blockManager.ApplyTransactions(coinbase, block.State(), block, transactions, true)
|
||||
receipts, txs, _, erroneous, err := blockManager.ApplyTransactions(coinbase, state, block, transactions, true)
|
||||
if err != nil {
|
||||
minerlogger.Debugln(err)
|
||||
}
|
||||
@ -203,21 +204,22 @@ func (self *Miner) mine() {
|
||||
block.SetReceipts(receipts)
|
||||
|
||||
// Accumulate the rewards included for this block
|
||||
blockManager.AccumelateRewards(block.State(), block, parent)
|
||||
blockManager.AccumelateRewards(state, block, parent)
|
||||
|
||||
block.State().Update(ethutil.Big0)
|
||||
state.Update(ethutil.Big0)
|
||||
block.SetRoot(state.Root())
|
||||
|
||||
minerlogger.Infof("Mining on block. Includes %v transactions", len(transactions))
|
||||
|
||||
// Find a valid nonce
|
||||
nonce := self.pow.Search(block, self.powQuitCh)
|
||||
if nonce != nil {
|
||||
block.Nonce = nonce
|
||||
block.Header().Nonce = nonce
|
||||
err := chainMan.InsertChain(types.Blocks{block})
|
||||
if err != nil {
|
||||
minerlogger.Infoln(err)
|
||||
} else {
|
||||
self.eth.Broadcast(wire.MsgBlockTy, []interface{}{block.Value().Val})
|
||||
self.eth.EventMux().Post(core.NewMinedBlockEvent{block})
|
||||
|
||||
minerlogger.Infof("🔨 Mined block %x\n", block.Hash())
|
||||
minerlogger.Infoln(block)
|
||||
@ -237,8 +239,8 @@ func (self *Miner) finiliseTxs() types.Transactions {
|
||||
key := self.eth.KeyManager()
|
||||
for i, ltx := range self.localTxs {
|
||||
tx := types.NewTransactionMessage(ltx.To, ethutil.Big(ltx.Value), ethutil.Big(ltx.Gas), ethutil.Big(ltx.GasPrice), ltx.Data)
|
||||
tx.Nonce = state.GetNonce(self.Coinbase)
|
||||
state.SetNonce(self.Coinbase, tx.Nonce+1)
|
||||
tx.SetNonce(state.GetNonce(self.Coinbase))
|
||||
state.SetNonce(self.Coinbase, tx.Nonce()+1)
|
||||
|
||||
tx.Sign(key.PrivateKey())
|
||||
|
||||
@ -246,8 +248,8 @@ func (self *Miner) finiliseTxs() types.Transactions {
|
||||
}
|
||||
|
||||
// Faster than append
|
||||
for _, tx := range self.eth.TxPool().CurrentTransactions() {
|
||||
if tx.GasPrice.Cmp(self.MinAcceptedGasPrice) >= 0 {
|
||||
for _, tx := range self.eth.TxPool().GetTransactions() {
|
||||
if tx.GasPrice().Cmp(self.MinAcceptedGasPrice) >= 0 {
|
||||
txs[actualSize] = tx
|
||||
actualSize++
|
||||
}
|
||||
|
12
nat.go
12
nat.go
@ -1,12 +0,0 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// protocol is either "udp" or "tcp"
|
||||
type NAT interface {
|
||||
GetExternalAddress() (addr net.IP, err error)
|
||||
AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
|
||||
DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
|
||||
}
|
55
natpmp.go
55
natpmp.go
@ -1,55 +0,0 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
natpmp "github.com/jackpal/go-nat-pmp"
|
||||
)
|
||||
|
||||
// Adapt the NAT-PMP protocol to the NAT interface
|
||||
|
||||
// TODO:
|
||||
// + Register for changes to the external address.
|
||||
// + Re-register port mapping when router reboots.
|
||||
// + A mechanism for keeping a port mapping registered.
|
||||
|
||||
type natPMPClient struct {
|
||||
client *natpmp.Client
|
||||
}
|
||||
|
||||
func NewNatPMP(gateway net.IP) (nat NAT) {
|
||||
return &natPMPClient{natpmp.NewClient(gateway)}
|
||||
}
|
||||
|
||||
func (n *natPMPClient) GetExternalAddress() (addr net.IP, err error) {
|
||||
response, err := n.client.GetExternalAddress()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ip := response.ExternalIPAddress
|
||||
addr = net.IPv4(ip[0], ip[1], ip[2], ip[3])
|
||||
return
|
||||
}
|
||||
|
||||
func (n *natPMPClient) AddPortMapping(protocol string, externalPort, internalPort int,
|
||||
description string, timeout int) (mappedExternalPort int, err error) {
|
||||
if timeout <= 0 {
|
||||
err = fmt.Errorf("timeout must not be <= 0")
|
||||
return
|
||||
}
|
||||
// Note order of port arguments is switched between our AddPortMapping and the client's AddPortMapping.
|
||||
response, err := n.client.AddPortMapping(protocol, internalPort, externalPort, timeout)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mappedExternalPort = int(response.MappedExternalPort)
|
||||
return
|
||||
}
|
||||
|
||||
func (n *natPMPClient) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
|
||||
// To destroy a mapping, send an add-port with
|
||||
// an internalPort of the internal port to destroy, an external port of zero and a time of zero.
|
||||
_, err = n.client.AddPortMapping(protocol, internalPort, 0, 0)
|
||||
return
|
||||
}
|
338
natupnp.go
338
natupnp.go
@ -1,338 +0,0 @@
|
||||
package eth
|
||||
|
||||
// Just enough UPnP to be able to forward ports
|
||||
//
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type upnpNAT struct {
|
||||
serviceURL string
|
||||
ourIP string
|
||||
}
|
||||
|
||||
func Discover() (nat NAT, err error) {
|
||||
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
conn, err := net.ListenPacket("udp4", ":0")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
socket := conn.(*net.UDPConn)
|
||||
defer socket.Close()
|
||||
|
||||
err = socket.SetDeadline(time.Now().Add(10 * time.Second))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
st := "ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
|
||||
buf := bytes.NewBufferString(
|
||||
"M-SEARCH * HTTP/1.1\r\n" +
|
||||
"HOST: 239.255.255.250:1900\r\n" +
|
||||
st +
|
||||
"MAN: \"ssdp:discover\"\r\n" +
|
||||
"MX: 2\r\n\r\n")
|
||||
message := buf.Bytes()
|
||||
answerBytes := make([]byte, 1024)
|
||||
for i := 0; i < 3; i++ {
|
||||
_, err = socket.WriteToUDP(message, ssdp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var n int
|
||||
n, _, err = socket.ReadFromUDP(answerBytes)
|
||||
if err != nil {
|
||||
continue
|
||||
// socket.Close()
|
||||
// return
|
||||
}
|
||||
answer := string(answerBytes[0:n])
|
||||
if strings.Index(answer, "\r\n"+st) < 0 {
|
||||
continue
|
||||
}
|
||||
// HTTP header field names are case-insensitive.
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
||||
locString := "\r\nlocation: "
|
||||
answer = strings.ToLower(answer)
|
||||
locIndex := strings.Index(answer, locString)
|
||||
if locIndex < 0 {
|
||||
continue
|
||||
}
|
||||
loc := answer[locIndex+len(locString):]
|
||||
endIndex := strings.Index(loc, "\r\n")
|
||||
if endIndex < 0 {
|
||||
continue
|
||||
}
|
||||
locURL := loc[0:endIndex]
|
||||
var serviceURL string
|
||||
serviceURL, err = getServiceURL(locURL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var ourIP string
|
||||
ourIP, err = getOurIP()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP}
|
||||
return
|
||||
}
|
||||
err = errors.New("UPnP port discovery failed.")
|
||||
return
|
||||
}
|
||||
|
||||
// service represents the Service type in an UPnP xml description.
|
||||
// Only the parts we care about are present and thus the xml may have more
|
||||
// fields than present in the structure.
|
||||
type service struct {
|
||||
ServiceType string `xml:"serviceType"`
|
||||
ControlURL string `xml:"controlURL"`
|
||||
}
|
||||
|
||||
// deviceList represents the deviceList type in an UPnP xml description.
|
||||
// Only the parts we care about are present and thus the xml may have more
|
||||
// fields than present in the structure.
|
||||
type deviceList struct {
|
||||
XMLName xml.Name `xml:"deviceList"`
|
||||
Device []device `xml:"device"`
|
||||
}
|
||||
|
||||
// serviceList represents the serviceList type in an UPnP xml description.
|
||||
// Only the parts we care about are present and thus the xml may have more
|
||||
// fields than present in the structure.
|
||||
type serviceList struct {
|
||||
XMLName xml.Name `xml:"serviceList"`
|
||||
Service []service `xml:"service"`
|
||||
}
|
||||
|
||||
// device represents the device type in an UPnP xml description.
|
||||
// Only the parts we care about are present and thus the xml may have more
|
||||
// fields than present in the structure.
|
||||
type device struct {
|
||||
XMLName xml.Name `xml:"device"`
|
||||
DeviceType string `xml:"deviceType"`
|
||||
DeviceList deviceList `xml:"deviceList"`
|
||||
ServiceList serviceList `xml:"serviceList"`
|
||||
}
|
||||
|
||||
// specVersion represents the specVersion in a UPnP xml description.
|
||||
// Only the parts we care about are present and thus the xml may have more
|
||||
// fields than present in the structure.
|
||||
type specVersion struct {
|
||||
XMLName xml.Name `xml:"specVersion"`
|
||||
Major int `xml:"major"`
|
||||
Minor int `xml:"minor"`
|
||||
}
|
||||
|
||||
// root represents the Root document for a UPnP xml description.
|
||||
// Only the parts we care about are present and thus the xml may have more
|
||||
// fields than present in the structure.
|
||||
type root struct {
|
||||
XMLName xml.Name `xml:"root"`
|
||||
SpecVersion specVersion
|
||||
Device device
|
||||
}
|
||||
|
||||
func getChildDevice(d *device, deviceType string) *device {
|
||||
dl := d.DeviceList.Device
|
||||
for i := 0; i < len(dl); i++ {
|
||||
if dl[i].DeviceType == deviceType {
|
||||
return &dl[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getChildService(d *device, serviceType string) *service {
|
||||
sl := d.ServiceList.Service
|
||||
for i := 0; i < len(sl); i++ {
|
||||
if sl[i].ServiceType == serviceType {
|
||||
return &sl[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getOurIP() (ip string, err error) {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p, err := net.LookupIP(hostname)
|
||||
if err != nil && len(p) > 0 {
|
||||
return
|
||||
}
|
||||
return p[0].String(), nil
|
||||
}
|
||||
|
||||
func getServiceURL(rootURL string) (url string, err error) {
|
||||
r, err := http.Get(rootURL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
if r.StatusCode >= 400 {
|
||||
err = errors.New(string(r.StatusCode))
|
||||
return
|
||||
}
|
||||
var root root
|
||||
err = xml.NewDecoder(r.Body).Decode(&root)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
a := &root.Device
|
||||
if a.DeviceType != "urn:schemas-upnp-org:device:InternetGatewayDevice:1" {
|
||||
err = errors.New("No InternetGatewayDevice")
|
||||
return
|
||||
}
|
||||
b := getChildDevice(a, "urn:schemas-upnp-org:device:WANDevice:1")
|
||||
if b == nil {
|
||||
err = errors.New("No WANDevice")
|
||||
return
|
||||
}
|
||||
c := getChildDevice(b, "urn:schemas-upnp-org:device:WANConnectionDevice:1")
|
||||
if c == nil {
|
||||
err = errors.New("No WANConnectionDevice")
|
||||
return
|
||||
}
|
||||
d := getChildService(c, "urn:schemas-upnp-org:service:WANIPConnection:1")
|
||||
if d == nil {
|
||||
err = errors.New("No WANIPConnection")
|
||||
return
|
||||
}
|
||||
url = combineURL(rootURL, d.ControlURL)
|
||||
return
|
||||
}
|
||||
|
||||
func combineURL(rootURL, subURL string) string {
|
||||
protocolEnd := "://"
|
||||
protoEndIndex := strings.Index(rootURL, protocolEnd)
|
||||
a := rootURL[protoEndIndex+len(protocolEnd):]
|
||||
rootIndex := strings.Index(a, "/")
|
||||
return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
|
||||
}
|
||||
|
||||
func soapRequest(url, function, message string) (r *http.Response, err error) {
|
||||
fullMessage := "<?xml version=\"1.0\" ?>" +
|
||||
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" +
|
||||
"<s:Body>" + message + "</s:Body></s:Envelope>"
|
||||
|
||||
req, err := http.NewRequest("POST", url, strings.NewReader(fullMessage))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "text/xml ; charset=\"utf-8\"")
|
||||
req.Header.Set("User-Agent", "Darwin/10.0.0, UPnP/1.0, MiniUPnPc/1.3")
|
||||
//req.Header.Set("Transfer-Encoding", "chunked")
|
||||
req.Header.Set("SOAPAction", "\"urn:schemas-upnp-org:service:WANIPConnection:1#"+function+"\"")
|
||||
req.Header.Set("Connection", "Close")
|
||||
req.Header.Set("Cache-Control", "no-cache")
|
||||
req.Header.Set("Pragma", "no-cache")
|
||||
|
||||
// log.Stderr("soapRequest ", req)
|
||||
//fmt.Println(fullMessage)
|
||||
|
||||
r, err = http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.Body != nil {
|
||||
defer r.Body.Close()
|
||||
}
|
||||
|
||||
if r.StatusCode >= 400 {
|
||||
// log.Stderr(function, r.StatusCode)
|
||||
err = errors.New("Error " + strconv.Itoa(r.StatusCode) + " for " + function)
|
||||
r = nil
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type statusInfo struct {
|
||||
externalIpAddress string
|
||||
}
|
||||
|
||||
func (n *upnpNAT) getStatusInfo() (info statusInfo, err error) {
|
||||
|
||||
message := "<u:GetStatusInfo xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
|
||||
"</u:GetStatusInfo>"
|
||||
|
||||
var response *http.Response
|
||||
response, err = soapRequest(n.serviceURL, "GetStatusInfo", message)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Write a soap reply parser. It has to eat the Body and envelope tags...
|
||||
|
||||
response.Body.Close()
|
||||
return
|
||||
}
|
||||
|
||||
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
|
||||
info, err := n.getStatusInfo()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
addr = net.ParseIP(info.externalIpAddress)
|
||||
return
|
||||
}
|
||||
|
||||
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
|
||||
// A single concatenation would break ARM compilation.
|
||||
message := "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
|
||||
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort)
|
||||
message += "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>"
|
||||
message += "<NewInternalPort>" + strconv.Itoa(internalPort) + "</NewInternalPort>" +
|
||||
"<NewInternalClient>" + n.ourIP + "</NewInternalClient>" +
|
||||
"<NewEnabled>1</NewEnabled><NewPortMappingDescription>"
|
||||
message += description +
|
||||
"</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) +
|
||||
"</NewLeaseDuration></u:AddPortMapping>"
|
||||
|
||||
var response *http.Response
|
||||
response, err = soapRequest(n.serviceURL, "AddPortMapping", message)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: check response to see if the port was forwarded
|
||||
// log.Println(message, response)
|
||||
mappedExternalPort = externalPort
|
||||
_ = response
|
||||
return
|
||||
}
|
||||
|
||||
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
|
||||
|
||||
message := "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
|
||||
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort) +
|
||||
"</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>" +
|
||||
"</u:DeletePortMapping>"
|
||||
|
||||
var response *http.Response
|
||||
response, err = soapRequest(n.serviceURL, "DeletePortMapping", message)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: check response to see if the port was deleted
|
||||
// log.Println(message, response)
|
||||
_ = response
|
||||
return
|
||||
}
|
@ -246,12 +246,7 @@ func (srv *Server) Stop() {
|
||||
|
||||
func (srv *Server) discLoop() {
|
||||
for peer := range srv.peerDisconnect {
|
||||
// peer has just disconnected. free up its slot.
|
||||
srvlog.Infof("%v is gone", peer)
|
||||
srv.peerSlots <- peer.slot
|
||||
srv.lock.Lock()
|
||||
srv.peers[peer.slot] = nil
|
||||
srv.lock.Unlock()
|
||||
srv.removePeer(peer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,7 +379,7 @@ func (srv *Server) addPeer(conn net.Conn, desc *peerAddr, slot int) *Peer {
|
||||
func (srv *Server) removePeer(peer *Peer) {
|
||||
srv.lock.Lock()
|
||||
defer srv.lock.Unlock()
|
||||
srvlog.Debugf("Removing peer %v %v (slot %v)\n", peer, peer.slot)
|
||||
srvlog.Debugf("Removing %v (slot %v)\n", peer, peer.slot)
|
||||
if srv.peers[peer.slot] != peer {
|
||||
srvlog.Warnln("Invalid peer to remove:", peer)
|
||||
return
|
||||
@ -416,6 +411,7 @@ func (srv *Server) verifyPeer(addr *peerAddr) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO replace with "Set"
|
||||
type Blacklist interface {
|
||||
Get([]byte) (bool, error)
|
||||
Put([]byte) error
|
||||
|
881
peer.go
881
peer.go
@ -1,881 +0,0 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
)
|
||||
|
||||
var peerlogger = logger.NewLogger("PEER")
|
||||
|
||||
const (
|
||||
// The size of the output buffer for writing messages
|
||||
outputBufferSize = 50
|
||||
// Current protocol version
|
||||
ProtocolVersion = 49
|
||||
// Current P2P version
|
||||
P2PVersion = 2
|
||||
// Ethereum network version
|
||||
NetVersion = 0
|
||||
// Interval for ping/pong message
|
||||
pingPongTimer = 2 * time.Second
|
||||
)
|
||||
|
||||
type DiscReason byte
|
||||
|
||||
const (
|
||||
// Values are given explicitly instead of by iota because these values are
|
||||
// defined by the wire protocol spec; it is easier for humans to ensure
|
||||
// correctness when values are explicit.
|
||||
DiscRequested DiscReason = iota
|
||||
DiscReTcpSysErr
|
||||
DiscBadProto
|
||||
DiscBadPeer
|
||||
DiscTooManyPeers
|
||||
DiscConnDup
|
||||
DiscGenesisErr
|
||||
DiscProtoErr
|
||||
DiscQuitting
|
||||
)
|
||||
|
||||
var discReasonToString = []string{
|
||||
"requested",
|
||||
"TCP sys error",
|
||||
"bad protocol",
|
||||
"useless peer",
|
||||
"too many peers",
|
||||
"already connected",
|
||||
"wrong genesis block",
|
||||
"incompatible network",
|
||||
"quitting",
|
||||
}
|
||||
|
||||
func (d DiscReason) String() string {
|
||||
if len(discReasonToString) < int(d) {
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
return discReasonToString[d]
|
||||
}
|
||||
|
||||
// Peer capabilities
|
||||
type Caps byte
|
||||
|
||||
const (
|
||||
CapPeerDiscTy Caps = 1 << iota
|
||||
CapTxTy
|
||||
CapChainTy
|
||||
|
||||
CapDefault = CapChainTy | CapTxTy | CapPeerDiscTy
|
||||
)
|
||||
|
||||
var capsToString = map[Caps]string{
|
||||
CapPeerDiscTy: "Peer discovery",
|
||||
CapTxTy: "Transaction relaying",
|
||||
CapChainTy: "Block chain relaying",
|
||||
}
|
||||
|
||||
func (c Caps) IsCap(cap Caps) bool {
|
||||
return c&cap > 0
|
||||
}
|
||||
|
||||
func (c Caps) String() string {
|
||||
var caps []string
|
||||
if c.IsCap(CapPeerDiscTy) {
|
||||
caps = append(caps, capsToString[CapPeerDiscTy])
|
||||
}
|
||||
if c.IsCap(CapChainTy) {
|
||||
caps = append(caps, capsToString[CapChainTy])
|
||||
}
|
||||
if c.IsCap(CapTxTy) {
|
||||
caps = append(caps, capsToString[CapTxTy])
|
||||
}
|
||||
|
||||
return strings.Join(caps, " | ")
|
||||
}
|
||||
|
||||
type Peer struct {
|
||||
// Ethereum interface
|
||||
ethereum *Ethereum
|
||||
// Net connection
|
||||
conn net.Conn
|
||||
// Output queue which is used to communicate and handle messages
|
||||
outputQueue chan *wire.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
|
||||
statusKnown bool
|
||||
|
||||
// Last received pong message
|
||||
lastPong int64
|
||||
lastBlockReceived time.Time
|
||||
doneFetchingHashes bool
|
||||
lastHashAt time.Time
|
||||
lastHashRequestedAt time.Time
|
||||
|
||||
host []byte
|
||||
port uint16
|
||||
caps Caps
|
||||
td *big.Int
|
||||
bestHash []byte
|
||||
lastReceivedHash []byte
|
||||
requestedHashes [][]byte
|
||||
|
||||
// This peer's public key
|
||||
pubkey []byte
|
||||
|
||||
// Indicated whether the node is catching up or not
|
||||
catchingUp bool
|
||||
diverted bool
|
||||
blocksRequested int
|
||||
|
||||
version string
|
||||
|
||||
// We use this to give some kind of pingtime to a node, not very accurate, could be improved.
|
||||
pingTime time.Duration
|
||||
pingStartTime time.Time
|
||||
|
||||
lastRequestedBlock *types.Block
|
||||
|
||||
protocolCaps *ethutil.Value
|
||||
}
|
||||
|
||||
func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer {
|
||||
pubkey := ethereum.KeyManager().PublicKey()[1:]
|
||||
|
||||
return &Peer{
|
||||
outputQueue: make(chan *wire.Msg, outputBufferSize),
|
||||
quit: make(chan bool),
|
||||
ethereum: ethereum,
|
||||
conn: conn,
|
||||
inbound: inbound,
|
||||
disconnect: 0,
|
||||
connected: 1,
|
||||
port: 30303,
|
||||
pubkey: pubkey,
|
||||
blocksRequested: 10,
|
||||
caps: ethereum.ServerCaps(),
|
||||
version: ethereum.ClientIdentity().String(),
|
||||
protocolCaps: ethutil.NewValue(nil),
|
||||
td: big.NewInt(0),
|
||||
doneFetchingHashes: true,
|
||||
}
|
||||
}
|
||||
|
||||
func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
|
||||
p := &Peer{
|
||||
outputQueue: make(chan *wire.Msg, outputBufferSize),
|
||||
quit: make(chan bool),
|
||||
ethereum: ethereum,
|
||||
inbound: false,
|
||||
connected: 0,
|
||||
disconnect: 0,
|
||||
port: 30303,
|
||||
caps: caps,
|
||||
version: ethereum.ClientIdentity().String(),
|
||||
protocolCaps: ethutil.NewValue(nil),
|
||||
td: big.NewInt(0),
|
||||
doneFetchingHashes: true,
|
||||
}
|
||||
|
||||
// Set up the connection in another goroutine so we don't block the main thread
|
||||
go func() {
|
||||
conn, err := p.Connect(addr)
|
||||
if err != nil {
|
||||
//peerlogger.Debugln("Connection to peer failed. Giving up.", err)
|
||||
p.Stop()
|
||||
return
|
||||
}
|
||||
p.conn = conn
|
||||
|
||||
// Atomically set the connection state
|
||||
atomic.StoreInt32(&p.connected, 1)
|
||||
atomic.StoreInt32(&p.disconnect, 0)
|
||||
|
||||
p.Start()
|
||||
}()
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (self *Peer) Connect(addr string) (conn net.Conn, err error) {
|
||||
const maxTries = 3
|
||||
for attempts := 0; attempts < maxTries; attempts++ {
|
||||
conn, err = net.DialTimeout("tcp", addr, 10*time.Second)
|
||||
if err != nil {
|
||||
time.Sleep(time.Duration(attempts*20) * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// Success
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Getters
|
||||
func (p *Peer) PingTime() string {
|
||||
return p.pingTime.String()
|
||||
}
|
||||
func (p *Peer) Inbound() bool {
|
||||
return p.inbound
|
||||
}
|
||||
func (p *Peer) LastSend() time.Time {
|
||||
return p.lastSend
|
||||
}
|
||||
func (p *Peer) LastPong() int64 {
|
||||
return p.lastPong
|
||||
}
|
||||
func (p *Peer) Host() []byte {
|
||||
return p.host
|
||||
}
|
||||
func (p *Peer) Port() uint16 {
|
||||
return p.port
|
||||
}
|
||||
func (p *Peer) Version() string {
|
||||
return p.version
|
||||
}
|
||||
func (p *Peer) Connected() *int32 {
|
||||
return &p.connected
|
||||
}
|
||||
|
||||
// Setters
|
||||
func (p *Peer) SetVersion(version string) {
|
||||
p.version = version
|
||||
}
|
||||
|
||||
// Outputs any RLP encoded data to the peer
|
||||
func (p *Peer) QueueMessage(msg *wire.Msg) {
|
||||
if atomic.LoadInt32(&p.connected) != 1 {
|
||||
return
|
||||
}
|
||||
p.outputQueue <- msg
|
||||
}
|
||||
|
||||
func (p *Peer) writeMessage(msg *wire.Msg) {
|
||||
// Ignore the write if we're not connected
|
||||
if atomic.LoadInt32(&p.connected) != 1 {
|
||||
return
|
||||
}
|
||||
|
||||
if !p.versionKnown {
|
||||
switch msg.Type {
|
||||
case wire.MsgHandshakeTy: // Ok
|
||||
default: // Anything but ack is allowed
|
||||
return
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
if !p.statusKnown {
|
||||
switch msg.Type {
|
||||
case wire.MsgStatusTy: // Ok
|
||||
default: // Anything but ack is allowed
|
||||
return
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
peerlogger.DebugDetailf("(%v) <= %v\n", p.conn.RemoteAddr(), formatMessage(msg))
|
||||
|
||||
err := wire.WriteMessage(p.conn, msg)
|
||||
if err != nil {
|
||||
peerlogger.Debugln(" 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
|
||||
pingTimer := time.NewTicker(pingPongTimer)
|
||||
serviceTimer := time.NewTicker(10 * time.Second)
|
||||
|
||||
out:
|
||||
for {
|
||||
skip:
|
||||
select {
|
||||
// Main message queue. All outbound messages are processed through here
|
||||
case msg := <-p.outputQueue:
|
||||
if !p.statusKnown {
|
||||
switch msg.Type {
|
||||
case wire.MsgTxTy, wire.MsgGetBlockHashesTy, wire.MsgBlockHashesTy, wire.MsgGetBlocksTy, wire.MsgBlockTy:
|
||||
break skip
|
||||
}
|
||||
}
|
||||
|
||||
switch msg.Type {
|
||||
case wire.MsgGetBlockHashesTy:
|
||||
p.lastHashRequestedAt = time.Now()
|
||||
}
|
||||
|
||||
p.writeMessage(msg)
|
||||
p.lastSend = time.Now()
|
||||
|
||||
// Ping timer
|
||||
case <-pingTimer.C:
|
||||
p.writeMessage(wire.NewMessage(wire.MsgPingTy, ""))
|
||||
p.pingStartTime = time.Now()
|
||||
|
||||
// Service timer takes care of peer broadcasting, transaction
|
||||
// posting or block posting
|
||||
case <-serviceTimer.C:
|
||||
p.QueueMessage(wire.NewMessage(wire.MsgGetPeersTy, ""))
|
||||
|
||||
case <-p.quit:
|
||||
// Break out of the for loop if a quit message is posted
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func formatMessage(msg *wire.Msg) (ret string) {
|
||||
ret = fmt.Sprintf("%v %v", msg.Type, msg.Data)
|
||||
|
||||
/*
|
||||
XXX Commented out because I need the log level here to determine
|
||||
if i should or shouldn't generate this message
|
||||
*/
|
||||
/*
|
||||
switch msg.Type {
|
||||
case wire.MsgPeersTy:
|
||||
ret += fmt.Sprintf("(%d entries)", msg.Data.Len())
|
||||
case wire.MsgBlockTy:
|
||||
b1, b2 := chain.NewBlockFromRlpValue(msg.Data.Get(0)), ethchain.NewBlockFromRlpValue(msg.Data.Get(msg.Data.Len()-1))
|
||||
ret += fmt.Sprintf("(%d entries) %x - %x", msg.Data.Len(), b1.Hash()[0:4], b2.Hash()[0:4])
|
||||
case wire.MsgBlockHashesTy:
|
||||
h1, h2 := msg.Data.Get(0).Bytes(), msg.Data.Get(msg.Data.Len()-1).Bytes()
|
||||
ret += fmt.Sprintf("(%d entries) %x - %x", msg.Data.Len(), h1, h2)
|
||||
}
|
||||
*/
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Inbound handler. Inbound messages are received here and passed to the appropriate methods
|
||||
func (p *Peer) HandleInbound() {
|
||||
for atomic.LoadInt32(&p.disconnect) == 0 {
|
||||
|
||||
// HMM?
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
// Wait for a message from the peer
|
||||
msgs, err := wire.ReadMessages(p.conn)
|
||||
if err != nil {
|
||||
peerlogger.Debugln(err)
|
||||
}
|
||||
for _, msg := range msgs {
|
||||
peerlogger.DebugDetailf("(%v) => %v\n", p.conn.RemoteAddr(), formatMessage(msg))
|
||||
|
||||
switch msg.Type {
|
||||
case wire.MsgHandshakeTy:
|
||||
// Version message
|
||||
p.handleHandshake(msg)
|
||||
|
||||
//if p.caps.IsCap(CapPeerDiscTy) {
|
||||
p.QueueMessage(wire.NewMessage(wire.MsgGetPeersTy, ""))
|
||||
//}
|
||||
|
||||
case wire.MsgDiscTy:
|
||||
p.Stop()
|
||||
peerlogger.Infoln("Disconnect peer: ", DiscReason(msg.Data.Get(0).Uint()))
|
||||
case wire.MsgPingTy:
|
||||
// Respond back with pong
|
||||
p.QueueMessage(wire.NewMessage(wire.MsgPongTy, ""))
|
||||
case wire.MsgPongTy:
|
||||
// If we received a pong back from a peer we set the
|
||||
// last pong so the peer handler knows this peer is still
|
||||
// active.
|
||||
p.lastPong = time.Now().Unix()
|
||||
p.pingTime = time.Since(p.pingStartTime)
|
||||
case wire.MsgTxTy:
|
||||
// If the message was a transaction queue the transaction
|
||||
// in the TxPool where it will undergo validation and
|
||||
// processing when a new block is found
|
||||
for i := 0; i < msg.Data.Len(); i++ {
|
||||
tx := types.NewTransactionFromValue(msg.Data.Get(i))
|
||||
err := p.ethereum.TxPool().Add(tx)
|
||||
if err != nil {
|
||||
peerlogger.Infoln(err)
|
||||
} else {
|
||||
peerlogger.Infof("tx OK (%x)\n", tx.Hash()[0:4])
|
||||
}
|
||||
}
|
||||
case wire.MsgGetPeersTy:
|
||||
// Peer asked for list of connected peers
|
||||
//p.pushPeers()
|
||||
case wire.MsgPeersTy:
|
||||
// Received a list of peers (probably because MsgGetPeersTy was send)
|
||||
data := msg.Data
|
||||
// Create new list of possible peers for the ethereum to process
|
||||
peers := make([]string, data.Len())
|
||||
// Parse each possible peer
|
||||
for i := 0; i < data.Len(); i++ {
|
||||
value := data.Get(i)
|
||||
peers[i] = unpackAddr(value.Get(0), value.Get(1).Uint())
|
||||
}
|
||||
|
||||
// Connect to the list of peers
|
||||
p.ethereum.ProcessPeerList(peers)
|
||||
|
||||
case wire.MsgStatusTy:
|
||||
// Handle peer's status msg
|
||||
p.handleStatus(msg)
|
||||
}
|
||||
|
||||
// TMP
|
||||
if p.statusKnown {
|
||||
switch msg.Type {
|
||||
|
||||
case wire.MsgGetBlockHashesTy:
|
||||
if msg.Data.Len() < 2 {
|
||||
peerlogger.Debugln("err: argument length invalid ", msg.Data.Len())
|
||||
}
|
||||
|
||||
hash := msg.Data.Get(0).Bytes()
|
||||
amount := msg.Data.Get(1).Uint()
|
||||
|
||||
hashes := p.ethereum.ChainManager().GetChainHashesFromHash(hash, amount)
|
||||
|
||||
p.QueueMessage(wire.NewMessage(wire.MsgBlockHashesTy, ethutil.ByteSliceToInterface(hashes)))
|
||||
|
||||
case wire.MsgGetBlocksTy:
|
||||
// Limit to max 300 blocks
|
||||
max := int(math.Min(float64(msg.Data.Len()), 300.0))
|
||||
var blocks []interface{}
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
hash := msg.Data.Get(i).Bytes()
|
||||
block := p.ethereum.ChainManager().GetBlock(hash)
|
||||
if block != nil {
|
||||
blocks = append(blocks, block.Value().Raw())
|
||||
}
|
||||
}
|
||||
|
||||
p.QueueMessage(wire.NewMessage(wire.MsgBlockTy, blocks))
|
||||
|
||||
case wire.MsgBlockHashesTy:
|
||||
p.catchingUp = true
|
||||
|
||||
blockPool := p.ethereum.blockPool
|
||||
|
||||
foundCommonHash := false
|
||||
p.lastHashAt = time.Now()
|
||||
|
||||
it := msg.Data.NewIterator()
|
||||
for it.Next() {
|
||||
hash := it.Value().Bytes()
|
||||
p.lastReceivedHash = hash
|
||||
|
||||
if blockPool.HasCommonHash(hash) {
|
||||
foundCommonHash = true
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
blockPool.AddHash(hash, p)
|
||||
}
|
||||
|
||||
if !foundCommonHash {
|
||||
p.FetchHashes()
|
||||
} else {
|
||||
peerlogger.Infof("Found common hash (%x...)\n", p.lastReceivedHash[0:4])
|
||||
p.doneFetchingHashes = true
|
||||
}
|
||||
|
||||
case wire.MsgBlockTy:
|
||||
p.catchingUp = true
|
||||
|
||||
blockPool := p.ethereum.blockPool
|
||||
|
||||
it := msg.Data.NewIterator()
|
||||
for it.Next() {
|
||||
block := types.NewBlockFromRlpValue(it.Value())
|
||||
blockPool.Add(block, p)
|
||||
|
||||
p.lastBlockReceived = time.Now()
|
||||
}
|
||||
case wire.MsgNewBlockTy:
|
||||
var (
|
||||
blockPool = p.ethereum.blockPool
|
||||
block = types.NewBlockFromRlpValue(msg.Data.Get(0))
|
||||
td = msg.Data.Get(1).BigInt()
|
||||
)
|
||||
|
||||
if td.Cmp(blockPool.td) > 0 {
|
||||
p.ethereum.blockPool.AddNew(block, p)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.Stop()
|
||||
}
|
||||
|
||||
func (self *Peer) FetchBlocks(hashes [][]byte) {
|
||||
if len(hashes) > 0 {
|
||||
peerlogger.Debugf("Fetching blocks (%d)\n", len(hashes))
|
||||
|
||||
self.QueueMessage(wire.NewMessage(wire.MsgGetBlocksTy, ethutil.ByteSliceToInterface(hashes)))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Peer) FetchHashes() bool {
|
||||
blockPool := self.ethereum.blockPool
|
||||
|
||||
return blockPool.FetchHashes(self)
|
||||
}
|
||||
|
||||
func (self *Peer) FetchingHashes() bool {
|
||||
return !self.doneFetchingHashes
|
||||
}
|
||||
|
||||
// General update method
|
||||
func (self *Peer) update() {
|
||||
serviceTimer := time.NewTicker(100 * time.Millisecond)
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-serviceTimer.C:
|
||||
if self.IsCap("eth") {
|
||||
var (
|
||||
sinceBlock = time.Since(self.lastBlockReceived)
|
||||
)
|
||||
|
||||
if sinceBlock > 5*time.Second {
|
||||
self.catchingUp = false
|
||||
}
|
||||
}
|
||||
case <-self.quit:
|
||||
break out
|
||||
}
|
||||
}
|
||||
|
||||
serviceTimer.Stop()
|
||||
}
|
||||
|
||||
func (p *Peer) Start() {
|
||||
peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String())
|
||||
servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
|
||||
|
||||
if p.inbound {
|
||||
p.host, p.port = packAddr(peerHost, peerPort)
|
||||
} else {
|
||||
p.host, p.port = packAddr(servHost, servPort)
|
||||
}
|
||||
|
||||
err := p.pushHandshake()
|
||||
if err != nil {
|
||||
peerlogger.Debugln("Peer can't send outbound version ack", err)
|
||||
|
||||
p.Stop()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
go p.HandleOutbound()
|
||||
// Run the inbound handler in a new goroutine
|
||||
go p.HandleInbound()
|
||||
// Run the general update handler
|
||||
go p.update()
|
||||
|
||||
// Wait a few seconds for startup and then ask for an initial ping
|
||||
time.Sleep(2 * time.Second)
|
||||
p.writeMessage(wire.NewMessage(wire.MsgPingTy, ""))
|
||||
p.pingStartTime = time.Now()
|
||||
|
||||
}
|
||||
|
||||
func (p *Peer) Stop() {
|
||||
p.StopWithReason(DiscRequested)
|
||||
}
|
||||
|
||||
func (p *Peer) StopWithReason(reason DiscReason) {
|
||||
if atomic.AddInt32(&p.disconnect, 1) != 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// Pre-emptively remove the peer; don't wait for reaping. We already know it's dead if we are here
|
||||
p.ethereum.RemovePeer(p)
|
||||
|
||||
close(p.quit)
|
||||
if atomic.LoadInt32(&p.connected) != 0 {
|
||||
p.writeMessage(wire.NewMessage(wire.MsgDiscTy, reason))
|
||||
p.conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Peer) peersMessage() *wire.Msg {
|
||||
outPeers := make([]interface{}, len(p.ethereum.InOutPeers()))
|
||||
// Serialise each peer
|
||||
for i, peer := range p.ethereum.InOutPeers() {
|
||||
// Don't return localhost as valid peer
|
||||
if !net.ParseIP(peer.conn.RemoteAddr().String()).IsLoopback() {
|
||||
outPeers[i] = peer.RlpData()
|
||||
}
|
||||
}
|
||||
|
||||
// Return the message to the peer with the known list of connected clients
|
||||
return wire.NewMessage(wire.MsgPeersTy, outPeers)
|
||||
}
|
||||
|
||||
// Pushes the list of outbound peers to the client when requested
|
||||
func (p *Peer) pushPeers() {
|
||||
p.QueueMessage(p.peersMessage())
|
||||
}
|
||||
|
||||
func (self *Peer) pushStatus() {
|
||||
msg := wire.NewMessage(wire.MsgStatusTy, []interface{}{
|
||||
uint32(ProtocolVersion),
|
||||
uint32(NetVersion),
|
||||
self.ethereum.ChainManager().TD,
|
||||
self.ethereum.ChainManager().CurrentBlock.Hash(),
|
||||
self.ethereum.ChainManager().Genesis().Hash(),
|
||||
})
|
||||
|
||||
self.QueueMessage(msg)
|
||||
}
|
||||
|
||||
func (self *Peer) handleStatus(msg *wire.Msg) {
|
||||
c := msg.Data
|
||||
|
||||
var (
|
||||
//protoVersion = c.Get(0).Uint()
|
||||
netVersion = c.Get(1).Uint()
|
||||
td = c.Get(2).BigInt()
|
||||
bestHash = c.Get(3).Bytes()
|
||||
genesis = c.Get(4).Bytes()
|
||||
)
|
||||
|
||||
if bytes.Compare(self.ethereum.ChainManager().Genesis().Hash(), genesis) != 0 {
|
||||
loggerger.Warnf("Invalid genisis hash %x. Disabling [eth]\n", genesis)
|
||||
return
|
||||
}
|
||||
|
||||
if netVersion != NetVersion {
|
||||
loggerger.Warnf("Invalid network version %d. Disabling [eth]\n", netVersion)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
if protoVersion != ProtocolVersion {
|
||||
loggerger.Warnf("Invalid protocol version %d. Disabling [eth]\n", protoVersion)
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
// Get the td and last hash
|
||||
self.td = td
|
||||
self.bestHash = bestHash
|
||||
self.lastReceivedHash = bestHash
|
||||
|
||||
self.statusKnown = true
|
||||
|
||||
// Compare the total TD with the blockchain TD. If remote is higher
|
||||
// fetch hashes from highest TD node.
|
||||
self.FetchHashes()
|
||||
|
||||
loggerger.Infof("Peer is [eth] capable. (TD = %v ~ %x)", self.td, self.bestHash)
|
||||
|
||||
}
|
||||
|
||||
func (p *Peer) pushHandshake() error {
|
||||
pubkey := p.ethereum.KeyManager().PublicKey()
|
||||
msg := wire.NewMessage(wire.MsgHandshakeTy, []interface{}{
|
||||
P2PVersion, []byte(p.version), []interface{}{[]interface{}{"eth", ProtocolVersion}}, p.port, pubkey[1:],
|
||||
})
|
||||
|
||||
p.QueueMessage(msg)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Peer) handleHandshake(msg *wire.Msg) {
|
||||
c := msg.Data
|
||||
|
||||
var (
|
||||
p2pVersion = c.Get(0).Uint()
|
||||
clientId = c.Get(1).Str()
|
||||
caps = c.Get(2)
|
||||
port = c.Get(3).Uint()
|
||||
pub = c.Get(4).Bytes()
|
||||
)
|
||||
|
||||
// Check correctness of p2p protocol version
|
||||
if p2pVersion != P2PVersion {
|
||||
peerlogger.Debugf("Invalid P2P version. Require protocol %d, received %d\n", P2PVersion, p2pVersion)
|
||||
p.Stop()
|
||||
return
|
||||
}
|
||||
|
||||
// Handle the pub key (validation, uniqueness)
|
||||
if len(pub) == 0 {
|
||||
peerlogger.Warnln("Pubkey required, not supplied in handshake.")
|
||||
p.Stop()
|
||||
return
|
||||
}
|
||||
|
||||
// Self connect detection
|
||||
pubkey := p.ethereum.KeyManager().PublicKey()
|
||||
if bytes.Compare(pubkey[1:], pub) == 0 {
|
||||
p.Stop()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Check for blacklisting
|
||||
for _, pk := range p.ethereum.blacklist {
|
||||
if bytes.Compare(pk, pub) == 0 {
|
||||
peerlogger.Debugf("Blacklisted peer tried to connect (%x...)\n", pubkey[0:4])
|
||||
p.StopWithReason(DiscBadPeer)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
usedPub := 0
|
||||
// This peer is already added to the peerlist so we expect to find a double pubkey at least once
|
||||
eachPeer(p.ethereum.Peers(), func(peer *Peer, e *list.Element) {
|
||||
if bytes.Compare(pub, peer.pubkey) == 0 {
|
||||
usedPub++
|
||||
}
|
||||
})
|
||||
|
||||
if usedPub > 0 {
|
||||
peerlogger.Debugf("Pubkey %x found more then once. Already connected to client.", p.pubkey)
|
||||
p.Stop()
|
||||
return
|
||||
}
|
||||
p.pubkey = pub
|
||||
|
||||
// If this is an inbound connection send an ack back
|
||||
if p.inbound {
|
||||
p.port = uint16(port)
|
||||
}
|
||||
|
||||
p.SetVersion(clientId)
|
||||
|
||||
p.versionKnown = true
|
||||
|
||||
p.ethereum.PushPeer(p)
|
||||
p.ethereum.eventMux.Post(PeerListEvent{p.ethereum.Peers()})
|
||||
|
||||
p.protocolCaps = caps
|
||||
|
||||
it := caps.NewIterator()
|
||||
var capsStrs []string
|
||||
for it.Next() {
|
||||
cap := it.Value().Get(0).Str()
|
||||
ver := it.Value().Get(1).Uint()
|
||||
switch cap {
|
||||
case "eth":
|
||||
if ver != ProtocolVersion {
|
||||
loggerger.Warnf("Invalid protocol version %d. Disabling [eth]\n", ver)
|
||||
continue
|
||||
}
|
||||
p.pushStatus()
|
||||
}
|
||||
|
||||
capsStrs = append(capsStrs, fmt.Sprintf("%s/%d", cap, ver))
|
||||
}
|
||||
|
||||
peerlogger.Infof("Added peer (%s) %d / %d (%v)\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers, capsStrs)
|
||||
|
||||
peerlogger.Debugln(p)
|
||||
}
|
||||
|
||||
func (self *Peer) IsCap(cap string) bool {
|
||||
capsIt := self.protocolCaps.NewIterator()
|
||||
for capsIt.Next() {
|
||||
if capsIt.Value().Str() == cap {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *Peer) Caps() *ethutil.Value {
|
||||
return self.protocolCaps
|
||||
}
|
||||
|
||||
func (p *Peer) String() string {
|
||||
var strBoundType string
|
||||
if p.inbound {
|
||||
strBoundType = "inbound"
|
||||
} else {
|
||||
strBoundType = "outbound"
|
||||
}
|
||||
var strConnectType string
|
||||
if atomic.LoadInt32(&p.disconnect) == 0 {
|
||||
strConnectType = "connected"
|
||||
} else {
|
||||
strConnectType = "disconnected"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[%s] (%s) %v %s", strConnectType, strBoundType, p.conn.RemoteAddr(), p.version)
|
||||
|
||||
}
|
||||
|
||||
func (p *Peer) RlpData() []interface{} {
|
||||
return []interface{}{p.host, p.port, p.pubkey}
|
||||
}
|
||||
|
||||
func packAddr(address, _port string) (host []byte, port uint16) {
|
||||
p, _ := strconv.Atoi(_port)
|
||||
port = uint16(p)
|
||||
|
||||
h := net.ParseIP(address)
|
||||
if ip := h.To4(); ip != nil {
|
||||
host = []byte(ip)
|
||||
} else {
|
||||
host = []byte(h)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func unpackAddr(value *ethutil.Value, p uint64) string {
|
||||
host, _ := net.IP(value.Bytes()).MarshalText()
|
||||
prt := strconv.Itoa(int(p))
|
||||
|
||||
return net.JoinHostPort(string(host), prt)
|
||||
}
|
@ -3,7 +3,7 @@ package pow
|
||||
import "math/big"
|
||||
|
||||
type Block interface {
|
||||
Diff() *big.Int
|
||||
Difficulty() *big.Int
|
||||
HashNoNonce() []byte
|
||||
N() []byte
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ type EasyPow struct {
|
||||
}
|
||||
|
||||
func New() *EasyPow {
|
||||
return &EasyPow{}
|
||||
return &EasyPow{turbo: true}
|
||||
}
|
||||
|
||||
func (pow *EasyPow) GetHashrate() int64 {
|
||||
@ -35,7 +35,7 @@ func (pow *EasyPow) Turbo(on bool) {
|
||||
func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
hash := block.HashNoNonce()
|
||||
diff := block.Diff()
|
||||
diff := block.Difficulty()
|
||||
i := int64(0)
|
||||
start := time.Now().UnixNano()
|
||||
t := time.Now()
|
||||
@ -59,7 +59,7 @@ func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
|
||||
}
|
||||
|
||||
sha := crypto.Sha3(big.NewInt(r.Int63()).Bytes())
|
||||
if pow.verify(hash, diff, sha) {
|
||||
if verify(hash, diff, sha) {
|
||||
return sha
|
||||
}
|
||||
}
|
||||
@ -72,7 +72,11 @@ func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pow *EasyPow) verify(hash []byte, diff *big.Int, nonce []byte) bool {
|
||||
func (pow *EasyPow) Verify(block pow.Block) bool {
|
||||
return Verify(block)
|
||||
}
|
||||
|
||||
func verify(hash []byte, diff *big.Int, nonce []byte) bool {
|
||||
sha := sha3.NewKeccak256()
|
||||
|
||||
d := append(hash, nonce...)
|
||||
@ -84,6 +88,6 @@ func (pow *EasyPow) verify(hash []byte, diff *big.Int, nonce []byte) bool {
|
||||
return res.Cmp(verification) <= 0
|
||||
}
|
||||
|
||||
func (pow *EasyPow) Verify(block pow.Block) bool {
|
||||
return pow.verify(block.HashNoNonce(), block.Diff(), block.N())
|
||||
func Verify(block pow.Block) bool {
|
||||
return verify(block.HashNoNonce(), block.Difficulty(), block.N())
|
||||
}
|
||||
|
3038
profile.cov
3038
profile.cov
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,7 @@
|
||||
package ptrie
|
||||
|
||||
import "fmt"
|
||||
|
||||
type FullNode struct {
|
||||
trie *Trie
|
||||
nodes [17]Node
|
||||
@ -21,8 +23,10 @@ func (self *FullNode) Branches() []Node {
|
||||
func (self *FullNode) Copy() Node {
|
||||
nnode := NewFullNode(self.trie)
|
||||
for i, node := range self.nodes {
|
||||
if node != nil {
|
||||
nnode.nodes[i] = node
|
||||
}
|
||||
}
|
||||
|
||||
return nnode
|
||||
}
|
||||
@ -56,6 +60,10 @@ func (self *FullNode) RlpData() interface{} {
|
||||
}
|
||||
|
||||
func (self *FullNode) set(k byte, value Node) {
|
||||
if _, ok := value.(*ValueNode); ok && k != 16 {
|
||||
fmt.Println(value, k)
|
||||
}
|
||||
|
||||
self.nodes[int(k)] = value
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ type Iterator struct {
|
||||
}
|
||||
|
||||
func NewIterator(trie *Trie) *Iterator {
|
||||
return &Iterator{trie: trie, Key: []byte{0}}
|
||||
return &Iterator{trie: trie, Key: make([]byte, 32)}
|
||||
}
|
||||
|
||||
func (self *Iterator) Next() bool {
|
||||
|
@ -17,7 +17,7 @@ type Node interface {
|
||||
func (self *ValueNode) String() string { return self.fstring("") }
|
||||
func (self *FullNode) String() string { return self.fstring("") }
|
||||
func (self *ShortNode) String() string { return self.fstring("") }
|
||||
func (self *ValueNode) fstring(ind string) string { return fmt.Sprintf("%s ", self.data) }
|
||||
func (self *ValueNode) fstring(ind string) string { return fmt.Sprintf("%x ", self.data) }
|
||||
func (self *HashNode) fstring(ind string) string { return fmt.Sprintf("%x ", self.key) }
|
||||
|
||||
// Full node
|
||||
@ -36,5 +36,5 @@ func (self *FullNode) fstring(ind string) string {
|
||||
|
||||
// Short node
|
||||
func (self *ShortNode) fstring(ind string) string {
|
||||
return fmt.Sprintf("[ %s: %v ] ", self.key, self.value.fstring(ind+" "))
|
||||
return fmt.Sprintf("[ %x: %v ] ", self.key, self.value.fstring(ind+" "))
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user