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
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.3
|
- tip
|
||||||
before_install:
|
before_install:
|
||||||
- sudo add-apt-repository ppa:ubuntu-sdk-team/ppa -y
|
- sudo add-apt-repository ppa:ubuntu-sdk-team/ppa -y
|
||||||
- sudo apt-get update -qq
|
- sudo apt-get update -qq
|
||||||
@ -8,10 +8,10 @@ before_install:
|
|||||||
install:
|
install:
|
||||||
- go get code.google.com/p/go.tools/cmd/goimports
|
- go get code.google.com/p/go.tools/cmd/goimports
|
||||||
- go get github.com/golang/lint/golint
|
- go get github.com/golang/lint/golint
|
||||||
# - go get code.google.com/p/go.tools/cmd/vet
|
# - go get golang.org/x/tools/cmd/vet
|
||||||
- go get code.google.com/p/go.tools/cmd/cover
|
- 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
|
- 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:
|
before_script:
|
||||||
- gofmt -l -w .
|
- gofmt -l -w .
|
||||||
- goimports -l -w .
|
- goimports -l -w .
|
||||||
@ -19,7 +19,7 @@ before_script:
|
|||||||
# - go vet ./...
|
# - go vet ./...
|
||||||
# - go test -race ./...
|
# - go test -race ./...
|
||||||
script:
|
script:
|
||||||
- ./gocoverage.sh && goveralls -coverprofile=profile.cov -service=travis-ci -repotoken $COVERALLS_TOKEN
|
- ./gocoverage.sh
|
||||||
env:
|
env:
|
||||||
- secure: "U2U1AmkU4NJBgKR/uUAebQY87cNL0+1JHjnLOmmXwxYYyj5ralWb1aSuSH3qSXiT93qLBmtaUkuv9fberHVqrbAeVlztVdUsKAq7JMQH+M99iFkC9UiRMqHmtjWJ0ok4COD1sRYixxi21wb/JrMe3M1iL4QJVS61iltjHhVdM64="
|
- 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
|
## Fetch and install serpent-go
|
||||||
RUN go get -v -d github.com/ethereum/serpent-go
|
RUN go get -v -d github.com/ethereum/serpent-go
|
||||||
WORKDIR $GOPATH/src/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 git submodule update --init
|
||||||
RUN go install -v
|
RUN go install -v
|
||||||
|
|
||||||
# Fetch and install go-ethereum
|
# Fetch and install go-ethereum
|
||||||
RUN go get -v -d github.com/ethereum/go-ethereum/...
|
RUN go get -v -d github.com/ethereum/go-ethereum/...
|
||||||
WORKDIR $GOPATH/src/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 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
|
RUN go install -v ./cmd/ethereum
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ Ethereum
|
|||||||
[![Build
|
[![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%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
|
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.
|
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"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/ethereum/repl"
|
"github.com/ethereum/go-ethereum/cmd/ethereum/repl"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/javascript"
|
"github.com/ethereum/go-ethereum/javascript"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,7 +38,8 @@ var (
|
|||||||
StartRpc bool
|
StartRpc bool
|
||||||
StartWebSockets bool
|
StartWebSockets bool
|
||||||
RpcPort int
|
RpcPort int
|
||||||
UseUPnP bool
|
NatType string
|
||||||
|
PMPGateway string
|
||||||
OutboundPort string
|
OutboundPort string
|
||||||
ShowGenesis bool
|
ShowGenesis bool
|
||||||
AddPeer string
|
AddPeer string
|
||||||
@ -57,6 +58,7 @@ var (
|
|||||||
DumpHash string
|
DumpHash string
|
||||||
DumpNumber int
|
DumpNumber int
|
||||||
VmType int
|
VmType int
|
||||||
|
ImportChain string
|
||||||
)
|
)
|
||||||
|
|
||||||
// flags specific to cli client
|
// flags specific to cli client
|
||||||
@ -84,8 +86,9 @@ func Init() {
|
|||||||
flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
|
flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
|
||||||
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
|
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
|
||||||
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
||||||
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
|
flag.StringVar(&NatType, "nat", "", "NAT support (UPNP|PMP) (none)")
|
||||||
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers")
|
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.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
|
||||||
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
|
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
|
||||||
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket 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.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.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.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.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]")
|
||||||
flag.StringVar(&DumpHash, "hash", "", "specify arg in hex")
|
flag.StringVar(&DumpHash, "hash", "", "specify arg in hex")
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
@ -30,7 +31,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
ClientIdentifier = "Ethereum(G)"
|
ClientIdentifier = "Ethereum(G)"
|
||||||
Version = "0.7.9"
|
Version = "0.7.11"
|
||||||
)
|
)
|
||||||
|
|
||||||
var clilogger = logger.NewLogger("CLI")
|
var clilogger = logger.NewLogger("CLI")
|
||||||
@ -38,6 +39,10 @@ var clilogger = logger.NewLogger("CLI")
|
|||||||
func main() {
|
func main() {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
logger.Flush()
|
||||||
|
}()
|
||||||
|
|
||||||
utils.HandleInterrupt()
|
utils.HandleInterrupt()
|
||||||
|
|
||||||
// precedence: code-internal flag default < config file < environment variables < command line
|
// precedence: code-internal flag default < config file < environment variables < command line
|
||||||
@ -69,15 +74,15 @@ func main() {
|
|||||||
// create, import, export keys
|
// create, import, export keys
|
||||||
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
|
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 {
|
if Dump {
|
||||||
var block *types.Block
|
var block *types.Block
|
||||||
|
|
||||||
if len(DumpHash) == 0 && DumpNumber == -1 {
|
if len(DumpHash) == 0 && DumpNumber == -1 {
|
||||||
block = ethereum.ChainManager().CurrentBlock
|
block = ethereum.ChainManager().CurrentBlock()
|
||||||
} else if len(DumpHash) > 0 {
|
} else if len(DumpHash) > 0 {
|
||||||
block = ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(DumpHash))
|
block = ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(DumpHash))
|
||||||
} else {
|
} else {
|
||||||
@ -93,9 +98,6 @@ func main() {
|
|||||||
os.Exit(1)
|
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
|
// Leave the Println. This needs clean output for piping
|
||||||
fmt.Printf("%s\n", block.State().Dump())
|
fmt.Printf("%s\n", block.State().Dump())
|
||||||
|
|
||||||
@ -112,6 +114,16 @@ func main() {
|
|||||||
utils.StartMining(ethereum)
|
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
|
// better reworked as cases
|
||||||
if StartJsConsole {
|
if StartJsConsole {
|
||||||
InitJsConsole(ethereum)
|
InitJsConsole(ethereum)
|
||||||
@ -131,5 +143,4 @@ func main() {
|
|||||||
|
|
||||||
// this blocks the thread
|
// this blocks the thread
|
||||||
ethereum.WaitForShutdown()
|
ethereum.WaitForShutdown()
|
||||||
logger.Flush()
|
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/javascript"
|
"github.com/ethereum/go-ethereum/javascript"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
@ -86,12 +86,6 @@ func (self *JSRepl) Stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *JSRepl) parseInput(code string) {
|
func (self *JSRepl) parseInput(code string) {
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
fmt.Println("[native] error", r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
value, err := self.re.Run(code)
|
value, err := self.re.Run(code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
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:
|
# or operating system, you probably want to add a global ignore instead:
|
||||||
# git config --global core.excludesfile ~/.gitignore_global
|
# git config --global core.excludesfile ~/.gitignore_global
|
||||||
|
|
||||||
|
*.swp
|
||||||
/tmp
|
/tmp
|
||||||
*/**/*un~
|
*/**/*un~
|
||||||
*un~
|
*un~
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*/**/.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
|
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)
|
gexp := ethutil.Big(test.Gas)
|
||||||
if gexp.Cmp(gas) != 0 {
|
if gexp.Cmp(gas) != 0 {
|
||||||
log.Printf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas)
|
log.Printf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas)
|
||||||
failed = 1
|
failed = 1
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for addr, account := range test.Post {
|
for addr, account := range test.Post {
|
||||||
obj := state.GetStateObject(helper.FromHex(addr))
|
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/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/ptrie"
|
||||||
"github.com/ethereum/go-ethereum/state"
|
"github.com/ethereum/go-ethereum/state"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
|
||||||
"github.com/ethereum/go-ethereum/vm"
|
"github.com/ethereum/go-ethereum/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ func main() {
|
|||||||
ethutil.ReadConfig("/tmp/evmtest", "/tmp/evm", "")
|
ethutil.ReadConfig("/tmp/evmtest", "/tmp/evm", "")
|
||||||
|
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb := state.New(trie.New(db, ""))
|
statedb := state.New(ptrie.New(nil, db))
|
||||||
sender := statedb.NewStateObject([]byte("sender"))
|
sender := statedb.NewStateObject([]byte("sender"))
|
||||||
receiver := statedb.NewStateObject([]byte("receiver"))
|
receiver := statedb.NewStateObject([]byte("receiver"))
|
||||||
//receiver.SetCode([]byte(*code))
|
//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 {
|
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execution {
|
||||||
evm := vm.New(self, vm.DebugVmTy)
|
return core.NewExecution(self, addr, data, gas, price, value)
|
||||||
|
|
||||||
return core.NewExecution(evm, addr, data, gas, price, value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||||
|
@ -66,7 +66,11 @@ Rectangle {
|
|||||||
onMessages: {
|
onMessages: {
|
||||||
// Bit of a cheat to get proper JSON
|
// Bit of a cheat to get proper JSON
|
||||||
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
|
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 {
|
Item {
|
||||||
@ -327,6 +331,33 @@ Rectangle {
|
|||||||
require(1);
|
require(1);
|
||||||
eth.uninstallFilter(data.args[0])
|
eth.uninstallFilter(data.args[0])
|
||||||
break;
|
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) {
|
} catch(e) {
|
||||||
console.log(data.call + ": " + e)
|
console.log(data.call + ": " + e)
|
||||||
@ -348,8 +379,8 @@ Rectangle {
|
|||||||
function postData(seed, data) {
|
function postData(seed, data) {
|
||||||
webview.experimental.postMessage(JSON.stringify({data: data, _id: seed}))
|
webview.experimental.postMessage(JSON.stringify({data: data, _id: seed}))
|
||||||
}
|
}
|
||||||
function postEvent(event, data) {
|
function postEvent(event, id, data) {
|
||||||
webview.experimental.postMessage(JSON.stringify({data: data, _event: event}))
|
webview.experimental.postMessage(JSON.stringify({data: data, _id: id, _event: event}))
|
||||||
}
|
}
|
||||||
|
|
||||||
function onWatchedCb(data, id) {
|
function onWatchedCb(data, id) {
|
@ -45,11 +45,12 @@ ApplicationWindow {
|
|||||||
// Takes care of loading all default plugins
|
// Takes care of loading all default plugins
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
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;
|
root.browser = browser;
|
||||||
addPlugin("./views/miner.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
addPlugin("./views/miner.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||||
|
|
||||||
addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"});
|
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/chain.qml", {noAdd: true, close: false, section: "legacy"});
|
||||||
addPlugin("./views/pending_tx.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"});
|
addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"});
|
||||||
@ -786,8 +787,8 @@ ApplicationWindow {
|
|||||||
title: "About"
|
title: "About"
|
||||||
minimumWidth: 350
|
minimumWidth: 350
|
||||||
maximumWidth: 350
|
maximumWidth: 350
|
||||||
maximumHeight: 200
|
maximumHeight: 280
|
||||||
minimumHeight: 200
|
minimumHeight: 280
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: aboutIcon
|
id: aboutIcon
|
||||||
@ -797,7 +798,7 @@ ApplicationWindow {
|
|||||||
smooth: true
|
smooth: true
|
||||||
source: "../facet.png"
|
source: "../facet.png"
|
||||||
x: 10
|
x: 10
|
||||||
y: 10
|
y: 30
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@ -806,7 +807,7 @@ ApplicationWindow {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: 30
|
anchors.topMargin: 30
|
||||||
font.pointSize: 12
|
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)
|
self.SetAsm(script)
|
||||||
|
|
||||||
block := self.lib.eth.ChainManager().CurrentBlock
|
block := self.lib.eth.ChainManager().CurrentBlock()
|
||||||
|
|
||||||
env := utils.NewEnv(statedb, block, account.Address(), value)
|
env := utils.NewEnv(statedb, block, account.Address(), value)
|
||||||
|
|
||||||
|
@ -36,10 +36,12 @@ var (
|
|||||||
Identifier string
|
Identifier string
|
||||||
KeyRing string
|
KeyRing string
|
||||||
KeyStore string
|
KeyStore string
|
||||||
|
PMPGateway string
|
||||||
StartRpc bool
|
StartRpc bool
|
||||||
StartWebSockets bool
|
StartWebSockets bool
|
||||||
RpcPort int
|
RpcPort int
|
||||||
UseUPnP bool
|
UseUPnP bool
|
||||||
|
NatType string
|
||||||
OutboundPort string
|
OutboundPort string
|
||||||
ShowGenesis bool
|
ShowGenesis bool
|
||||||
AddPeer string
|
AddPeer string
|
||||||
@ -104,17 +106,19 @@ func Init() {
|
|||||||
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
|
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
|
||||||
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
||||||
flag.BoolVar(&UseUPnP, "upnp", true, "enable UPnP support")
|
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.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
|
||||||
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
|
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
|
||||||
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server")
|
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server")
|
||||||
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
|
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
|
||||||
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
||||||
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
|
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(&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(&ExportDir, "export", "", "exports the session keyring to files in the directory given")
|
||||||
flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
|
flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
|
||||||
flag.StringVar(&Datadir, "datadir", defaultDataDir(), "specifies the datadir to use")
|
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(&ConfigFile, "conf", defaultConfigFile, "config file")
|
||||||
flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)")
|
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)")
|
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"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/miner"
|
"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"
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
"gopkg.in/qml.v1"
|
"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")
|
var guilogger = logger.NewLogger("GUI")
|
||||||
|
|
||||||
type Gui struct {
|
type Gui struct {
|
||||||
@ -88,6 +57,7 @@ type Gui struct {
|
|||||||
|
|
||||||
// The public Ethereum library
|
// The public Ethereum library
|
||||||
uiLib *UiLib
|
uiLib *UiLib
|
||||||
|
whisper *qwhisper.Whisper
|
||||||
|
|
||||||
txDb *ethdb.LDBDatabase
|
txDb *ethdb.LDBDatabase
|
||||||
|
|
||||||
@ -97,7 +67,7 @@ type Gui struct {
|
|||||||
pipe *xeth.JSXEth
|
pipe *xeth.JSXEth
|
||||||
|
|
||||||
Session string
|
Session string
|
||||||
clientIdentity *wire.SimpleClientIdentity
|
clientIdentity *p2p.SimpleClientIdentity
|
||||||
config *ethutil.ConfigManager
|
config *ethutil.ConfigManager
|
||||||
|
|
||||||
plugins map[string]plugin
|
plugins map[string]plugin
|
||||||
@ -107,7 +77,7 @@ type Gui struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create GUI, but doesn't start it
|
// 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")
|
db, err := ethdb.NewLDBDatabase("tx_database")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -138,10 +108,12 @@ func (gui *Gui) Start(assetPath string) {
|
|||||||
gui.engine = qml.NewEngine()
|
gui.engine = qml.NewEngine()
|
||||||
context := gui.engine.Context()
|
context := gui.engine.Context()
|
||||||
gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath)
|
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
|
// Expose the eth library and the ui library to QML
|
||||||
context.SetVar("gui", gui)
|
context.SetVar("gui", gui)
|
||||||
context.SetVar("eth", gui.uiLib)
|
context.SetVar("eth", gui.uiLib)
|
||||||
|
context.SetVar("shh", gui.whisper)
|
||||||
|
|
||||||
// Load the main QML interface
|
// Load the main QML interface
|
||||||
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
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) {
|
func (gui *Gui) setInitialChain(ancientBlocks bool) {
|
||||||
sBlk := gui.eth.ChainManager().LastBlockHash
|
sBlk := gui.eth.ChainManager().LastBlockHash()
|
||||||
blk := gui.eth.ChainManager().GetBlock(sBlk)
|
blk := gui.eth.ChainManager().GetBlock(sBlk)
|
||||||
for ; blk != nil; 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)
|
gui.processBlock(blk, true)
|
||||||
}
|
}
|
||||||
@ -297,7 +269,7 @@ func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
|
|||||||
addr := gui.address()
|
addr := gui.address()
|
||||||
|
|
||||||
var inout string
|
var inout string
|
||||||
if bytes.Compare(tx.Sender(), addr) == 0 {
|
if bytes.Compare(tx.From(), addr) == 0 {
|
||||||
inout = "send"
|
inout = "send"
|
||||||
} else {
|
} else {
|
||||||
inout = "recv"
|
inout = "recv"
|
||||||
@ -305,27 +277,27 @@ func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
ptx = xeth.NewJSTx(tx, pipe.World().State())
|
ptx = xeth.NewJSTx(tx, pipe.World().State())
|
||||||
send = nameReg.Storage(tx.Sender())
|
send = nameReg.Storage(tx.From())
|
||||||
rec = nameReg.Storage(tx.Recipient)
|
rec = nameReg.Storage(tx.To())
|
||||||
s, r string
|
s, r string
|
||||||
)
|
)
|
||||||
|
|
||||||
if tx.CreatesContract() {
|
if core.MessageCreatesContract(tx) {
|
||||||
rec = nameReg.Storage(tx.CreationAddress(pipe.World().State()))
|
rec = nameReg.Storage(core.AddressFromMessage(tx))
|
||||||
}
|
}
|
||||||
|
|
||||||
if send.Len() != 0 {
|
if send.Len() != 0 {
|
||||||
s = strings.Trim(send.Str(), "\x00")
|
s = strings.Trim(send.Str(), "\x00")
|
||||||
} else {
|
} else {
|
||||||
s = ethutil.Bytes2Hex(tx.Sender())
|
s = ethutil.Bytes2Hex(tx.From())
|
||||||
}
|
}
|
||||||
if rec.Len() != 0 {
|
if rec.Len() != 0 {
|
||||||
r = strings.Trim(rec.Str(), "\x00")
|
r = strings.Trim(rec.Str(), "\x00")
|
||||||
} else {
|
} else {
|
||||||
if tx.CreatesContract() {
|
if core.MessageCreatesContract(tx) {
|
||||||
r = ethutil.Bytes2Hex(tx.CreationAddress(pipe.World().State()))
|
r = ethutil.Bytes2Hex(core.AddressFromMessage(tx))
|
||||||
} else {
|
} else {
|
||||||
r = ethutil.Bytes2Hex(tx.Recipient)
|
r = ethutil.Bytes2Hex(tx.To())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ptx.Sender = s
|
ptx.Sender = s
|
||||||
@ -350,7 +322,7 @@ func (gui *Gui) readPreviousTransactions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) processBlock(block *types.Block, initial bool) {
|
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 := xeth.NewJSBlock(block)
|
||||||
b.Name = name
|
b.Name = name
|
||||||
|
|
||||||
@ -391,6 +363,8 @@ func (gui *Gui) update() {
|
|||||||
gui.setPeerInfo()
|
gui.setPeerInfo()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
gui.whisper.SetView(gui.win.Root().ObjectByName("whisperView"))
|
||||||
|
|
||||||
for _, plugin := range gui.plugins {
|
for _, plugin := range gui.plugins {
|
||||||
guilogger.Infoln("Loading plugin ", plugin.Name)
|
guilogger.Infoln("Loading plugin ", plugin.Name)
|
||||||
|
|
||||||
@ -409,8 +383,7 @@ func (gui *Gui) update() {
|
|||||||
miningLabel := gui.getObjectByName("miningLabel")
|
miningLabel := gui.getObjectByName("miningLabel")
|
||||||
|
|
||||||
events := gui.eth.EventMux().Subscribe(
|
events := gui.eth.EventMux().Subscribe(
|
||||||
eth.ChainSyncEvent{},
|
//eth.PeerListEvent{},
|
||||||
eth.PeerListEvent{},
|
|
||||||
core.NewBlockEvent{},
|
core.NewBlockEvent{},
|
||||||
core.TxPreEvent{},
|
core.TxPreEvent{},
|
||||||
core.TxPostEvent{},
|
core.TxPostEvent{},
|
||||||
@ -427,7 +400,7 @@ func (gui *Gui) update() {
|
|||||||
switch ev := ev.(type) {
|
switch ev := ev.(type) {
|
||||||
case core.NewBlockEvent:
|
case core.NewBlockEvent:
|
||||||
gui.processBlock(ev.Block, false)
|
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)
|
gui.setWalletValue(gui.eth.ChainManager().State().GetBalance(gui.address()), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,30 +421,28 @@ func (gui *Gui) update() {
|
|||||||
tx := ev.Tx
|
tx := ev.Tx
|
||||||
object := state.GetAccount(gui.address())
|
object := state.GetAccount(gui.address())
|
||||||
|
|
||||||
if bytes.Compare(tx.Sender(), gui.address()) == 0 {
|
if bytes.Compare(tx.From(), gui.address()) == 0 {
|
||||||
object.SubAmount(tx.Value)
|
object.SubAmount(tx.Value())
|
||||||
|
|
||||||
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||||
} else if bytes.Compare(tx.Recipient, gui.address()) == 0 {
|
} else if bytes.Compare(tx.To(), gui.address()) == 0 {
|
||||||
object.AddAmount(tx.Value)
|
object.AddAmount(tx.Value())
|
||||||
|
|
||||||
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.setWalletValue(object.Balance(), nil)
|
gui.setWalletValue(object.Balance(), nil)
|
||||||
state.UpdateStateObject(object)
|
state.UpdateStateObject(object)
|
||||||
|
|
||||||
case eth.PeerListEvent:
|
|
||||||
gui.setPeerInfo()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-peerUpdateTicker.C:
|
case <-peerUpdateTicker.C:
|
||||||
gui.setPeerInfo()
|
gui.setPeerInfo()
|
||||||
case <-generalUpdateTicker.C:
|
case <-generalUpdateTicker.C:
|
||||||
statusText := "#" + gui.eth.ChainManager().CurrentBlock.Number.String()
|
statusText := "#" + gui.eth.ChainManager().CurrentBlock().Number().String()
|
||||||
lastBlockLabel.Set("text", statusText)
|
lastBlockLabel.Set("text", statusText)
|
||||||
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash")
|
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash")
|
||||||
|
|
||||||
|
/*
|
||||||
blockLength := gui.eth.BlockPool().BlocksProcessed
|
blockLength := gui.eth.BlockPool().BlocksProcessed
|
||||||
chainLength := gui.eth.BlockPool().ChainLength
|
chainLength := gui.eth.BlockPool().ChainLength
|
||||||
|
|
||||||
@ -482,6 +453,7 @@ func (gui *Gui) update() {
|
|||||||
)
|
)
|
||||||
dlWidget.Set("value", pct)
|
dlWidget.Set("value", pct)
|
||||||
dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength))
|
dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength))
|
||||||
|
*/
|
||||||
|
|
||||||
case <-statsUpdateTicker.C:
|
case <-statsUpdateTicker.C:
|
||||||
gui.setStatsPane()
|
gui.setStatsPane()
|
||||||
@ -509,7 +481,7 @@ Heap Alloc: %d
|
|||||||
CGNext: %x
|
CGNext: %x
|
||||||
NumGC: %d
|
NumGC: %d
|
||||||
`, Version, runtime.Version(),
|
`, Version, runtime.Version(),
|
||||||
eth.ProtocolVersion, eth.P2PVersion,
|
eth.ProtocolVersion, 2,
|
||||||
runtime.NumCPU, runtime.NumGoroutine(), runtime.NumCgoCall(),
|
runtime.NumCPU, runtime.NumGoroutine(), runtime.NumCgoCall(),
|
||||||
memStats.Alloc, memStats.HeapAlloc,
|
memStats.Alloc, memStats.HeapAlloc,
|
||||||
memStats.NextGC, memStats.NumGC,
|
memStats.NextGC, memStats.NumGC,
|
||||||
@ -531,3 +503,35 @@ func (gui *Gui) privateKey() string {
|
|||||||
func (gui *Gui) address() []byte {
|
func (gui *Gui) address() []byte {
|
||||||
return gui.eth.KeyManager().Address()
|
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) {
|
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)
|
app.webView.Call("onNewBlockCb", b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,15 +23,15 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"gopkg.in/qml.v1"
|
"gopkg.in/qml.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ClientIdentifier = "Mist"
|
ClientIdentifier = "Mist"
|
||||||
Version = "0.7.9"
|
Version = "0.7.11"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ethereum *eth.Ethereum
|
var ethereum *eth.Ethereum
|
||||||
@ -58,8 +58,8 @@ func run() error {
|
|||||||
|
|
||||||
// create, import, export keys
|
// create, import, export keys
|
||||||
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
|
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 ShowGenesis {
|
if ShowGenesis {
|
||||||
utils.ShowGenesis(ethereum)
|
utils.ShowGenesis(ethereum)
|
||||||
@ -69,6 +69,10 @@ func run() error {
|
|||||||
utils.StartRpc(ethereum, RpcPort)
|
utils.StartRpc(ethereum, RpcPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if StartWebSockets {
|
||||||
|
utils.StartWebSockets(ethereum)
|
||||||
|
}
|
||||||
|
|
||||||
gui := NewWindow(ethereum, config, clientIdentity, KeyRing, LogLevel)
|
gui := NewWindow(ethereum, config, clientIdentity, KeyRing, LogLevel)
|
||||||
gui.stdLog = stdLog
|
gui.stdLog = stdLog
|
||||||
|
|
||||||
@ -100,16 +104,10 @@ func main() {
|
|||||||
|
|
||||||
utils.HandleInterrupt()
|
utils.HandleInterrupt()
|
||||||
|
|
||||||
if StartWebSockets {
|
|
||||||
utils.StartWebSockets(ethereum)
|
|
||||||
}
|
|
||||||
|
|
||||||
// we need to run the interrupt callbacks in case gui is closed
|
// we need to run the interrupt callbacks in case gui is closed
|
||||||
// this skips if we got here by actual interrupt stopping the GUI
|
// this skips if we got here by actual interrupt stopping the GUI
|
||||||
if !interrupted {
|
if !interrupted {
|
||||||
utils.RunInterruptCallbacks(os.Interrupt)
|
utils.RunInterruptCallbacks(os.Interrupt)
|
||||||
}
|
}
|
||||||
// this blocks the thread
|
|
||||||
ethereum.WaitForShutdown()
|
|
||||||
logger.Flush()
|
logger.Flush()
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ func (app *QmlApplication) NewWatcher(quitChan chan bool) {
|
|||||||
|
|
||||||
// Events
|
// Events
|
||||||
func (app *QmlApplication) NewBlock(block *types.Block) {
|
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)
|
app.win.Call("onNewBlockCb", pblock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,11 +24,12 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/event/filter"
|
||||||
"github.com/ethereum/go-ethereum/javascript"
|
"github.com/ethereum/go-ethereum/javascript"
|
||||||
"github.com/ethereum/go-ethereum/miner"
|
"github.com/ethereum/go-ethereum/miner"
|
||||||
"github.com/ethereum/go-ethereum/state"
|
"github.com/ethereum/go-ethereum/state"
|
||||||
@ -57,6 +58,7 @@ type UiLib struct {
|
|||||||
jsEngine *javascript.JSRE
|
jsEngine *javascript.JSRE
|
||||||
|
|
||||||
filterCallbacks map[int][]int
|
filterCallbacks map[int][]int
|
||||||
|
filterManager *filter.FilterManager
|
||||||
|
|
||||||
miner *miner.Miner
|
miner *miner.Miner
|
||||||
}
|
}
|
||||||
@ -64,6 +66,7 @@ type UiLib struct {
|
|||||||
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
|
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 := &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.miner = miner.New(eth.KeyManager().Address(), eth)
|
||||||
|
lib.filterManager = filter.NewFilterManager(eth.EventMux())
|
||||||
|
|
||||||
return lib
|
return lib
|
||||||
}
|
}
|
||||||
@ -123,7 +126,8 @@ func (self *UiLib) LookupAddress(name string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *UiLib) PastPeers() *ethutil.List {
|
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) {
|
func (self *UiLib) ImportTx(rlpTx string) {
|
||||||
@ -191,7 +195,7 @@ func (ui *UiLib) Connect(button qml.Object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ui *UiLib) ConnectToPeer(addr string) {
|
func (ui *UiLib) ConnectToPeer(addr string) {
|
||||||
ui.eth.ConnectToPeer(addr)
|
ui.eth.SuggestPeer(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *UiLib) AssetPath(p string) string {
|
func (ui *UiLib) AssetPath(p string) string {
|
||||||
@ -221,94 +225,6 @@ func (self *UiLib) StartDebugger() {
|
|||||||
dbWindow.Show()
|
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) {
|
func (self *UiLib) Transact(params map[string]interface{}) (string, error) {
|
||||||
object := mapToTxParams(params)
|
object := mapToTxParams(params)
|
||||||
|
|
||||||
@ -372,3 +288,104 @@ func (self *UiLib) ToggleMining() bool {
|
|||||||
return false
|
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"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
|
||||||
|
|
||||||
"bitbucket.org/kardianos/osext"
|
"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/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/miner"
|
"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/rpc"
|
||||||
"github.com/ethereum/go-ethereum/wire"
|
|
||||||
"github.com/ethereum/go-ethereum/xeth"
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -144,17 +146,32 @@ func NewDatabase() ethutil.Database {
|
|||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClientIdentity(clientIdentifier, version, customIdentifier string) *wire.SimpleClientIdentity {
|
func NewClientIdentity(clientIdentifier, version, customIdentifier string, pubkey string) *p2p.SimpleClientIdentity {
|
||||||
return wire.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier)
|
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 {
|
func NatType(natType string, gateway string) (nat p2p.NAT) {
|
||||||
ethereum, err := eth.New(db, clientIdentity, keyManager, eth.CapDefault, usePnp)
|
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 {
|
if err != nil {
|
||||||
clilogger.Fatalln("eth start err:", err)
|
clilogger.Fatalln("eth start err:", err)
|
||||||
}
|
}
|
||||||
ethereum.Port = OutboundPort
|
|
||||||
ethereum.MaxPeers = MaxPeer
|
|
||||||
return ethereum
|
return ethereum
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,11 +285,6 @@ func StartMining(ethereum *eth.Ethereum) bool {
|
|||||||
if gminer == nil {
|
if gminer == nil {
|
||||||
gminer = miner.New(addr, ethereum)
|
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()
|
gminer.Start()
|
||||||
}()
|
}()
|
||||||
RegisterInterrupt(func(os.Signal) {
|
RegisterInterrupt(func(os.Signal) {
|
||||||
@ -315,7 +327,7 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
|
|||||||
return fmt.Errorf("unknown block %x", hash)
|
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)
|
_, err := ethereum.BlockManager().TransitionState(parent.State(), parent, block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -325,3 +337,25 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
|
|||||||
return nil
|
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) Origin() []byte { return self.transactor }
|
||||||
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number }
|
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() }
|
||||||
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
|
func (self *VMEnv) PrevHash() []byte { return self.block.ParentHash() }
|
||||||
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
|
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase() }
|
||||||
func (self *VMEnv) Time() int64 { return self.block.Time }
|
func (self *VMEnv) Time() int64 { return self.block.Time() }
|
||||||
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
|
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() }
|
||||||
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
|
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) Value() *big.Int { return self.value }
|
||||||
func (self *VMEnv) State() *state.StateDB { return self.state }
|
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) Depth() int { return self.depth }
|
||||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||||
func (self *VMEnv) AddLog(log state.Log) {
|
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 {
|
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execution {
|
||||||
evm := vm.New(self, vm.DebugVmTy)
|
return core.NewExecution(self, addr, data, gas, price, value)
|
||||||
|
|
||||||
return core.NewExecution(evm, addr, data, gas, price, value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/websocket"
|
"github.com/ethereum/go-ethereum/websocket"
|
||||||
"github.com/ethereum/go-ethereum/xeth"
|
"github.com/ethereum/go-ethereum/xeth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var wslogger = logger.NewLogger("WS")
|
||||||
|
|
||||||
func args(v ...interface{}) []interface{} {
|
func args(v ...interface{}) []interface{} {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
@ -106,6 +109,8 @@ func (self *WebSocketServer) Serv() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StartWebSockets(eth *eth.Ethereum) {
|
func StartWebSockets(eth *eth.Ethereum) {
|
||||||
|
wslogger.Infoln("Starting WebSockets")
|
||||||
|
|
||||||
sock := NewWebSocketServer(eth)
|
sock := NewWebSocketServer(eth)
|
||||||
go sock.Serv()
|
go sock.Serv()
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"container/list"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
@ -14,10 +13,11 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/pow"
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
"github.com/ethereum/go-ethereum/pow/ezp"
|
"github.com/ethereum/go-ethereum/pow/ezp"
|
||||||
"github.com/ethereum/go-ethereum/state"
|
"github.com/ethereum/go-ethereum/state"
|
||||||
"github.com/ethereum/go-ethereum/wire"
|
"gopkg.in/fatih/set.v0"
|
||||||
)
|
)
|
||||||
|
|
||||||
var statelogger = logger.NewLogger("BLOCK")
|
var statelogger = logger.NewLogger("BLOCK")
|
||||||
@ -38,13 +38,12 @@ type EthManager interface {
|
|||||||
BlockManager() *BlockManager
|
BlockManager() *BlockManager
|
||||||
ChainManager() *ChainManager
|
ChainManager() *ChainManager
|
||||||
TxPool() *TxPool
|
TxPool() *TxPool
|
||||||
Broadcast(msgType wire.MsgType, data []interface{})
|
|
||||||
PeerCount() int
|
PeerCount() int
|
||||||
IsMining() bool
|
IsMining() bool
|
||||||
IsListening() bool
|
IsListening() bool
|
||||||
Peers() *list.List
|
Peers() []*p2p.Peer
|
||||||
KeyManager() *crypto.KeyManager
|
KeyManager() *crypto.KeyManager
|
||||||
ClientIdentity() wire.ClientIdentity
|
ClientIdentity() p2p.ClientIdentity
|
||||||
Db() ethutil.Database
|
Db() ethutil.Database
|
||||||
EventMux() *event.TypeMux
|
EventMux() *event.TypeMux
|
||||||
}
|
}
|
||||||
@ -58,8 +57,8 @@ type BlockManager struct {
|
|||||||
mem map[string]*big.Int
|
mem map[string]*big.Int
|
||||||
// Proof of work used for validating
|
// Proof of work used for validating
|
||||||
Pow pow.PoW
|
Pow pow.PoW
|
||||||
// The ethereum manager interface
|
|
||||||
eth EthManager
|
txpool *TxPool
|
||||||
|
|
||||||
// The last attempted block is mainly used for debugging purposes
|
// The last attempted block is mainly used for debugging purposes
|
||||||
// This does not have to be a valid block and will be set during
|
// This does not have to be a valid block and will be set during
|
||||||
@ -71,21 +70,21 @@ type BlockManager struct {
|
|||||||
eventMux *event.TypeMux
|
eventMux *event.TypeMux
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlockManager(ethereum EthManager) *BlockManager {
|
func NewBlockManager(txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockManager {
|
||||||
sm := &BlockManager{
|
sm := &BlockManager{
|
||||||
mem: make(map[string]*big.Int),
|
mem: make(map[string]*big.Int),
|
||||||
Pow: ezp.New(),
|
Pow: ezp.New(),
|
||||||
eth: ethereum,
|
bc: chainManager,
|
||||||
bc: ethereum.ChainManager(),
|
eventMux: eventMux,
|
||||||
eventMux: ethereum.EventMux(),
|
txpool: txpool,
|
||||||
}
|
}
|
||||||
|
|
||||||
return sm
|
return sm
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *BlockManager) TransitionState(statedb *state.StateDB, parent, block *types.Block) (receipts types.Receipts, err error) {
|
func (sm *BlockManager) TransitionState(statedb *state.StateDB, parent, block *types.Block) (receipts types.Receipts, err error) {
|
||||||
coinbase := statedb.GetOrNewStateObject(block.Coinbase)
|
coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase)
|
||||||
coinbase.SetGasPool(block.CalcGasLimit(parent))
|
coinbase.SetGasPool(CalcGasLimit(parent, block))
|
||||||
|
|
||||||
// Process the transactions on to current block
|
// Process the transactions on to current block
|
||||||
receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), false)
|
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
|
// If we are mining this block and validating we want to set the logs back to 0
|
||||||
state.EmptyLogs()
|
state.EmptyLogs()
|
||||||
|
|
||||||
txGas := new(big.Int).Set(tx.Gas)
|
txGas := new(big.Int).Set(tx.Gas())
|
||||||
|
|
||||||
cb := state.GetStateObject(coinbase.Address())
|
cb := state.GetStateObject(coinbase.Address())
|
||||||
st := NewStateTransition(cb, tx, state, block)
|
st := NewStateTransition(cb, tx, state, block)
|
||||||
err = st.TransitionState()
|
_, err = st.TransitionState()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch {
|
switch {
|
||||||
case IsNonceErr(err):
|
case IsNonceErr(err):
|
||||||
@ -129,12 +128,11 @@ done:
|
|||||||
statelogger.Infoln(err)
|
statelogger.Infoln(err)
|
||||||
erroneous = append(erroneous, tx)
|
erroneous = append(erroneous, tx)
|
||||||
err = nil
|
err = nil
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
txGas.Sub(txGas, st.gas)
|
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
|
// Update the state with pending changes
|
||||||
state.Update(txGas)
|
state.Update(txGas)
|
||||||
@ -143,6 +141,7 @@ done:
|
|||||||
receipt := types.NewReceipt(state.Root(), cumulative)
|
receipt := types.NewReceipt(state.Root(), cumulative)
|
||||||
receipt.SetLogs(state.Logs())
|
receipt.SetLogs(state.Logs())
|
||||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||||
|
chainlogger.Debugln(receipt)
|
||||||
|
|
||||||
// Notify all subscribers
|
// Notify all subscribers
|
||||||
if !transientProcess {
|
if !transientProcess {
|
||||||
@ -158,7 +157,7 @@ done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
block.Reward = cumulativeSum
|
block.Reward = cumulativeSum
|
||||||
block.GasUsed = totalUsedGas
|
block.Header().GasUsed = totalUsedGas
|
||||||
|
|
||||||
return receipts, handled, unhandled, erroneous, err
|
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()
|
sm.mutex.Lock()
|
||||||
defer sm.mutex.Unlock()
|
defer sm.mutex.Unlock()
|
||||||
|
|
||||||
if sm.bc.HasBlock(block.Hash()) {
|
header := block.Header()
|
||||||
return nil, nil, &KnownBlockError{block.Number, block.Hash()}
|
if sm.bc.HasBlock(header.Hash()) {
|
||||||
|
return nil, nil, &KnownBlockError{header.Number, header.Hash()}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sm.bc.HasBlock(block.PrevHash) {
|
if !sm.bc.HasBlock(header.ParentHash) {
|
||||||
return nil, nil, ParentError(block.PrevHash)
|
return nil, nil, ParentError(header.ParentHash)
|
||||||
}
|
}
|
||||||
parent := sm.bc.GetBlock(block.PrevHash)
|
parent := sm.bc.GetBlock(header.ParentHash)
|
||||||
|
|
||||||
return sm.ProcessWithParent(block, parent)
|
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) {
|
func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) {
|
||||||
sm.lastAttemptedBlock = block
|
sm.lastAttemptedBlock = block
|
||||||
|
|
||||||
state := parent.State().Copy()
|
state := state.New(parent.Trie().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()
|
|
||||||
|
|
||||||
// Block validation
|
// Block validation
|
||||||
if err = sm.ValidateBlock(block, parent); err != nil {
|
if err = sm.ValidateBlock(block, parent); err != nil {
|
||||||
@ -201,21 +195,24 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
header := block.Header()
|
||||||
|
|
||||||
rbloom := types.CreateBloom(receipts)
|
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)
|
err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
txSha := types.DeriveSha(block.Transactions())
|
txSha := types.DeriveSha(block.Transactions())
|
||||||
if bytes.Compare(txSha, block.TxSha) != 0 {
|
if bytes.Compare(txSha, header.TxHash) != 0 {
|
||||||
err = fmt.Errorf("validating transaction root. received=%x got=%x", block.TxSha, txSha)
|
err = fmt.Errorf("validating transaction root. received=%x got=%x", header.TxHash, txSha)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
receiptSha := types.DeriveSha(receipts)
|
receiptSha := types.DeriveSha(receipts)
|
||||||
if bytes.Compare(receiptSha, block.ReceiptSha) != 0 {
|
if bytes.Compare(receiptSha, header.ReceiptHash) != 0 {
|
||||||
err = fmt.Errorf("validating receipt root. received=%x got=%x", block.ReceiptSha, receiptSha)
|
fmt.Println("receipts", receipts)
|
||||||
|
err = fmt.Errorf("validating receipt root. received=%x got=%x", header.ReceiptHash, receiptSha)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,8 +222,8 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
|
|||||||
|
|
||||||
state.Update(ethutil.Big0)
|
state.Update(ethutil.Big0)
|
||||||
|
|
||||||
if !block.State().Cmp(state) {
|
if !bytes.Equal(header.Root, state.Root()) {
|
||||||
err = fmt.Errorf("invalid merkle root. received=%x got=%x", block.Root(), state.Root())
|
err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,9 +235,9 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
|
|||||||
messages := state.Manifest().Messages
|
messages := state.Manifest().Messages
|
||||||
state.Manifest().Reset()
|
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
|
return td, messages, nil
|
||||||
} else {
|
} 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) {
|
func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) {
|
||||||
uncleDiff := new(big.Int)
|
uncleDiff := new(big.Int)
|
||||||
for _, uncle := range block.Uncles {
|
for _, uncle := range block.Uncles() {
|
||||||
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
|
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(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
|
||||||
td := new(big.Int)
|
td := new(big.Int)
|
||||||
td = td.Add(sm.bc.TD, uncleDiff)
|
td = td.Add(sm.bc.Td(), uncleDiff)
|
||||||
td = td.Add(td, block.Difficulty)
|
td = td.Add(td, block.Header().Difficulty)
|
||||||
|
|
||||||
// The new TD will only be accepted if the new difficulty is
|
// The new TD will only be accepted if the new difficulty is
|
||||||
// is greater than the previous.
|
// is greater than the previous.
|
||||||
if td.Cmp(sm.bc.TD) > 0 {
|
if td.Cmp(sm.bc.Td()) > 0 {
|
||||||
return td, true
|
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)
|
// Validation validates easy over difficult (dagger takes longer time = difficult)
|
||||||
func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
|
func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
|
||||||
expd := CalcDifficulty(block, parent)
|
expd := CalcDifficulty(block, parent)
|
||||||
if expd.Cmp(block.Difficulty) < 0 {
|
if expd.Cmp(block.Header().Difficulty) < 0 {
|
||||||
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
|
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 {
|
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
|
/* 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
|
// 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*/) {
|
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
|
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 {
|
func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent *types.Block) error {
|
||||||
reward := new(big.Int).Set(BlockReward)
|
reward := new(big.Int).Set(BlockReward)
|
||||||
|
|
||||||
knownUncles := ethutil.Set(parent.Uncles)
|
knownUncles := set.New()
|
||||||
nonces := ethutil.NewSet(block.Nonce)
|
for _, uncle := range parent.Uncles() {
|
||||||
for _, uncle := range block.Uncles {
|
knownUncles.Add(string(uncle.Hash()))
|
||||||
|
}
|
||||||
|
|
||||||
|
nonces := ethutil.NewSet(block.Header().Nonce)
|
||||||
|
for _, uncle := range block.Uncles() {
|
||||||
if nonces.Include(uncle.Nonce) {
|
if nonces.Include(uncle.Nonce) {
|
||||||
// Error not unique
|
// Error not unique
|
||||||
return UncleError("Uncle not unique")
|
return UncleError("Uncle not unique")
|
||||||
}
|
}
|
||||||
|
|
||||||
uncleParent := sm.bc.GetBlock(uncle.PrevHash)
|
uncleParent := sm.bc.GetBlock(uncle.ParentHash)
|
||||||
if uncleParent == nil {
|
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")
|
return UncleError("Uncle too old")
|
||||||
}
|
}
|
||||||
|
|
||||||
if knownUncles.Include(uncle.Hash()) {
|
if knownUncles.Has(string(uncle.Hash())) {
|
||||||
return UncleError("Uncle in chain")
|
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
|
// 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
|
// Reward amount of ether to the coinbase address
|
||||||
account.AddAmount(reward)
|
account.AddAmount(reward)
|
||||||
|
|
||||||
statedb.Manifest().AddMessage(&state.Message{
|
statedb.Manifest().AddMessage(&state.Message{
|
||||||
To: block.Coinbase,
|
To: block.Header().Coinbase,
|
||||||
Input: nil,
|
Input: nil,
|
||||||
Origin: 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),
|
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) {
|
func (sm *BlockManager) GetMessages(block *types.Block) (messages []*state.Message, err error) {
|
||||||
if !sm.bc.HasBlock(block.PrevHash) {
|
if !sm.bc.HasBlock(block.Header().ParentHash) {
|
||||||
return nil, ParentError(block.PrevHash)
|
return nil, ParentError(block.Header().ParentHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.lastAttemptedBlock = block
|
sm.lastAttemptedBlock = block
|
||||||
|
|
||||||
var (
|
var (
|
||||||
parent = sm.bc.GetBlock(block.PrevHash)
|
parent = sm.bc.GetBlock(block.Header().ParentHash)
|
||||||
state = parent.State().Copy()
|
state = state.New(parent.Trie().Copy())
|
||||||
)
|
)
|
||||||
|
|
||||||
defer state.Reset()
|
defer state.Reset()
|
||||||
|
@ -1,18 +1,22 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/state"
|
"github.com/ethereum/go-ethereum/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
var chainlogger = logger.NewLogger("CHAIN")
|
var chainlogger = logger.NewLogger("CHAIN")
|
||||||
|
|
||||||
|
/*
|
||||||
func AddTestNetFunds(block *types.Block) {
|
func AddTestNetFunds(block *types.Block) {
|
||||||
for _, addr := range []string{
|
for _, addr := range []string{
|
||||||
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
||||||
@ -30,39 +34,87 @@ func AddTestNetFunds(block *types.Block) {
|
|||||||
block.State().UpdateStateObject(account)
|
block.State().UpdateStateObject(account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func CalcDifficulty(block, parent *types.Block) *big.Int {
|
func CalcDifficulty(block, parent *types.Block) *big.Int {
|
||||||
diff := new(big.Int)
|
diff := new(big.Int)
|
||||||
|
|
||||||
adjust := new(big.Int).Rsh(parent.Difficulty, 10)
|
bh, ph := block.Header(), parent.Header()
|
||||||
if block.Time >= parent.Time+5 {
|
adjust := new(big.Int).Rsh(ph.Difficulty, 10)
|
||||||
diff.Sub(parent.Difficulty, adjust)
|
if bh.Time >= ph.Time+5 {
|
||||||
|
diff.Sub(ph.Difficulty, adjust)
|
||||||
} else {
|
} else {
|
||||||
diff.Add(parent.Difficulty, adjust)
|
diff.Add(ph.Difficulty, adjust)
|
||||||
}
|
}
|
||||||
|
|
||||||
return diff
|
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 {
|
type ChainManager struct {
|
||||||
//eth EthManager
|
//eth EthManager
|
||||||
processor types.BlockProcessor
|
processor types.BlockProcessor
|
||||||
eventMux *event.TypeMux
|
eventMux *event.TypeMux
|
||||||
genesisBlock *types.Block
|
genesisBlock *types.Block
|
||||||
// Last known total difficulty
|
// Last known total difficulty
|
||||||
TD *big.Int
|
mu sync.RWMutex
|
||||||
|
td *big.Int
|
||||||
LastBlockNumber uint64
|
lastBlockNumber uint64
|
||||||
|
currentBlock *types.Block
|
||||||
CurrentBlock *types.Block
|
lastBlockHash []byte
|
||||||
LastBlockHash []byte
|
|
||||||
|
|
||||||
transState *state.StateDB
|
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 {
|
func NewChainManager(mux *event.TypeMux) *ChainManager {
|
||||||
bc := &ChainManager{}
|
bc := &ChainManager{}
|
||||||
bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis))
|
bc.genesisBlock = GenesisBlock()
|
||||||
bc.eventMux = mux
|
bc.eventMux = mux
|
||||||
|
|
||||||
bc.setLastBlock()
|
bc.setLastBlock()
|
||||||
@ -72,12 +124,19 @@ func NewChainManager(mux *event.TypeMux) *ChainManager {
|
|||||||
return bc
|
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) {
|
func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
|
||||||
self.processor = proc
|
self.processor = proc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ChainManager) State() *state.StateDB {
|
func (self *ChainManager) State() *state.StateDB {
|
||||||
return self.CurrentBlock.State()
|
return state.New(self.CurrentBlock().Trie())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ChainManager) TransState() *state.StateDB {
|
func (self *ChainManager) TransState() *state.StateDB {
|
||||||
@ -87,46 +146,48 @@ func (self *ChainManager) TransState() *state.StateDB {
|
|||||||
func (bc *ChainManager) setLastBlock() {
|
func (bc *ChainManager) setLastBlock() {
|
||||||
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
|
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
|
||||||
if len(data) != 0 {
|
if len(data) != 0 {
|
||||||
// Prep genesis
|
var block types.Block
|
||||||
AddTestNetFunds(bc.genesisBlock)
|
rlp.Decode(bytes.NewReader(data), &block)
|
||||||
|
bc.currentBlock = &block
|
||||||
block := types.NewBlockFromBytes(data)
|
bc.lastBlockHash = block.Hash()
|
||||||
bc.CurrentBlock = block
|
bc.lastBlockNumber = block.Header().Number.Uint64()
|
||||||
bc.LastBlockHash = block.Hash()
|
|
||||||
bc.LastBlockNumber = block.Number.Uint64()
|
|
||||||
|
|
||||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
// 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 {
|
} else {
|
||||||
bc.Reset()
|
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
|
// Block creation & chain handling
|
||||||
func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
|
func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
|
||||||
var root interface{}
|
bc.mu.RLock()
|
||||||
hash := ZeroHash256
|
defer bc.mu.RUnlock()
|
||||||
|
|
||||||
|
var root []byte
|
||||||
|
parentHash := ZeroHash256
|
||||||
|
|
||||||
if bc.CurrentBlock != nil {
|
if bc.CurrentBlock != nil {
|
||||||
root = bc.CurrentBlock.Root()
|
root = bc.currentBlock.Header().Root
|
||||||
hash = bc.LastBlockHash
|
parentHash = bc.lastBlockHash
|
||||||
}
|
}
|
||||||
|
|
||||||
block := types.CreateBlock(
|
block := types.NewBlock(
|
||||||
root,
|
parentHash,
|
||||||
hash,
|
|
||||||
coinbase,
|
coinbase,
|
||||||
|
root,
|
||||||
ethutil.BigPow(2, 32),
|
ethutil.BigPow(2, 32),
|
||||||
nil,
|
nil,
|
||||||
"")
|
"")
|
||||||
|
|
||||||
parent := bc.CurrentBlock
|
parent := bc.currentBlock
|
||||||
if parent != nil {
|
if parent != nil {
|
||||||
block.Difficulty = CalcDifficulty(block, parent)
|
header := block.Header()
|
||||||
block.Number = new(big.Int).Add(bc.CurrentBlock.Number, ethutil.Big1)
|
header.Difficulty = CalcDifficulty(block, parent)
|
||||||
block.GasLimit = block.CalcGasLimit(bc.CurrentBlock)
|
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() {
|
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
|
// Prepare the genesis block
|
||||||
bc.write(bc.genesisBlock)
|
bc.write(bc.genesisBlock)
|
||||||
bc.insert(bc.genesisBlock)
|
bc.insert(bc.genesisBlock)
|
||||||
bc.CurrentBlock = bc.genesisBlock
|
bc.currentBlock = bc.genesisBlock
|
||||||
|
|
||||||
bc.SetTotalDifficulty(ethutil.Big("0"))
|
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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ChainManager) Export() []byte {
|
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)
|
chainlogger.Infof("exporting %v blocks...\n", self.currentBlock.Header().Number)
|
||||||
for block := self.CurrentBlock; block != nil; block = self.GetBlock(block.PrevHash) {
|
|
||||||
blocks[block.Number.Int64()] = block
|
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)
|
return ethutil.Encode(blocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *ChainManager) insert(block *types.Block) {
|
func (bc *ChainManager) insert(block *types.Block) {
|
||||||
encodedBlock := block.RlpEncode()
|
encodedBlock := ethutil.Encode(block)
|
||||||
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
|
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
|
||||||
bc.CurrentBlock = block
|
bc.currentBlock = block
|
||||||
bc.LastBlockHash = block.Hash()
|
bc.lastBlockHash = block.Hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *ChainManager) write(block *types.Block) {
|
func (bc *ChainManager) write(block *types.Block) {
|
||||||
bc.writeBlockInfo(block)
|
bc.writeBlockInfo(block)
|
||||||
|
|
||||||
encodedBlock := block.RlpEncode()
|
encodedBlock := ethutil.Encode(block)
|
||||||
ethutil.Config.Db.Put(block.Hash(), encodedBlock)
|
ethutil.Config.Db.Put(block.Hash(), encodedBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +249,7 @@ func (bc *ChainManager) HasBlock(hash []byte) bool {
|
|||||||
return len(data) != 0
|
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)
|
block := self.GetBlock(hash)
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return
|
return
|
||||||
@ -193,11 +259,11 @@ func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain
|
|||||||
for i := uint64(0); i < max; i++ {
|
for i := uint64(0); i < max; i++ {
|
||||||
chain = append(chain, block.Hash())
|
chain = append(chain, block.Hash())
|
||||||
|
|
||||||
if block.Number.Cmp(ethutil.Big0) <= 0 {
|
if block.Header().Number.Cmp(ethutil.Big0) <= 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
block = self.GetBlock(block.PrevHash)
|
block = self.GetBlock(block.Header().ParentHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -208,65 +274,61 @@ func (self *ChainManager) GetBlock(hash []byte) *types.Block {
|
|||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil
|
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 {
|
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
|
||||||
block := self.CurrentBlock
|
self.mu.RLock()
|
||||||
for ; block != nil; block = self.GetBlock(block.PrevHash) {
|
defer self.mu.RUnlock()
|
||||||
if block.Number.Uint64() == num {
|
|
||||||
|
block := self.currentBlock
|
||||||
|
for ; block != nil; block = self.GetBlock(block.Header().ParentHash) {
|
||||||
|
if block.Header().Number.Uint64() == num {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if block != nil && block.Number.Uint64() == 0 && num != 0 {
|
if block != nil && block.Header().Number.Uint64() == 0 && num != 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *ChainManager) SetTotalDifficulty(td *big.Int) {
|
func (bc *ChainManager) setTotalDifficulty(td *big.Int) {
|
||||||
ethutil.Config.Db.Put([]byte("LTD"), td.Bytes())
|
ethutil.Config.Db.Put([]byte("LTD"), td.Bytes())
|
||||||
bc.TD = td
|
bc.td = td
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
|
func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
|
||||||
parent := self.GetBlock(block.PrevHash)
|
parent := self.GetBlock(block.Header().ParentHash)
|
||||||
if parent == nil {
|
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)
|
uncleDiff := new(big.Int)
|
||||||
for _, uncle := range block.Uncles {
|
for _, uncle := range block.Uncles() {
|
||||||
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
|
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
|
||||||
}
|
}
|
||||||
|
|
||||||
td := new(big.Int)
|
td := new(big.Int)
|
||||||
td = td.Add(parentTd, uncleDiff)
|
td = td.Add(parentTd, uncleDiff)
|
||||||
td = td.Add(td, block.Difficulty)
|
td = td.Add(td, block.Header().Difficulty)
|
||||||
|
|
||||||
return td, nil
|
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
|
// Unexported method for writing extra non-essential block info to the db
|
||||||
func (bc *ChainManager) writeBlockInfo(block *types.Block) {
|
func (bc *ChainManager) writeBlockInfo(block *types.Block) {
|
||||||
bc.LastBlockNumber++
|
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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *ChainManager) Stop() {
|
func (bc *ChainManager) Stop() {
|
||||||
@ -283,24 +345,30 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
|
|||||||
continue
|
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(block)
|
||||||
chainlogger.Infoln(err)
|
chainlogger.Infoln(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.mu.Lock()
|
||||||
|
{
|
||||||
self.write(block)
|
self.write(block)
|
||||||
if td.Cmp(self.TD) > 0 {
|
cblock := self.currentBlock
|
||||||
if block.Number.Cmp(new(big.Int).Add(self.CurrentBlock.Number, ethutil.Big1)) < 0 {
|
if td.Cmp(self.td) > 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])
|
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.insert(block)
|
||||||
self.transState = self.State().Copy()
|
self.transState = state.New(cblock.Trie().Copy())
|
||||||
//sm.eth.TxPool().RemoveSet(block.Transactions())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
self.mu.Unlock()
|
||||||
|
|
||||||
self.eventMux.Post(NewBlockEvent{block})
|
self.eventMux.Post(NewBlockEvent{block})
|
||||||
self.eventMux.Post(messages)
|
self.eventMux.Post(messages)
|
||||||
}
|
}
|
||||||
|
@ -2,18 +2,138 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"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/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestChainInsertions(t *testing.T) {
|
//var Logger logpkg.LogSystem
|
||||||
c1, err := ethutil.ReadAllFile(path.Join("..", "_data", "chain1"))
|
|
||||||
|
//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 {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
t.FailNow()
|
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.
|
// NewBlockEvent is posted when a block has been imported.
|
||||||
type NewBlockEvent struct{ Block *types.Block }
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
|
||||||
"github.com/ethereum/go-ethereum/state"
|
"github.com/ethereum/go-ethereum/state"
|
||||||
"github.com/ethereum/go-ethereum/vm"
|
"github.com/ethereum/go-ethereum/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Execution struct {
|
type Execution struct {
|
||||||
vm vm.VirtualMachine
|
env vm.Environment
|
||||||
address, input []byte
|
address, input []byte
|
||||||
Gas, price, value *big.Int
|
Gas, price, value *big.Int
|
||||||
object *state.StateObject
|
|
||||||
SkipTransfer bool
|
SkipTransfer bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewExecution(vm vm.VirtualMachine, address, input []byte, gas, gasPrice, value *big.Int) *Execution {
|
func NewExecution(env vm.Environment, address, input []byte, gas, gasPrice, value *big.Int) *Execution {
|
||||||
return &Execution{vm: vm, address: address, input: input, Gas: gas, price: gasPrice, value: value}
|
return &Execution{env: env, address: address, input: input, Gas: gas, price: gasPrice, value: value}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Execution) Addr() []byte {
|
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) {
|
func (self *Execution) Call(codeAddr []byte, caller vm.ClosureRef) ([]byte, error) {
|
||||||
// Retrieve the executing code
|
// Retrieve the executing code
|
||||||
code := self.vm.Env().State().GetCode(codeAddr)
|
code := self.env.State().GetCode(codeAddr)
|
||||||
|
|
||||||
return self.exec(code, codeAddr, caller)
|
return self.exec(code, codeAddr, caller)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Execution) exec(code, contextAddr []byte, caller vm.ClosureRef) (ret []byte, err error) {
|
func (self *Execution) exec(code, contextAddr []byte, caller vm.ClosureRef) (ret []byte, err error) {
|
||||||
env := self.vm.Env()
|
env := self.env
|
||||||
chainlogger.Debugf("pre state %x\n", env.State().Root())
|
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)
|
from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address)
|
||||||
// Skipping transfer is used on testing for the initial call
|
// 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()
|
snapshot := env.State().Copy()
|
||||||
defer func() {
|
start := time.Now()
|
||||||
if vm.IsDepthErr(err) || vm.IsOOGErr(err) {
|
ret, err = evm.Run(to, caller, code, self.value, self.Gas, self.price, self.input)
|
||||||
|
if err != nil {
|
||||||
env.State().Set(snapshot)
|
env.State().Set(snapshot)
|
||||||
}
|
}
|
||||||
chainlogger.Debugf("post state %x\n", env.State().Root())
|
chainlogger.Debugf("vm took %v\n", time.Since(start))
|
||||||
}()
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Execution) Create(caller vm.ClosureRef) (ret []byte, err error, account *state.StateObject) {
|
func (self *Execution) Create(caller vm.ClosureRef) (ret []byte, err error, account *state.StateObject) {
|
||||||
ret, err = self.exec(self.input, nil, caller)
|
ret, err = self.exec(self.input, nil, caller)
|
||||||
account = self.vm.Env().State().GetStateObject(self.address)
|
account = self.env.State().GetStateObject(self.address)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -76,13 +76,14 @@ func (self *Filter) SetSkip(skip int) {
|
|||||||
|
|
||||||
// Run filters messages with the current parameters set
|
// Run filters messages with the current parameters set
|
||||||
func (self *Filter) Find() []*state.Message {
|
func (self *Filter) Find() []*state.Message {
|
||||||
|
earliestBlock := self.eth.ChainManager().CurrentBlock()
|
||||||
var earliestBlockNo uint64 = uint64(self.earliest)
|
var earliestBlockNo uint64 = uint64(self.earliest)
|
||||||
if self.earliest == -1 {
|
if self.earliest == -1 {
|
||||||
earliestBlockNo = self.eth.ChainManager().CurrentBlock.Number.Uint64()
|
earliestBlockNo = earliestBlock.NumberU64()
|
||||||
}
|
}
|
||||||
var latestBlockNo uint64 = uint64(self.latest)
|
var latestBlockNo uint64 = uint64(self.latest)
|
||||||
if self.latest == -1 {
|
if self.latest == -1 {
|
||||||
latestBlockNo = self.eth.ChainManager().CurrentBlock.Number.Uint64()
|
latestBlockNo = earliestBlock.NumberU64()
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -93,7 +94,7 @@ func (self *Filter) Find() []*state.Message {
|
|||||||
for i := 0; !quit && block != nil; i++ {
|
for i := 0; !quit && block != nil; i++ {
|
||||||
// Quit on latest
|
// Quit on latest
|
||||||
switch {
|
switch {
|
||||||
case block.Number.Uint64() == earliestBlockNo, block.Number.Uint64() == 0:
|
case block.NumberU64() == earliestBlockNo, block.NumberU64() == 0:
|
||||||
quit = true
|
quit = true
|
||||||
case self.max <= len(messages):
|
case self.max <= len(messages):
|
||||||
break
|
break
|
||||||
@ -113,7 +114,7 @@ func (self *Filter) Find() []*state.Message {
|
|||||||
messages = append(messages, self.FilterMessages(msgs)...)
|
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)))
|
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
|
var fromIncluded, toIncluded bool
|
||||||
if len(self.from) > 0 {
|
if len(self.from) > 0 {
|
||||||
for _, from := range self.from {
|
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
|
fromIncluded = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -187,7 +188,7 @@ func (self *Filter) bloomFilter(block *types.Block) bool {
|
|||||||
|
|
||||||
if len(self.to) > 0 {
|
if len(self.to) > 0 {
|
||||||
for _, to := range self.to {
|
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
|
toIncluded = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
// import "testing"
|
|
||||||
|
|
||||||
// func TestFilter(t *testing.T) {
|
|
||||||
// NewFilter(NewTestManager())
|
|
||||||
// }
|
|
||||||
|
@ -3,8 +3,10 @@ package core
|
|||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"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 EmptyShaList = crypto.Sha3(ethutil.Encode([]interface{}{}))
|
||||||
var EmptyListRoot = crypto.Sha3(ethutil.Encode(""))
|
var EmptyListRoot = crypto.Sha3(ethutil.Encode(""))
|
||||||
|
|
||||||
var GenesisHeader = []interface{}{
|
func GenesisBlock() *types.Block {
|
||||||
// Previous hash (none)
|
genesis := types.NewBlock(ZeroHash256, ZeroHash160, nil, big.NewInt(131072), crypto.Sha3(big.NewInt(42).Bytes()), "")
|
||||||
ZeroHash256,
|
genesis.Header().Number = ethutil.Big0
|
||||||
// Empty uncles
|
genesis.Header().GasLimit = big.NewInt(1000000)
|
||||||
EmptyShaList,
|
genesis.Header().GasUsed = ethutil.Big0
|
||||||
// Coinbase
|
genesis.Header().Time = 0
|
||||||
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()),
|
|
||||||
}
|
|
||||||
|
|
||||||
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/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/wire"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Implement our EthTest Manager
|
// Implement our EthTest Manager
|
||||||
@ -54,11 +54,11 @@ func (tm *TestManager) TxPool() *TxPool {
|
|||||||
func (tm *TestManager) EventMux() *event.TypeMux {
|
func (tm *TestManager) EventMux() *event.TypeMux {
|
||||||
return tm.eventMux
|
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")
|
fmt.Println("Broadcast not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *TestManager) ClientIdentity() wire.ClientIdentity {
|
func (tm *TestManager) ClientIdentity() p2p.ClientIdentity {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (tm *TestManager) KeyManager() *crypto.KeyManager {
|
func (tm *TestManager) KeyManager() *crypto.KeyManager {
|
||||||
|
@ -5,6 +5,8 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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/state"
|
||||||
"github.com/ethereum/go-ethereum/vm"
|
"github.com/ethereum/go-ethereum/vm"
|
||||||
)
|
)
|
||||||
@ -27,48 +29,69 @@ import (
|
|||||||
*/
|
*/
|
||||||
type StateTransition struct {
|
type StateTransition struct {
|
||||||
coinbase, receiver []byte
|
coinbase, receiver []byte
|
||||||
tx *types.Transaction
|
msg Message
|
||||||
gas, gasPrice *big.Int
|
gas, gasPrice *big.Int
|
||||||
|
initialGas *big.Int
|
||||||
value *big.Int
|
value *big.Int
|
||||||
data []byte
|
data []byte
|
||||||
state *state.StateDB
|
state *state.StateDB
|
||||||
block *types.Block
|
block *types.Block
|
||||||
|
|
||||||
cb, rec, sen *state.StateObject
|
cb, rec, sen *state.StateObject
|
||||||
|
|
||||||
|
Env vm.Environment
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStateTransition(coinbase *state.StateObject, tx *types.Transaction, state *state.StateDB, block *types.Block) *StateTransition {
|
type Message interface {
|
||||||
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}
|
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 {
|
func (self *StateTransition) Coinbase() *state.StateObject {
|
||||||
if self.cb != nil {
|
return self.state.GetOrNewStateObject(self.coinbase)
|
||||||
return self.cb
|
|
||||||
}
|
|
||||||
|
|
||||||
self.cb = self.state.GetOrNewStateObject(self.coinbase)
|
|
||||||
return self.cb
|
|
||||||
}
|
}
|
||||||
func (self *StateTransition) Sender() *state.StateObject {
|
func (self *StateTransition) From() *state.StateObject {
|
||||||
if self.sen != nil {
|
return self.state.GetOrNewStateObject(self.msg.From())
|
||||||
return self.sen
|
|
||||||
}
|
|
||||||
|
|
||||||
self.sen = self.state.GetOrNewStateObject(self.tx.Sender())
|
|
||||||
|
|
||||||
return self.sen
|
|
||||||
}
|
}
|
||||||
func (self *StateTransition) Receiver() *state.StateObject {
|
func (self *StateTransition) To() *state.StateObject {
|
||||||
if self.tx != nil && self.tx.CreatesContract() {
|
if self.msg != nil && MessageCreatesContract(self.msg) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return self.state.GetOrNewStateObject(self.msg.To())
|
||||||
if self.rec != nil {
|
|
||||||
return self.rec
|
|
||||||
}
|
|
||||||
|
|
||||||
self.rec = self.state.GetOrNewStateObject(self.tx.Recipient)
|
|
||||||
return self.rec
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateTransition) UseGas(amount *big.Int) error {
|
func (self *StateTransition) UseGas(amount *big.Int) error {
|
||||||
@ -87,41 +110,33 @@ func (self *StateTransition) AddGas(amount *big.Int) {
|
|||||||
func (self *StateTransition) BuyGas() error {
|
func (self *StateTransition) BuyGas() error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
sender := self.Sender()
|
sender := self.From()
|
||||||
if sender.Balance().Cmp(self.tx.GasValue()) < 0 {
|
if sender.Balance().Cmp(MessageGasValue(self.msg)) < 0 {
|
||||||
return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Balance())
|
return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address()[:4], MessageGasValue(self.msg), sender.Balance())
|
||||||
}
|
}
|
||||||
|
|
||||||
coinbase := self.Coinbase()
|
coinbase := self.Coinbase()
|
||||||
err = coinbase.BuyGas(self.tx.Gas, self.tx.GasPrice)
|
err = coinbase.BuyGas(self.msg.Gas(), self.msg.GasPrice())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
self.AddGas(self.tx.Gas)
|
self.AddGas(self.msg.Gas())
|
||||||
sender.SubAmount(self.tx.GasValue())
|
self.initialGas.Set(self.msg.Gas())
|
||||||
|
sender.SubAmount(MessageGasValue(self.msg))
|
||||||
|
|
||||||
return nil
|
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) {
|
func (self *StateTransition) preCheck() (err error) {
|
||||||
var (
|
var (
|
||||||
tx = self.tx
|
msg = self.msg
|
||||||
sender = self.Sender()
|
sender = self.From()
|
||||||
)
|
)
|
||||||
|
|
||||||
// Make sure this transaction's nonce is correct
|
// Make sure this transaction's nonce is correct
|
||||||
if sender.Nonce != tx.Nonce {
|
if sender.Nonce != msg.Nonce() {
|
||||||
return NonceError(tx.Nonce, sender.Nonce)
|
return NonceError(msg.Nonce(), sender.Nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-pay gas / Buy gas of the coinbase account
|
// Pre-pay gas / Buy gas of the coinbase account
|
||||||
@ -132,8 +147,8 @@ func (self *StateTransition) preCheck() (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateTransition) TransitionState() (err error) {
|
func (self *StateTransition) TransitionState() (ret []byte, err error) {
|
||||||
statelogger.Debugf("(~) %x\n", self.tx.Hash())
|
statelogger.Debugf("(~) %x\n", self.msg.Hash())
|
||||||
|
|
||||||
// XXX Transactions after this point are considered valid.
|
// XXX Transactions after this point are considered valid.
|
||||||
if err = self.preCheck(); err != nil {
|
if err = self.preCheck(); err != nil {
|
||||||
@ -141,8 +156,8 @@ func (self *StateTransition) TransitionState() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tx = self.tx
|
msg = self.msg
|
||||||
sender = self.Sender()
|
sender = self.From()
|
||||||
)
|
)
|
||||||
|
|
||||||
defer self.RefundGas()
|
defer self.RefundGas()
|
||||||
@ -168,30 +183,56 @@ func (self *StateTransition) TransitionState() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret []byte
|
vmenv := self.VmEnv()
|
||||||
vmenv := NewEnv(self.state, self.tx, self.block)
|
|
||||||
var ref vm.ClosureRef
|
var ref vm.ClosureRef
|
||||||
if tx.CreatesContract() {
|
if MessageCreatesContract(msg) {
|
||||||
self.rec = MakeContract(tx, self.state)
|
contract := MakeContract(msg, self.state)
|
||||||
|
ret, err, ref = vmenv.Create(sender, contract.Address(), self.msg.Data(), self.gas, self.gasPrice, self.value)
|
||||||
ret, err, ref = vmenv.Create(sender, self.rec.Address(), self.tx.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)
|
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 {
|
if err != nil {
|
||||||
statelogger.Debugln(err)
|
self.UseGas(self.gas)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts an transaction in to a state object
|
// Converts an transaction in to a state object
|
||||||
func MakeContract(tx *types.Transaction, state *state.StateDB) *state.StateObject {
|
func MakeContract(msg Message, state *state.StateDB) *state.StateObject {
|
||||||
addr := tx.CreationAddress(state)
|
addr := AddressFromMessage(msg)
|
||||||
|
|
||||||
contract := state.GetOrNewStateObject(addr)
|
contract := state.GetOrNewStateObject(addr)
|
||||||
contract.InitCode = tx.Data
|
contract.InitCode = msg.Data()
|
||||||
|
|
||||||
return contract
|
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"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/state"
|
"github.com/ethereum/go-ethereum/state"
|
||||||
"github.com/ethereum/go-ethereum/wire"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var txplogger = logger.NewLogger("TXP")
|
var txplogger = logger.NewLogger("TXP")
|
||||||
@ -18,7 +18,9 @@ var txplogger = logger.NewLogger("TXP")
|
|||||||
const txPoolQueueSize = 50
|
const txPoolQueueSize = 50
|
||||||
|
|
||||||
type TxPoolHook chan *types.Transaction
|
type TxPoolHook chan *types.Transaction
|
||||||
type TxMsgTy byte
|
type TxMsg struct {
|
||||||
|
Tx *types.Transaction
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
minGasPrice = 1000000
|
minGasPrice = 1000000
|
||||||
@ -26,11 +28,6 @@ const (
|
|||||||
|
|
||||||
var MinGasPrice = big.NewInt(10000000000000)
|
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) {
|
func EachTx(pool *list.List, it func(*types.Transaction, *list.Element) bool) {
|
||||||
for e := pool.Front(); e != nil; e = e.Next() {
|
for e := pool.Front(); e != nil; e = e.Next() {
|
||||||
if it(e.Value.(*types.Transaction), e) {
|
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
|
// pool is being drained or synced for whatever reason the transactions
|
||||||
// will simple queue up and handled when the mutex is freed.
|
// will simple queue up and handled when the mutex is freed.
|
||||||
type TxPool struct {
|
type TxPool struct {
|
||||||
Ethereum EthManager
|
|
||||||
// The mutex for accessing the Tx pool.
|
// The mutex for accessing the Tx pool.
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
// Queueing channel for reading and writing incoming
|
// Queueing channel for reading and writing incoming
|
||||||
@ -75,14 +71,18 @@ type TxPool struct {
|
|||||||
SecondaryProcessor TxProcessor
|
SecondaryProcessor TxProcessor
|
||||||
|
|
||||||
subscribers []chan TxMsg
|
subscribers []chan TxMsg
|
||||||
|
|
||||||
|
chainManager *ChainManager
|
||||||
|
eventMux *event.TypeMux
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTxPool(ethereum EthManager) *TxPool {
|
func NewTxPool(chainManager *ChainManager, eventMux *event.TypeMux) *TxPool {
|
||||||
return &TxPool{
|
return &TxPool{
|
||||||
pool: list.New(),
|
pool: list.New(),
|
||||||
queueChan: make(chan *types.Transaction, txPoolQueueSize),
|
queueChan: make(chan *types.Transaction, txPoolQueueSize),
|
||||||
quit: make(chan bool),
|
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)
|
pool.pool.PushBack(tx)
|
||||||
|
|
||||||
// Broadcast the transaction to the rest of the peers
|
// 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 {
|
func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
|
||||||
// Get the last block so we can retrieve the sender and receiver from
|
// Get the last block so we can retrieve the sender and receiver from
|
||||||
// the merkle trie
|
// the merkle trie
|
||||||
block := pool.Ethereum.ChainManager().CurrentBlock
|
block := pool.chainManager.CurrentBlock
|
||||||
// Something has gone horribly wrong if this happens
|
// Something has gone horribly wrong if this happens
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return fmt.Errorf("No last block on the block chain")
|
return fmt.Errorf("No last block on the block chain")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(tx.Recipient) != 0 && len(tx.Recipient) != 20 {
|
if len(tx.To()) != 0 && len(tx.To()) != 20 {
|
||||||
return fmt.Errorf("Invalid recipient. len = %d", len(tx.Recipient))
|
return fmt.Errorf("Invalid recipient. len = %d", len(tx.To()))
|
||||||
}
|
}
|
||||||
|
|
||||||
v, _, _ := tx.Curve()
|
v, _, _ := tx.Curve()
|
||||||
@ -116,19 +116,17 @@ func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the sender
|
// 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
|
// Make sure there's enough in the sender's account. Having insufficient
|
||||||
// funds won't invalidate this transaction but simple ignores it.
|
// funds won't invalidate this transaction but simple ignores it.
|
||||||
if sender.Balance().Cmp(totAmount) < 0 {
|
if sender.Balance().Cmp(totAmount) < 0 {
|
||||||
return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.Sender())
|
return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.From())
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment the nonce making each tx valid only once to prevent replay
|
// 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)
|
self.addTransaction(tx)
|
||||||
|
|
||||||
tmp := make([]byte, 4)
|
txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.From()[:4], tx.To()[:4], tx.Value, tx.Hash())
|
||||||
copy(tmp, tx.Recipient)
|
|
||||||
|
|
||||||
txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.Sender()[:4], tmp, tx.Value, tx.Hash())
|
|
||||||
|
|
||||||
// Notify the subscribers
|
// Notify the subscribers
|
||||||
go self.Ethereum.EventMux().Post(TxPreEvent{tx})
|
go self.eventMux.Post(TxPreEvent{tx})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -169,7 +164,17 @@ func (self *TxPool) Size() int {
|
|||||||
return self.pool.Len()
|
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()
|
pool.mutex.Lock()
|
||||||
defer pool.mutex.Unlock()
|
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() {
|
for e := pool.pool.Front(); e != nil; e = e.Next() {
|
||||||
tx := e.Value.(*types.Transaction)
|
tx := e.Value.(*types.Transaction)
|
||||||
sender := state.GetAccount(tx.Sender())
|
sender := state.GetAccount(tx.From())
|
||||||
err := pool.ValidateTransaction(tx)
|
err := pool.ValidateTransaction(tx)
|
||||||
if err != nil || sender.Nonce >= tx.Nonce {
|
if err != nil || sender.Nonce >= tx.Nonce() {
|
||||||
pool.pool.Remove(e)
|
pool.pool.Remove(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,7 +221,7 @@ func (self *TxPool) RemoveSet(txs types.Transactions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pool *TxPool) Flush() []*types.Transaction {
|
func (pool *TxPool) Flush() []*types.Transaction {
|
||||||
txList := pool.CurrentTransactions()
|
txList := pool.GetTransactions()
|
||||||
|
|
||||||
// Recreate a new list all together
|
// Recreate a new list all together
|
||||||
// XXX Is this the fastest way?
|
// XXX Is this the fastest way?
|
||||||
|
@ -9,41 +9,240 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"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/state"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type BlockInfo struct {
|
type Header struct {
|
||||||
Number uint64
|
// Hash to the previous block
|
||||||
Hash []byte
|
ParentHash ethutil.Bytes
|
||||||
Parent []byte
|
// Uncles of this block
|
||||||
TD *big.Int
|
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) {
|
func (self *Header) rlpData(withNonce bool) []interface{} {
|
||||||
decoder := ethutil.NewValueFromBytes(data)
|
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()
|
return fields
|
||||||
bi.Hash = decoder.Get(1).Bytes()
|
|
||||||
bi.Parent = decoder.Get(2).Bytes()
|
|
||||||
bi.TD = decoder.Get(3).BigInt()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BlockInfo) RlpEncode() []byte {
|
func (self *Header) RlpData() interface{} {
|
||||||
return ethutil.Encode([]interface{}{bi.Number, bi.Hash, bi.Parent, bi.TD})
|
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
|
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
|
type BlockBy func(b1, b2 *Block) bool
|
||||||
|
|
||||||
func (self BlockBy) Sort(blocks Blocks) {
|
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 (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 }
|
func Number(b1, b2 *Block) bool { return b1.Header().Number.Cmp(b2.Header().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 }
|
|
||||||
|
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 (
|
import (
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/ptrie"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DerivableList interface {
|
type DerivableList interface {
|
||||||
@ -11,10 +11,10 @@ type DerivableList interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DeriveSha(list DerivableList) []byte {
|
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++ {
|
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
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/state"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/obscuren/secp256k1-go"
|
"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 {
|
func IsContractAddr(addr []byte) bool {
|
||||||
return len(addr) == 0
|
return len(addr) == 0
|
||||||
//return bytes.Compare(addr, ContractAddr) == 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Transaction struct {
|
type Transaction struct {
|
||||||
Nonce uint64
|
AccountNonce uint64
|
||||||
|
Price *big.Int
|
||||||
|
GasLimit *big.Int
|
||||||
Recipient []byte
|
Recipient []byte
|
||||||
Value *big.Int
|
Amount *big.Int
|
||||||
Gas *big.Int
|
Payload []byte
|
||||||
GasPrice *big.Int
|
V uint64
|
||||||
Data []byte
|
R, S []byte
|
||||||
v byte
|
|
||||||
r, s []byte
|
|
||||||
|
|
||||||
// Indicates whether this tx is a contract creation transaction
|
|
||||||
contractCreation bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction {
|
func NewContractCreationTx(Amount, gasAmount, price *big.Int, data []byte) *Transaction {
|
||||||
return &Transaction{Recipient: nil, Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true}
|
return NewTransactionMessage(nil, Amount, gasAmount, price, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction {
|
func NewTransactionMessage(to []byte, Amount, gasAmount, price *big.Int, data []byte) *Transaction {
|
||||||
return &Transaction{Recipient: to, Value: value, GasPrice: gasPrice, Gas: gas, Data: data, contractCreation: IsContractAddr(to)}
|
return &Transaction{Recipient: to, Amount: Amount, Price: price, GasLimit: gasAmount, Payload: data}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTransactionFromBytes(data []byte) *Transaction {
|
func NewTransactionFromBytes(data []byte) *Transaction {
|
||||||
@ -46,46 +41,55 @@ func NewTransactionFromBytes(data []byte) *Transaction {
|
|||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTransactionFromValue(val *ethutil.Value) *Transaction {
|
func NewTransactionFromAmount(val *ethutil.Value) *Transaction {
|
||||||
tx := &Transaction{}
|
tx := &Transaction{}
|
||||||
tx.RlpValueDecode(val)
|
tx.RlpValueDecode(val)
|
||||||
|
|
||||||
return tx
|
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 {
|
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 {
|
func (self *Transaction) Data() []byte {
|
||||||
return tx.contractCreation
|
return self.Payload
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Deprecated */
|
func (self *Transaction) Gas() *big.Int {
|
||||||
func (tx *Transaction) IsContract() bool {
|
return self.GasLimit
|
||||||
return tx.CreatesContract()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Transaction) CreationAddress(state *state.StateDB) []byte {
|
func (self *Transaction) GasPrice() *big.Int {
|
||||||
// Generate a new address
|
return self.Price
|
||||||
return crypto.Sha3(ethutil.NewValue([]interface{}{tx.Sender(), tx.Nonce}).Encode())[12:]
|
}
|
||||||
|
|
||||||
|
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) {
|
func (tx *Transaction) Curve() (v byte, r []byte, s []byte) {
|
||||||
v = tx.v
|
v = byte(tx.V)
|
||||||
r = ethutil.LeftPadBytes(tx.r, 32)
|
r = ethutil.LeftPadBytes(tx.R, 32)
|
||||||
s = ethutil.LeftPadBytes(tx.s, 32)
|
s = ethutil.LeftPadBytes(tx.S, 32)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -106,18 +110,18 @@ func (tx *Transaction) PublicKey() []byte {
|
|||||||
sig := append(r, s...)
|
sig := append(r, s...)
|
||||||
sig = append(sig, v-27)
|
sig = append(sig, v-27)
|
||||||
|
|
||||||
pubkey := crypto.Ecrecover(append(hash, sig...))
|
//pubkey := crypto.Ecrecover(append(hash, sig...))
|
||||||
//pubkey, _ := secp256k1.RecoverPubkey(hash, sig)
|
pubkey, _ := secp256k1.RecoverPubkey(hash, sig)
|
||||||
|
|
||||||
return pubkey
|
return pubkey
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Transaction) Sender() []byte {
|
func (tx *Transaction) sender() []byte {
|
||||||
pubkey := tx.PublicKey()
|
pubkey := tx.PublicKey()
|
||||||
|
|
||||||
// Validate the returned key.
|
// Validate the returned key.
|
||||||
// Return nil if public key isn't in full format
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,48 +132,37 @@ func (tx *Transaction) Sign(privk []byte) error {
|
|||||||
|
|
||||||
sig := tx.Signature(privk)
|
sig := tx.Signature(privk)
|
||||||
|
|
||||||
tx.r = sig[:32]
|
tx.R = sig[:32]
|
||||||
tx.s = sig[32:64]
|
tx.S = sig[32:64]
|
||||||
tx.v = sig[64] + 27
|
tx.V = uint64(sig[64] + 27)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Transaction) RlpData() interface{} {
|
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())
|
||||||
|
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Transaction) RlpEncode() []byte {
|
func (tx *Transaction) RlpEncode() []byte {
|
||||||
return tx.RlpValue().Encode()
|
return ethutil.Encode(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Transaction) RlpDecode(data []byte) {
|
func (tx *Transaction) RlpDecode(data []byte) {
|
||||||
tx.RlpValueDecode(ethutil.NewValueFromBytes(data))
|
rlp.Decode(bytes.NewReader(data), tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
|
func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
|
||||||
tx.Nonce = decoder.Get(0).Uint()
|
tx.AccountNonce = decoder.Get(0).Uint()
|
||||||
tx.GasPrice = decoder.Get(1).BigInt()
|
tx.Price = decoder.Get(1).BigInt()
|
||||||
tx.Gas = decoder.Get(2).BigInt()
|
tx.GasLimit = decoder.Get(2).BigInt()
|
||||||
tx.Recipient = decoder.Get(3).Bytes()
|
tx.Recipient = decoder.Get(3).Bytes()
|
||||||
tx.Value = decoder.Get(4).BigInt()
|
tx.Amount = decoder.Get(4).BigInt()
|
||||||
tx.Data = decoder.Get(5).Bytes()
|
tx.Payload = decoder.Get(5).Bytes()
|
||||||
tx.v = byte(decoder.Get(6).Uint())
|
tx.V = decoder.Get(6).Uint()
|
||||||
|
tx.R = decoder.Get(7).Bytes()
|
||||||
tx.r = decoder.Get(7).Bytes()
|
tx.S = decoder.Get(8).Bytes()
|
||||||
tx.s = decoder.Get(8).Bytes()
|
|
||||||
|
|
||||||
if IsContractAddr(tx.Recipient) {
|
|
||||||
tx.contractCreation = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Transaction) String() string {
|
func (tx *Transaction) String() string {
|
||||||
@ -180,25 +173,28 @@ func (tx *Transaction) String() string {
|
|||||||
To: %x
|
To: %x
|
||||||
Nonce: %v
|
Nonce: %v
|
||||||
GasPrice: %v
|
GasPrice: %v
|
||||||
Gas: %v
|
GasLimit %v
|
||||||
Value: %v
|
Value: %v
|
||||||
Data: 0x%x
|
Data: 0x%x
|
||||||
V: 0x%x
|
V: 0x%x
|
||||||
R: 0x%x
|
R: 0x%x
|
||||||
S: 0x%x
|
S: 0x%x
|
||||||
`,
|
Hex: %x
|
||||||
|
`,
|
||||||
tx.Hash(),
|
tx.Hash(),
|
||||||
len(tx.Recipient) == 0,
|
len(tx.Recipient) == 0,
|
||||||
tx.Sender(),
|
tx.From(),
|
||||||
tx.Recipient,
|
tx.To(),
|
||||||
tx.Nonce,
|
tx.AccountNonce,
|
||||||
tx.GasPrice,
|
tx.Price,
|
||||||
tx.Gas,
|
tx.GasLimit,
|
||||||
tx.Value,
|
tx.Amount,
|
||||||
tx.Data,
|
tx.Payload,
|
||||||
tx.v,
|
tx.V,
|
||||||
tx.r,
|
tx.R,
|
||||||
tx.s)
|
tx.S,
|
||||||
|
ethutil.Encode(tx),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transaction slice type for basic sorting
|
// 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 }
|
type TxByNonce struct{ Transactions }
|
||||||
|
|
||||||
func (s TxByNonce) Less(i, j int) bool {
|
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 {
|
type VMEnv struct {
|
||||||
state *state.StateDB
|
state *state.StateDB
|
||||||
block *types.Block
|
block *types.Block
|
||||||
tx *types.Transaction
|
msg Message
|
||||||
depth int
|
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{
|
return &VMEnv{
|
||||||
state: state,
|
state: state,
|
||||||
block: block,
|
block: block,
|
||||||
tx: tx,
|
msg: msg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *VMEnv) Origin() []byte { return self.tx.Sender() }
|
func (self *VMEnv) Origin() []byte { return self.msg.From() }
|
||||||
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number }
|
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() }
|
||||||
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
|
func (self *VMEnv) PrevHash() []byte { return self.block.ParentHash() }
|
||||||
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
|
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase() }
|
||||||
func (self *VMEnv) Time() int64 { return self.block.Time }
|
func (self *VMEnv) Time() int64 { return self.block.Time() }
|
||||||
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
|
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() }
|
||||||
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
|
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) 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) Depth() int { return self.depth }
|
||||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||||
func (self *VMEnv) AddLog(log state.Log) {
|
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 {
|
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *Execution {
|
||||||
evm := vm.New(self, vm.DebugVmTy)
|
return NewExecution(self, addr, data, gas, price, value)
|
||||||
|
|
||||||
return NewExecution(evm, addr, data, gas, price, value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *VMEnv) Call(me vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
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)
|
return self.db.NewIterator(nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *LDBDatabase) Write(batch *leveldb.Batch) error {
|
||||||
|
return self.db.Write(batch, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func (self *LDBDatabase) Close() {
|
func (self *LDBDatabase) Close() {
|
||||||
// Close the leveldb database
|
// Close the leveldb database
|
||||||
self.db.Close()
|
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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -193,8 +192,13 @@ func Encode(object interface{}) []byte {
|
|||||||
if blen < 56 {
|
if blen < 56 {
|
||||||
buff.WriteByte(byte(blen) + 0xc0)
|
buff.WriteByte(byte(blen) + 0xc0)
|
||||||
} else {
|
} else {
|
||||||
buff.WriteByte(byte(intlen(int64(blen))) + 0xf7)
|
ilen := byte(intlen(int64(blen)))
|
||||||
binary.Write(&buff, binary.BigEndian, 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)
|
buff.ReadFrom(&b)
|
||||||
}
|
}
|
||||||
|
@ -397,5 +397,5 @@ func (it *ValueIterator) Value() *Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (it *ValueIterator) Idx() int {
|
func (it *ValueIterator) Idx() int {
|
||||||
return it.idx
|
return it.idx - 1
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,29 @@ package filter
|
|||||||
|
|
||||||
type Generic struct {
|
type Generic struct {
|
||||||
Str1, Str2, Str3 string
|
Str1, Str2, Str3 string
|
||||||
|
Data map[string]struct{}
|
||||||
|
|
||||||
Fn func(data interface{})
|
Fn func(data interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// self = registered, f = incoming
|
||||||
func (self Generic) Compare(f Filter) bool {
|
func (self Generic) Compare(f Filter) bool {
|
||||||
|
var strMatch, dataMatch = true, true
|
||||||
|
|
||||||
filter := f.(Generic)
|
filter := f.(Generic)
|
||||||
if (len(self.Str1) == 0 || filter.Str1 == self.Str1) &&
|
if (len(self.Str1) > 0 && filter.Str1 != self.Str1) ||
|
||||||
(len(self.Str2) == 0 || filter.Str2 == self.Str2) &&
|
(len(self.Str2) > 0 && filter.Str2 != self.Str2) ||
|
||||||
(len(self.Str3) == 0 || filter.Str3 == self.Str3) {
|
(len(self.Str3) > 0 && filter.Str3 != self.Str3) {
|
||||||
return true
|
strMatch = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for k, _ := range self.Data {
|
||||||
|
if _, ok := filter.Data[k]; !ok {
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strMatch && dataMatch
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Generic) Trigger(data interface{}) {
|
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
|
do
|
||||||
if ls $dir/*.go &> /dev/null; then
|
if ls $dir/*.go &> /dev/null; then
|
||||||
# echo $dir
|
# echo $dir
|
||||||
|
if [[ $dir != "./tests/vm" ]]
|
||||||
|
then
|
||||||
go test -covermode=count -coverprofile=$dir/profile.tmp $dir
|
go test -covermode=count -coverprofile=$dir/profile.tmp $dir
|
||||||
|
fi
|
||||||
if [ -f $dir/profile.tmp ]
|
if [ -f $dir/profile.tmp ]
|
||||||
then
|
then
|
||||||
cat $dir/profile.tmp | tail -n +2 >> profile.cov
|
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,
|
# To submit the test coverage result to coveralls.io,
|
||||||
# use goveralls (https://github.com/mattn/goveralls)
|
# 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"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
@ -203,7 +203,7 @@ func (self *JSRE) addPeer(call otto.FunctionCall) otto.Value {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return otto.FalseValue()
|
return otto.FalseValue()
|
||||||
}
|
}
|
||||||
self.ethereum.ConnectToPeer(host)
|
self.ethereum.SuggestPeer(host)
|
||||||
|
|
||||||
return otto.TrueValue()
|
return otto.TrueValue()
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package javascript
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/state"
|
"github.com/ethereum/go-ethereum/state"
|
||||||
"github.com/ethereum/go-ethereum/ui"
|
"github.com/ethereum/go-ethereum/ui"
|
||||||
@ -18,11 +18,11 @@ type JSStateObject struct {
|
|||||||
|
|
||||||
func (self *JSStateObject) EachStorage(call otto.FunctionCall) otto.Value {
|
func (self *JSStateObject) EachStorage(call otto.FunctionCall) otto.Value {
|
||||||
cb := call.Argument(0)
|
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()
|
return otto.UndefinedValue()
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/ethutil"
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
"github.com/ethereum/go-ethereum/pow"
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
"github.com/ethereum/go-ethereum/pow/ezp"
|
"github.com/ethereum/go-ethereum/pow/ezp"
|
||||||
@ -36,7 +36,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/wire"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type LocalTx struct {
|
type LocalTx struct {
|
||||||
@ -57,7 +56,7 @@ type Miner struct {
|
|||||||
eth *eth.Ethereum
|
eth *eth.Ethereum
|
||||||
events event.Subscription
|
events event.Subscription
|
||||||
|
|
||||||
uncles types.Blocks
|
uncles []*types.Header
|
||||||
localTxs map[int]*LocalTx
|
localTxs map[int]*LocalTx
|
||||||
localTxId int
|
localTxId int
|
||||||
|
|
||||||
@ -185,15 +184,17 @@ func (self *Miner) mine() {
|
|||||||
block.SetUncles(self.uncles)
|
block.SetUncles(self.uncles)
|
||||||
}
|
}
|
||||||
|
|
||||||
parent := chainMan.GetBlock(block.PrevHash)
|
parent := chainMan.GetBlock(block.ParentHash())
|
||||||
coinbase := block.State().GetOrNewStateObject(block.Coinbase)
|
coinbase := block.State().GetOrNewStateObject(block.Coinbase())
|
||||||
coinbase.SetGasPool(block.CalcGasLimit(parent))
|
coinbase.SetGasPool(core.CalcGasLimit(parent, block))
|
||||||
|
|
||||||
transactions := self.finiliseTxs()
|
transactions := self.finiliseTxs()
|
||||||
|
|
||||||
|
state := block.State()
|
||||||
|
|
||||||
// Accumulate all valid transactions and apply them to the new state
|
// Accumulate all valid transactions and apply them to the new state
|
||||||
// Error may be ignored. It's not important during mining
|
// 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 {
|
if err != nil {
|
||||||
minerlogger.Debugln(err)
|
minerlogger.Debugln(err)
|
||||||
}
|
}
|
||||||
@ -203,21 +204,22 @@ func (self *Miner) mine() {
|
|||||||
block.SetReceipts(receipts)
|
block.SetReceipts(receipts)
|
||||||
|
|
||||||
// Accumulate the rewards included for this block
|
// 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))
|
minerlogger.Infof("Mining on block. Includes %v transactions", len(transactions))
|
||||||
|
|
||||||
// Find a valid nonce
|
// Find a valid nonce
|
||||||
nonce := self.pow.Search(block, self.powQuitCh)
|
nonce := self.pow.Search(block, self.powQuitCh)
|
||||||
if nonce != nil {
|
if nonce != nil {
|
||||||
block.Nonce = nonce
|
block.Header().Nonce = nonce
|
||||||
err := chainMan.InsertChain(types.Blocks{block})
|
err := chainMan.InsertChain(types.Blocks{block})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
minerlogger.Infoln(err)
|
minerlogger.Infoln(err)
|
||||||
} else {
|
} 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.Infof("🔨 Mined block %x\n", block.Hash())
|
||||||
minerlogger.Infoln(block)
|
minerlogger.Infoln(block)
|
||||||
@ -237,8 +239,8 @@ func (self *Miner) finiliseTxs() types.Transactions {
|
|||||||
key := self.eth.KeyManager()
|
key := self.eth.KeyManager()
|
||||||
for i, ltx := range self.localTxs {
|
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 := types.NewTransactionMessage(ltx.To, ethutil.Big(ltx.Value), ethutil.Big(ltx.Gas), ethutil.Big(ltx.GasPrice), ltx.Data)
|
||||||
tx.Nonce = state.GetNonce(self.Coinbase)
|
tx.SetNonce(state.GetNonce(self.Coinbase))
|
||||||
state.SetNonce(self.Coinbase, tx.Nonce+1)
|
state.SetNonce(self.Coinbase, tx.Nonce()+1)
|
||||||
|
|
||||||
tx.Sign(key.PrivateKey())
|
tx.Sign(key.PrivateKey())
|
||||||
|
|
||||||
@ -246,8 +248,8 @@ func (self *Miner) finiliseTxs() types.Transactions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Faster than append
|
// Faster than append
|
||||||
for _, tx := range self.eth.TxPool().CurrentTransactions() {
|
for _, tx := range self.eth.TxPool().GetTransactions() {
|
||||||
if tx.GasPrice.Cmp(self.MinAcceptedGasPrice) >= 0 {
|
if tx.GasPrice().Cmp(self.MinAcceptedGasPrice) >= 0 {
|
||||||
txs[actualSize] = tx
|
txs[actualSize] = tx
|
||||||
actualSize++
|
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() {
|
func (srv *Server) discLoop() {
|
||||||
for peer := range srv.peerDisconnect {
|
for peer := range srv.peerDisconnect {
|
||||||
// peer has just disconnected. free up its slot.
|
srv.removePeer(peer)
|
||||||
srvlog.Infof("%v is gone", peer)
|
|
||||||
srv.peerSlots <- peer.slot
|
|
||||||
srv.lock.Lock()
|
|
||||||
srv.peers[peer.slot] = nil
|
|
||||||
srv.lock.Unlock()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,7 +379,7 @@ func (srv *Server) addPeer(conn net.Conn, desc *peerAddr, slot int) *Peer {
|
|||||||
func (srv *Server) removePeer(peer *Peer) {
|
func (srv *Server) removePeer(peer *Peer) {
|
||||||
srv.lock.Lock()
|
srv.lock.Lock()
|
||||||
defer srv.lock.Unlock()
|
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 {
|
if srv.peers[peer.slot] != peer {
|
||||||
srvlog.Warnln("Invalid peer to remove:", peer)
|
srvlog.Warnln("Invalid peer to remove:", peer)
|
||||||
return
|
return
|
||||||
@ -416,6 +411,7 @@ func (srv *Server) verifyPeer(addr *peerAddr) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO replace with "Set"
|
||||||
type Blacklist interface {
|
type Blacklist interface {
|
||||||
Get([]byte) (bool, error)
|
Get([]byte) (bool, error)
|
||||||
Put([]byte) 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"
|
import "math/big"
|
||||||
|
|
||||||
type Block interface {
|
type Block interface {
|
||||||
Diff() *big.Int
|
Difficulty() *big.Int
|
||||||
HashNoNonce() []byte
|
HashNoNonce() []byte
|
||||||
N() []byte
|
N() []byte
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ type EasyPow struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func New() *EasyPow {
|
func New() *EasyPow {
|
||||||
return &EasyPow{}
|
return &EasyPow{turbo: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pow *EasyPow) GetHashrate() int64 {
|
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 {
|
func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
|
||||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
hash := block.HashNoNonce()
|
hash := block.HashNoNonce()
|
||||||
diff := block.Diff()
|
diff := block.Difficulty()
|
||||||
i := int64(0)
|
i := int64(0)
|
||||||
start := time.Now().UnixNano()
|
start := time.Now().UnixNano()
|
||||||
t := time.Now()
|
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())
|
sha := crypto.Sha3(big.NewInt(r.Int63()).Bytes())
|
||||||
if pow.verify(hash, diff, sha) {
|
if verify(hash, diff, sha) {
|
||||||
return sha
|
return sha
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,7 +72,11 @@ func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
|
|||||||
return nil
|
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()
|
sha := sha3.NewKeccak256()
|
||||||
|
|
||||||
d := append(hash, nonce...)
|
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
|
return res.Cmp(verification) <= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pow *EasyPow) Verify(block pow.Block) bool {
|
func Verify(block pow.Block) bool {
|
||||||
return pow.verify(block.HashNoNonce(), block.Diff(), block.N())
|
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
|
package ptrie
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
type FullNode struct {
|
type FullNode struct {
|
||||||
trie *Trie
|
trie *Trie
|
||||||
nodes [17]Node
|
nodes [17]Node
|
||||||
@ -21,8 +23,10 @@ func (self *FullNode) Branches() []Node {
|
|||||||
func (self *FullNode) Copy() Node {
|
func (self *FullNode) Copy() Node {
|
||||||
nnode := NewFullNode(self.trie)
|
nnode := NewFullNode(self.trie)
|
||||||
for i, node := range self.nodes {
|
for i, node := range self.nodes {
|
||||||
|
if node != nil {
|
||||||
nnode.nodes[i] = node
|
nnode.nodes[i] = node
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nnode
|
return nnode
|
||||||
}
|
}
|
||||||
@ -56,6 +60,10 @@ func (self *FullNode) RlpData() interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *FullNode) set(k byte, value Node) {
|
func (self *FullNode) set(k byte, value Node) {
|
||||||
|
if _, ok := value.(*ValueNode); ok && k != 16 {
|
||||||
|
fmt.Println(value, k)
|
||||||
|
}
|
||||||
|
|
||||||
self.nodes[int(k)] = value
|
self.nodes[int(k)] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ type Iterator struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewIterator(trie *Trie) *Iterator {
|
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 {
|
func (self *Iterator) Next() bool {
|
||||||
|
@ -17,7 +17,7 @@ type Node interface {
|
|||||||
func (self *ValueNode) String() string { return self.fstring("") }
|
func (self *ValueNode) String() string { return self.fstring("") }
|
||||||
func (self *FullNode) String() string { return self.fstring("") }
|
func (self *FullNode) String() string { return self.fstring("") }
|
||||||
func (self *ShortNode) 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) }
|
func (self *HashNode) fstring(ind string) string { return fmt.Sprintf("%x ", self.key) }
|
||||||
|
|
||||||
// Full node
|
// Full node
|
||||||
@ -36,5 +36,5 @@ func (self *FullNode) fstring(ind string) string {
|
|||||||
|
|
||||||
// Short node
|
// Short node
|
||||||
func (self *ShortNode) fstring(ind string) string {
|
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