Merge branch 'poc8' into docbranch
This commit is contained in:
commit
0fb1bcd321
10
.travis.yml
10
.travis.yml
|
@ -1,6 +1,6 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.3
|
||||
- tip
|
||||
before_install:
|
||||
- sudo add-apt-repository ppa:ubuntu-sdk-team/ppa -y
|
||||
- sudo apt-get update -qq
|
||||
|
@ -8,10 +8,10 @@ before_install:
|
|||
install:
|
||||
- go get code.google.com/p/go.tools/cmd/goimports
|
||||
- go get github.com/golang/lint/golint
|
||||
# - go get code.google.com/p/go.tools/cmd/vet
|
||||
- go get code.google.com/p/go.tools/cmd/cover
|
||||
# - go get golang.org/x/tools/cmd/vet
|
||||
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
|
||||
- go get github.com/mattn/goveralls
|
||||
- ./install_deps.sh
|
||||
- ETH_DEPS=$(go list -f '{{.Imports}} {{.TestImports}} {{.XTestImports}}' github.com/ethereum/go-ethereum/... | sed -e 's/\[//g' | sed -e 's/\]//g' | sed -e 's/C //g'); if [ "$ETH_DEPS" ]; then go get $ETH_DEPS; fi
|
||||
before_script:
|
||||
- gofmt -l -w .
|
||||
- goimports -l -w .
|
||||
|
@ -19,7 +19,7 @@ before_script:
|
|||
# - go vet ./...
|
||||
# - go test -race ./...
|
||||
script:
|
||||
- ./gocoverage.sh && goveralls -coverprofile=profile.cov -service=travis-ci -repotoken $COVERALLS_TOKEN
|
||||
- ./gocoverage.sh
|
||||
env:
|
||||
- secure: "U2U1AmkU4NJBgKR/uUAebQY87cNL0+1JHjnLOmmXwxYYyj5ralWb1aSuSH3qSXiT93qLBmtaUkuv9fberHVqrbAeVlztVdUsKAq7JMQH+M99iFkC9UiRMqHmtjWJ0ok4COD1sRYixxi21wb/JrMe3M1iL4QJVS61iltjHhVdM64="
|
||||
|
||||
|
|
|
@ -25,14 +25,14 @@ RUN apt-get install -y qtbase5-private-dev qtdeclarative5-private-dev libqt5open
|
|||
## Fetch and install serpent-go
|
||||
RUN go get -v -d github.com/ethereum/serpent-go
|
||||
WORKDIR $GOPATH/src/github.com/ethereum/serpent-go
|
||||
RUN git checkout master
|
||||
# RUN git checkout master
|
||||
RUN git submodule update --init
|
||||
RUN go install -v
|
||||
|
||||
# Fetch and install go-ethereum
|
||||
RUN go get -v -d github.com/ethereum/go-ethereum/...
|
||||
WORKDIR $GOPATH/src/github.com/ethereum/go-ethereum
|
||||
RUN git checkout poc8
|
||||
# RUN git checkout develop
|
||||
RUN ETH_DEPS=$(go list -f '{{.Imports}} {{.TestImports}} {{.XTestImports}}' github.com/ethereum/go-ethereum/... | sed -e 's/\[//g' | sed -e 's/\]//g' | sed -e 's/C //g'); if [ "$ETH_DEPS" ]; then go get $ETH_DEPS; fi
|
||||
RUN go install -v ./cmd/ethereum
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ Ethereum
|
|||
[![Build
|
||||
Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20master%20branch)](http://build.ethdev.com:8010/builders/Linux%20Go%20master%20branch/builds/-1) master [![Build
|
||||
Status](http://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](http://build.ethdev.com:8010/builders/Linux%20Go%20develop%20branch/builds/-1) develop
|
||||
[![Coverage Status](https://coveralls.io/repos/ethereum/go-ethereum/badge.png?branch=tests)](https://coveralls.io/r/ethereum/go-ethereum?branch=tests) tests
|
||||
|
||||
Ethereum Go Client © 2014 Jeffrey Wilcke.
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
351
block_pool.go
351
block_pool.go
|
@ -1,351 +0,0 @@
|
|||
package eth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
)
|
||||
|
||||
var poollogger = logger.NewLogger("BPOOL")
|
||||
|
||||
type block struct {
|
||||
from *Peer
|
||||
peer *Peer
|
||||
block *types.Block
|
||||
reqAt time.Time
|
||||
requested int
|
||||
}
|
||||
|
||||
type BlockPool struct {
|
||||
mut sync.Mutex
|
||||
|
||||
eth *Ethereum
|
||||
|
||||
hashes [][]byte
|
||||
pool map[string]*block
|
||||
|
||||
td *big.Int
|
||||
quit chan bool
|
||||
|
||||
fetchingHashes bool
|
||||
downloadStartedAt time.Time
|
||||
|
||||
ChainLength, BlocksProcessed int
|
||||
|
||||
peer *Peer
|
||||
}
|
||||
|
||||
func NewBlockPool(eth *Ethereum) *BlockPool {
|
||||
return &BlockPool{
|
||||
eth: eth,
|
||||
pool: make(map[string]*block),
|
||||
td: ethutil.Big0,
|
||||
quit: make(chan bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BlockPool) Len() int {
|
||||
return len(self.hashes)
|
||||
}
|
||||
|
||||
func (self *BlockPool) Reset() {
|
||||
self.pool = make(map[string]*block)
|
||||
self.hashes = nil
|
||||
}
|
||||
|
||||
func (self *BlockPool) HasLatestHash() bool {
|
||||
self.mut.Lock()
|
||||
defer self.mut.Unlock()
|
||||
|
||||
return self.pool[string(self.eth.ChainManager().CurrentBlock.Hash())] != nil
|
||||
}
|
||||
|
||||
func (self *BlockPool) HasCommonHash(hash []byte) bool {
|
||||
return self.eth.ChainManager().GetBlock(hash) != nil
|
||||
}
|
||||
|
||||
func (self *BlockPool) Blocks() (blocks types.Blocks) {
|
||||
for _, item := range self.pool {
|
||||
if item.block != nil {
|
||||
blocks = append(blocks, item.block)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *BlockPool) FetchHashes(peer *Peer) bool {
|
||||
highestTd := self.eth.HighestTDPeer()
|
||||
|
||||
if (self.peer == nil && peer.td.Cmp(highestTd) >= 0) || (self.peer != nil && peer.td.Cmp(self.peer.td) > 0) || self.peer == peer {
|
||||
if self.peer != peer {
|
||||
poollogger.Infof("Found better suitable peer (%v vs %v)\n", self.td, peer.td)
|
||||
|
||||
if self.peer != nil {
|
||||
self.peer.doneFetchingHashes = true
|
||||
}
|
||||
}
|
||||
|
||||
self.peer = peer
|
||||
self.td = peer.td
|
||||
|
||||
if !self.HasLatestHash() {
|
||||
self.fetchHashes()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *BlockPool) fetchHashes() {
|
||||
peer := self.peer
|
||||
|
||||
peer.doneFetchingHashes = false
|
||||
|
||||
const amount = 256
|
||||
peerlogger.Debugf("Fetching hashes (%d) %x...\n", amount, peer.lastReceivedHash[0:4])
|
||||
peer.QueueMessage(wire.NewMessage(wire.MsgGetBlockHashesTy, []interface{}{peer.lastReceivedHash, uint32(amount)}))
|
||||
}
|
||||
|
||||
func (self *BlockPool) AddHash(hash []byte, peer *Peer) {
|
||||
self.mut.Lock()
|
||||
defer self.mut.Unlock()
|
||||
|
||||
if self.pool[string(hash)] == nil {
|
||||
self.pool[string(hash)] = &block{peer, nil, nil, time.Now(), 0}
|
||||
|
||||
self.hashes = append([][]byte{hash}, self.hashes...)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BlockPool) Add(b *types.Block, peer *Peer) {
|
||||
self.addBlock(b, peer, false)
|
||||
}
|
||||
|
||||
func (self *BlockPool) AddNew(b *types.Block, peer *Peer) {
|
||||
self.addBlock(b, peer, true)
|
||||
}
|
||||
|
||||
func (self *BlockPool) addBlock(b *types.Block, peer *Peer, newBlock bool) {
|
||||
self.mut.Lock()
|
||||
defer self.mut.Unlock()
|
||||
|
||||
hash := string(b.Hash())
|
||||
|
||||
if self.pool[hash] == nil && !self.eth.ChainManager().HasBlock(b.Hash()) {
|
||||
poollogger.Infof("Got unrequested block (%x...)\n", hash[0:4])
|
||||
|
||||
self.hashes = append(self.hashes, b.Hash())
|
||||
self.pool[hash] = &block{peer, peer, b, time.Now(), 0}
|
||||
|
||||
// The following is only performed on an unrequested new block
|
||||
if newBlock {
|
||||
fmt.Println("1.", !self.eth.ChainManager().HasBlock(b.PrevHash), ethutil.Bytes2Hex(b.Hash()[0:4]), ethutil.Bytes2Hex(b.PrevHash[0:4]))
|
||||
fmt.Println("2.", self.pool[string(b.PrevHash)] == nil)
|
||||
fmt.Println("3.", !self.fetchingHashes)
|
||||
if !self.eth.ChainManager().HasBlock(b.PrevHash) /*&& self.pool[string(b.PrevHash)] == nil*/ && !self.fetchingHashes {
|
||||
poollogger.Infof("Unknown chain, requesting (%x...)\n", b.PrevHash[0:4])
|
||||
peer.QueueMessage(wire.NewMessage(wire.MsgGetBlockHashesTy, []interface{}{b.Hash(), uint32(256)}))
|
||||
}
|
||||
}
|
||||
} else if self.pool[hash] != nil {
|
||||
self.pool[hash].block = b
|
||||
}
|
||||
|
||||
self.BlocksProcessed++
|
||||
}
|
||||
|
||||
func (self *BlockPool) Remove(hash []byte) {
|
||||
self.mut.Lock()
|
||||
defer self.mut.Unlock()
|
||||
|
||||
self.hashes = ethutil.DeleteFromByteSlice(self.hashes, hash)
|
||||
delete(self.pool, string(hash))
|
||||
}
|
||||
|
||||
func (self *BlockPool) DistributeHashes() {
|
||||
self.mut.Lock()
|
||||
defer self.mut.Unlock()
|
||||
|
||||
var (
|
||||
peerLen = self.eth.peers.Len()
|
||||
amount = 256 * peerLen
|
||||
dist = make(map[*Peer][][]byte)
|
||||
)
|
||||
|
||||
num := int(math.Min(float64(amount), float64(len(self.pool))))
|
||||
for i, j := 0, 0; i < len(self.hashes) && j < num; i++ {
|
||||
hash := self.hashes[i]
|
||||
item := self.pool[string(hash)]
|
||||
|
||||
if item != nil && item.block == nil {
|
||||
var peer *Peer
|
||||
lastFetchFailed := time.Since(item.reqAt) > 5*time.Second
|
||||
|
||||
// Handle failed requests
|
||||
if lastFetchFailed && item.requested > 5 && item.peer != nil {
|
||||
if item.requested < 100 {
|
||||
// Select peer the hash was retrieved off
|
||||
peer = item.from
|
||||
} else {
|
||||
// Remove it
|
||||
self.hashes = ethutil.DeleteFromByteSlice(self.hashes, hash)
|
||||
delete(self.pool, string(hash))
|
||||
}
|
||||
} else if lastFetchFailed || item.peer == nil {
|
||||
// Find a suitable, available peer
|
||||
eachPeer(self.eth.peers, func(p *Peer, v *list.Element) {
|
||||
if peer == nil && len(dist[p]) < amount/peerLen && p.statusKnown {
|
||||
peer = p
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if peer != nil {
|
||||
item.reqAt = time.Now()
|
||||
item.peer = peer
|
||||
item.requested++
|
||||
|
||||
dist[peer] = append(dist[peer], hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for peer, hashes := range dist {
|
||||
peer.FetchBlocks(hashes)
|
||||
}
|
||||
|
||||
if len(dist) > 0 {
|
||||
self.downloadStartedAt = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BlockPool) Start() {
|
||||
go self.downloadThread()
|
||||
go self.chainThread()
|
||||
}
|
||||
|
||||
func (self *BlockPool) Stop() {
|
||||
close(self.quit)
|
||||
}
|
||||
|
||||
func (self *BlockPool) downloadThread() {
|
||||
serviceTimer := time.NewTicker(100 * time.Millisecond)
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-self.quit:
|
||||
break out
|
||||
case <-serviceTimer.C:
|
||||
// Check if we're catching up. If not distribute the hashes to
|
||||
// the peers and download the blockchain
|
||||
self.fetchingHashes = false
|
||||
eachPeer(self.eth.peers, func(p *Peer, v *list.Element) {
|
||||
if p.statusKnown && p.FetchingHashes() {
|
||||
self.fetchingHashes = true
|
||||
}
|
||||
})
|
||||
|
||||
if len(self.hashes) > 0 {
|
||||
self.DistributeHashes()
|
||||
}
|
||||
|
||||
if self.ChainLength < len(self.hashes) {
|
||||
self.ChainLength = len(self.hashes)
|
||||
}
|
||||
|
||||
if self.peer != nil &&
|
||||
!self.peer.doneFetchingHashes &&
|
||||
time.Since(self.peer.lastHashAt) > 10*time.Second &&
|
||||
time.Since(self.peer.lastHashRequestedAt) > 5*time.Second {
|
||||
self.fetchHashes()
|
||||
}
|
||||
|
||||
/*
|
||||
if !self.fetchingHashes {
|
||||
blocks := self.Blocks()
|
||||
chain.BlockBy(chain.Number).Sort(blocks)
|
||||
|
||||
if len(blocks) > 0 {
|
||||
if !self.eth.ChainManager().HasBlock(b.PrevHash) && self.pool[string(b.PrevHash)] == nil && !self.fetchingHashes {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BlockPool) chainThread() {
|
||||
procTimer := time.NewTicker(500 * time.Millisecond)
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-self.quit:
|
||||
break out
|
||||
case <-procTimer.C:
|
||||
blocks := self.Blocks()
|
||||
types.BlockBy(types.Number).Sort(blocks)
|
||||
|
||||
// Find common block
|
||||
for i, block := range blocks {
|
||||
if self.eth.ChainManager().HasBlock(block.PrevHash) {
|
||||
blocks = blocks[i:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(blocks) > 0 {
|
||||
if self.eth.ChainManager().HasBlock(blocks[0].PrevHash) {
|
||||
for i, block := range blocks[1:] {
|
||||
// NOTE: The Ith element in this loop refers to the previous block in
|
||||
// outer "blocks"
|
||||
if bytes.Compare(block.PrevHash, blocks[i].Hash()) != 0 {
|
||||
blocks = blocks[:i]
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
blocks = nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(blocks) > 0 {
|
||||
chainman := self.eth.ChainManager()
|
||||
|
||||
err := chainman.InsertChain(blocks)
|
||||
if err != nil {
|
||||
poollogger.Debugln(err)
|
||||
|
||||
self.Reset()
|
||||
|
||||
if self.peer != nil && self.peer.conn != nil {
|
||||
poollogger.Debugf("Punishing peer for supplying bad chain (%v)\n", self.peer.conn.RemoteAddr())
|
||||
}
|
||||
|
||||
// This peer gave us bad hashes and made us fetch a bad chain, therefor he shall be punished.
|
||||
self.eth.BlacklistPeer(self.peer)
|
||||
self.peer.StopWithReason(DiscBadPeer)
|
||||
self.td = ethutil.Big0
|
||||
self.peer = nil
|
||||
}
|
||||
|
||||
for _, block := range blocks {
|
||||
self.Remove(block.Hash())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,9 +21,9 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/cmd/ethereum/repl"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/javascript"
|
||||
)
|
||||
|
||||
|
|
|
@ -38,7 +38,8 @@ var (
|
|||
StartRpc bool
|
||||
StartWebSockets bool
|
||||
RpcPort int
|
||||
UseUPnP bool
|
||||
NatType string
|
||||
PMPGateway string
|
||||
OutboundPort string
|
||||
ShowGenesis bool
|
||||
AddPeer string
|
||||
|
@ -57,6 +58,7 @@ var (
|
|||
DumpHash string
|
||||
DumpNumber int
|
||||
VmType int
|
||||
ImportChain string
|
||||
)
|
||||
|
||||
// flags specific to cli client
|
||||
|
@ -84,8 +86,9 @@ func Init() {
|
|||
flag.StringVar(&KeyRing, "keyring", "", "identifier for keyring to use")
|
||||
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
|
||||
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
||||
flag.BoolVar(&UseUPnP, "upnp", false, "enable UPnP support")
|
||||
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers")
|
||||
flag.StringVar(&NatType, "nat", "", "NAT support (UPNP|PMP) (none)")
|
||||
flag.StringVar(&PMPGateway, "pmp", "", "Gateway IP for PMP")
|
||||
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
|
||||
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
|
||||
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
|
||||
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server")
|
||||
|
@ -102,6 +105,7 @@ func Init() {
|
|||
flag.BoolVar(&DiffTool, "difftool", false, "creates output for diff'ing. Sets LogLevel=0")
|
||||
flag.StringVar(&DiffType, "diff", "all", "sets the level of diff output [vm, all]. Has no effect if difftool=false")
|
||||
flag.BoolVar(&ShowGenesis, "genesis", false, "Dump the genesis block")
|
||||
flag.StringVar(&ImportChain, "chain", "", "Imports fiven chain")
|
||||
|
||||
flag.BoolVar(&Dump, "dump", false, "output the ethereum state in JSON format. Sub args [number, hash]")
|
||||
flag.StringVar(&DumpHash, "hash", "", "specify arg in hex")
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
@ -30,7 +31,7 @@ import (
|
|||
|
||||
const (
|
||||
ClientIdentifier = "Ethereum(G)"
|
||||
Version = "0.7.9"
|
||||
Version = "0.7.11"
|
||||
)
|
||||
|
||||
var clilogger = logger.NewLogger("CLI")
|
||||
|
@ -38,6 +39,10 @@ var clilogger = logger.NewLogger("CLI")
|
|||
func main() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
defer func() {
|
||||
logger.Flush()
|
||||
}()
|
||||
|
||||
utils.HandleInterrupt()
|
||||
|
||||
// precedence: code-internal flag default < config file < environment variables < command line
|
||||
|
@ -69,15 +74,15 @@ func main() {
|
|||
// create, import, export keys
|
||||
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
|
||||
|
||||
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier)
|
||||
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier, string(keyManager.PublicKey()))
|
||||
|
||||
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
|
||||
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, utils.NatType(NatType, PMPGateway), OutboundPort, MaxPeer)
|
||||
|
||||
if Dump {
|
||||
var block *types.Block
|
||||
|
||||
if len(DumpHash) == 0 && DumpNumber == -1 {
|
||||
block = ethereum.ChainManager().CurrentBlock
|
||||
block = ethereum.ChainManager().CurrentBlock()
|
||||
} else if len(DumpHash) > 0 {
|
||||
block = ethereum.ChainManager().GetBlock(ethutil.Hex2Bytes(DumpHash))
|
||||
} else {
|
||||
|
@ -93,9 +98,6 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
// block.GetRoot() does not exist
|
||||
//fmt.Printf("RLP: %x\nstate: %x\nhash: %x\n", ethutil.Rlp(block), block.GetRoot(), block.Hash())
|
||||
|
||||
// Leave the Println. This needs clean output for piping
|
||||
fmt.Printf("%s\n", block.State().Dump())
|
||||
|
||||
|
@ -112,6 +114,16 @@ func main() {
|
|||
utils.StartMining(ethereum)
|
||||
}
|
||||
|
||||
if len(ImportChain) > 0 {
|
||||
start := time.Now()
|
||||
err := utils.ImportChain(ethereum, ImportChain)
|
||||
if err != nil {
|
||||
clilogger.Infoln(err)
|
||||
}
|
||||
clilogger.Infoln("import done in", time.Since(start))
|
||||
return
|
||||
}
|
||||
|
||||
// better reworked as cases
|
||||
if StartJsConsole {
|
||||
InitJsConsole(ethereum)
|
||||
|
@ -131,5 +143,4 @@ func main() {
|
|||
|
||||
// this blocks the thread
|
||||
ethereum.WaitForShutdown()
|
||||
logger.Flush()
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/javascript"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
|
@ -86,12 +86,6 @@ func (self *JSRepl) Stop() {
|
|||
}
|
||||
|
||||
func (self *JSRepl) parseInput(code string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("[native] error", r)
|
||||
}
|
||||
}()
|
||||
|
||||
value, err := self.re.Run(code)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"directory": "example/js/",
|
||||
"cwd": "./",
|
||||
"analytics": false
|
||||
}
|
|
@ -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
|
|
@ -4,9 +4,15 @@
|
|||
# or operating system, you probably want to add a global ignore instead:
|
||||
# git config --global core.excludesfile ~/.gitignore_global
|
||||
|
||||
*.swp
|
||||
/tmp
|
||||
*/**/*un~
|
||||
*un~
|
||||
.DS_Store
|
||||
*/**/.DS_Store
|
||||
|
||||
ethereum/ethereum
|
||||
ethereal/ethereal
|
||||
example/js
|
||||
node_modules
|
||||
bower_components
|
||||
npm-debug.log
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
example/js
|
||||
node_modules
|
||||
test
|
||||
.gitignore
|
||||
.editorconfig
|
||||
.travis.yml
|
||||
.npmignore
|
||||
component.json
|
||||
testling.html
|
|
@ -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
|
|
@ -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/>.
|
|
@ -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
|
|
@ -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"
|
||||
]
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
});
|
|
@ -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']);
|
||||
|
|
@ -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;
|
|
@ -0,0 +1,5 @@
|
|||
var web3 = require('./lib/main');
|
||||
web3.providers.QtProvider = require('./lib/qt');
|
||||
web3.contract = require('./lib/contract');
|
||||
|
||||
module.exports = web3;
|
|
@ -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
|
||||
};
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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,10 +95,15 @@ func RunVmTest(js string) (failed int) {
|
|||
failed = 1
|
||||
}
|
||||
|
||||
gexp := ethutil.Big(test.Gas)
|
||||
if gexp.Cmp(gas) != 0 {
|
||||
log.Printf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas)
|
||||
if len(test.Gas) == 0 && err == nil {
|
||||
log.Printf("0 gas indicates error but no error given by VM")
|
||||
failed = 1
|
||||
} else {
|
||||
gexp := ethutil.Big(test.Gas)
|
||||
if gexp.Cmp(gas) != 0 {
|
||||
log.Printf("%s's gas failed. Expected %v, got %v\n", name, gexp, gas)
|
||||
failed = 1
|
||||
}
|
||||
}
|
||||
|
||||
for addr, account := range test.Post {
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"name": "ethereum.js",
|
||||
"namespace": "ethereum",
|
||||
"version": "0.0.5",
|
||||
"description": "Ethereum Compatible JavaScript API",
|
||||
"main": "./index.js",
|
||||
"directories": {
|
||||
"lib": "./lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"es6-promise": "*",
|
||||
"ws": "*",
|
||||
"xmlhttprequest": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bower": ">=1.3.0",
|
||||
"browserify": ">=6.0",
|
||||
"del": ">=0.1.1",
|
||||
"envify": "^3.0.0",
|
||||
"exorcist": "^0.1.6",
|
||||
"gulp": ">=3.4.0",
|
||||
"gulp-jshint": ">=1.5.0",
|
||||
"gulp-rename": ">=1.2.0",
|
||||
"gulp-uglify": ">=1.0.0",
|
||||
"jshint": ">=2.5.0",
|
||||
"uglifyify": "^2.6.0",
|
||||
"unreachable-branch-transform": "^0.1.0",
|
||||
"vinyl-source-stream": "^1.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp",
|
||||
"watch": "gulp watch",
|
||||
"lint": "gulp lint"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ethereum/ethereum.js.git"
|
||||
},
|
||||
"homepage": "https://github.com/ethereum/ethereum.js",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ethereum/ethereum.js/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"ethereum",
|
||||
"javascript",
|
||||
"API"
|
||||
],
|
||||
"author": "ethdev.com",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jeffery Wilcke",
|
||||
"email": "jeff@ethdev.com",
|
||||
"url": "https://github.com/obscuren"
|
||||
},
|
||||
{
|
||||
"name": "Marek Kotewicz",
|
||||
"email": "marek@ethdev.com",
|
||||
"url": "https://github.com/debris"
|
||||
},
|
||||
{
|
||||
"name": "Marian Oancea",
|
||||
"email": "marian@ethdev.com",
|
||||
"url": "https://github.com/cubedro"
|
||||
}
|
||||
],
|
||||
"license": "LGPL-3.0"
|
||||
}
|
|
@ -37,8 +37,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/ptrie"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/vm"
|
||||
)
|
||||
|
||||
|
@ -65,7 +65,7 @@ func main() {
|
|||
ethutil.ReadConfig("/tmp/evmtest", "/tmp/evm", "")
|
||||
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb := state.New(trie.New(db, ""))
|
||||
statedb := state.New(ptrie.New(nil, db))
|
||||
sender := statedb.NewStateObject([]byte("sender"))
|
||||
receiver := statedb.NewStateObject([]byte("receiver"))
|
||||
//receiver.SetCode([]byte(*code))
|
||||
|
@ -141,9 +141,7 @@ func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
|
|||
}
|
||||
|
||||
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execution {
|
||||
evm := vm.New(self, vm.DebugVmTy)
|
||||
|
||||
return core.NewExecution(evm, addr, data, gas, price, value)
|
||||
return core.NewExecution(self, addr, data, gas, price, value)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
|
|
|
@ -66,7 +66,11 @@ Rectangle {
|
|||
onMessages: {
|
||||
// Bit of a cheat to get proper JSON
|
||||
var m = JSON.parse(JSON.parse(JSON.stringify(messages)))
|
||||
webview.postEvent("messages", [m, id]);
|
||||
webview.postEvent("messages", id, m);
|
||||
}
|
||||
|
||||
function onShhMessage(message, id) {
|
||||
webview.postEvent("shhChanged", id, message)
|
||||
}
|
||||
|
||||
Item {
|
||||
|
@ -327,6 +331,33 @@ Rectangle {
|
|||
require(1);
|
||||
eth.uninstallFilter(data.args[0])
|
||||
break;
|
||||
|
||||
|
||||
case "shhNewFilter":
|
||||
require(1);
|
||||
var id = shh.watch(data.args[0], window);
|
||||
postData(data._id, id);
|
||||
break;
|
||||
|
||||
case "newIdentity":
|
||||
postData(data._id, shh.newIdentity())
|
||||
break
|
||||
|
||||
case "post":
|
||||
require(1);
|
||||
var params = data.args[0];
|
||||
var fields = ["payload", "to", "from"];
|
||||
for(var i = 0; i < fields.length; i++) {
|
||||
params[fields[i]] = params[fields[i]] || "";
|
||||
}
|
||||
if(typeof params.payload !== "object") { params.payload = [params.payload]; } //params.payload = params.payload.join(""); }
|
||||
params.topics = params.topics || [];
|
||||
params.priority = params.priority || 1000;
|
||||
params.ttl = params.ttl || 100;
|
||||
|
||||
console.log(JSON.stringify(params))
|
||||
shh.post(params.payload, params.to, params.from, params.topics, params.priority, params.ttl);
|
||||
break;
|
||||
}
|
||||
} catch(e) {
|
||||
console.log(data.call + ": " + e)
|
||||
|
@ -348,8 +379,8 @@ Rectangle {
|
|||
function postData(seed, data) {
|
||||
webview.experimental.postMessage(JSON.stringify({data: data, _id: seed}))
|
||||
}
|
||||
function postEvent(event, data) {
|
||||
webview.experimental.postMessage(JSON.stringify({data: data, _event: event}))
|
||||
function postEvent(event, id, data) {
|
||||
webview.experimental.postMessage(JSON.stringify({data: data, _id: id, _event: event}))
|
||||
}
|
||||
|
||||
function onWatchedCb(data, id) {
|
|
@ -45,11 +45,12 @@ ApplicationWindow {
|
|||
// Takes care of loading all default plugins
|
||||
Component.onCompleted: {
|
||||
var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||
var browser = addPlugin("./webapp.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||
var browser = addPlugin("./browser.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||
root.browser = browser;
|
||||
addPlugin("./views/miner.qml", {noAdd: true, close: false, section: "ethereum", active: true});
|
||||
|
||||
addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
addPlugin("./views/whisper.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"});
|
||||
|
@ -786,8 +787,8 @@ ApplicationWindow {
|
|||
title: "About"
|
||||
minimumWidth: 350
|
||||
maximumWidth: 350
|
||||
maximumHeight: 200
|
||||
minimumHeight: 200
|
||||
maximumHeight: 280
|
||||
minimumHeight: 280
|
||||
|
||||
Image {
|
||||
id: aboutIcon
|
||||
|
@ -797,7 +798,7 @@ ApplicationWindow {
|
|||
smooth: true
|
||||
source: "../facet.png"
|
||||
x: 10
|
||||
y: 10
|
||||
y: 30
|
||||
}
|
||||
|
||||
Text {
|
||||
|
@ -806,7 +807,7 @@ ApplicationWindow {
|
|||
anchors.top: parent.top
|
||||
anchors.topMargin: 30
|
||||
font.pointSize: 12
|
||||
text: "<h2>Mist (0.6.5)</h2><h4>Amalthea</h4><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor Trón<br><h3>Building</h3>Maran Hidskes"
|
||||
text: "<h2>Mist (0.7.10)</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor Trón<br>Felix Lange<br>Taylor Gerring<br>Daniel Nagy<br><h3>UX</h3>Alex van de Sande<br>"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0;
|
||||
import QtQuick.Layouts 1.0;
|
||||
import QtQuick.Dialogs 1.0;
|
||||
import QtQuick.Window 2.1;
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import Ethereum 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property var title: "Whisper Traffic"
|
||||
property var iconSource: "../facet.png"
|
||||
property var menuItem
|
||||
|
||||
objectName: "whisperView"
|
||||
anchors.fill: parent
|
||||
|
||||
property var identity: ""
|
||||
Component.onCompleted: {
|
||||
identity = shh.newIdentity()
|
||||
console.log("New identity:", identity)
|
||||
|
||||
var t = shh.watch({}, root)
|
||||
}
|
||||
|
||||
function onShhMessage(message, i) {
|
||||
whisperModel.insert(0, {from: message.from, payload: eth.toAscii(message.payload)})
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: input
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 20
|
||||
top: parent.top
|
||||
topMargin: 20
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: to
|
||||
placeholderText: "To"
|
||||
}
|
||||
TextField {
|
||||
id: data
|
||||
placeholderText: "Data"
|
||||
}
|
||||
TextField {
|
||||
id: topics
|
||||
placeholderText: "topic1, topic2, topic3, ..."
|
||||
}
|
||||
Button {
|
||||
text: "Send"
|
||||
onClicked: {
|
||||
shh.post([eth.toHex(data.text)], "", identity, topics.text.split(","), 500, 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TableView {
|
||||
id: txTableView
|
||||
anchors {
|
||||
top: input.bottom
|
||||
topMargin: 10
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
TableViewColumn{ id: fromRole; role: "from" ; title: "From"; width: 300 }
|
||||
TableViewColumn{ role: "payload" ; title: "Payload" ; width: parent.width - fromRole.width - 2 }
|
||||
|
||||
model: ListModel {
|
||||
id: whisperModel
|
||||
}
|
||||
}
|
||||
}
|
|
@ -149,7 +149,7 @@ func (self *DebuggerWindow) Debug(valueStr, gasStr, gasPriceStr, scriptStr, data
|
|||
|
||||
self.SetAsm(script)
|
||||
|
||||
block := self.lib.eth.ChainManager().CurrentBlock
|
||||
block := self.lib.eth.ChainManager().CurrentBlock()
|
||||
|
||||
env := utils.NewEnv(statedb, block, account.Address(), value)
|
||||
|
||||
|
|
|
@ -36,10 +36,12 @@ var (
|
|||
Identifier string
|
||||
KeyRing string
|
||||
KeyStore string
|
||||
PMPGateway string
|
||||
StartRpc bool
|
||||
StartWebSockets bool
|
||||
RpcPort int
|
||||
UseUPnP bool
|
||||
NatType string
|
||||
OutboundPort string
|
||||
ShowGenesis bool
|
||||
AddPeer string
|
||||
|
@ -104,17 +106,19 @@ func Init() {
|
|||
flag.StringVar(&KeyStore, "keystore", "db", "system to store keyrings: db|file (db)")
|
||||
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
||||
flag.BoolVar(&UseUPnP, "upnp", true, "enable UPnP support")
|
||||
flag.IntVar(&MaxPeer, "maxpeer", 10, "maximum desired peers")
|
||||
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
|
||||
flag.IntVar(&RpcPort, "rpcport", 8080, "port to start json-rpc server on")
|
||||
flag.BoolVar(&StartRpc, "rpc", false, "start rpc server")
|
||||
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server")
|
||||
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
|
||||
flag.BoolVar(&UseSeed, "seed", true, "seed peers")
|
||||
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
|
||||
flag.StringVar(&NatType, "nat", "", "NAT support (UPNP|PMP) (none)")
|
||||
flag.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)")
|
||||
flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given")
|
||||
flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
|
||||
flag.StringVar(&Datadir, "datadir", defaultDataDir(), "specifies the datadir to use")
|
||||
flag.StringVar(&PMPGateway, "pmp", "", "Gateway IP for PMP")
|
||||
flag.StringVar(&ConfigFile, "conf", defaultConfigFile, "config file")
|
||||
flag.StringVar(&DebugFile, "debug", "", "debug file (no debugging if not set)")
|
||||
flag.IntVar(&LogLevel, "loglevel", int(logger.InfoLevel), "loglevel: 0-5: silent,error,warn,info,debug,debug detail)")
|
||||
|
|
144
cmd/mist/gui.go
144
cmd/mist/gui.go
|
@ -30,50 +30,19 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/ui/qt/qwhisper"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
"gopkg.in/qml.v1"
|
||||
)
|
||||
|
||||
/*
|
||||
func LoadExtension(path string) (uintptr, error) {
|
||||
lib, err := ffi.NewLibrary(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
so, err := lib.Fct("sharedObject", ffi.Pointer, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ptr := so()
|
||||
|
||||
err = lib.Close()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return ptr.Interface().(uintptr), nil
|
||||
}
|
||||
*/
|
||||
/*
|
||||
vec, errr := LoadExtension("/Users/jeffrey/Desktop/build-libqmltest-Desktop_Qt_5_2_1_clang_64bit-Debug/liblibqmltest_debug.dylib")
|
||||
fmt.Printf("Fetched vec with addr: %#x\n", vec)
|
||||
if errr != nil {
|
||||
fmt.Println(errr)
|
||||
} else {
|
||||
context.SetVar("vec", (unsafe.Pointer)(vec))
|
||||
}
|
||||
*/
|
||||
|
||||
var guilogger = logger.NewLogger("GUI")
|
||||
|
||||
type Gui struct {
|
||||
|
@ -87,7 +56,8 @@ type Gui struct {
|
|||
eth *eth.Ethereum
|
||||
|
||||
// The public Ethereum library
|
||||
uiLib *UiLib
|
||||
uiLib *UiLib
|
||||
whisper *qwhisper.Whisper
|
||||
|
||||
txDb *ethdb.LDBDatabase
|
||||
|
||||
|
@ -97,7 +67,7 @@ type Gui struct {
|
|||
pipe *xeth.JSXEth
|
||||
|
||||
Session string
|
||||
clientIdentity *wire.SimpleClientIdentity
|
||||
clientIdentity *p2p.SimpleClientIdentity
|
||||
config *ethutil.ConfigManager
|
||||
|
||||
plugins map[string]plugin
|
||||
|
@ -107,7 +77,7 @@ type Gui struct {
|
|||
}
|
||||
|
||||
// Create GUI, but doesn't start it
|
||||
func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIdentity *wire.SimpleClientIdentity, session string, logLevel int) *Gui {
|
||||
func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIdentity *p2p.SimpleClientIdentity, session string, logLevel int) *Gui {
|
||||
db, err := ethdb.NewLDBDatabase("tx_database")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -138,10 +108,12 @@ func (gui *Gui) Start(assetPath string) {
|
|||
gui.engine = qml.NewEngine()
|
||||
context := gui.engine.Context()
|
||||
gui.uiLib = NewUiLib(gui.engine, gui.eth, assetPath)
|
||||
gui.whisper = qwhisper.New(gui.eth.Whisper())
|
||||
|
||||
// Expose the eth library and the ui library to QML
|
||||
context.SetVar("gui", gui)
|
||||
context.SetVar("eth", gui.uiLib)
|
||||
context.SetVar("shh", gui.whisper)
|
||||
|
||||
// Load the main QML interface
|
||||
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
|
||||
|
@ -246,10 +218,10 @@ func (gui *Gui) CreateAndSetPrivKey() (string, string, string, string) {
|
|||
}
|
||||
|
||||
func (gui *Gui) setInitialChain(ancientBlocks bool) {
|
||||
sBlk := gui.eth.ChainManager().LastBlockHash
|
||||
sBlk := gui.eth.ChainManager().LastBlockHash()
|
||||
blk := gui.eth.ChainManager().GetBlock(sBlk)
|
||||
for ; blk != nil; blk = gui.eth.ChainManager().GetBlock(sBlk) {
|
||||
sBlk = blk.PrevHash
|
||||
sBlk = blk.ParentHash()
|
||||
|
||||
gui.processBlock(blk, true)
|
||||
}
|
||||
|
@ -297,7 +269,7 @@ func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
|
|||
addr := gui.address()
|
||||
|
||||
var inout string
|
||||
if bytes.Compare(tx.Sender(), addr) == 0 {
|
||||
if bytes.Compare(tx.From(), addr) == 0 {
|
||||
inout = "send"
|
||||
} else {
|
||||
inout = "recv"
|
||||
|
@ -305,27 +277,27 @@ func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
|
|||
|
||||
var (
|
||||
ptx = xeth.NewJSTx(tx, pipe.World().State())
|
||||
send = nameReg.Storage(tx.Sender())
|
||||
rec = nameReg.Storage(tx.Recipient)
|
||||
send = nameReg.Storage(tx.From())
|
||||
rec = nameReg.Storage(tx.To())
|
||||
s, r string
|
||||
)
|
||||
|
||||
if tx.CreatesContract() {
|
||||
rec = nameReg.Storage(tx.CreationAddress(pipe.World().State()))
|
||||
if core.MessageCreatesContract(tx) {
|
||||
rec = nameReg.Storage(core.AddressFromMessage(tx))
|
||||
}
|
||||
|
||||
if send.Len() != 0 {
|
||||
s = strings.Trim(send.Str(), "\x00")
|
||||
} else {
|
||||
s = ethutil.Bytes2Hex(tx.Sender())
|
||||
s = ethutil.Bytes2Hex(tx.From())
|
||||
}
|
||||
if rec.Len() != 0 {
|
||||
r = strings.Trim(rec.Str(), "\x00")
|
||||
} else {
|
||||
if tx.CreatesContract() {
|
||||
r = ethutil.Bytes2Hex(tx.CreationAddress(pipe.World().State()))
|
||||
if core.MessageCreatesContract(tx) {
|
||||
r = ethutil.Bytes2Hex(core.AddressFromMessage(tx))
|
||||
} else {
|
||||
r = ethutil.Bytes2Hex(tx.Recipient)
|
||||
r = ethutil.Bytes2Hex(tx.To())
|
||||
}
|
||||
}
|
||||
ptx.Sender = s
|
||||
|
@ -350,7 +322,7 @@ func (gui *Gui) readPreviousTransactions() {
|
|||
}
|
||||
|
||||
func (gui *Gui) processBlock(block *types.Block, initial bool) {
|
||||
name := strings.Trim(gui.pipe.World().Config().Get("NameReg").Storage(block.Coinbase).Str(), "\x00")
|
||||
name := strings.Trim(gui.pipe.World().Config().Get("NameReg").Storage(block.Coinbase()).Str(), "\x00")
|
||||
b := xeth.NewJSBlock(block)
|
||||
b.Name = name
|
||||
|
||||
|
@ -391,6 +363,8 @@ func (gui *Gui) update() {
|
|||
gui.setPeerInfo()
|
||||
}()
|
||||
|
||||
gui.whisper.SetView(gui.win.Root().ObjectByName("whisperView"))
|
||||
|
||||
for _, plugin := range gui.plugins {
|
||||
guilogger.Infoln("Loading plugin ", plugin.Name)
|
||||
|
||||
|
@ -409,8 +383,7 @@ func (gui *Gui) update() {
|
|||
miningLabel := gui.getObjectByName("miningLabel")
|
||||
|
||||
events := gui.eth.EventMux().Subscribe(
|
||||
eth.ChainSyncEvent{},
|
||||
eth.PeerListEvent{},
|
||||
//eth.PeerListEvent{},
|
||||
core.NewBlockEvent{},
|
||||
core.TxPreEvent{},
|
||||
core.TxPostEvent{},
|
||||
|
@ -427,7 +400,7 @@ func (gui *Gui) update() {
|
|||
switch ev := ev.(type) {
|
||||
case core.NewBlockEvent:
|
||||
gui.processBlock(ev.Block, false)
|
||||
if bytes.Compare(ev.Block.Coinbase, gui.address()) == 0 {
|
||||
if bytes.Compare(ev.Block.Coinbase(), gui.address()) == 0 {
|
||||
gui.setWalletValue(gui.eth.ChainManager().State().GetBalance(gui.address()), nil)
|
||||
}
|
||||
|
||||
|
@ -448,40 +421,39 @@ func (gui *Gui) update() {
|
|||
tx := ev.Tx
|
||||
object := state.GetAccount(gui.address())
|
||||
|
||||
if bytes.Compare(tx.Sender(), gui.address()) == 0 {
|
||||
object.SubAmount(tx.Value)
|
||||
if bytes.Compare(tx.From(), gui.address()) == 0 {
|
||||
object.SubAmount(tx.Value())
|
||||
|
||||
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||
} else if bytes.Compare(tx.Recipient, gui.address()) == 0 {
|
||||
object.AddAmount(tx.Value)
|
||||
} else if bytes.Compare(tx.To(), gui.address()) == 0 {
|
||||
object.AddAmount(tx.Value())
|
||||
|
||||
gui.txDb.Put(tx.Hash(), tx.RlpEncode())
|
||||
}
|
||||
|
||||
gui.setWalletValue(object.Balance(), nil)
|
||||
state.UpdateStateObject(object)
|
||||
|
||||
case eth.PeerListEvent:
|
||||
gui.setPeerInfo()
|
||||
}
|
||||
|
||||
case <-peerUpdateTicker.C:
|
||||
gui.setPeerInfo()
|
||||
case <-generalUpdateTicker.C:
|
||||
statusText := "#" + gui.eth.ChainManager().CurrentBlock.Number.String()
|
||||
statusText := "#" + gui.eth.ChainManager().CurrentBlock().Number().String()
|
||||
lastBlockLabel.Set("text", statusText)
|
||||
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash")
|
||||
|
||||
blockLength := gui.eth.BlockPool().BlocksProcessed
|
||||
chainLength := gui.eth.BlockPool().ChainLength
|
||||
/*
|
||||
blockLength := gui.eth.BlockPool().BlocksProcessed
|
||||
chainLength := gui.eth.BlockPool().ChainLength
|
||||
|
||||
var (
|
||||
pct float64 = 1.0 / float64(chainLength) * float64(blockLength)
|
||||
dlWidget = gui.win.Root().ObjectByName("downloadIndicator")
|
||||
dlLabel = gui.win.Root().ObjectByName("downloadLabel")
|
||||
)
|
||||
dlWidget.Set("value", pct)
|
||||
dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength))
|
||||
var (
|
||||
pct float64 = 1.0 / float64(chainLength) * float64(blockLength)
|
||||
dlWidget = gui.win.Root().ObjectByName("downloadIndicator")
|
||||
dlLabel = gui.win.Root().ObjectByName("downloadLabel")
|
||||
)
|
||||
dlWidget.Set("value", pct)
|
||||
dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength))
|
||||
*/
|
||||
|
||||
case <-statsUpdateTicker.C:
|
||||
gui.setStatsPane()
|
||||
|
@ -509,7 +481,7 @@ Heap Alloc: %d
|
|||
CGNext: %x
|
||||
NumGC: %d
|
||||
`, Version, runtime.Version(),
|
||||
eth.ProtocolVersion, eth.P2PVersion,
|
||||
eth.ProtocolVersion, 2,
|
||||
runtime.NumCPU, runtime.NumGoroutine(), runtime.NumCgoCall(),
|
||||
memStats.Alloc, memStats.HeapAlloc,
|
||||
memStats.NextGC, memStats.NumGC,
|
||||
|
@ -531,3 +503,35 @@ func (gui *Gui) privateKey() string {
|
|||
func (gui *Gui) address() []byte {
|
||||
return gui.eth.KeyManager().Address()
|
||||
}
|
||||
|
||||
/*
|
||||
func LoadExtension(path string) (uintptr, error) {
|
||||
lib, err := ffi.NewLibrary(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
so, err := lib.Fct("sharedObject", ffi.Pointer, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ptr := so()
|
||||
|
||||
err = lib.Close()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return ptr.Interface().(uintptr), nil
|
||||
}
|
||||
*/
|
||||
/*
|
||||
vec, errr := LoadExtension("/Users/jeffrey/Desktop/build-libqmltest-Desktop_Qt_5_2_1_clang_64bit-Debug/liblibqmltest_debug.dylib")
|
||||
fmt.Printf("Fetched vec with addr: %#x\n", vec)
|
||||
if errr != nil {
|
||||
fmt.Println(errr)
|
||||
} else {
|
||||
context.SetVar("vec", (unsafe.Pointer)(vec))
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -139,7 +139,7 @@ func (app *HtmlApplication) Window() *qml.Window {
|
|||
}
|
||||
|
||||
func (app *HtmlApplication) NewBlock(block *types.Block) {
|
||||
b := &xeth.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
|
||||
b := &xeth.JSBlock{Number: int(block.NumberU64()), Hash: ethutil.Bytes2Hex(block.Hash())}
|
||||
app.webView.Call("onNewBlockCb", b)
|
||||
}
|
||||
|
||||
|
|
|
@ -23,15 +23,15 @@ import (
|
|||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"gopkg.in/qml.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
ClientIdentifier = "Mist"
|
||||
Version = "0.7.9"
|
||||
Version = "0.7.11"
|
||||
)
|
||||
|
||||
var ethereum *eth.Ethereum
|
||||
|
@ -58,8 +58,8 @@ func run() error {
|
|||
|
||||
// create, import, export keys
|
||||
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
|
||||
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier)
|
||||
ethereum = utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
|
||||
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier, string(keyManager.PublicKey()))
|
||||
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, utils.NatType(NatType, PMPGateway), OutboundPort, MaxPeer)
|
||||
|
||||
if ShowGenesis {
|
||||
utils.ShowGenesis(ethereum)
|
||||
|
@ -69,6 +69,10 @@ func run() error {
|
|||
utils.StartRpc(ethereum, RpcPort)
|
||||
}
|
||||
|
||||
if StartWebSockets {
|
||||
utils.StartWebSockets(ethereum)
|
||||
}
|
||||
|
||||
gui := NewWindow(ethereum, config, clientIdentity, KeyRing, LogLevel)
|
||||
gui.stdLog = stdLog
|
||||
|
||||
|
@ -100,16 +104,10 @@ func main() {
|
|||
|
||||
utils.HandleInterrupt()
|
||||
|
||||
if StartWebSockets {
|
||||
utils.StartWebSockets(ethereum)
|
||||
}
|
||||
|
||||
// we need to run the interrupt callbacks in case gui is closed
|
||||
// this skips if we got here by actual interrupt stopping the GUI
|
||||
if !interrupted {
|
||||
utils.RunInterruptCallbacks(os.Interrupt)
|
||||
}
|
||||
// this blocks the thread
|
||||
ethereum.WaitForShutdown()
|
||||
logger.Flush()
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ func (app *QmlApplication) NewWatcher(quitChan chan bool) {
|
|||
|
||||
// Events
|
||||
func (app *QmlApplication) NewBlock(block *types.Block) {
|
||||
pblock := &xeth.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
|
||||
pblock := &xeth.JSBlock{Number: int(block.NumberU64()), Hash: ethutil.Bytes2Hex(block.Hash())}
|
||||
app.win.Call("onNewBlockCb", pblock)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,11 +24,12 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event/filter"
|
||||
"github.com/ethereum/go-ethereum/javascript"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
|
@ -57,6 +58,7 @@ type UiLib struct {
|
|||
jsEngine *javascript.JSRE
|
||||
|
||||
filterCallbacks map[int][]int
|
||||
filterManager *filter.FilterManager
|
||||
|
||||
miner *miner.Miner
|
||||
}
|
||||
|
@ -64,6 +66,7 @@ type UiLib struct {
|
|||
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
|
||||
lib := &UiLib{JSXEth: xeth.NewJSXEth(eth), engine: engine, eth: eth, assetPath: assetPath, jsEngine: javascript.NewJSRE(eth), filterCallbacks: make(map[int][]int)} //, filters: make(map[int]*xeth.JSFilter)}
|
||||
lib.miner = miner.New(eth.KeyManager().Address(), eth)
|
||||
lib.filterManager = filter.NewFilterManager(eth.EventMux())
|
||||
|
||||
return lib
|
||||
}
|
||||
|
@ -123,7 +126,8 @@ func (self *UiLib) LookupAddress(name string) string {
|
|||
}
|
||||
|
||||
func (self *UiLib) PastPeers() *ethutil.List {
|
||||
return ethutil.NewList(eth.PastPeers())
|
||||
return ethutil.NewList([]string{})
|
||||
//return ethutil.NewList(eth.PastPeers())
|
||||
}
|
||||
|
||||
func (self *UiLib) ImportTx(rlpTx string) {
|
||||
|
@ -191,7 +195,7 @@ func (ui *UiLib) Connect(button qml.Object) {
|
|||
}
|
||||
|
||||
func (ui *UiLib) ConnectToPeer(addr string) {
|
||||
ui.eth.ConnectToPeer(addr)
|
||||
ui.eth.SuggestPeer(addr)
|
||||
}
|
||||
|
||||
func (ui *UiLib) AssetPath(p string) string {
|
||||
|
@ -221,94 +225,6 @@ func (self *UiLib) StartDebugger() {
|
|||
dbWindow.Show()
|
||||
}
|
||||
|
||||
func (self *UiLib) NewFilter(object map[string]interface{}) (id int) {
|
||||
filter := qt.NewFilterFromMap(object, self.eth)
|
||||
filter.MessageCallback = func(messages state.Messages) {
|
||||
self.win.Root().Call("invokeFilterCallback", xeth.ToJSMessages(messages), id)
|
||||
}
|
||||
id = self.eth.InstallFilter(filter)
|
||||
return id
|
||||
}
|
||||
|
||||
func (self *UiLib) NewFilterString(typ string) (id int) {
|
||||
filter := core.NewFilter(self.eth)
|
||||
filter.BlockCallback = func(block *types.Block) {
|
||||
if self.win != nil && self.win.Root() != nil {
|
||||
self.win.Root().Call("invokeFilterCallback", "{}", id)
|
||||
} else {
|
||||
fmt.Println("QML is lagging")
|
||||
}
|
||||
}
|
||||
id = self.eth.InstallFilter(filter)
|
||||
return id
|
||||
}
|
||||
|
||||
func (self *UiLib) Messages(id int) *ethutil.List {
|
||||
filter := self.eth.GetFilter(id)
|
||||
if filter != nil {
|
||||
messages := xeth.ToJSMessages(filter.Find())
|
||||
|
||||
return messages
|
||||
}
|
||||
|
||||
return ethutil.EmptyList()
|
||||
}
|
||||
|
||||
func (self *UiLib) UninstallFilter(id int) {
|
||||
self.eth.UninstallFilter(id)
|
||||
}
|
||||
|
||||
func mapToTxParams(object map[string]interface{}) map[string]string {
|
||||
// Default values
|
||||
if object["from"] == nil {
|
||||
object["from"] = ""
|
||||
}
|
||||
if object["to"] == nil {
|
||||
object["to"] = ""
|
||||
}
|
||||
if object["value"] == nil {
|
||||
object["value"] = ""
|
||||
}
|
||||
if object["gas"] == nil {
|
||||
object["gas"] = ""
|
||||
}
|
||||
if object["gasPrice"] == nil {
|
||||
object["gasPrice"] = ""
|
||||
}
|
||||
|
||||
var dataStr string
|
||||
var data []string
|
||||
if list, ok := object["data"].(*qml.List); ok {
|
||||
list.Convert(&data)
|
||||
} else if str, ok := object["data"].(string); ok {
|
||||
data = []string{str}
|
||||
}
|
||||
|
||||
for _, str := range data {
|
||||
if ethutil.IsHex(str) {
|
||||
str = str[2:]
|
||||
|
||||
if len(str) != 64 {
|
||||
str = ethutil.LeftPadString(str, 64)
|
||||
}
|
||||
} else {
|
||||
str = ethutil.Bytes2Hex(ethutil.LeftPadBytes(ethutil.Big(str).Bytes(), 32))
|
||||
}
|
||||
|
||||
dataStr += str
|
||||
}
|
||||
object["data"] = dataStr
|
||||
|
||||
conv := make(map[string]string)
|
||||
for key, value := range object {
|
||||
if v, ok := value.(string); ok {
|
||||
conv[key] = v
|
||||
}
|
||||
}
|
||||
|
||||
return conv
|
||||
}
|
||||
|
||||
func (self *UiLib) Transact(params map[string]interface{}) (string, error) {
|
||||
object := mapToTxParams(params)
|
||||
|
||||
|
@ -372,3 +288,104 @@ func (self *UiLib) ToggleMining() bool {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (self *UiLib) ToHex(data string) string {
|
||||
return "0x" + ethutil.Bytes2Hex([]byte(data))
|
||||
}
|
||||
|
||||
func (self *UiLib) ToAscii(data string) string {
|
||||
start := 0
|
||||
if len(data) > 1 && data[0:2] == "0x" {
|
||||
start = 2
|
||||
}
|
||||
return string(ethutil.Hex2Bytes(data[start:]))
|
||||
}
|
||||
|
||||
/// Ethereum filter methods
|
||||
func (self *UiLib) NewFilter(object map[string]interface{}) (id int) {
|
||||
filter := qt.NewFilterFromMap(object, self.eth)
|
||||
filter.MessageCallback = func(messages state.Messages) {
|
||||
self.win.Root().Call("invokeFilterCallback", xeth.ToJSMessages(messages), id)
|
||||
}
|
||||
id = self.filterManager.InstallFilter(filter)
|
||||
return id
|
||||
}
|
||||
|
||||
func (self *UiLib) NewFilterString(typ string) (id int) {
|
||||
filter := core.NewFilter(self.eth)
|
||||
filter.BlockCallback = func(block *types.Block) {
|
||||
if self.win != nil && self.win.Root() != nil {
|
||||
self.win.Root().Call("invokeFilterCallback", "{}", id)
|
||||
} else {
|
||||
fmt.Println("QML is lagging")
|
||||
}
|
||||
}
|
||||
id = self.filterManager.InstallFilter(filter)
|
||||
return id
|
||||
}
|
||||
|
||||
func (self *UiLib) Messages(id int) *ethutil.List {
|
||||
filter := self.filterManager.GetFilter(id)
|
||||
if filter != nil {
|
||||
messages := xeth.ToJSMessages(filter.Find())
|
||||
|
||||
return messages
|
||||
}
|
||||
|
||||
return ethutil.EmptyList()
|
||||
}
|
||||
|
||||
func (self *UiLib) UninstallFilter(id int) {
|
||||
self.filterManager.UninstallFilter(id)
|
||||
}
|
||||
|
||||
func mapToTxParams(object map[string]interface{}) map[string]string {
|
||||
// Default values
|
||||
if object["from"] == nil {
|
||||
object["from"] = ""
|
||||
}
|
||||
if object["to"] == nil {
|
||||
object["to"] = ""
|
||||
}
|
||||
if object["value"] == nil {
|
||||
object["value"] = ""
|
||||
}
|
||||
if object["gas"] == nil {
|
||||
object["gas"] = ""
|
||||
}
|
||||
if object["gasPrice"] == nil {
|
||||
object["gasPrice"] = ""
|
||||
}
|
||||
|
||||
var dataStr string
|
||||
var data []string
|
||||
if list, ok := object["data"].(*qml.List); ok {
|
||||
list.Convert(&data)
|
||||
} else if str, ok := object["data"].(string); ok {
|
||||
data = []string{str}
|
||||
}
|
||||
|
||||
for _, str := range data {
|
||||
if ethutil.IsHex(str) {
|
||||
str = str[2:]
|
||||
|
||||
if len(str) != 64 {
|
||||
str = ethutil.LeftPadString(str, 64)
|
||||
}
|
||||
} else {
|
||||
str = ethutil.Bytes2Hex(ethutil.LeftPadBytes(ethutil.Big(str).Bytes(), 32))
|
||||
}
|
||||
|
||||
dataStr += str
|
||||
}
|
||||
object["data"] = dataStr
|
||||
|
||||
conv := make(map[string]string)
|
||||
for key, value := range object {
|
||||
if v, ok := value.(string); ok {
|
||||
conv[key] = v
|
||||
}
|
||||
}
|
||||
|
||||
return conv
|
||||
}
|
||||
|
|
|
@ -4,23 +4,25 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"bitbucket.org/kardianos/osext"
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
|
||||
|
@ -144,17 +146,32 @@ func NewDatabase() ethutil.Database {
|
|||
return db
|
||||
}
|
||||
|
||||
func NewClientIdentity(clientIdentifier, version, customIdentifier string) *wire.SimpleClientIdentity {
|
||||
return wire.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier)
|
||||
func NewClientIdentity(clientIdentifier, version, customIdentifier string, pubkey string) *p2p.SimpleClientIdentity {
|
||||
return p2p.NewSimpleClientIdentity(clientIdentifier, version, customIdentifier, pubkey)
|
||||
}
|
||||
|
||||
func NewEthereum(db ethutil.Database, clientIdentity wire.ClientIdentity, keyManager *crypto.KeyManager, usePnp bool, OutboundPort string, MaxPeer int) *eth.Ethereum {
|
||||
ethereum, err := eth.New(db, clientIdentity, keyManager, eth.CapDefault, usePnp)
|
||||
func NatType(natType string, gateway string) (nat p2p.NAT) {
|
||||
switch natType {
|
||||
case "UPNP":
|
||||
nat = p2p.UPNP()
|
||||
case "PMP":
|
||||
ip := net.ParseIP(gateway)
|
||||
if ip == nil {
|
||||
clilogger.Fatalf("cannot resolve PMP gateway IP %s", gateway)
|
||||
}
|
||||
nat = p2p.PMP(ip)
|
||||
case "":
|
||||
default:
|
||||
clilogger.Fatalf("unrecognised NAT type '%s'", natType)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NewEthereum(db ethutil.Database, clientIdentity p2p.ClientIdentity, keyManager *crypto.KeyManager, nat p2p.NAT, OutboundPort string, MaxPeer int) *eth.Ethereum {
|
||||
ethereum, err := eth.New(db, clientIdentity, keyManager, nat, OutboundPort, MaxPeer)
|
||||
if err != nil {
|
||||
clilogger.Fatalln("eth start err:", err)
|
||||
}
|
||||
ethereum.Port = OutboundPort
|
||||
ethereum.MaxPeers = MaxPeer
|
||||
return ethereum
|
||||
}
|
||||
|
||||
|
@ -268,11 +285,6 @@ func StartMining(ethereum *eth.Ethereum) bool {
|
|||
if gminer == nil {
|
||||
gminer = miner.New(addr, ethereum)
|
||||
}
|
||||
// Give it some time to connect with peers
|
||||
time.Sleep(3 * time.Second)
|
||||
for !ethereum.IsUpToDate() {
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
gminer.Start()
|
||||
}()
|
||||
RegisterInterrupt(func(os.Signal) {
|
||||
|
@ -315,7 +327,7 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
|
|||
return fmt.Errorf("unknown block %x", hash)
|
||||
}
|
||||
|
||||
parent := ethereum.ChainManager().GetBlock(block.PrevHash)
|
||||
parent := ethereum.ChainManager().GetBlock(block.ParentHash())
|
||||
|
||||
_, err := ethereum.BlockManager().TransitionState(parent.State(), parent, block)
|
||||
if err != nil {
|
||||
|
@ -325,3 +337,25 @@ func BlockDo(ethereum *eth.Ethereum, hash []byte) error {
|
|||
return nil
|
||||
|
||||
}
|
||||
|
||||
func ImportChain(ethereum *eth.Ethereum, fn string) error {
|
||||
clilogger.Infof("importing chain '%s'\n", fn)
|
||||
fh, err := os.OpenFile(fn, os.O_RDONLY, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
var chain types.Blocks
|
||||
if err := rlp.Decode(fh, &chain); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ethereum.ChainManager().Reset()
|
||||
if err := ethereum.ChainManager().InsertChain(chain); err != nil {
|
||||
return err
|
||||
}
|
||||
clilogger.Infof("imported %d blocks\n", len(chain))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -30,15 +30,15 @@ func NewEnv(state *state.StateDB, block *types.Block, transactor []byte, value *
|
|||
}
|
||||
|
||||
func (self *VMEnv) Origin() []byte { return self.transactor }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number }
|
||||
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
|
||||
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
|
||||
func (self *VMEnv) Time() int64 { return self.block.Time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() }
|
||||
func (self *VMEnv) PrevHash() []byte { return self.block.ParentHash() }
|
||||
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase() }
|
||||
func (self *VMEnv) Time() int64 { return self.block.Time() }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() }
|
||||
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit() }
|
||||
func (self *VMEnv) Value() *big.Int { return self.value }
|
||||
func (self *VMEnv) State() *state.StateDB { return self.state }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit }
|
||||
func (self *VMEnv) Depth() int { return self.depth }
|
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||
func (self *VMEnv) AddLog(log state.Log) {
|
||||
|
@ -49,9 +49,7 @@ func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
|
|||
}
|
||||
|
||||
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *core.Execution {
|
||||
evm := vm.New(self, vm.DebugVmTy)
|
||||
|
||||
return core.NewExecution(evm, addr, data, gas, price, value)
|
||||
return core.NewExecution(self, addr, data, gas, price, value)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Call(caller vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/websocket"
|
||||
"github.com/ethereum/go-ethereum/xeth"
|
||||
)
|
||||
|
||||
var wslogger = logger.NewLogger("WS")
|
||||
|
||||
func args(v ...interface{}) []interface{} {
|
||||
return v
|
||||
}
|
||||
|
@ -106,6 +109,8 @@ func (self *WebSocketServer) Serv() {
|
|||
}
|
||||
|
||||
func StartWebSockets(eth *eth.Ethereum) {
|
||||
wslogger.Infoln("Starting WebSockets")
|
||||
|
||||
sock := NewWebSocketServer(eth)
|
||||
go sock.Serv()
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package core
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
@ -14,10 +13,11 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/pow/ezp"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
var statelogger = logger.NewLogger("BLOCK")
|
||||
|
@ -38,13 +38,12 @@ type EthManager interface {
|
|||
BlockManager() *BlockManager
|
||||
ChainManager() *ChainManager
|
||||
TxPool() *TxPool
|
||||
Broadcast(msgType wire.MsgType, data []interface{})
|
||||
PeerCount() int
|
||||
IsMining() bool
|
||||
IsListening() bool
|
||||
Peers() *list.List
|
||||
Peers() []*p2p.Peer
|
||||
KeyManager() *crypto.KeyManager
|
||||
ClientIdentity() wire.ClientIdentity
|
||||
ClientIdentity() p2p.ClientIdentity
|
||||
Db() ethutil.Database
|
||||
EventMux() *event.TypeMux
|
||||
}
|
||||
|
@ -58,8 +57,8 @@ type BlockManager struct {
|
|||
mem map[string]*big.Int
|
||||
// Proof of work used for validating
|
||||
Pow pow.PoW
|
||||
// The ethereum manager interface
|
||||
eth EthManager
|
||||
|
||||
txpool *TxPool
|
||||
|
||||
// The last attempted block is mainly used for debugging purposes
|
||||
// This does not have to be a valid block and will be set during
|
||||
|
@ -71,21 +70,21 @@ type BlockManager struct {
|
|||
eventMux *event.TypeMux
|
||||
}
|
||||
|
||||
func NewBlockManager(ethereum EthManager) *BlockManager {
|
||||
func NewBlockManager(txpool *TxPool, chainManager *ChainManager, eventMux *event.TypeMux) *BlockManager {
|
||||
sm := &BlockManager{
|
||||
mem: make(map[string]*big.Int),
|
||||
Pow: ezp.New(),
|
||||
eth: ethereum,
|
||||
bc: ethereum.ChainManager(),
|
||||
eventMux: ethereum.EventMux(),
|
||||
bc: chainManager,
|
||||
eventMux: eventMux,
|
||||
txpool: txpool,
|
||||
}
|
||||
|
||||
return sm
|
||||
}
|
||||
|
||||
func (sm *BlockManager) TransitionState(statedb *state.StateDB, parent, block *types.Block) (receipts types.Receipts, err error) {
|
||||
coinbase := statedb.GetOrNewStateObject(block.Coinbase)
|
||||
coinbase.SetGasPool(block.CalcGasLimit(parent))
|
||||
coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase)
|
||||
coinbase.SetGasPool(CalcGasLimit(parent, block))
|
||||
|
||||
// Process the transactions on to current block
|
||||
receipts, _, _, _, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), false)
|
||||
|
@ -111,11 +110,11 @@ done:
|
|||
// If we are mining this block and validating we want to set the logs back to 0
|
||||
state.EmptyLogs()
|
||||
|
||||
txGas := new(big.Int).Set(tx.Gas)
|
||||
txGas := new(big.Int).Set(tx.Gas())
|
||||
|
||||
cb := state.GetStateObject(coinbase.Address())
|
||||
st := NewStateTransition(cb, tx, state, block)
|
||||
err = st.TransitionState()
|
||||
_, err = st.TransitionState()
|
||||
if err != nil {
|
||||
switch {
|
||||
case IsNonceErr(err):
|
||||
|
@ -129,12 +128,11 @@ done:
|
|||
statelogger.Infoln(err)
|
||||
erroneous = append(erroneous, tx)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
txGas.Sub(txGas, st.gas)
|
||||
cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice))
|
||||
cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice()))
|
||||
|
||||
// Update the state with pending changes
|
||||
state.Update(txGas)
|
||||
|
@ -143,6 +141,7 @@ done:
|
|||
receipt := types.NewReceipt(state.Root(), cumulative)
|
||||
receipt.SetLogs(state.Logs())
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||
chainlogger.Debugln(receipt)
|
||||
|
||||
// Notify all subscribers
|
||||
if !transientProcess {
|
||||
|
@ -158,7 +157,7 @@ done:
|
|||
}
|
||||
|
||||
block.Reward = cumulativeSum
|
||||
block.GasUsed = totalUsedGas
|
||||
block.Header().GasUsed = totalUsedGas
|
||||
|
||||
return receipts, handled, unhandled, erroneous, err
|
||||
}
|
||||
|
@ -168,14 +167,15 @@ func (sm *BlockManager) Process(block *types.Block) (td *big.Int, msgs state.Mes
|
|||
sm.mutex.Lock()
|
||||
defer sm.mutex.Unlock()
|
||||
|
||||
if sm.bc.HasBlock(block.Hash()) {
|
||||
return nil, nil, &KnownBlockError{block.Number, block.Hash()}
|
||||
header := block.Header()
|
||||
if sm.bc.HasBlock(header.Hash()) {
|
||||
return nil, nil, &KnownBlockError{header.Number, header.Hash()}
|
||||
}
|
||||
|
||||
if !sm.bc.HasBlock(block.PrevHash) {
|
||||
return nil, nil, ParentError(block.PrevHash)
|
||||
if !sm.bc.HasBlock(header.ParentHash) {
|
||||
return nil, nil, ParentError(header.ParentHash)
|
||||
}
|
||||
parent := sm.bc.GetBlock(block.PrevHash)
|
||||
parent := sm.bc.GetBlock(header.ParentHash)
|
||||
|
||||
return sm.ProcessWithParent(block, parent)
|
||||
}
|
||||
|
@ -183,13 +183,7 @@ func (sm *BlockManager) Process(block *types.Block) (td *big.Int, msgs state.Mes
|
|||
func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) {
|
||||
sm.lastAttemptedBlock = block
|
||||
|
||||
state := parent.State().Copy()
|
||||
|
||||
// Defer the Undo on the Trie. If the block processing happened
|
||||
// we don't want to undo but since undo only happens on dirty
|
||||
// nodes this won't happen because Commit would have been called
|
||||
// before that.
|
||||
defer state.Reset()
|
||||
state := state.New(parent.Trie().Copy())
|
||||
|
||||
// Block validation
|
||||
if err = sm.ValidateBlock(block, parent); err != nil {
|
||||
|
@ -201,21 +195,24 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
|
|||
return
|
||||
}
|
||||
|
||||
header := block.Header()
|
||||
|
||||
rbloom := types.CreateBloom(receipts)
|
||||
if bytes.Compare(rbloom, block.LogsBloom) != 0 {
|
||||
if bytes.Compare(rbloom, header.Bloom) != 0 {
|
||||
err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
|
||||
return
|
||||
}
|
||||
|
||||
txSha := types.DeriveSha(block.Transactions())
|
||||
if bytes.Compare(txSha, block.TxSha) != 0 {
|
||||
err = fmt.Errorf("validating transaction root. received=%x got=%x", block.TxSha, txSha)
|
||||
if bytes.Compare(txSha, header.TxHash) != 0 {
|
||||
err = fmt.Errorf("validating transaction root. received=%x got=%x", header.TxHash, txSha)
|
||||
return
|
||||
}
|
||||
|
||||
receiptSha := types.DeriveSha(receipts)
|
||||
if bytes.Compare(receiptSha, block.ReceiptSha) != 0 {
|
||||
err = fmt.Errorf("validating receipt root. received=%x got=%x", block.ReceiptSha, receiptSha)
|
||||
if bytes.Compare(receiptSha, header.ReceiptHash) != 0 {
|
||||
fmt.Println("receipts", receipts)
|
||||
err = fmt.Errorf("validating receipt root. received=%x got=%x", header.ReceiptHash, receiptSha)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -225,8 +222,8 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
|
|||
|
||||
state.Update(ethutil.Big0)
|
||||
|
||||
if !block.State().Cmp(state) {
|
||||
err = fmt.Errorf("invalid merkle root. received=%x got=%x", block.Root(), state.Root())
|
||||
if !bytes.Equal(header.Root, state.Root()) {
|
||||
err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -238,9 +235,9 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
|
|||
messages := state.Manifest().Messages
|
||||
state.Manifest().Reset()
|
||||
|
||||
chainlogger.Infof("Processed block #%d (%x...)\n", block.Number, block.Hash()[0:4])
|
||||
chainlogger.Infof("Processed block #%d (%x...)\n", header.Number, block.Hash()[0:4])
|
||||
|
||||
sm.eth.TxPool().RemoveSet(block.Transactions())
|
||||
sm.txpool.RemoveSet(block.Transactions())
|
||||
|
||||
return td, messages, nil
|
||||
} else {
|
||||
|
@ -250,18 +247,18 @@ func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.I
|
|||
|
||||
func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) {
|
||||
uncleDiff := new(big.Int)
|
||||
for _, uncle := range block.Uncles {
|
||||
for _, uncle := range block.Uncles() {
|
||||
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
|
||||
}
|
||||
|
||||
// TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
|
||||
td := new(big.Int)
|
||||
td = td.Add(sm.bc.TD, uncleDiff)
|
||||
td = td.Add(td, block.Difficulty)
|
||||
td = td.Add(sm.bc.Td(), uncleDiff)
|
||||
td = td.Add(td, block.Header().Difficulty)
|
||||
|
||||
// The new TD will only be accepted if the new difficulty is
|
||||
// is greater than the previous.
|
||||
if td.Cmp(sm.bc.TD) > 0 {
|
||||
if td.Cmp(sm.bc.Td()) > 0 {
|
||||
return td, true
|
||||
}
|
||||
|
||||
|
@ -273,13 +270,13 @@ func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) {
|
|||
// Validation validates easy over difficult (dagger takes longer time = difficult)
|
||||
func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
|
||||
expd := CalcDifficulty(block, parent)
|
||||
if expd.Cmp(block.Difficulty) < 0 {
|
||||
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
|
||||
if expd.Cmp(block.Header().Difficulty) < 0 {
|
||||
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Header().Difficulty, expd)
|
||||
}
|
||||
|
||||
diff := block.Time - parent.Time
|
||||
diff := block.Header().Time - parent.Header().Time
|
||||
if diff < 0 {
|
||||
return ValidationError("Block timestamp less then prev block %v (%v - %v)", diff, block.Time, sm.bc.CurrentBlock.Time)
|
||||
return ValidationError("Block timestamp less then prev block %v (%v - %v)", diff, block.Header().Time, sm.bc.CurrentBlock().Header().Time)
|
||||
}
|
||||
|
||||
/* XXX
|
||||
|
@ -291,7 +288,7 @@ func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
|
|||
|
||||
// Verify the nonce of the block. Return an error if it's not valid
|
||||
if !sm.Pow.Verify(block /*block.HashNoNonce(), block.Difficulty, block.Nonce*/) {
|
||||
return ValidationError("Block's nonce is invalid (= %v)", ethutil.Bytes2Hex(block.Nonce))
|
||||
return ValidationError("Block's nonce is invalid (= %v)", ethutil.Bytes2Hex(block.Header().Nonce))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -300,24 +297,28 @@ func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
|
|||
func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent *types.Block) error {
|
||||
reward := new(big.Int).Set(BlockReward)
|
||||
|
||||
knownUncles := ethutil.Set(parent.Uncles)
|
||||
nonces := ethutil.NewSet(block.Nonce)
|
||||
for _, uncle := range block.Uncles {
|
||||
knownUncles := set.New()
|
||||
for _, uncle := range parent.Uncles() {
|
||||
knownUncles.Add(string(uncle.Hash()))
|
||||
}
|
||||
|
||||
nonces := ethutil.NewSet(block.Header().Nonce)
|
||||
for _, uncle := range block.Uncles() {
|
||||
if nonces.Include(uncle.Nonce) {
|
||||
// Error not unique
|
||||
return UncleError("Uncle not unique")
|
||||
}
|
||||
|
||||
uncleParent := sm.bc.GetBlock(uncle.PrevHash)
|
||||
uncleParent := sm.bc.GetBlock(uncle.ParentHash)
|
||||
if uncleParent == nil {
|
||||
return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.PrevHash[0:4]))
|
||||
return UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
|
||||
}
|
||||
|
||||
if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 {
|
||||
if uncleParent.Header().Number.Cmp(new(big.Int).Sub(parent.Header().Number, big.NewInt(6))) < 0 {
|
||||
return UncleError("Uncle too old")
|
||||
}
|
||||
|
||||
if knownUncles.Include(uncle.Hash()) {
|
||||
if knownUncles.Has(string(uncle.Hash())) {
|
||||
return UncleError("Uncle in chain")
|
||||
}
|
||||
|
||||
|
@ -333,15 +334,15 @@ func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent
|
|||
}
|
||||
|
||||
// Get the account associated with the coinbase
|
||||
account := statedb.GetAccount(block.Coinbase)
|
||||
account := statedb.GetAccount(block.Header().Coinbase)
|
||||
// Reward amount of ether to the coinbase address
|
||||
account.AddAmount(reward)
|
||||
|
||||
statedb.Manifest().AddMessage(&state.Message{
|
||||
To: block.Coinbase,
|
||||
To: block.Header().Coinbase,
|
||||
Input: nil,
|
||||
Origin: nil,
|
||||
Block: block.Hash(), Timestamp: block.Time, Coinbase: block.Coinbase, Number: block.Number,
|
||||
Block: block.Hash(), Timestamp: int64(block.Header().Time), Coinbase: block.Header().Coinbase, Number: block.Header().Number,
|
||||
Value: new(big.Int).Add(reward, block.Reward),
|
||||
})
|
||||
|
||||
|
@ -349,15 +350,15 @@ func (sm *BlockManager) AccumelateRewards(statedb *state.StateDB, block, parent
|
|||
}
|
||||
|
||||
func (sm *BlockManager) GetMessages(block *types.Block) (messages []*state.Message, err error) {
|
||||
if !sm.bc.HasBlock(block.PrevHash) {
|
||||
return nil, ParentError(block.PrevHash)
|
||||
if !sm.bc.HasBlock(block.Header().ParentHash) {
|
||||
return nil, ParentError(block.Header().ParentHash)
|
||||
}
|
||||
|
||||
sm.lastAttemptedBlock = block
|
||||
|
||||
var (
|
||||
parent = sm.bc.GetBlock(block.PrevHash)
|
||||
state = parent.State().Copy()
|
||||
parent = sm.bc.GetBlock(block.Header().ParentHash)
|
||||
state = state.New(parent.Trie().Copy())
|
||||
)
|
||||
|
||||
defer state.Reset()
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
var chainlogger = logger.NewLogger("CHAIN")
|
||||
|
||||
/*
|
||||
func AddTestNetFunds(block *types.Block) {
|
||||
for _, addr := range []string{
|
||||
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
||||
|
@ -30,39 +34,87 @@ func AddTestNetFunds(block *types.Block) {
|
|||
block.State().UpdateStateObject(account)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func CalcDifficulty(block, parent *types.Block) *big.Int {
|
||||
diff := new(big.Int)
|
||||
|
||||
adjust := new(big.Int).Rsh(parent.Difficulty, 10)
|
||||
if block.Time >= parent.Time+5 {
|
||||
diff.Sub(parent.Difficulty, adjust)
|
||||
bh, ph := block.Header(), parent.Header()
|
||||
adjust := new(big.Int).Rsh(ph.Difficulty, 10)
|
||||
if bh.Time >= ph.Time+5 {
|
||||
diff.Sub(ph.Difficulty, adjust)
|
||||
} else {
|
||||
diff.Add(parent.Difficulty, adjust)
|
||||
diff.Add(ph.Difficulty, adjust)
|
||||
}
|
||||
|
||||
return diff
|
||||
}
|
||||
|
||||
func CalcGasLimit(parent, block *types.Block) *big.Int {
|
||||
if block.Number().Cmp(big.NewInt(0)) == 0 {
|
||||
return ethutil.BigPow(10, 6)
|
||||
}
|
||||
|
||||
// ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024
|
||||
|
||||
previous := new(big.Int).Mul(big.NewInt(1024-1), parent.GasLimit())
|
||||
current := new(big.Rat).Mul(new(big.Rat).SetInt(parent.GasUsed()), big.NewRat(6, 5))
|
||||
curInt := new(big.Int).Div(current.Num(), current.Denom())
|
||||
|
||||
result := new(big.Int).Add(previous, curInt)
|
||||
result.Div(result, big.NewInt(1024))
|
||||
|
||||
min := big.NewInt(125000)
|
||||
|
||||
return ethutil.BigMax(min, result)
|
||||
}
|
||||
|
||||
type ChainManager struct {
|
||||
//eth EthManager
|
||||
processor types.BlockProcessor
|
||||
eventMux *event.TypeMux
|
||||
genesisBlock *types.Block
|
||||
// Last known total difficulty
|
||||
TD *big.Int
|
||||
|
||||
LastBlockNumber uint64
|
||||
|
||||
CurrentBlock *types.Block
|
||||
LastBlockHash []byte
|
||||
mu sync.RWMutex
|
||||
td *big.Int
|
||||
lastBlockNumber uint64
|
||||
currentBlock *types.Block
|
||||
lastBlockHash []byte
|
||||
|
||||
transState *state.StateDB
|
||||
}
|
||||
|
||||
func (self *ChainManager) Td() *big.Int {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return self.td
|
||||
}
|
||||
|
||||
func (self *ChainManager) LastBlockNumber() uint64 {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return self.lastBlockNumber
|
||||
}
|
||||
|
||||
func (self *ChainManager) LastBlockHash() []byte {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return self.lastBlockHash
|
||||
}
|
||||
|
||||
func (self *ChainManager) CurrentBlock() *types.Block {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return self.currentBlock
|
||||
}
|
||||
|
||||
func NewChainManager(mux *event.TypeMux) *ChainManager {
|
||||
bc := &ChainManager{}
|
||||
bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis))
|
||||
bc.genesisBlock = GenesisBlock()
|
||||
bc.eventMux = mux
|
||||
|
||||
bc.setLastBlock()
|
||||
|
@ -72,12 +124,19 @@ func NewChainManager(mux *event.TypeMux) *ChainManager {
|
|||
return bc
|
||||
}
|
||||
|
||||
func (self *ChainManager) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
return self.td, self.currentBlock.Hash(), self.Genesis().Hash()
|
||||
}
|
||||
|
||||
func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
|
||||
self.processor = proc
|
||||
}
|
||||
|
||||
func (self *ChainManager) State() *state.StateDB {
|
||||
return self.CurrentBlock.State()
|
||||
return state.New(self.CurrentBlock().Trie())
|
||||
}
|
||||
|
||||
func (self *ChainManager) TransState() *state.StateDB {
|
||||
|
@ -87,46 +146,48 @@ func (self *ChainManager) TransState() *state.StateDB {
|
|||
func (bc *ChainManager) setLastBlock() {
|
||||
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
|
||||
if len(data) != 0 {
|
||||
// Prep genesis
|
||||
AddTestNetFunds(bc.genesisBlock)
|
||||
|
||||
block := types.NewBlockFromBytes(data)
|
||||
bc.CurrentBlock = block
|
||||
bc.LastBlockHash = block.Hash()
|
||||
bc.LastBlockNumber = block.Number.Uint64()
|
||||
var block types.Block
|
||||
rlp.Decode(bytes.NewReader(data), &block)
|
||||
bc.currentBlock = &block
|
||||
bc.lastBlockHash = block.Hash()
|
||||
bc.lastBlockNumber = block.Header().Number.Uint64()
|
||||
|
||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
||||
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
|
||||
bc.td = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
|
||||
} else {
|
||||
bc.Reset()
|
||||
}
|
||||
|
||||
chainlogger.Infof("Last block (#%d) %x\n", bc.LastBlockNumber, bc.CurrentBlock.Hash())
|
||||
chainlogger.Infof("Last block (#%d) %x\n", bc.lastBlockNumber, bc.currentBlock.Hash())
|
||||
}
|
||||
|
||||
// Block creation & chain handling
|
||||
func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
|
||||
var root interface{}
|
||||
hash := ZeroHash256
|
||||
bc.mu.RLock()
|
||||
defer bc.mu.RUnlock()
|
||||
|
||||
var root []byte
|
||||
parentHash := ZeroHash256
|
||||
|
||||
if bc.CurrentBlock != nil {
|
||||
root = bc.CurrentBlock.Root()
|
||||
hash = bc.LastBlockHash
|
||||
root = bc.currentBlock.Header().Root
|
||||
parentHash = bc.lastBlockHash
|
||||
}
|
||||
|
||||
block := types.CreateBlock(
|
||||
root,
|
||||
hash,
|
||||
block := types.NewBlock(
|
||||
parentHash,
|
||||
coinbase,
|
||||
root,
|
||||
ethutil.BigPow(2, 32),
|
||||
nil,
|
||||
"")
|
||||
|
||||
parent := bc.CurrentBlock
|
||||
parent := bc.currentBlock
|
||||
if parent != nil {
|
||||
block.Difficulty = CalcDifficulty(block, parent)
|
||||
block.Number = new(big.Int).Add(bc.CurrentBlock.Number, ethutil.Big1)
|
||||
block.GasLimit = block.CalcGasLimit(bc.CurrentBlock)
|
||||
header := block.Header()
|
||||
header.Difficulty = CalcDifficulty(block, parent)
|
||||
header.Number = new(big.Int).Add(parent.Header().Number, ethutil.Big1)
|
||||
header.GasLimit = CalcGasLimit(parent, block)
|
||||
|
||||
}
|
||||
|
||||
|
@ -134,41 +195,46 @@ func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
|
|||
}
|
||||
|
||||
func (bc *ChainManager) Reset() {
|
||||
AddTestNetFunds(bc.genesisBlock)
|
||||
bc.mu.Lock()
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) {
|
||||
ethutil.Config.Db.Delete(block.Hash())
|
||||
}
|
||||
|
||||
bc.genesisBlock.Trie().Sync()
|
||||
// Prepare the genesis block
|
||||
bc.write(bc.genesisBlock)
|
||||
bc.insert(bc.genesisBlock)
|
||||
bc.CurrentBlock = bc.genesisBlock
|
||||
bc.currentBlock = bc.genesisBlock
|
||||
|
||||
bc.SetTotalDifficulty(ethutil.Big("0"))
|
||||
|
||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
||||
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
|
||||
bc.setTotalDifficulty(ethutil.Big("0"))
|
||||
}
|
||||
|
||||
func (self *ChainManager) Export() []byte {
|
||||
chainlogger.Infoln("exporting", self.CurrentBlock.Number, "blocks")
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
blocks := make(types.Blocks, int(self.CurrentBlock.Number.Int64())+1)
|
||||
for block := self.CurrentBlock; block != nil; block = self.GetBlock(block.PrevHash) {
|
||||
blocks[block.Number.Int64()] = block
|
||||
chainlogger.Infof("exporting %v blocks...\n", self.currentBlock.Header().Number)
|
||||
|
||||
blocks := make([]*types.Block, int(self.currentBlock.NumberU64())+1)
|
||||
for block := self.currentBlock; block != nil; block = self.GetBlock(block.Header().ParentHash) {
|
||||
blocks[block.NumberU64()] = block
|
||||
}
|
||||
|
||||
return ethutil.Encode(blocks)
|
||||
}
|
||||
|
||||
func (bc *ChainManager) insert(block *types.Block) {
|
||||
encodedBlock := block.RlpEncode()
|
||||
encodedBlock := ethutil.Encode(block)
|
||||
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
|
||||
bc.CurrentBlock = block
|
||||
bc.LastBlockHash = block.Hash()
|
||||
bc.currentBlock = block
|
||||
bc.lastBlockHash = block.Hash()
|
||||
}
|
||||
|
||||
func (bc *ChainManager) write(block *types.Block) {
|
||||
bc.writeBlockInfo(block)
|
||||
|
||||
encodedBlock := block.RlpEncode()
|
||||
encodedBlock := ethutil.Encode(block)
|
||||
ethutil.Config.Db.Put(block.Hash(), encodedBlock)
|
||||
}
|
||||
|
||||
|
@ -183,7 +249,7 @@ func (bc *ChainManager) HasBlock(hash []byte) bool {
|
|||
return len(data) != 0
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain [][]byte) {
|
||||
func (self *ChainManager) GetBlockHashesFromHash(hash []byte, max uint64) (chain [][]byte) {
|
||||
block := self.GetBlock(hash)
|
||||
if block == nil {
|
||||
return
|
||||
|
@ -193,11 +259,11 @@ func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain
|
|||
for i := uint64(0); i < max; i++ {
|
||||
chain = append(chain, block.Hash())
|
||||
|
||||
if block.Number.Cmp(ethutil.Big0) <= 0 {
|
||||
if block.Header().Number.Cmp(ethutil.Big0) <= 0 {
|
||||
break
|
||||
}
|
||||
|
||||
block = self.GetBlock(block.PrevHash)
|
||||
block = self.GetBlock(block.Header().ParentHash)
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -208,65 +274,61 @@ func (self *ChainManager) GetBlock(hash []byte) *types.Block {
|
|||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
var block types.Block
|
||||
if err := rlp.Decode(bytes.NewReader(data), &block); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return types.NewBlockFromBytes(data)
|
||||
return &block
|
||||
}
|
||||
|
||||
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
|
||||
block := self.CurrentBlock
|
||||
for ; block != nil; block = self.GetBlock(block.PrevHash) {
|
||||
if block.Number.Uint64() == num {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
block := self.currentBlock
|
||||
for ; block != nil; block = self.GetBlock(block.Header().ParentHash) {
|
||||
if block.Header().Number.Uint64() == num {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if block != nil && block.Number.Uint64() == 0 && num != 0 {
|
||||
if block != nil && block.Header().Number.Uint64() == 0 && num != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func (bc *ChainManager) SetTotalDifficulty(td *big.Int) {
|
||||
func (bc *ChainManager) setTotalDifficulty(td *big.Int) {
|
||||
ethutil.Config.Db.Put([]byte("LTD"), td.Bytes())
|
||||
bc.TD = td
|
||||
bc.td = td
|
||||
}
|
||||
|
||||
func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
|
||||
parent := self.GetBlock(block.PrevHash)
|
||||
parent := self.GetBlock(block.Header().ParentHash)
|
||||
if parent == nil {
|
||||
return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.PrevHash)
|
||||
return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.Header().ParentHash)
|
||||
}
|
||||
|
||||
parentTd := parent.BlockInfo().TD
|
||||
parentTd := parent.Td
|
||||
|
||||
uncleDiff := new(big.Int)
|
||||
for _, uncle := range block.Uncles {
|
||||
for _, uncle := range block.Uncles() {
|
||||
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
|
||||
}
|
||||
|
||||
td := new(big.Int)
|
||||
td = td.Add(parentTd, uncleDiff)
|
||||
td = td.Add(td, block.Difficulty)
|
||||
td = td.Add(td, block.Header().Difficulty)
|
||||
|
||||
return td, nil
|
||||
}
|
||||
|
||||
func (bc *ChainManager) BlockInfo(block *types.Block) types.BlockInfo {
|
||||
bi := types.BlockInfo{}
|
||||
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
|
||||
bi.RlpDecode(data)
|
||||
|
||||
return bi
|
||||
}
|
||||
|
||||
// Unexported method for writing extra non-essential block info to the db
|
||||
func (bc *ChainManager) writeBlockInfo(block *types.Block) {
|
||||
bc.LastBlockNumber++
|
||||
bi := types.BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash, TD: bc.TD}
|
||||
|
||||
// For now we use the block hash with the words "info" appended as key
|
||||
ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
|
||||
bc.lastBlockNumber++
|
||||
}
|
||||
|
||||
func (bc *ChainManager) Stop() {
|
||||
|
@ -283,23 +345,29 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
|
|||
continue
|
||||
}
|
||||
|
||||
chainlogger.Infof("block #%v process failed (%x)\n", block.Number, block.Hash()[:4])
|
||||
h := block.Header()
|
||||
chainlogger.Infof("block #%v process failed (%x)\n", h.Number, h.Hash()[:4])
|
||||
chainlogger.Infoln(block)
|
||||
chainlogger.Infoln(err)
|
||||
return err
|
||||
}
|
||||
|
||||
self.write(block)
|
||||
if td.Cmp(self.TD) > 0 {
|
||||
if block.Number.Cmp(new(big.Int).Add(self.CurrentBlock.Number, ethutil.Big1)) < 0 {
|
||||
chainlogger.Infof("Split detected. New head #%v (%x), was #%v (%x)\n", block.Number, block.Hash()[:4], self.CurrentBlock.Number, self.CurrentBlock.Hash()[:4])
|
||||
self.mu.Lock()
|
||||
{
|
||||
self.write(block)
|
||||
cblock := self.currentBlock
|
||||
if td.Cmp(self.td) > 0 {
|
||||
if block.Header().Number.Cmp(new(big.Int).Add(cblock.Header().Number, ethutil.Big1)) < 0 {
|
||||
chainlogger.Infof("Split detected. New head #%v (%x), was #%v (%x)\n", block.Header().Number, block.Hash()[:4], cblock.Header().Number, cblock.Hash()[:4])
|
||||
}
|
||||
|
||||
self.setTotalDifficulty(td)
|
||||
self.insert(block)
|
||||
self.transState = state.New(cblock.Trie().Copy())
|
||||
}
|
||||
|
||||
self.SetTotalDifficulty(td)
|
||||
self.insert(block)
|
||||
self.transState = self.State().Copy()
|
||||
//sm.eth.TxPool().RemoveSet(block.Transactions())
|
||||
}
|
||||
self.mu.Unlock()
|
||||
|
||||
self.eventMux.Post(NewBlockEvent{block})
|
||||
self.eventMux.Post(messages)
|
||||
|
|
|
@ -2,18 +2,138 @@ package core
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
func TestChainInsertions(t *testing.T) {
|
||||
c1, err := ethutil.ReadAllFile(path.Join("..", "_data", "chain1"))
|
||||
//var Logger logpkg.LogSystem
|
||||
|
||||
//var Log = logpkg.NewLogger("TEST")
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
//Logger = logpkg.NewStdLogSystem(os.Stdout, log.LstdFlags, logpkg.DebugLevel)
|
||||
//logpkg.AddLogSystem(Logger)
|
||||
|
||||
ethutil.ReadConfig("/tmp/ethtest", "/tmp/ethtest", "ETH")
|
||||
|
||||
db, err := ethdb.NewMemDatabase()
|
||||
if err != nil {
|
||||
panic("Could not create mem-db, failing")
|
||||
}
|
||||
ethutil.Config.Db = db
|
||||
}
|
||||
|
||||
func loadChain(fn string, t *testing.T) (types.Blocks, error) {
|
||||
fh, err := os.OpenFile(path.Join("..", "_data", fn), os.O_RDONLY, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
var chain types.Blocks
|
||||
if err := rlp.Decode(fh, &chain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return chain, nil
|
||||
}
|
||||
|
||||
func insertChain(done chan bool, chainMan *ChainManager, chain types.Blocks, t *testing.T) {
|
||||
err := chainMan.InsertChain(chain)
|
||||
done <- true
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
data1, _ := ethutil.Decode([]byte(c1), 0)
|
||||
fmt.Println(data1)
|
||||
}
|
||||
|
||||
func TestChainInsertions(t *testing.T) {
|
||||
chain1, err := loadChain("valid1", t)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
fmt.Println(len(chain1))
|
||||
|
||||
chain2, err := loadChain("valid2", t)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
var eventMux event.TypeMux
|
||||
chainMan := NewChainManager(&eventMux)
|
||||
txPool := NewTxPool(chainMan, &eventMux)
|
||||
blockMan := NewBlockManager(txPool, chainMan, &eventMux)
|
||||
chainMan.SetProcessor(blockMan)
|
||||
|
||||
const max = 2
|
||||
done := make(chan bool, max)
|
||||
|
||||
go insertChain(done, chainMan, chain1, t)
|
||||
go insertChain(done, chainMan, chain2, t)
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(chain2[len(chain2)-1], chainMan.CurrentBlock()) {
|
||||
t.Error("chain2 is canonical and shouldn't be")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(chain1[len(chain1)-1], chainMan.CurrentBlock()) {
|
||||
t.Error("chain1 isn't canonical and should be")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainMultipleInsertions(t *testing.T) {
|
||||
const max = 4
|
||||
chains := make([]types.Blocks, max)
|
||||
var longest int
|
||||
for i := 0; i < max; i++ {
|
||||
var err error
|
||||
name := "valid" + strconv.Itoa(i+1)
|
||||
chains[i], err = loadChain(name, t)
|
||||
if len(chains[i]) >= len(chains[longest]) {
|
||||
longest = i
|
||||
}
|
||||
fmt.Println("loaded", name, "with a length of", len(chains[i]))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
var eventMux event.TypeMux
|
||||
chainMan := NewChainManager(&eventMux)
|
||||
txPool := NewTxPool(chainMan, &eventMux)
|
||||
blockMan := NewBlockManager(txPool, chainMan, &eventMux)
|
||||
chainMan.SetProcessor(blockMan)
|
||||
done := make(chan bool, max)
|
||||
for i, chain := range chains {
|
||||
var i int = i
|
||||
go func() {
|
||||
insertChain(done, chainMan, chain, t)
|
||||
fmt.Println(i, "done")
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
<-done
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(chains[longest][len(chains[longest])-1], chainMan.CurrentBlock()) {
|
||||
t.Error("Invalid canonical chain")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,3 +10,6 @@ type TxPostEvent struct{ Tx *types.Transaction }
|
|||
|
||||
// NewBlockEvent is posted when a block has been imported.
|
||||
type NewBlockEvent struct{ Block *types.Block }
|
||||
|
||||
// NewMinedBlockEvent is posted when a block has been imported.
|
||||
type NewMinedBlockEvent struct{ Block *types.Block }
|
||||
|
|
|
@ -3,22 +3,21 @@ package core
|
|||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/vm"
|
||||
)
|
||||
|
||||
type Execution struct {
|
||||
vm vm.VirtualMachine
|
||||
env vm.Environment
|
||||
address, input []byte
|
||||
Gas, price, value *big.Int
|
||||
object *state.StateObject
|
||||
SkipTransfer bool
|
||||
}
|
||||
|
||||
func NewExecution(vm vm.VirtualMachine, address, input []byte, gas, gasPrice, value *big.Int) *Execution {
|
||||
return &Execution{vm: vm, address: address, input: input, Gas: gas, price: gasPrice, value: value}
|
||||
func NewExecution(env vm.Environment, address, input []byte, gas, gasPrice, value *big.Int) *Execution {
|
||||
return &Execution{env: env, address: address, input: input, Gas: gas, price: gasPrice, value: value}
|
||||
}
|
||||
|
||||
func (self *Execution) Addr() []byte {
|
||||
|
@ -27,14 +26,19 @@ func (self *Execution) Addr() []byte {
|
|||
|
||||
func (self *Execution) Call(codeAddr []byte, caller vm.ClosureRef) ([]byte, error) {
|
||||
// Retrieve the executing code
|
||||
code := self.vm.Env().State().GetCode(codeAddr)
|
||||
code := self.env.State().GetCode(codeAddr)
|
||||
|
||||
return self.exec(code, codeAddr, caller)
|
||||
}
|
||||
|
||||
func (self *Execution) exec(code, contextAddr []byte, caller vm.ClosureRef) (ret []byte, err error) {
|
||||
env := self.vm.Env()
|
||||
chainlogger.Debugf("pre state %x\n", env.State().Root())
|
||||
env := self.env
|
||||
evm := vm.New(env, vm.DebugVmTy)
|
||||
|
||||
if env.Depth() == vm.MaxCallDepth {
|
||||
// Consume all gas (by not returning it) and return a depth error
|
||||
return nil, vm.DepthError{}
|
||||
}
|
||||
|
||||
from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(self.address)
|
||||
// Skipping transfer is used on testing for the initial call
|
||||
|
@ -49,32 +53,19 @@ func (self *Execution) exec(code, contextAddr []byte, caller vm.ClosureRef) (ret
|
|||
}
|
||||
|
||||
snapshot := env.State().Copy()
|
||||
defer func() {
|
||||
if vm.IsDepthErr(err) || vm.IsOOGErr(err) {
|
||||
env.State().Set(snapshot)
|
||||
}
|
||||
chainlogger.Debugf("post state %x\n", env.State().Root())
|
||||
}()
|
||||
|
||||
self.object = to
|
||||
// Pre-compiled contracts (address.go) 1, 2 & 3.
|
||||
naddr := ethutil.BigD(contextAddr).Uint64()
|
||||
if p := vm.Precompiled[naddr]; p != nil {
|
||||
if self.Gas.Cmp(p.Gas(len(self.input))) >= 0 {
|
||||
ret = p.Call(self.input)
|
||||
self.vm.Printf("NATIVE_FUNC(%x) => %x", naddr, ret)
|
||||
self.vm.Endl()
|
||||
}
|
||||
} else {
|
||||
ret, err = self.vm.Run(to, caller, code, self.value, self.Gas, self.price, self.input)
|
||||
start := time.Now()
|
||||
ret, err = evm.Run(to, caller, code, self.value, self.Gas, self.price, self.input)
|
||||
if err != nil {
|
||||
env.State().Set(snapshot)
|
||||
}
|
||||
chainlogger.Debugf("vm took %v\n", time.Since(start))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Execution) Create(caller vm.ClosureRef) (ret []byte, err error, account *state.StateObject) {
|
||||
ret, err = self.exec(self.input, nil, caller)
|
||||
account = self.vm.Env().State().GetStateObject(self.address)
|
||||
account = self.env.State().GetStateObject(self.address)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -76,13 +76,14 @@ func (self *Filter) SetSkip(skip int) {
|
|||
|
||||
// Run filters messages with the current parameters set
|
||||
func (self *Filter) Find() []*state.Message {
|
||||
earliestBlock := self.eth.ChainManager().CurrentBlock()
|
||||
var earliestBlockNo uint64 = uint64(self.earliest)
|
||||
if self.earliest == -1 {
|
||||
earliestBlockNo = self.eth.ChainManager().CurrentBlock.Number.Uint64()
|
||||
earliestBlockNo = earliestBlock.NumberU64()
|
||||
}
|
||||
var latestBlockNo uint64 = uint64(self.latest)
|
||||
if self.latest == -1 {
|
||||
latestBlockNo = self.eth.ChainManager().CurrentBlock.Number.Uint64()
|
||||
latestBlockNo = earliestBlock.NumberU64()
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -93,7 +94,7 @@ func (self *Filter) Find() []*state.Message {
|
|||
for i := 0; !quit && block != nil; i++ {
|
||||
// Quit on latest
|
||||
switch {
|
||||
case block.Number.Uint64() == earliestBlockNo, block.Number.Uint64() == 0:
|
||||
case block.NumberU64() == earliestBlockNo, block.NumberU64() == 0:
|
||||
quit = true
|
||||
case self.max <= len(messages):
|
||||
break
|
||||
|
@ -113,7 +114,7 @@ func (self *Filter) Find() []*state.Message {
|
|||
messages = append(messages, self.FilterMessages(msgs)...)
|
||||
}
|
||||
|
||||
block = self.eth.ChainManager().GetBlock(block.PrevHash)
|
||||
block = self.eth.ChainManager().GetBlock(block.ParentHash())
|
||||
}
|
||||
|
||||
skip := int(math.Min(float64(len(messages)), float64(self.skip)))
|
||||
|
@ -176,7 +177,7 @@ func (self *Filter) bloomFilter(block *types.Block) bool {
|
|||
var fromIncluded, toIncluded bool
|
||||
if len(self.from) > 0 {
|
||||
for _, from := range self.from {
|
||||
if types.BloomLookup(block.LogsBloom, from) || bytes.Equal(block.Coinbase, from) {
|
||||
if types.BloomLookup(block.Bloom(), from) || bytes.Equal(block.Coinbase(), from) {
|
||||
fromIncluded = true
|
||||
break
|
||||
}
|
||||
|
@ -187,7 +188,7 @@ func (self *Filter) bloomFilter(block *types.Block) bool {
|
|||
|
||||
if len(self.to) > 0 {
|
||||
for _, to := range self.to {
|
||||
if types.BloomLookup(block.LogsBloom, ethutil.U256(new(big.Int).Add(ethutil.Big1, ethutil.BigD(to))).Bytes()) || bytes.Equal(block.Coinbase, to) {
|
||||
if types.BloomLookup(block.Bloom(), ethutil.U256(new(big.Int).Add(ethutil.Big1, ethutil.BigD(to))).Bytes()) || bytes.Equal(block.Coinbase(), to) {
|
||||
toIncluded = true
|
||||
break
|
||||
}
|
||||
|
|
|
@ -1,7 +1 @@
|
|||
package core
|
||||
|
||||
// import "testing"
|
||||
|
||||
// func TestFilter(t *testing.T) {
|
||||
// NewFilter(NewTestManager())
|
||||
// }
|
||||
|
|
|
@ -3,8 +3,10 @@ package core
|
|||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -17,36 +19,35 @@ var ZeroHash512 = make([]byte, 64)
|
|||
var EmptyShaList = crypto.Sha3(ethutil.Encode([]interface{}{}))
|
||||
var EmptyListRoot = crypto.Sha3(ethutil.Encode(""))
|
||||
|
||||
var GenesisHeader = []interface{}{
|
||||
// Previous hash (none)
|
||||
ZeroHash256,
|
||||
// Empty uncles
|
||||
EmptyShaList,
|
||||
// Coinbase
|
||||
ZeroHash160,
|
||||
// Root state
|
||||
EmptyShaList,
|
||||
// tx root
|
||||
EmptyListRoot,
|
||||
// receipt root
|
||||
EmptyListRoot,
|
||||
// bloom
|
||||
ZeroHash512,
|
||||
// Difficulty
|
||||
//ethutil.BigPow(2, 22),
|
||||
big.NewInt(131072),
|
||||
// Number
|
||||
ethutil.Big0,
|
||||
// Block upper gas bound
|
||||
big.NewInt(1000000),
|
||||
// Block gas used
|
||||
ethutil.Big0,
|
||||
// Time
|
||||
ethutil.Big0,
|
||||
// Extra
|
||||
nil,
|
||||
// Nonce
|
||||
crypto.Sha3(big.NewInt(42).Bytes()),
|
||||
}
|
||||
func GenesisBlock() *types.Block {
|
||||
genesis := types.NewBlock(ZeroHash256, ZeroHash160, nil, big.NewInt(131072), crypto.Sha3(big.NewInt(42).Bytes()), "")
|
||||
genesis.Header().Number = ethutil.Big0
|
||||
genesis.Header().GasLimit = big.NewInt(1000000)
|
||||
genesis.Header().GasUsed = ethutil.Big0
|
||||
genesis.Header().Time = 0
|
||||
|
||||
var Genesis = []interface{}{GenesisHeader, []interface{}{}, []interface{}{}}
|
||||
genesis.SetUncles([]*types.Header{})
|
||||
genesis.SetTransactions(types.Transactions{})
|
||||
genesis.SetReceipts(types.Receipts{})
|
||||
|
||||
statedb := state.New(genesis.Trie())
|
||||
for _, addr := range []string{
|
||||
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
|
||||
"e4157b34ea9615cfbde6b4fda419828124b70c78",
|
||||
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
|
||||
"6c386a4b26f73c802f34673f7248bb118f97424a",
|
||||
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
|
||||
"2ef47100e0787b915105fd5e3f4ff6752079d5cb",
|
||||
"e6716f9544a56c530d868e4bfbacb172315bdead",
|
||||
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
|
||||
} {
|
||||
codedAddr := ethutil.Hex2Bytes(addr)
|
||||
account := statedb.GetAccount(codedAddr)
|
||||
account.SetBalance(ethutil.Big("1606938044258990275541962092341162602522202993782792835301376")) //ethutil.BigPow(2, 200)
|
||||
statedb.UpdateStateObject(account)
|
||||
}
|
||||
statedb.Sync()
|
||||
genesis.Header().Root = statedb.Root()
|
||||
|
||||
return genesis
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
)
|
||||
|
||||
// Implement our EthTest Manager
|
||||
|
@ -54,11 +54,11 @@ func (tm *TestManager) TxPool() *TxPool {
|
|||
func (tm *TestManager) EventMux() *event.TypeMux {
|
||||
return tm.eventMux
|
||||
}
|
||||
func (tm *TestManager) Broadcast(msgType wire.MsgType, data []interface{}) {
|
||||
func (tm *TestManager) Broadcast(msgType p2p.Msg, data []interface{}) {
|
||||
fmt.Println("Broadcast not implemented")
|
||||
}
|
||||
|
||||
func (tm *TestManager) ClientIdentity() wire.ClientIdentity {
|
||||
func (tm *TestManager) ClientIdentity() p2p.ClientIdentity {
|
||||
return nil
|
||||
}
|
||||
func (tm *TestManager) KeyManager() *crypto.KeyManager {
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/vm"
|
||||
)
|
||||
|
@ -27,48 +29,69 @@ import (
|
|||
*/
|
||||
type StateTransition struct {
|
||||
coinbase, receiver []byte
|
||||
tx *types.Transaction
|
||||
msg Message
|
||||
gas, gasPrice *big.Int
|
||||
initialGas *big.Int
|
||||
value *big.Int
|
||||
data []byte
|
||||
state *state.StateDB
|
||||
block *types.Block
|
||||
|
||||
cb, rec, sen *state.StateObject
|
||||
|
||||
Env vm.Environment
|
||||
}
|
||||
|
||||
func NewStateTransition(coinbase *state.StateObject, tx *types.Transaction, state *state.StateDB, block *types.Block) *StateTransition {
|
||||
return &StateTransition{coinbase.Address(), tx.Recipient, tx, new(big.Int), new(big.Int).Set(tx.GasPrice), tx.Value, tx.Data, state, block, coinbase, nil, nil}
|
||||
type Message interface {
|
||||
Hash() []byte
|
||||
|
||||
From() []byte
|
||||
To() []byte
|
||||
|
||||
GasPrice() *big.Int
|
||||
Gas() *big.Int
|
||||
Value() *big.Int
|
||||
|
||||
Nonce() uint64
|
||||
Data() []byte
|
||||
}
|
||||
|
||||
func AddressFromMessage(msg Message) []byte {
|
||||
// Generate a new address
|
||||
return crypto.Sha3(ethutil.NewValue([]interface{}{msg.From(), msg.Nonce()}).Encode())[12:]
|
||||
}
|
||||
|
||||
func MessageCreatesContract(msg Message) bool {
|
||||
return len(msg.To()) == 0
|
||||
}
|
||||
|
||||
func MessageGasValue(msg Message) *big.Int {
|
||||
return new(big.Int).Mul(msg.Gas(), msg.GasPrice())
|
||||
}
|
||||
|
||||
func NewStateTransition(coinbase *state.StateObject, msg Message, state *state.StateDB, block *types.Block) *StateTransition {
|
||||
return &StateTransition{coinbase.Address(), msg.To(), msg, new(big.Int), new(big.Int).Set(msg.GasPrice()), new(big.Int), msg.Value(), msg.Data(), state, block, coinbase, nil, nil, nil}
|
||||
}
|
||||
|
||||
func (self *StateTransition) VmEnv() vm.Environment {
|
||||
if self.Env == nil {
|
||||
self.Env = NewEnv(self.state, self.msg, self.block)
|
||||
}
|
||||
|
||||
return self.Env
|
||||
}
|
||||
|
||||
func (self *StateTransition) Coinbase() *state.StateObject {
|
||||
if self.cb != nil {
|
||||
return self.cb
|
||||
}
|
||||
|
||||
self.cb = self.state.GetOrNewStateObject(self.coinbase)
|
||||
return self.cb
|
||||
return self.state.GetOrNewStateObject(self.coinbase)
|
||||
}
|
||||
func (self *StateTransition) Sender() *state.StateObject {
|
||||
if self.sen != nil {
|
||||
return self.sen
|
||||
}
|
||||
|
||||
self.sen = self.state.GetOrNewStateObject(self.tx.Sender())
|
||||
|
||||
return self.sen
|
||||
func (self *StateTransition) From() *state.StateObject {
|
||||
return self.state.GetOrNewStateObject(self.msg.From())
|
||||
}
|
||||
func (self *StateTransition) Receiver() *state.StateObject {
|
||||
if self.tx != nil && self.tx.CreatesContract() {
|
||||
func (self *StateTransition) To() *state.StateObject {
|
||||
if self.msg != nil && MessageCreatesContract(self.msg) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if self.rec != nil {
|
||||
return self.rec
|
||||
}
|
||||
|
||||
self.rec = self.state.GetOrNewStateObject(self.tx.Recipient)
|
||||
return self.rec
|
||||
return self.state.GetOrNewStateObject(self.msg.To())
|
||||
}
|
||||
|
||||
func (self *StateTransition) UseGas(amount *big.Int) error {
|
||||
|
@ -87,41 +110,33 @@ func (self *StateTransition) AddGas(amount *big.Int) {
|
|||
func (self *StateTransition) BuyGas() error {
|
||||
var err error
|
||||
|
||||
sender := self.Sender()
|
||||
if sender.Balance().Cmp(self.tx.GasValue()) < 0 {
|
||||
return fmt.Errorf("Insufficient funds to pre-pay gas. Req %v, has %v", self.tx.GasValue(), sender.Balance())
|
||||
sender := self.From()
|
||||
if sender.Balance().Cmp(MessageGasValue(self.msg)) < 0 {
|
||||
return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address()[:4], MessageGasValue(self.msg), sender.Balance())
|
||||
}
|
||||
|
||||
coinbase := self.Coinbase()
|
||||
err = coinbase.BuyGas(self.tx.Gas, self.tx.GasPrice)
|
||||
err = coinbase.BuyGas(self.msg.Gas(), self.msg.GasPrice())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.AddGas(self.tx.Gas)
|
||||
sender.SubAmount(self.tx.GasValue())
|
||||
self.AddGas(self.msg.Gas())
|
||||
self.initialGas.Set(self.msg.Gas())
|
||||
sender.SubAmount(MessageGasValue(self.msg))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *StateTransition) RefundGas() {
|
||||
coinbase, sender := self.Coinbase(), self.Sender()
|
||||
coinbase.RefundGas(self.gas, self.tx.GasPrice)
|
||||
|
||||
// Return remaining gas
|
||||
remaining := new(big.Int).Mul(self.gas, self.tx.GasPrice)
|
||||
sender.AddAmount(remaining)
|
||||
}
|
||||
|
||||
func (self *StateTransition) preCheck() (err error) {
|
||||
var (
|
||||
tx = self.tx
|
||||
sender = self.Sender()
|
||||
msg = self.msg
|
||||
sender = self.From()
|
||||
)
|
||||
|
||||
// Make sure this transaction's nonce is correct
|
||||
if sender.Nonce != tx.Nonce {
|
||||
return NonceError(tx.Nonce, sender.Nonce)
|
||||
if sender.Nonce != msg.Nonce() {
|
||||
return NonceError(msg.Nonce(), sender.Nonce)
|
||||
}
|
||||
|
||||
// Pre-pay gas / Buy gas of the coinbase account
|
||||
|
@ -132,8 +147,8 @@ func (self *StateTransition) preCheck() (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (self *StateTransition) TransitionState() (err error) {
|
||||
statelogger.Debugf("(~) %x\n", self.tx.Hash())
|
||||
func (self *StateTransition) TransitionState() (ret []byte, err error) {
|
||||
statelogger.Debugf("(~) %x\n", self.msg.Hash())
|
||||
|
||||
// XXX Transactions after this point are considered valid.
|
||||
if err = self.preCheck(); err != nil {
|
||||
|
@ -141,8 +156,8 @@ func (self *StateTransition) TransitionState() (err error) {
|
|||
}
|
||||
|
||||
var (
|
||||
tx = self.tx
|
||||
sender = self.Sender()
|
||||
msg = self.msg
|
||||
sender = self.From()
|
||||
)
|
||||
|
||||
defer self.RefundGas()
|
||||
|
@ -168,30 +183,56 @@ func (self *StateTransition) TransitionState() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
var ret []byte
|
||||
vmenv := NewEnv(self.state, self.tx, self.block)
|
||||
vmenv := self.VmEnv()
|
||||
var ref vm.ClosureRef
|
||||
if tx.CreatesContract() {
|
||||
self.rec = MakeContract(tx, self.state)
|
||||
|
||||
ret, err, ref = vmenv.Create(sender, self.rec.Address(), self.tx.Data, self.gas, self.gasPrice, self.value)
|
||||
ref.SetCode(ret)
|
||||
if MessageCreatesContract(msg) {
|
||||
contract := MakeContract(msg, self.state)
|
||||
ret, err, ref = vmenv.Create(sender, contract.Address(), self.msg.Data(), self.gas, self.gasPrice, self.value)
|
||||
if err == nil {
|
||||
dataGas := big.NewInt(int64(len(ret)))
|
||||
dataGas.Mul(dataGas, vm.GasCreateByte)
|
||||
if err = self.UseGas(dataGas); err == nil {
|
||||
//self.state.SetCode(ref.Address(), ret)
|
||||
ref.SetCode(ret)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret, err = vmenv.Call(self.Sender(), self.Receiver().Address(), self.tx.Data, self.gas, self.gasPrice, self.value)
|
||||
ret, err = vmenv.Call(self.From(), self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
statelogger.Debugln(err)
|
||||
self.UseGas(self.gas)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Converts an transaction in to a state object
|
||||
func MakeContract(tx *types.Transaction, state *state.StateDB) *state.StateObject {
|
||||
addr := tx.CreationAddress(state)
|
||||
func MakeContract(msg Message, state *state.StateDB) *state.StateObject {
|
||||
addr := AddressFromMessage(msg)
|
||||
|
||||
contract := state.GetOrNewStateObject(addr)
|
||||
contract.InitCode = tx.Data
|
||||
contract.InitCode = msg.Data()
|
||||
|
||||
return contract
|
||||
}
|
||||
|
||||
func (self *StateTransition) RefundGas() {
|
||||
coinbase, sender := self.Coinbase(), self.From()
|
||||
// Return remaining gas
|
||||
remaining := new(big.Int).Mul(self.gas, self.msg.GasPrice())
|
||||
sender.AddAmount(remaining)
|
||||
|
||||
uhalf := new(big.Int).Div(self.GasUsed(), ethutil.Big2)
|
||||
for addr, ref := range self.state.Refunds() {
|
||||
refund := ethutil.BigMin(uhalf, ref)
|
||||
self.gas.Add(self.gas, refund)
|
||||
self.state.AddBalance([]byte(addr), refund.Mul(refund, self.msg.GasPrice()))
|
||||
}
|
||||
|
||||
coinbase.RefundGas(self.gas, self.msg.GasPrice())
|
||||
}
|
||||
|
||||
func (self *StateTransition) GasUsed() *big.Int {
|
||||
return new(big.Int).Sub(self.initialGas, self.gas)
|
||||
}
|
||||
|
|
|
@ -8,9 +8,9 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
)
|
||||
|
||||
var txplogger = logger.NewLogger("TXP")
|
||||
|
@ -18,7 +18,9 @@ var txplogger = logger.NewLogger("TXP")
|
|||
const txPoolQueueSize = 50
|
||||
|
||||
type TxPoolHook chan *types.Transaction
|
||||
type TxMsgTy byte
|
||||
type TxMsg struct {
|
||||
Tx *types.Transaction
|
||||
}
|
||||
|
||||
const (
|
||||
minGasPrice = 1000000
|
||||
|
@ -26,11 +28,6 @@ const (
|
|||
|
||||
var MinGasPrice = big.NewInt(10000000000000)
|
||||
|
||||
type TxMsg struct {
|
||||
Tx *types.Transaction
|
||||
Type TxMsgTy
|
||||
}
|
||||
|
||||
func EachTx(pool *list.List, it func(*types.Transaction, *list.Element) bool) {
|
||||
for e := pool.Front(); e != nil; e = e.Next() {
|
||||
if it(e.Value.(*types.Transaction), e) {
|
||||
|
@ -61,7 +58,6 @@ type TxProcessor interface {
|
|||
// pool is being drained or synced for whatever reason the transactions
|
||||
// will simple queue up and handled when the mutex is freed.
|
||||
type TxPool struct {
|
||||
Ethereum EthManager
|
||||
// The mutex for accessing the Tx pool.
|
||||
mutex sync.Mutex
|
||||
// Queueing channel for reading and writing incoming
|
||||
|
@ -75,14 +71,18 @@ type TxPool struct {
|
|||
SecondaryProcessor TxProcessor
|
||||
|
||||
subscribers []chan TxMsg
|
||||
|
||||
chainManager *ChainManager
|
||||
eventMux *event.TypeMux
|
||||
}
|
||||
|
||||
func NewTxPool(ethereum EthManager) *TxPool {
|
||||
func NewTxPool(chainManager *ChainManager, eventMux *event.TypeMux) *TxPool {
|
||||
return &TxPool{
|
||||
pool: list.New(),
|
||||
queueChan: make(chan *types.Transaction, txPoolQueueSize),
|
||||
quit: make(chan bool),
|
||||
Ethereum: ethereum,
|
||||
pool: list.New(),
|
||||
queueChan: make(chan *types.Transaction, txPoolQueueSize),
|
||||
quit: make(chan bool),
|
||||
chainManager: chainManager,
|
||||
eventMux: eventMux,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,20 +94,20 @@ func (pool *TxPool) addTransaction(tx *types.Transaction) {
|
|||
pool.pool.PushBack(tx)
|
||||
|
||||
// Broadcast the transaction to the rest of the peers
|
||||
pool.Ethereum.Broadcast(wire.MsgTxTy, []interface{}{tx.RlpData()})
|
||||
pool.eventMux.Post(TxPreEvent{tx})
|
||||
}
|
||||
|
||||
func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
|
||||
// Get the last block so we can retrieve the sender and receiver from
|
||||
// the merkle trie
|
||||
block := pool.Ethereum.ChainManager().CurrentBlock
|
||||
block := pool.chainManager.CurrentBlock
|
||||
// Something has gone horribly wrong if this happens
|
||||
if block == nil {
|
||||
return fmt.Errorf("No last block on the block chain")
|
||||
}
|
||||
|
||||
if len(tx.Recipient) != 0 && len(tx.Recipient) != 20 {
|
||||
return fmt.Errorf("Invalid recipient. len = %d", len(tx.Recipient))
|
||||
if len(tx.To()) != 0 && len(tx.To()) != 20 {
|
||||
return fmt.Errorf("Invalid recipient. len = %d", len(tx.To()))
|
||||
}
|
||||
|
||||
v, _, _ := tx.Curve()
|
||||
|
@ -116,19 +116,17 @@ func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
|
|||
}
|
||||
|
||||
// Get the sender
|
||||
sender := pool.Ethereum.ChainManager().State().GetAccount(tx.Sender())
|
||||
senderAddr := tx.From()
|
||||
if senderAddr == nil {
|
||||
return fmt.Errorf("invalid sender")
|
||||
}
|
||||
sender := pool.chainManager.State().GetAccount(senderAddr)
|
||||
|
||||
totAmount := new(big.Int).Set(tx.Value)
|
||||
totAmount := new(big.Int).Set(tx.Value())
|
||||
// Make sure there's enough in the sender's account. Having insufficient
|
||||
// funds won't invalidate this transaction but simple ignores it.
|
||||
if sender.Balance().Cmp(totAmount) < 0 {
|
||||
return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.Sender())
|
||||
}
|
||||
|
||||
if tx.IsContract() {
|
||||
if tx.GasPrice.Cmp(big.NewInt(minGasPrice)) < 0 {
|
||||
return fmt.Errorf("Gasprice too low, %s given should be at least %d.", tx.GasPrice, minGasPrice)
|
||||
}
|
||||
return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.From())
|
||||
}
|
||||
|
||||
// Increment the nonce making each tx valid only once to prevent replay
|
||||
|
@ -154,13 +152,10 @@ func (self *TxPool) Add(tx *types.Transaction) error {
|
|||
|
||||
self.addTransaction(tx)
|
||||
|
||||
tmp := make([]byte, 4)
|
||||
copy(tmp, tx.Recipient)
|
||||
|
||||
txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.Sender()[:4], tmp, tx.Value, tx.Hash())
|
||||
txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.From()[:4], tx.To()[:4], tx.Value, tx.Hash())
|
||||
|
||||
// Notify the subscribers
|
||||
go self.Ethereum.EventMux().Post(TxPreEvent{tx})
|
||||
go self.eventMux.Post(TxPreEvent{tx})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -169,7 +164,17 @@ func (self *TxPool) Size() int {
|
|||
return self.pool.Len()
|
||||
}
|
||||
|
||||
func (pool *TxPool) CurrentTransactions() []*types.Transaction {
|
||||
func (self *TxPool) AddTransactions(txs []*types.Transaction) {
|
||||
for _, tx := range txs {
|
||||
if err := self.Add(tx); err != nil {
|
||||
txplogger.Infoln(err)
|
||||
} else {
|
||||
txplogger.Infof("tx %x\n", tx.Hash()[0:4])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pool *TxPool) GetTransactions() []*types.Transaction {
|
||||
pool.mutex.Lock()
|
||||
defer pool.mutex.Unlock()
|
||||
|
||||
|
@ -192,9 +197,9 @@ func (pool *TxPool) RemoveInvalid(state *state.StateDB) {
|
|||
|
||||
for e := pool.pool.Front(); e != nil; e = e.Next() {
|
||||
tx := e.Value.(*types.Transaction)
|
||||
sender := state.GetAccount(tx.Sender())
|
||||
sender := state.GetAccount(tx.From())
|
||||
err := pool.ValidateTransaction(tx)
|
||||
if err != nil || sender.Nonce >= tx.Nonce {
|
||||
if err != nil || sender.Nonce >= tx.Nonce() {
|
||||
pool.pool.Remove(e)
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +221,7 @@ func (self *TxPool) RemoveSet(txs types.Transactions) {
|
|||
}
|
||||
|
||||
func (pool *TxPool) Flush() []*types.Transaction {
|
||||
txList := pool.CurrentTransactions()
|
||||
txList := pool.GetTransactions()
|
||||
|
||||
// Recreate a new list all together
|
||||
// XXX Is this the fastest way?
|
||||
|
|
|
@ -9,41 +9,240 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/ptrie"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
type BlockInfo struct {
|
||||
Number uint64
|
||||
Hash []byte
|
||||
Parent []byte
|
||||
TD *big.Int
|
||||
type Header struct {
|
||||
// Hash to the previous block
|
||||
ParentHash ethutil.Bytes
|
||||
// Uncles of this block
|
||||
UncleHash []byte
|
||||
// The coin base address
|
||||
Coinbase []byte
|
||||
// Block Trie state
|
||||
Root []byte
|
||||
// Tx sha
|
||||
TxHash []byte
|
||||
// Receipt sha
|
||||
ReceiptHash []byte
|
||||
// Bloom
|
||||
Bloom []byte
|
||||
// Difficulty for the current block
|
||||
Difficulty *big.Int
|
||||
// The block number
|
||||
Number *big.Int
|
||||
// Gas limit
|
||||
GasLimit *big.Int
|
||||
// Gas used
|
||||
GasUsed *big.Int
|
||||
// Creation time
|
||||
Time uint64
|
||||
// Extra data
|
||||
Extra string
|
||||
// Block Nonce for verification
|
||||
Nonce ethutil.Bytes
|
||||
}
|
||||
|
||||
func (bi *BlockInfo) RlpDecode(data []byte) {
|
||||
decoder := ethutil.NewValueFromBytes(data)
|
||||
func (self *Header) rlpData(withNonce bool) []interface{} {
|
||||
fields := []interface{}{self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra}
|
||||
if withNonce {
|
||||
fields = append(fields, self.Nonce)
|
||||
}
|
||||
|
||||
bi.Number = decoder.Get(0).Uint()
|
||||
bi.Hash = decoder.Get(1).Bytes()
|
||||
bi.Parent = decoder.Get(2).Bytes()
|
||||
bi.TD = decoder.Get(3).BigInt()
|
||||
return fields
|
||||
}
|
||||
|
||||
func (bi *BlockInfo) RlpEncode() []byte {
|
||||
return ethutil.Encode([]interface{}{bi.Number, bi.Hash, bi.Parent, bi.TD})
|
||||
func (self *Header) RlpData() interface{} {
|
||||
return self.rlpData(true)
|
||||
}
|
||||
|
||||
func (self *Header) Hash() []byte {
|
||||
return crypto.Sha3(ethutil.Encode(self.rlpData(true)))
|
||||
}
|
||||
|
||||
func (self *Header) HashNoNonce() []byte {
|
||||
return crypto.Sha3(ethutil.Encode(self.rlpData(false)))
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
header *Header
|
||||
uncles []*Header
|
||||
transactions Transactions
|
||||
Td *big.Int
|
||||
|
||||
receipts Receipts
|
||||
Reward *big.Int
|
||||
}
|
||||
|
||||
func NewBlock(parentHash []byte, coinbase []byte, root []byte, difficulty *big.Int, nonce []byte, extra string) *Block {
|
||||
header := &Header{
|
||||
Root: root,
|
||||
ParentHash: parentHash,
|
||||
Coinbase: coinbase,
|
||||
Difficulty: difficulty,
|
||||
Nonce: nonce,
|
||||
Time: uint64(time.Now().Unix()),
|
||||
Extra: extra,
|
||||
GasUsed: new(big.Int),
|
||||
GasLimit: new(big.Int),
|
||||
}
|
||||
|
||||
block := &Block{header: header, Reward: new(big.Int)}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func NewBlockWithHeader(header *Header) *Block {
|
||||
return &Block{header: header}
|
||||
}
|
||||
|
||||
func (self *Block) DecodeRLP(s *rlp.Stream) error {
|
||||
if _, err := s.List(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var header Header
|
||||
if err := s.Decode(&header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var transactions []*Transaction
|
||||
if err := s.Decode(&transactions); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var uncleHeaders []*Header
|
||||
if err := s.Decode(&uncleHeaders); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tdBytes []byte
|
||||
if err := s.Decode(&tdBytes); err != nil {
|
||||
// If this block comes from the network that's fine. If loaded from disk it should be there
|
||||
// Blocks don't store their Td when propagated over the network
|
||||
} else {
|
||||
self.Td = ethutil.BigD(tdBytes)
|
||||
}
|
||||
|
||||
if err := s.ListEnd(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.header = &header
|
||||
self.uncles = uncleHeaders
|
||||
self.transactions = transactions
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Block) Header() *Header {
|
||||
return self.header
|
||||
}
|
||||
|
||||
func (self *Block) Uncles() []*Header {
|
||||
return self.uncles
|
||||
}
|
||||
|
||||
func (self *Block) SetUncles(uncleHeaders []*Header) {
|
||||
self.uncles = uncleHeaders
|
||||
self.header.UncleHash = crypto.Sha3(ethutil.Encode(uncleHeaders))
|
||||
}
|
||||
|
||||
func (self *Block) Transactions() Transactions {
|
||||
return self.transactions
|
||||
}
|
||||
|
||||
func (self *Block) Transaction(hash []byte) *Transaction {
|
||||
for _, transaction := range self.transactions {
|
||||
if bytes.Equal(hash, transaction.Hash()) {
|
||||
return transaction
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Block) SetTransactions(transactions Transactions) {
|
||||
self.transactions = transactions
|
||||
self.header.TxHash = DeriveSha(transactions)
|
||||
}
|
||||
|
||||
func (self *Block) Receipts() Receipts {
|
||||
return self.receipts
|
||||
}
|
||||
|
||||
func (self *Block) SetReceipts(receipts Receipts) {
|
||||
self.receipts = receipts
|
||||
self.header.ReceiptHash = DeriveSha(receipts)
|
||||
self.header.Bloom = CreateBloom(receipts)
|
||||
}
|
||||
|
||||
func (self *Block) RlpData() interface{} {
|
||||
return []interface{}{self.header, self.transactions, self.uncles}
|
||||
}
|
||||
|
||||
func (self *Block) RlpDataForStorage() interface{} {
|
||||
return []interface{}{self.header, self.transactions, self.uncles, self.Td /* TODO receipts */}
|
||||
}
|
||||
|
||||
// Header accessors (add as you need them)
|
||||
func (self *Block) Number() *big.Int { return self.header.Number }
|
||||
func (self *Block) NumberU64() uint64 { return self.header.Number.Uint64() }
|
||||
func (self *Block) ParentHash() []byte { return self.header.ParentHash }
|
||||
func (self *Block) Bloom() []byte { return self.header.Bloom }
|
||||
func (self *Block) Coinbase() []byte { return self.header.Coinbase }
|
||||
func (self *Block) Time() int64 { return int64(self.header.Time) }
|
||||
func (self *Block) GasLimit() *big.Int { return self.header.GasLimit }
|
||||
func (self *Block) GasUsed() *big.Int { return self.header.GasUsed }
|
||||
func (self *Block) Hash() []byte { return self.header.Hash() }
|
||||
func (self *Block) Trie() *ptrie.Trie { return ptrie.New(self.header.Root, ethutil.Config.Db) }
|
||||
func (self *Block) State() *state.StateDB { return state.New(self.Trie()) }
|
||||
func (self *Block) Size() ethutil.StorageSize { return ethutil.StorageSize(len(ethutil.Encode(self))) }
|
||||
func (self *Block) SetRoot(root []byte) { self.header.Root = root }
|
||||
|
||||
// Implement block.Pow
|
||||
func (self *Block) Difficulty() *big.Int { return self.header.Difficulty }
|
||||
func (self *Block) N() []byte { return self.header.Nonce }
|
||||
func (self *Block) HashNoNonce() []byte {
|
||||
return crypto.Sha3(ethutil.Encode(self.header.rlpData(false)))
|
||||
}
|
||||
|
||||
func (self *Block) String() string {
|
||||
return fmt.Sprintf(`BLOCK(%x): Size: %v {
|
||||
Header:
|
||||
[
|
||||
%v
|
||||
]
|
||||
Transactions:
|
||||
%v
|
||||
Uncles:
|
||||
%v
|
||||
}
|
||||
`, self.header.Hash(), self.Size(), self.header, self.transactions, self.uncles)
|
||||
}
|
||||
|
||||
func (self *Header) String() string {
|
||||
return fmt.Sprintf(`
|
||||
ParentHash: %x
|
||||
UncleHash: %x
|
||||
Coinbase: %x
|
||||
Root: %x
|
||||
TxSha %x
|
||||
ReceiptSha: %x
|
||||
Bloom: %x
|
||||
Difficulty: %v
|
||||
Number: %v
|
||||
GasLimit: %v
|
||||
GasUsed: %v
|
||||
Time: %v
|
||||
Extra: %v
|
||||
Nonce: %x
|
||||
`, self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra, self.Nonce)
|
||||
}
|
||||
|
||||
type Blocks []*Block
|
||||
|
||||
func (self Blocks) AsSet() ethutil.UniqueSet {
|
||||
set := make(ethutil.UniqueSet)
|
||||
for _, block := range self {
|
||||
set.Insert(block.Hash())
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
type BlockBy func(b1, b2 *Block) bool
|
||||
|
||||
func (self BlockBy) Sort(blocks Blocks) {
|
||||
|
@ -65,352 +264,4 @@ func (self blockSorter) Swap(i, j int) {
|
|||
}
|
||||
func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) }
|
||||
|
||||
func Number(b1, b2 *Block) bool { return b1.Number.Cmp(b2.Number) < 0 }
|
||||
|
||||
type Block struct {
|
||||
// Hash to the previous block
|
||||
PrevHash ethutil.Bytes
|
||||
// Uncles of this block
|
||||
Uncles Blocks
|
||||
UncleSha []byte
|
||||
// The coin base address
|
||||
Coinbase []byte
|
||||
// Block Trie state
|
||||
//state *ethutil.Trie
|
||||
state *state.StateDB
|
||||
// Difficulty for the current block
|
||||
Difficulty *big.Int
|
||||
// Creation time
|
||||
Time int64
|
||||
// The block number
|
||||
Number *big.Int
|
||||
// Gas limit
|
||||
GasLimit *big.Int
|
||||
// Gas used
|
||||
GasUsed *big.Int
|
||||
// Extra data
|
||||
Extra string
|
||||
// Block Nonce for verification
|
||||
Nonce ethutil.Bytes
|
||||
// List of transactions and/or contracts
|
||||
transactions Transactions
|
||||
receipts Receipts
|
||||
TxSha, ReceiptSha []byte
|
||||
LogsBloom []byte
|
||||
|
||||
Reward *big.Int
|
||||
}
|
||||
|
||||
func NewBlockFromBytes(raw []byte) *Block {
|
||||
block := &Block{}
|
||||
block.RlpDecode(raw)
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
// New block takes a raw encoded string
|
||||
func NewBlockFromRlpValue(rlpValue *ethutil.Value) *Block {
|
||||
block := &Block{}
|
||||
block.RlpValueDecode(rlpValue)
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func CreateBlock(root interface{},
|
||||
prevHash []byte,
|
||||
base []byte,
|
||||
Difficulty *big.Int,
|
||||
Nonce []byte,
|
||||
extra string) *Block {
|
||||
|
||||
block := &Block{
|
||||
PrevHash: prevHash,
|
||||
Coinbase: base,
|
||||
Difficulty: Difficulty,
|
||||
Nonce: Nonce,
|
||||
Time: time.Now().Unix(),
|
||||
Extra: extra,
|
||||
UncleSha: nil,
|
||||
GasUsed: new(big.Int),
|
||||
GasLimit: new(big.Int),
|
||||
}
|
||||
block.SetUncles([]*Block{})
|
||||
|
||||
block.state = state.New(trie.New(ethutil.Config.Db, root))
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
// Returns a hash of the block
|
||||
func (block *Block) Hash() ethutil.Bytes {
|
||||
return crypto.Sha3(ethutil.NewValue(block.header()).Encode())
|
||||
//return crypto.Sha3(block.Value().Encode())
|
||||
}
|
||||
|
||||
func (block *Block) HashNoNonce() []byte {
|
||||
return crypto.Sha3(ethutil.Encode(block.miningHeader()))
|
||||
}
|
||||
|
||||
func (block *Block) State() *state.StateDB {
|
||||
return block.state
|
||||
}
|
||||
|
||||
func (block *Block) Transactions() Transactions {
|
||||
return block.transactions
|
||||
}
|
||||
|
||||
func (block *Block) CalcGasLimit(parent *Block) *big.Int {
|
||||
if block.Number.Cmp(big.NewInt(0)) == 0 {
|
||||
return ethutil.BigPow(10, 6)
|
||||
}
|
||||
|
||||
// ((1024-1) * parent.gasLimit + (gasUsed * 6 / 5)) / 1024
|
||||
|
||||
previous := new(big.Int).Mul(big.NewInt(1024-1), parent.GasLimit)
|
||||
current := new(big.Rat).Mul(new(big.Rat).SetInt(parent.GasUsed), big.NewRat(6, 5))
|
||||
curInt := new(big.Int).Div(current.Num(), current.Denom())
|
||||
|
||||
result := new(big.Int).Add(previous, curInt)
|
||||
result.Div(result, big.NewInt(1024))
|
||||
|
||||
min := big.NewInt(125000)
|
||||
|
||||
return ethutil.BigMax(min, result)
|
||||
}
|
||||
|
||||
func (block *Block) BlockInfo() BlockInfo {
|
||||
bi := BlockInfo{}
|
||||
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
|
||||
bi.RlpDecode(data)
|
||||
|
||||
return bi
|
||||
}
|
||||
|
||||
func (self *Block) GetTransaction(hash []byte) *Transaction {
|
||||
for _, tx := range self.transactions {
|
||||
if bytes.Compare(tx.Hash(), hash) == 0 {
|
||||
return tx
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sync the block's state and contract respectively
|
||||
func (block *Block) Sync() {
|
||||
block.state.Sync()
|
||||
}
|
||||
|
||||
func (block *Block) Undo() {
|
||||
// Sync the block state itself
|
||||
block.state.Reset()
|
||||
}
|
||||
|
||||
/////// Block Encoding
|
||||
func (block *Block) rlpReceipts() interface{} {
|
||||
// Marshal the transactions of this block
|
||||
encR := make([]interface{}, len(block.receipts))
|
||||
for i, r := range block.receipts {
|
||||
// Cast it to a string (safe)
|
||||
encR[i] = r.RlpData()
|
||||
}
|
||||
|
||||
return encR
|
||||
}
|
||||
|
||||
func (block *Block) rlpUncles() interface{} {
|
||||
// Marshal the transactions of this block
|
||||
uncles := make([]interface{}, len(block.Uncles))
|
||||
for i, uncle := range block.Uncles {
|
||||
// Cast it to a string (safe)
|
||||
uncles[i] = uncle.header()
|
||||
}
|
||||
|
||||
return uncles
|
||||
}
|
||||
|
||||
func (block *Block) SetUncles(uncles []*Block) {
|
||||
block.Uncles = uncles
|
||||
block.UncleSha = crypto.Sha3(ethutil.Encode(block.rlpUncles()))
|
||||
}
|
||||
|
||||
func (self *Block) SetReceipts(receipts Receipts) {
|
||||
self.receipts = receipts
|
||||
self.ReceiptSha = DeriveSha(receipts)
|
||||
self.LogsBloom = CreateBloom(receipts)
|
||||
}
|
||||
|
||||
func (self *Block) SetTransactions(txs Transactions) {
|
||||
self.transactions = txs
|
||||
self.TxSha = DeriveSha(txs)
|
||||
}
|
||||
|
||||
func (block *Block) Value() *ethutil.Value {
|
||||
return ethutil.NewValue([]interface{}{block.header(), block.transactions, block.rlpUncles()})
|
||||
}
|
||||
|
||||
func (block *Block) RlpEncode() []byte {
|
||||
// Encode a slice interface which contains the header and the list of
|
||||
// transactions.
|
||||
return block.Value().Encode()
|
||||
}
|
||||
|
||||
func (block *Block) RlpDecode(data []byte) {
|
||||
rlpValue := ethutil.NewValueFromBytes(data)
|
||||
block.RlpValueDecode(rlpValue)
|
||||
}
|
||||
|
||||
func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
|
||||
block.setHeader(decoder.Get(0))
|
||||
|
||||
// Tx list might be empty if this is an uncle. Uncles only have their
|
||||
// header set.
|
||||
if decoder.Get(1).IsNil() == false { // Yes explicitness
|
||||
//receipts := decoder.Get(1)
|
||||
//block.receipts = make([]*Receipt, receipts.Len())
|
||||
txs := decoder.Get(1)
|
||||
block.transactions = make(Transactions, txs.Len())
|
||||
for i := 0; i < txs.Len(); i++ {
|
||||
block.transactions[i] = NewTransactionFromValue(txs.Get(i))
|
||||
//receipt := NewRecieptFromValue(receipts.Get(i))
|
||||
//block.transactions[i] = receipt.Tx
|
||||
//block.receipts[i] = receipt
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if decoder.Get(2).IsNil() == false { // Yes explicitness
|
||||
uncles := decoder.Get(2)
|
||||
block.Uncles = make([]*Block, uncles.Len())
|
||||
for i := 0; i < uncles.Len(); i++ {
|
||||
block.Uncles[i] = NewUncleBlockFromValue(uncles.Get(i))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (self *Block) setHeader(header *ethutil.Value) {
|
||||
self.PrevHash = header.Get(0).Bytes()
|
||||
self.UncleSha = header.Get(1).Bytes()
|
||||
self.Coinbase = header.Get(2).Bytes()
|
||||
self.state = state.New(trie.New(ethutil.Config.Db, header.Get(3).Val))
|
||||
self.TxSha = header.Get(4).Bytes()
|
||||
self.ReceiptSha = header.Get(5).Bytes()
|
||||
self.LogsBloom = header.Get(6).Bytes()
|
||||
self.Difficulty = header.Get(7).BigInt()
|
||||
self.Number = header.Get(8).BigInt()
|
||||
self.GasLimit = header.Get(9).BigInt()
|
||||
self.GasUsed = header.Get(10).BigInt()
|
||||
self.Time = int64(header.Get(11).BigInt().Uint64())
|
||||
self.Extra = header.Get(12).Str()
|
||||
self.Nonce = header.Get(13).Bytes()
|
||||
}
|
||||
|
||||
func NewUncleBlockFromValue(header *ethutil.Value) *Block {
|
||||
block := &Block{}
|
||||
block.setHeader(header)
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
func (block *Block) Trie() *trie.Trie {
|
||||
return block.state.Trie
|
||||
}
|
||||
|
||||
func (block *Block) Root() interface{} {
|
||||
return block.state.Root()
|
||||
}
|
||||
|
||||
func (block *Block) Diff() *big.Int {
|
||||
return block.Difficulty
|
||||
}
|
||||
|
||||
func (self *Block) Receipts() []*Receipt {
|
||||
return self.receipts
|
||||
}
|
||||
|
||||
func (block *Block) miningHeader() []interface{} {
|
||||
return []interface{}{
|
||||
// Sha of the previous block
|
||||
block.PrevHash,
|
||||
// Sha of uncles
|
||||
block.UncleSha,
|
||||
// Coinbase address
|
||||
block.Coinbase,
|
||||
// root state
|
||||
block.Root(),
|
||||
// tx root
|
||||
block.TxSha,
|
||||
// Sha of tx
|
||||
block.ReceiptSha,
|
||||
// Bloom
|
||||
block.LogsBloom,
|
||||
// Current block Difficulty
|
||||
block.Difficulty,
|
||||
// The block number
|
||||
block.Number,
|
||||
// Block upper gas bound
|
||||
block.GasLimit,
|
||||
// Block gas used
|
||||
block.GasUsed,
|
||||
// Time the block was found?
|
||||
block.Time,
|
||||
// Extra data
|
||||
block.Extra,
|
||||
}
|
||||
}
|
||||
|
||||
func (block *Block) header() []interface{} {
|
||||
return append(block.miningHeader(), block.Nonce)
|
||||
}
|
||||
|
||||
func (block *Block) String() string {
|
||||
return fmt.Sprintf(`
|
||||
BLOCK(%x): Size: %v
|
||||
PrevHash: %x
|
||||
UncleSha: %x
|
||||
Coinbase: %x
|
||||
Root: %x
|
||||
TxSha %x
|
||||
ReceiptSha: %x
|
||||
Bloom: %x
|
||||
Difficulty: %v
|
||||
Number: %v
|
||||
MaxLimit: %v
|
||||
GasUsed: %v
|
||||
Time: %v
|
||||
Extra: %v
|
||||
Nonce: %x
|
||||
NumTx: %v
|
||||
`,
|
||||
block.Hash(),
|
||||
block.Size(),
|
||||
block.PrevHash,
|
||||
block.UncleSha,
|
||||
block.Coinbase,
|
||||
block.Root(),
|
||||
block.TxSha,
|
||||
block.ReceiptSha,
|
||||
block.LogsBloom,
|
||||
block.Difficulty,
|
||||
block.Number,
|
||||
block.GasLimit,
|
||||
block.GasUsed,
|
||||
block.Time,
|
||||
block.Extra,
|
||||
block.Nonce,
|
||||
len(block.transactions),
|
||||
)
|
||||
}
|
||||
|
||||
func (self *Block) Size() ethutil.StorageSize {
|
||||
return ethutil.StorageSize(len(self.RlpEncode()))
|
||||
}
|
||||
|
||||
// Implement RlpEncodable
|
||||
func (self *Block) RlpData() interface{} {
|
||||
return self.Value().Val
|
||||
}
|
||||
|
||||
// Implement pow.Block
|
||||
func (self *Block) N() []byte { return self.Nonce }
|
||||
func Number(b1, b2 *Block) bool { return b1.Header().Number.Cmp(b2.Header().Number) < 0 }
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
|
||||
ethutil.Config.Db, _ = ethdb.NewMemDatabase()
|
||||
}
|
||||
|
||||
func TestNewBlock(t *testing.T) {
|
||||
block := GenesisBlock()
|
||||
data := ethutil.Encode(block)
|
||||
|
||||
var genesis Block
|
||||
err := rlp.Decode(bytes.NewReader(data), &genesis)
|
||||
}
|
|
@ -2,7 +2,7 @@ package types
|
|||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/ptrie"
|
||||
)
|
||||
|
||||
type DerivableList interface {
|
||||
|
@ -11,10 +11,10 @@ type DerivableList interface {
|
|||
}
|
||||
|
||||
func DeriveSha(list DerivableList) []byte {
|
||||
trie := trie.New(ethutil.Config.Db, "")
|
||||
trie := ptrie.New(nil, ethutil.Config.Db)
|
||||
for i := 0; i < list.Len(); i++ {
|
||||
trie.Update(string(ethutil.NewValue(i).Encode()), string(list.GetRlp(i)))
|
||||
trie.Update(ethutil.Encode(i), list.GetRlp(i))
|
||||
}
|
||||
|
||||
return trie.GetRoot()
|
||||
return trie.Root()
|
||||
}
|
||||
|
|
|
@ -1,42 +1,37 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/obscuren/secp256k1-go"
|
||||
)
|
||||
|
||||
var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
func IsContractAddr(addr []byte) bool {
|
||||
return len(addr) == 0
|
||||
//return bytes.Compare(addr, ContractAddr) == 0
|
||||
}
|
||||
|
||||
type Transaction struct {
|
||||
Nonce uint64
|
||||
Recipient []byte
|
||||
Value *big.Int
|
||||
Gas *big.Int
|
||||
GasPrice *big.Int
|
||||
Data []byte
|
||||
v byte
|
||||
r, s []byte
|
||||
|
||||
// Indicates whether this tx is a contract creation transaction
|
||||
contractCreation bool
|
||||
AccountNonce uint64
|
||||
Price *big.Int
|
||||
GasLimit *big.Int
|
||||
Recipient []byte
|
||||
Amount *big.Int
|
||||
Payload []byte
|
||||
V uint64
|
||||
R, S []byte
|
||||
}
|
||||
|
||||
func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction {
|
||||
return &Transaction{Recipient: nil, Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true}
|
||||
func NewContractCreationTx(Amount, gasAmount, price *big.Int, data []byte) *Transaction {
|
||||
return NewTransactionMessage(nil, Amount, gasAmount, price, data)
|
||||
}
|
||||
|
||||
func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction {
|
||||
return &Transaction{Recipient: to, Value: value, GasPrice: gasPrice, Gas: gas, Data: data, contractCreation: IsContractAddr(to)}
|
||||
func NewTransactionMessage(to []byte, Amount, gasAmount, price *big.Int, data []byte) *Transaction {
|
||||
return &Transaction{Recipient: to, Amount: Amount, Price: price, GasLimit: gasAmount, Payload: data}
|
||||
}
|
||||
|
||||
func NewTransactionFromBytes(data []byte) *Transaction {
|
||||
|
@ -46,46 +41,55 @@ func NewTransactionFromBytes(data []byte) *Transaction {
|
|||
return tx
|
||||
}
|
||||
|
||||
func NewTransactionFromValue(val *ethutil.Value) *Transaction {
|
||||
func NewTransactionFromAmount(val *ethutil.Value) *Transaction {
|
||||
tx := &Transaction{}
|
||||
tx.RlpValueDecode(val)
|
||||
|
||||
return tx
|
||||
}
|
||||
|
||||
func (self *Transaction) GasValue() *big.Int {
|
||||
return new(big.Int).Mul(self.Gas, self.GasPrice)
|
||||
}
|
||||
|
||||
func (self *Transaction) TotalValue() *big.Int {
|
||||
v := self.GasValue()
|
||||
return v.Add(v, self.Value)
|
||||
}
|
||||
|
||||
func (tx *Transaction) Hash() []byte {
|
||||
data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data}
|
||||
data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload}
|
||||
|
||||
return crypto.Sha3(ethutil.NewValue(data).Encode())
|
||||
return crypto.Sha3(ethutil.Encode(data))
|
||||
}
|
||||
|
||||
func (tx *Transaction) CreatesContract() bool {
|
||||
return tx.contractCreation
|
||||
func (self *Transaction) Data() []byte {
|
||||
return self.Payload
|
||||
}
|
||||
|
||||
/* Deprecated */
|
||||
func (tx *Transaction) IsContract() bool {
|
||||
return tx.CreatesContract()
|
||||
func (self *Transaction) Gas() *big.Int {
|
||||
return self.GasLimit
|
||||
}
|
||||
|
||||
func (tx *Transaction) CreationAddress(state *state.StateDB) []byte {
|
||||
// Generate a new address
|
||||
return crypto.Sha3(ethutil.NewValue([]interface{}{tx.Sender(), tx.Nonce}).Encode())[12:]
|
||||
func (self *Transaction) GasPrice() *big.Int {
|
||||
return self.Price
|
||||
}
|
||||
|
||||
func (self *Transaction) Value() *big.Int {
|
||||
return self.Amount
|
||||
}
|
||||
|
||||
func (self *Transaction) Nonce() uint64 {
|
||||
return self.AccountNonce
|
||||
}
|
||||
|
||||
func (self *Transaction) SetNonce(AccountNonce uint64) {
|
||||
self.AccountNonce = AccountNonce
|
||||
}
|
||||
|
||||
func (self *Transaction) From() []byte {
|
||||
return self.sender()
|
||||
}
|
||||
|
||||
func (self *Transaction) To() []byte {
|
||||
return self.Recipient
|
||||
}
|
||||
|
||||
func (tx *Transaction) Curve() (v byte, r []byte, s []byte) {
|
||||
v = tx.v
|
||||
r = ethutil.LeftPadBytes(tx.r, 32)
|
||||
s = ethutil.LeftPadBytes(tx.s, 32)
|
||||
v = byte(tx.V)
|
||||
r = ethutil.LeftPadBytes(tx.R, 32)
|
||||
s = ethutil.LeftPadBytes(tx.S, 32)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -106,18 +110,18 @@ func (tx *Transaction) PublicKey() []byte {
|
|||
sig := append(r, s...)
|
||||
sig = append(sig, v-27)
|
||||
|
||||
pubkey := crypto.Ecrecover(append(hash, sig...))
|
||||
//pubkey, _ := secp256k1.RecoverPubkey(hash, sig)
|
||||
//pubkey := crypto.Ecrecover(append(hash, sig...))
|
||||
pubkey, _ := secp256k1.RecoverPubkey(hash, sig)
|
||||
|
||||
return pubkey
|
||||
}
|
||||
|
||||
func (tx *Transaction) Sender() []byte {
|
||||
func (tx *Transaction) sender() []byte {
|
||||
pubkey := tx.PublicKey()
|
||||
|
||||
// Validate the returned key.
|
||||
// Return nil if public key isn't in full format
|
||||
if len(pubkey) != 0 && pubkey[0] != 4 {
|
||||
if len(pubkey) == 0 || pubkey[0] != 4 {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -128,48 +132,37 @@ func (tx *Transaction) Sign(privk []byte) error {
|
|||
|
||||
sig := tx.Signature(privk)
|
||||
|
||||
tx.r = sig[:32]
|
||||
tx.s = sig[32:64]
|
||||
tx.v = sig[64] + 27
|
||||
tx.R = sig[:32]
|
||||
tx.S = sig[32:64]
|
||||
tx.V = uint64(sig[64] + 27)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpData() interface{} {
|
||||
data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data}
|
||||
data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload}
|
||||
|
||||
// TODO Remove prefixing zero's
|
||||
|
||||
return append(data, tx.v, new(big.Int).SetBytes(tx.r).Bytes(), new(big.Int).SetBytes(tx.s).Bytes())
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpValue() *ethutil.Value {
|
||||
return ethutil.NewValue(tx.RlpData())
|
||||
return append(data, tx.V, new(big.Int).SetBytes(tx.R).Bytes(), new(big.Int).SetBytes(tx.S).Bytes())
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpEncode() []byte {
|
||||
return tx.RlpValue().Encode()
|
||||
return ethutil.Encode(tx)
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpDecode(data []byte) {
|
||||
tx.RlpValueDecode(ethutil.NewValueFromBytes(data))
|
||||
rlp.Decode(bytes.NewReader(data), tx)
|
||||
}
|
||||
|
||||
func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) {
|
||||
tx.Nonce = decoder.Get(0).Uint()
|
||||
tx.GasPrice = decoder.Get(1).BigInt()
|
||||
tx.Gas = decoder.Get(2).BigInt()
|
||||
tx.AccountNonce = decoder.Get(0).Uint()
|
||||
tx.Price = decoder.Get(1).BigInt()
|
||||
tx.GasLimit = decoder.Get(2).BigInt()
|
||||
tx.Recipient = decoder.Get(3).Bytes()
|
||||
tx.Value = decoder.Get(4).BigInt()
|
||||
tx.Data = decoder.Get(5).Bytes()
|
||||
tx.v = byte(decoder.Get(6).Uint())
|
||||
|
||||
tx.r = decoder.Get(7).Bytes()
|
||||
tx.s = decoder.Get(8).Bytes()
|
||||
|
||||
if IsContractAddr(tx.Recipient) {
|
||||
tx.contractCreation = true
|
||||
}
|
||||
tx.Amount = decoder.Get(4).BigInt()
|
||||
tx.Payload = decoder.Get(5).Bytes()
|
||||
tx.V = decoder.Get(6).Uint()
|
||||
tx.R = decoder.Get(7).Bytes()
|
||||
tx.S = decoder.Get(8).Bytes()
|
||||
}
|
||||
|
||||
func (tx *Transaction) String() string {
|
||||
|
@ -180,25 +173,28 @@ func (tx *Transaction) String() string {
|
|||
To: %x
|
||||
Nonce: %v
|
||||
GasPrice: %v
|
||||
Gas: %v
|
||||
GasLimit %v
|
||||
Value: %v
|
||||
Data: 0x%x
|
||||
V: 0x%x
|
||||
R: 0x%x
|
||||
S: 0x%x
|
||||
`,
|
||||
Hex: %x
|
||||
`,
|
||||
tx.Hash(),
|
||||
len(tx.Recipient) == 0,
|
||||
tx.Sender(),
|
||||
tx.Recipient,
|
||||
tx.Nonce,
|
||||
tx.GasPrice,
|
||||
tx.Gas,
|
||||
tx.Value,
|
||||
tx.Data,
|
||||
tx.v,
|
||||
tx.r,
|
||||
tx.s)
|
||||
tx.From(),
|
||||
tx.To(),
|
||||
tx.AccountNonce,
|
||||
tx.Price,
|
||||
tx.GasLimit,
|
||||
tx.Amount,
|
||||
tx.Payload,
|
||||
tx.V,
|
||||
tx.R,
|
||||
tx.S,
|
||||
ethutil.Encode(tx),
|
||||
)
|
||||
}
|
||||
|
||||
// Transaction slice type for basic sorting
|
||||
|
@ -221,5 +217,5 @@ func (s Transactions) GetRlp(i int) []byte { return ethutil.Rlp(s[i]) }
|
|||
type TxByNonce struct{ Transactions }
|
||||
|
||||
func (s TxByNonce) Less(i, j int) bool {
|
||||
return s.Transactions[i].Nonce < s.Transactions[j].Nonce
|
||||
return s.Transactions[i].AccountNonce < s.Transactions[j].AccountNonce
|
||||
}
|
||||
|
|
|
@ -11,28 +11,28 @@ import (
|
|||
type VMEnv struct {
|
||||
state *state.StateDB
|
||||
block *types.Block
|
||||
tx *types.Transaction
|
||||
msg Message
|
||||
depth int
|
||||
}
|
||||
|
||||
func NewEnv(state *state.StateDB, tx *types.Transaction, block *types.Block) *VMEnv {
|
||||
func NewEnv(state *state.StateDB, msg Message, block *types.Block) *VMEnv {
|
||||
return &VMEnv{
|
||||
state: state,
|
||||
block: block,
|
||||
tx: tx,
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *VMEnv) Origin() []byte { return self.tx.Sender() }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number }
|
||||
func (self *VMEnv) PrevHash() []byte { return self.block.PrevHash }
|
||||
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase }
|
||||
func (self *VMEnv) Time() int64 { return self.block.Time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty }
|
||||
func (self *VMEnv) Origin() []byte { return self.msg.From() }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() }
|
||||
func (self *VMEnv) PrevHash() []byte { return self.block.ParentHash() }
|
||||
func (self *VMEnv) Coinbase() []byte { return self.block.Coinbase() }
|
||||
func (self *VMEnv) Time() int64 { return self.block.Time() }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() }
|
||||
func (self *VMEnv) BlockHash() []byte { return self.block.Hash() }
|
||||
func (self *VMEnv) Value() *big.Int { return self.tx.Value }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit() }
|
||||
func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
|
||||
func (self *VMEnv) State() *state.StateDB { return self.state }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit }
|
||||
func (self *VMEnv) Depth() int { return self.depth }
|
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||
func (self *VMEnv) AddLog(log state.Log) {
|
||||
|
@ -43,9 +43,7 @@ func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
|
|||
}
|
||||
|
||||
func (self *VMEnv) vm(addr, data []byte, gas, price, value *big.Int) *Execution {
|
||||
evm := vm.New(self, vm.DebugVmTy)
|
||||
|
||||
return NewExecution(evm, addr, data, gas, price, value)
|
||||
return NewExecution(self, addr, data, gas, price, value)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Call(me vm.ClosureRef, addr, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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()
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
package eth
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
)
|
||||
|
||||
type testMsgReadWriter struct {
|
||||
in chan p2p.Msg
|
||||
out chan p2p.Msg
|
||||
}
|
||||
|
||||
func (self *testMsgReadWriter) In(msg p2p.Msg) {
|
||||
self.in <- msg
|
||||
}
|
||||
|
||||
func (self *testMsgReadWriter) Out(msg p2p.Msg) {
|
||||
self.in <- msg
|
||||
}
|
||||
|
||||
func (self *testMsgReadWriter) WriteMsg(msg p2p.Msg) error {
|
||||
self.out <- msg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *testMsgReadWriter) EncodeMsg(code uint64, data ...interface{}) error {
|
||||
return self.WriteMsg(p2p.NewMsg(code, data))
|
||||
}
|
||||
|
||||
func (self *testMsgReadWriter) ReadMsg() (p2p.Msg, error) {
|
||||
msg, ok := <-self.in
|
||||
if !ok {
|
||||
return msg, io.EOF
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func errorCheck(t *testing.T, expCode int, err error) {
|
||||
perr, ok := err.(*protocolError)
|
||||
if ok && perr != nil {
|
||||
if code := perr.Code; code != expCode {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
t.Errorf("expected error code %v, got %v", ErrNoStatusMsg, err)
|
||||
}
|
||||
}
|
||||
|
||||
type TestBackend struct {
|
||||
getTransactions func() []*types.Transaction
|
||||
addTransactions func(txs []*types.Transaction)
|
||||
getBlockHashes func(hash []byte, amount uint32) (hashes [][]byte)
|
||||
addBlockHashes func(next func() ([]byte, bool), peerId string)
|
||||
getBlock func(hash []byte) *types.Block
|
||||
addBlock func(block *types.Block, peerId string) (err error)
|
||||
addPeer func(td *big.Int, currentBlock []byte, peerId string, requestHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool)
|
||||
removePeer func(peerId string)
|
||||
status func() (td *big.Int, currentBlock []byte, genesisBlock []byte)
|
||||
}
|
||||
|
||||
func (self *TestBackend) GetTransactions() (txs []*types.Transaction) {
|
||||
if self.getTransactions != nil {
|
||||
txs = self.getTransactions()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *TestBackend) AddTransactions(txs []*types.Transaction) {
|
||||
if self.addTransactions != nil {
|
||||
self.addTransactions(txs)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *TestBackend) GetBlockHashes(hash []byte, amount uint32) (hashes [][]byte) {
|
||||
if self.getBlockHashes != nil {
|
||||
hashes = self.getBlockHashes(hash, amount)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
func (self *TestBackend) AddBlockHashes(next func() ([]byte, bool), peerId string) {
|
||||
if self.addBlockHashes != nil {
|
||||
self.addBlockHashes(next, peerId)
|
||||
}
|
||||
}
|
||||
|
||||
=======
|
||||
func (self *TestBackend) AddHash(hash []byte, peer *p2p.Peer) (more bool) {
|
||||
if self.addHash != nil {
|
||||
more = self.addHash(hash, peer)
|
||||
=======
|
||||
func (self *TestBackend) AddBlockHashes(next func() ([]byte, bool), peerId string) {
|
||||
if self.addBlockHashes != nil {
|
||||
self.addBlockHashes(next, peerId)
|
||||
>>>>>>> eth protocol changes
|
||||
}
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> initial commit for eth-p2p integration
|
||||
=======
|
||||
|
||||
>>>>>>> eth protocol changes
|
||||
func (self *TestBackend) GetBlock(hash []byte) (block *types.Block) {
|
||||
if self.getBlock != nil {
|
||||
block = self.getBlock(hash)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
func (self *TestBackend) AddBlock(block *types.Block, peerId string) (err error) {
|
||||
if self.addBlock != nil {
|
||||
err = self.addBlock(block, peerId)
|
||||
=======
|
||||
func (self *TestBackend) AddBlock(td *big.Int, block *types.Block, peer *p2p.Peer) (fetchHashes bool, err error) {
|
||||
if self.addBlock != nil {
|
||||
fetchHashes, err = self.addBlock(td, block, peer)
|
||||
>>>>>>> initial commit for eth-p2p integration
|
||||
=======
|
||||
func (self *TestBackend) AddBlock(block *types.Block, peerId string) (err error) {
|
||||
if self.addBlock != nil {
|
||||
err = self.addBlock(block, peerId)
|
||||
>>>>>>> eth protocol changes
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool) {
|
||||
if self.addPeer != nil {
|
||||
best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, invalidBlock)
|
||||
=======
|
||||
func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peer *p2p.Peer) (fetchHashes bool) {
|
||||
if self.addPeer != nil {
|
||||
fetchHashes = self.addPeer(td, currentBlock, peer)
|
||||
>>>>>>> initial commit for eth-p2p integration
|
||||
=======
|
||||
func (self *TestBackend) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, invalidBlock func(error)) (best bool) {
|
||||
if self.addPeer != nil {
|
||||
best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, invalidBlock)
|
||||
>>>>>>> eth protocol changes
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
>>>>>>> eth protocol changes
|
||||
func (self *TestBackend) RemovePeer(peerId string) {
|
||||
if self.removePeer != nil {
|
||||
self.removePeer(peerId)
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
>>>>>>> initial commit for eth-p2p integration
|
||||
=======
|
||||
>>>>>>> eth protocol changes
|
||||
func (self *TestBackend) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) {
|
||||
if self.status != nil {
|
||||
td, currentBlock, genesisBlock = self.status()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
>>>>>>> eth protocol changes
|
||||
// TODO: refactor this into p2p/client_identity
|
||||
type peerId struct {
|
||||
pubkey []byte
|
||||
}
|
||||
|
||||
func (self *peerId) String() string {
|
||||
return "test peer"
|
||||
}
|
||||
|
||||
func (self *peerId) Pubkey() (pubkey []byte) {
|
||||
pubkey = self.pubkey
|
||||
if len(pubkey) == 0 {
|
||||
pubkey = crypto.GenerateNewKeyPair().PublicKey
|
||||
self.pubkey = pubkey
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func testPeer() *p2p.Peer {
|
||||
return p2p.NewPeer(&peerId{}, []p2p.Cap{})
|
||||
}
|
||||
|
||||
func TestErrNoStatusMsg(t *testing.T) {
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
func TestEth(t *testing.T) {
|
||||
>>>>>>> initial commit for eth-p2p integration
|
||||
=======
|
||||
>>>>>>> eth protocol changes
|
||||
quit := make(chan bool)
|
||||
rw := &testMsgReadWriter{make(chan p2p.Msg, 10), make(chan p2p.Msg, 10)}
|
||||
testBackend := &TestBackend{}
|
||||
var err error
|
||||
go func() {
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
err = runEthProtocol(testBackend, testPeer(), rw)
|
||||
=======
|
||||
err = runEthProtocol(testBackend, nil, rw)
|
||||
>>>>>>> initial commit for eth-p2p integration
|
||||
=======
|
||||
err = runEthProtocol(testBackend, testPeer(), rw)
|
||||
>>>>>>> eth protocol changes
|
||||
close(quit)
|
||||
}()
|
||||
statusMsg := p2p.NewMsg(4)
|
||||
rw.In(statusMsg)
|
||||
<-quit
|
||||
errorCheck(t, ErrNoStatusMsg, err)
|
||||
// read(t, remote, []byte("hello, world"), nil)
|
||||
}
|
|
@ -71,6 +71,10 @@ func (self *LDBDatabase) NewIterator() iterator.Iterator {
|
|||
return self.db.NewIterator(nil, nil)
|
||||
}
|
||||
|
||||
func (self *LDBDatabase) Write(batch *leveldb.Batch) error {
|
||||
return self.db.Write(batch, nil)
|
||||
}
|
||||
|
||||
func (self *LDBDatabase) Close() {
|
||||
// Close the leveldb database
|
||||
self.db.Close()
|
||||
|
|
659
ethereum.go
659
ethereum.go
|
@ -1,659 +0,0 @@
|
|||
package eth
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"net"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
)
|
||||
|
||||
const (
|
||||
seedTextFileUri string = "http://www.ethereum.org/servers.poc3.txt"
|
||||
seedNodeAddress = "poc-7.ethdev.com:30303"
|
||||
)
|
||||
|
||||
var loggerger = logger.NewLogger("SERV")
|
||||
|
||||
func eachPeer(peers *list.List, callback func(*Peer, *list.Element)) {
|
||||
// Loop thru the peers and close them (if we had them)
|
||||
for e := peers.Front(); e != nil; e = e.Next() {
|
||||
callback(e.Value.(*Peer), e)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
processReapingTimeout = 60 // TODO increase
|
||||
)
|
||||
|
||||
type Ethereum struct {
|
||||
// Channel for shutting down the ethereum
|
||||
shutdownChan chan bool
|
||||
quit chan bool
|
||||
|
||||
// DB interface
|
||||
db ethutil.Database
|
||||
// State manager for processing new blocks and managing the over all states
|
||||
blockManager *core.BlockManager
|
||||
// The transaction pool. Transaction can be pushed on this pool
|
||||
// for later including in the blocks
|
||||
txPool *core.TxPool
|
||||
// The canonical chain
|
||||
blockChain *core.ChainManager
|
||||
// The block pool
|
||||
blockPool *BlockPool
|
||||
// Eventer
|
||||
eventMux event.TypeMux
|
||||
// Peers
|
||||
peers *list.List
|
||||
// Nonce
|
||||
Nonce uint64
|
||||
|
||||
Addr net.Addr
|
||||
Port string
|
||||
|
||||
blacklist [][]byte
|
||||
|
||||
peerMut sync.Mutex
|
||||
|
||||
// Capabilities for outgoing peers
|
||||
serverCaps Caps
|
||||
|
||||
nat NAT
|
||||
|
||||
// Specifies the desired amount of maximum peers
|
||||
MaxPeers int
|
||||
|
||||
Mining bool
|
||||
|
||||
listening bool
|
||||
|
||||
RpcServer *rpc.JsonRpcServer
|
||||
|
||||
keyManager *crypto.KeyManager
|
||||
|
||||
clientIdentity wire.ClientIdentity
|
||||
|
||||
isUpToDate bool
|
||||
|
||||
filterMu sync.RWMutex
|
||||
filterId int
|
||||
filters map[int]*core.Filter
|
||||
}
|
||||
|
||||
func New(db ethutil.Database, clientIdentity wire.ClientIdentity, keyManager *crypto.KeyManager, caps Caps, usePnp bool) (*Ethereum, error) {
|
||||
var err error
|
||||
var nat NAT
|
||||
|
||||
if usePnp {
|
||||
nat, err = Discover()
|
||||
if err != nil {
|
||||
loggerger.Debugln("UPnP failed", err)
|
||||
}
|
||||
}
|
||||
|
||||
bootstrapDb(db)
|
||||
|
||||
ethutil.Config.Db = db
|
||||
|
||||
nonce, _ := ethutil.RandomUint64()
|
||||
ethereum := &Ethereum{
|
||||
shutdownChan: make(chan bool),
|
||||
quit: make(chan bool),
|
||||
db: db,
|
||||
peers: list.New(),
|
||||
Nonce: nonce,
|
||||
serverCaps: caps,
|
||||
nat: nat,
|
||||
keyManager: keyManager,
|
||||
clientIdentity: clientIdentity,
|
||||
isUpToDate: true,
|
||||
filters: make(map[int]*core.Filter),
|
||||
}
|
||||
|
||||
ethereum.blockPool = NewBlockPool(ethereum)
|
||||
ethereum.txPool = core.NewTxPool(ethereum)
|
||||
ethereum.blockChain = core.NewChainManager(ethereum.EventMux())
|
||||
ethereum.blockManager = core.NewBlockManager(ethereum)
|
||||
ethereum.blockChain.SetProcessor(ethereum.blockManager)
|
||||
|
||||
// Start the tx pool
|
||||
ethereum.txPool.Start()
|
||||
|
||||
return ethereum, nil
|
||||
}
|
||||
|
||||
func (s *Ethereum) KeyManager() *crypto.KeyManager {
|
||||
return s.keyManager
|
||||
}
|
||||
|
||||
func (s *Ethereum) ClientIdentity() wire.ClientIdentity {
|
||||
return s.clientIdentity
|
||||
}
|
||||
|
||||
func (s *Ethereum) ChainManager() *core.ChainManager {
|
||||
return s.blockChain
|
||||
}
|
||||
|
||||
func (s *Ethereum) BlockManager() *core.BlockManager {
|
||||
return s.blockManager
|
||||
}
|
||||
|
||||
func (s *Ethereum) TxPool() *core.TxPool {
|
||||
return s.txPool
|
||||
}
|
||||
func (s *Ethereum) BlockPool() *BlockPool {
|
||||
return s.blockPool
|
||||
}
|
||||
func (s *Ethereum) EventMux() *event.TypeMux {
|
||||
return &s.eventMux
|
||||
}
|
||||
func (self *Ethereum) Db() ethutil.Database {
|
||||
return self.db
|
||||
}
|
||||
|
||||
func (s *Ethereum) ServerCaps() Caps {
|
||||
return s.serverCaps
|
||||
}
|
||||
func (s *Ethereum) IsMining() bool {
|
||||
return s.Mining
|
||||
}
|
||||
func (s *Ethereum) PeerCount() int {
|
||||
return s.peers.Len()
|
||||
}
|
||||
func (s *Ethereum) IsUpToDate() bool {
|
||||
upToDate := true
|
||||
eachPeer(s.peers, func(peer *Peer, e *list.Element) {
|
||||
if atomic.LoadInt32(&peer.connected) == 1 {
|
||||
if peer.catchingUp == true && peer.versionKnown {
|
||||
upToDate = false
|
||||
}
|
||||
}
|
||||
})
|
||||
return upToDate
|
||||
}
|
||||
func (s *Ethereum) PushPeer(peer *Peer) {
|
||||
s.peers.PushBack(peer)
|
||||
}
|
||||
func (s *Ethereum) IsListening() bool {
|
||||
return s.listening
|
||||
}
|
||||
|
||||
func (s *Ethereum) HighestTDPeer() (td *big.Int) {
|
||||
td = big.NewInt(0)
|
||||
|
||||
eachPeer(s.peers, func(p *Peer, v *list.Element) {
|
||||
if p.td.Cmp(td) > 0 {
|
||||
td = p.td
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Ethereum) BlacklistPeer(peer *Peer) {
|
||||
self.blacklist = append(self.blacklist, peer.pubkey)
|
||||
}
|
||||
|
||||
func (s *Ethereum) AddPeer(conn net.Conn) {
|
||||
peer := NewPeer(conn, s, true)
|
||||
|
||||
if peer != nil {
|
||||
if s.peers.Len() < s.MaxPeers {
|
||||
peer.Start()
|
||||
} else {
|
||||
loggerger.Debugf("Max connected peers reached. Not adding incoming peer.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Ethereum) ProcessPeerList(addrs []string) {
|
||||
for _, addr := range addrs {
|
||||
// TODO Probably requires some sanity checks
|
||||
s.ConnectToPeer(addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Ethereum) ConnectToPeer(addr string) error {
|
||||
if s.peers.Len() < s.MaxPeers {
|
||||
var alreadyConnected bool
|
||||
|
||||
ahost, aport, _ := net.SplitHostPort(addr)
|
||||
var chost string
|
||||
|
||||
ips, err := net.LookupIP(ahost)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
// If more then one ip is available try stripping away the ipv6 ones
|
||||
if len(ips) > 1 {
|
||||
var ipsv4 []net.IP
|
||||
// For now remove the ipv6 addresses
|
||||
for _, ip := range ips {
|
||||
if strings.Contains(ip.String(), "::") {
|
||||
continue
|
||||
} else {
|
||||
ipsv4 = append(ipsv4, ip)
|
||||
}
|
||||
}
|
||||
if len(ipsv4) == 0 {
|
||||
return fmt.Errorf("[SERV] No IPV4 addresses available for hostname")
|
||||
}
|
||||
|
||||
// Pick a random ipv4 address, simulating round-robin DNS.
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
i := rand.Intn(len(ipsv4))
|
||||
chost = ipsv4[i].String()
|
||||
} else {
|
||||
if len(ips) == 0 {
|
||||
return fmt.Errorf("[SERV] No IPs resolved for the given hostname")
|
||||
return nil
|
||||
}
|
||||
chost = ips[0].String()
|
||||
}
|
||||
}
|
||||
|
||||
eachPeer(s.peers, func(p *Peer, v *list.Element) {
|
||||
if p.conn == nil {
|
||||
return
|
||||
}
|
||||
phost, pport, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
|
||||
|
||||
if phost == chost && pport == aport {
|
||||
alreadyConnected = true
|
||||
//loggerger.Debugf("Peer %s already added.\n", chost)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
if alreadyConnected {
|
||||
return nil
|
||||
}
|
||||
|
||||
NewOutboundPeer(addr, s, s.serverCaps)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Ethereum) OutboundPeers() []*Peer {
|
||||
// Create a new peer slice with at least the length of the total peers
|
||||
outboundPeers := make([]*Peer, s.peers.Len())
|
||||
length := 0
|
||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
||||
if !p.inbound && p.conn != nil {
|
||||
outboundPeers[length] = p
|
||||
length++
|
||||
}
|
||||
})
|
||||
|
||||
return outboundPeers[:length]
|
||||
}
|
||||
|
||||
func (s *Ethereum) InboundPeers() []*Peer {
|
||||
// Create a new peer slice with at least the length of the total peers
|
||||
inboundPeers := make([]*Peer, s.peers.Len())
|
||||
length := 0
|
||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
||||
if p.inbound {
|
||||
inboundPeers[length] = p
|
||||
length++
|
||||
}
|
||||
})
|
||||
|
||||
return inboundPeers[:length]
|
||||
}
|
||||
|
||||
func (s *Ethereum) InOutPeers() []*Peer {
|
||||
// Reap the dead peers first
|
||||
s.reapPeers()
|
||||
|
||||
// Create a new peer slice with at least the length of the total peers
|
||||
inboundPeers := make([]*Peer, s.peers.Len())
|
||||
length := 0
|
||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
||||
// Only return peers with an actual ip
|
||||
if len(p.host) > 0 {
|
||||
inboundPeers[length] = p
|
||||
length++
|
||||
}
|
||||
})
|
||||
|
||||
return inboundPeers[:length]
|
||||
}
|
||||
|
||||
func (s *Ethereum) Broadcast(msgType wire.MsgType, data []interface{}) {
|
||||
msg := wire.NewMessage(msgType, data)
|
||||
s.BroadcastMsg(msg)
|
||||
}
|
||||
|
||||
func (s *Ethereum) BroadcastMsg(msg *wire.Msg) {
|
||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
||||
p.QueueMessage(msg)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Ethereum) Peers() *list.List {
|
||||
return s.peers
|
||||
}
|
||||
|
||||
func (s *Ethereum) reapPeers() {
|
||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
||||
if atomic.LoadInt32(&p.disconnect) == 1 || (p.inbound && (time.Now().Unix()-p.lastPong) > int64(5*time.Minute)) {
|
||||
s.removePeerElement(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Ethereum) removePeerElement(e *list.Element) {
|
||||
s.peerMut.Lock()
|
||||
defer s.peerMut.Unlock()
|
||||
|
||||
s.peers.Remove(e)
|
||||
|
||||
s.eventMux.Post(PeerListEvent{s.peers})
|
||||
}
|
||||
|
||||
func (s *Ethereum) RemovePeer(p *Peer) {
|
||||
eachPeer(s.peers, func(peer *Peer, e *list.Element) {
|
||||
if peer == p {
|
||||
s.removePeerElement(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Ethereum) reapDeadPeerHandler() {
|
||||
reapTimer := time.NewTicker(processReapingTimeout * time.Second)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-reapTimer.C:
|
||||
s.reapPeers()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start the ethereum
|
||||
func (s *Ethereum) Start(seed bool) {
|
||||
s.blockPool.Start()
|
||||
|
||||
// Bind to addr and port
|
||||
ln, err := net.Listen("tcp", ":"+s.Port)
|
||||
if err != nil {
|
||||
loggerger.Warnf("Port %s in use. Connection listening disabled. Acting as client", s.Port)
|
||||
s.listening = false
|
||||
} else {
|
||||
s.listening = true
|
||||
// Starting accepting connections
|
||||
loggerger.Infoln("Ready and accepting connections")
|
||||
// Start the peer handler
|
||||
go s.peerHandler(ln)
|
||||
}
|
||||
|
||||
if s.nat != nil {
|
||||
go s.upnpUpdateThread()
|
||||
}
|
||||
|
||||
// Start the reaping processes
|
||||
go s.reapDeadPeerHandler()
|
||||
go s.update()
|
||||
go s.filterLoop()
|
||||
|
||||
if seed {
|
||||
s.Seed()
|
||||
}
|
||||
s.ConnectToPeer("localhost:40404")
|
||||
loggerger.Infoln("Server started")
|
||||
}
|
||||
|
||||
func (s *Ethereum) Seed() {
|
||||
// Sorry Py person. I must blacklist. you perform badly
|
||||
s.blacklist = append(s.blacklist, ethutil.Hex2Bytes("64656330303561383532336435376331616537643864663236623336313863373537353163636634333530626263396330346237336262623931383064393031"))
|
||||
ips := PastPeers()
|
||||
if len(ips) > 0 {
|
||||
for _, ip := range ips {
|
||||
loggerger.Infoln("Connecting to previous peer ", ip)
|
||||
s.ConnectToPeer(ip)
|
||||
}
|
||||
} else {
|
||||
loggerger.Debugln("Retrieving seed nodes")
|
||||
|
||||
// Eth-Go Bootstrapping
|
||||
ips, er := net.LookupIP("seed.bysh.me")
|
||||
if er == nil {
|
||||
peers := []string{}
|
||||
for _, ip := range ips {
|
||||
node := fmt.Sprintf("%s:%d", ip.String(), 30303)
|
||||
loggerger.Debugln("Found DNS Go Peer:", node)
|
||||
peers = append(peers, node)
|
||||
}
|
||||
s.ProcessPeerList(peers)
|
||||
}
|
||||
|
||||
// Official DNS Bootstrapping
|
||||
_, nodes, err := net.LookupSRV("eth", "tcp", "ethereum.org")
|
||||
if err == nil {
|
||||
peers := []string{}
|
||||
// Iterate SRV nodes
|
||||
for _, n := range nodes {
|
||||
target := n.Target
|
||||
port := strconv.Itoa(int(n.Port))
|
||||
// Resolve target to ip (Go returns list, so may resolve to multiple ips?)
|
||||
addr, err := net.LookupHost(target)
|
||||
if err == nil {
|
||||
for _, a := range addr {
|
||||
// Build string out of SRV port and Resolved IP
|
||||
peer := net.JoinHostPort(a, port)
|
||||
loggerger.Debugln("Found DNS Bootstrap Peer:", peer)
|
||||
peers = append(peers, peer)
|
||||
}
|
||||
} else {
|
||||
loggerger.Debugln("Couldn't resolve :", target)
|
||||
}
|
||||
}
|
||||
// Connect to Peer list
|
||||
s.ProcessPeerList(peers)
|
||||
}
|
||||
|
||||
s.ConnectToPeer(seedNodeAddress)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Ethereum) peerHandler(listener net.Listener) {
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
loggerger.Debugln(err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
go s.AddPeer(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Ethereum) Stop() {
|
||||
// Stop eventMux first, it will close all subscriptions.
|
||||
s.eventMux.Stop()
|
||||
|
||||
// Close the database
|
||||
defer s.db.Close()
|
||||
|
||||
var ips []string
|
||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
||||
ips = append(ips, p.conn.RemoteAddr().String())
|
||||
})
|
||||
|
||||
if len(ips) > 0 {
|
||||
d, _ := json.MarshalIndent(ips, "", " ")
|
||||
ethutil.WriteFile(path.Join(ethutil.Config.ExecPath, "known_peers.json"), d)
|
||||
}
|
||||
|
||||
eachPeer(s.peers, func(p *Peer, e *list.Element) {
|
||||
p.Stop()
|
||||
})
|
||||
|
||||
close(s.quit)
|
||||
|
||||
if s.RpcServer != nil {
|
||||
s.RpcServer.Stop()
|
||||
}
|
||||
s.txPool.Stop()
|
||||
s.blockPool.Stop()
|
||||
|
||||
loggerger.Infoln("Server stopped")
|
||||
close(s.shutdownChan)
|
||||
}
|
||||
|
||||
// This function will wait for a shutdown and resumes main thread execution
|
||||
func (s *Ethereum) WaitForShutdown() {
|
||||
<-s.shutdownChan
|
||||
}
|
||||
|
||||
func (s *Ethereum) upnpUpdateThread() {
|
||||
// Go off immediately to prevent code duplication, thereafter we renew
|
||||
// lease every 15 minutes.
|
||||
timer := time.NewTimer(5 * time.Minute)
|
||||
lport, _ := strconv.ParseInt(s.Port, 10, 16)
|
||||
first := true
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
var err error
|
||||
_, err = s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60)
|
||||
if err != nil {
|
||||
loggerger.Debugln("can't add UPnP port mapping:", err)
|
||||
break out
|
||||
}
|
||||
if first && err == nil {
|
||||
_, err = s.nat.GetExternalAddress()
|
||||
if err != nil {
|
||||
loggerger.Debugln("UPnP can't get external address:", err)
|
||||
continue out
|
||||
}
|
||||
first = false
|
||||
}
|
||||
timer.Reset(time.Minute * 15)
|
||||
case <-s.quit:
|
||||
break out
|
||||
}
|
||||
}
|
||||
|
||||
timer.Stop()
|
||||
|
||||
if err := s.nat.DeletePortMapping("TCP", int(lport), int(lport)); err != nil {
|
||||
loggerger.Debugln("unable to remove UPnP port mapping:", err)
|
||||
} else {
|
||||
loggerger.Debugln("succesfully disestablished UPnP port mapping")
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Ethereum) update() {
|
||||
upToDateTimer := time.NewTicker(1 * time.Second)
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-upToDateTimer.C:
|
||||
if self.IsUpToDate() && !self.isUpToDate {
|
||||
self.eventMux.Post(ChainSyncEvent{false})
|
||||
self.isUpToDate = true
|
||||
} else if !self.IsUpToDate() && self.isUpToDate {
|
||||
self.eventMux.Post(ChainSyncEvent{true})
|
||||
self.isUpToDate = false
|
||||
}
|
||||
case <-self.quit:
|
||||
break out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InstallFilter adds filter for blockchain events.
|
||||
// The filter's callbacks will run for matching blocks and messages.
|
||||
// The filter should not be modified after it has been installed.
|
||||
func (self *Ethereum) InstallFilter(filter *core.Filter) (id int) {
|
||||
self.filterMu.Lock()
|
||||
id = self.filterId
|
||||
self.filters[id] = filter
|
||||
self.filterId++
|
||||
self.filterMu.Unlock()
|
||||
return id
|
||||
}
|
||||
|
||||
func (self *Ethereum) UninstallFilter(id int) {
|
||||
self.filterMu.Lock()
|
||||
delete(self.filters, id)
|
||||
self.filterMu.Unlock()
|
||||
}
|
||||
|
||||
// GetFilter retrieves a filter installed using InstallFilter.
|
||||
// The filter may not be modified.
|
||||
func (self *Ethereum) GetFilter(id int) *core.Filter {
|
||||
self.filterMu.RLock()
|
||||
defer self.filterMu.RUnlock()
|
||||
return self.filters[id]
|
||||
}
|
||||
|
||||
func (self *Ethereum) filterLoop() {
|
||||
// Subscribe to events
|
||||
events := self.eventMux.Subscribe(core.NewBlockEvent{}, state.Messages(nil))
|
||||
for event := range events.Chan() {
|
||||
switch event := event.(type) {
|
||||
case core.NewBlockEvent:
|
||||
self.filterMu.RLock()
|
||||
for _, filter := range self.filters {
|
||||
if filter.BlockCallback != nil {
|
||||
filter.BlockCallback(event.Block)
|
||||
}
|
||||
}
|
||||
self.filterMu.RUnlock()
|
||||
|
||||
case state.Messages:
|
||||
self.filterMu.RLock()
|
||||
for _, filter := range self.filters {
|
||||
if filter.MessageCallback != nil {
|
||||
msgs := filter.FilterMessages(event)
|
||||
if len(msgs) > 0 {
|
||||
filter.MessageCallback(msgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.filterMu.RUnlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func bootstrapDb(db ethutil.Database) {
|
||||
d, _ := db.Get([]byte("ProtocolVersion"))
|
||||
protov := ethutil.NewValue(d).Uint()
|
||||
|
||||
if protov == 0 {
|
||||
db.Put([]byte("ProtocolVersion"), ethutil.NewValue(ProtocolVersion).Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
func PastPeers() []string {
|
||||
var ips []string
|
||||
data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "known_peers.json"))
|
||||
json.Unmarshal([]byte(data), &ips)
|
||||
|
||||
return ips
|
||||
}
|
|
@ -2,7 +2,6 @@ package ethutil
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
@ -193,8 +192,13 @@ func Encode(object interface{}) []byte {
|
|||
if blen < 56 {
|
||||
buff.WriteByte(byte(blen) + 0xc0)
|
||||
} else {
|
||||
buff.WriteByte(byte(intlen(int64(blen))) + 0xf7)
|
||||
binary.Write(&buff, binary.BigEndian, int64(blen))
|
||||
ilen := byte(intlen(int64(blen)))
|
||||
buff.WriteByte(ilen + 0xf7)
|
||||
t := make([]byte, ilen)
|
||||
for i := byte(0); i < ilen; i++ {
|
||||
t[ilen-i-1] = byte(blen >> (i * 8))
|
||||
}
|
||||
buff.Write(t)
|
||||
}
|
||||
buff.ReadFrom(&b)
|
||||
}
|
||||
|
|
|
@ -397,5 +397,5 @@ func (it *ValueIterator) Value() *Value {
|
|||
}
|
||||
|
||||
func (it *ValueIterator) Idx() int {
|
||||
return it.idx
|
||||
return it.idx - 1
|
||||
}
|
||||
|
|
|
@ -2,19 +2,29 @@ package filter
|
|||
|
||||
type Generic struct {
|
||||
Str1, Str2, Str3 string
|
||||
Data map[string]struct{}
|
||||
|
||||
Fn func(data interface{})
|
||||
}
|
||||
|
||||
// self = registered, f = incoming
|
||||
func (self Generic) Compare(f Filter) bool {
|
||||
var strMatch, dataMatch = true, true
|
||||
|
||||
filter := f.(Generic)
|
||||
if (len(self.Str1) == 0 || filter.Str1 == self.Str1) &&
|
||||
(len(self.Str2) == 0 || filter.Str2 == self.Str2) &&
|
||||
(len(self.Str3) == 0 || filter.Str3 == self.Str3) {
|
||||
return true
|
||||
if (len(self.Str1) > 0 && filter.Str1 != self.Str1) ||
|
||||
(len(self.Str2) > 0 && filter.Str2 != self.Str2) ||
|
||||
(len(self.Str3) > 0 && filter.Str3 != self.Str3) {
|
||||
strMatch = false
|
||||
}
|
||||
|
||||
return false
|
||||
for k, _ := range self.Data {
|
||||
if _, ok := filter.Data[k]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return strMatch && dataMatch
|
||||
}
|
||||
|
||||
func (self Generic) Trigger(data interface{}) {
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
// XXX This is the old filter system specifically for messages. This is till in used and could use some refactoring
|
||||
package filter
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
)
|
||||
|
||||
type FilterManager struct {
|
||||
eventMux *event.TypeMux
|
||||
|
||||
filterMu sync.RWMutex
|
||||
filterId int
|
||||
filters map[int]*core.Filter
|
||||
|
||||
quit chan struct{}
|
||||
}
|
||||
|
||||
func NewFilterManager(mux *event.TypeMux) *FilterManager {
|
||||
return &FilterManager{
|
||||
eventMux: mux,
|
||||
filters: make(map[int]*core.Filter),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *FilterManager) Start() {
|
||||
go self.filterLoop()
|
||||
}
|
||||
|
||||
func (self *FilterManager) Stop() {
|
||||
close(self.quit)
|
||||
}
|
||||
|
||||
func (self *FilterManager) InstallFilter(filter *core.Filter) (id int) {
|
||||
self.filterMu.Lock()
|
||||
id = self.filterId
|
||||
self.filters[id] = filter
|
||||
self.filterId++
|
||||
self.filterMu.Unlock()
|
||||
return id
|
||||
}
|
||||
|
||||
func (self *FilterManager) UninstallFilter(id int) {
|
||||
self.filterMu.Lock()
|
||||
delete(self.filters, id)
|
||||
self.filterMu.Unlock()
|
||||
}
|
||||
|
||||
// GetFilter retrieves a filter installed using InstallFilter.
|
||||
// The filter may not be modified.
|
||||
func (self *FilterManager) GetFilter(id int) *core.Filter {
|
||||
self.filterMu.RLock()
|
||||
defer self.filterMu.RUnlock()
|
||||
return self.filters[id]
|
||||
}
|
||||
|
||||
func (self *FilterManager) filterLoop() {
|
||||
// Subscribe to events
|
||||
events := self.eventMux.Subscribe(core.NewBlockEvent{}, state.Messages(nil))
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-self.quit:
|
||||
break out
|
||||
case event := <-events.Chan():
|
||||
switch event := event.(type) {
|
||||
case core.NewBlockEvent:
|
||||
self.filterMu.RLock()
|
||||
for _, filter := range self.filters {
|
||||
if filter.BlockCallback != nil {
|
||||
filter.BlockCallback(event.Block)
|
||||
}
|
||||
}
|
||||
self.filterMu.RUnlock()
|
||||
|
||||
case state.Messages:
|
||||
self.filterMu.RLock()
|
||||
for _, filter := range self.filters {
|
||||
if filter.MessageCallback != nil {
|
||||
msgs := filter.FilterMessages(event)
|
||||
if len(msgs) > 0 {
|
||||
filter.MessageCallback(msgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.filterMu.RUnlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
events.go
11
events.go
|
@ -1,11 +0,0 @@
|
|||
package eth
|
||||
|
||||
import "container/list"
|
||||
|
||||
type PeerListEvent struct {
|
||||
Peers *list.List
|
||||
}
|
||||
|
||||
type ChainSyncEvent struct {
|
||||
InSync bool
|
||||
}
|
|
@ -13,7 +13,10 @@ for dir in $(find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -type d)
|
|||
do
|
||||
if ls $dir/*.go &> /dev/null; then
|
||||
# echo $dir
|
||||
go test -covermode=count -coverprofile=$dir/profile.tmp $dir
|
||||
if [[ $dir != "./tests/vm" ]]
|
||||
then
|
||||
go test -covermode=count -coverprofile=$dir/profile.tmp $dir
|
||||
fi
|
||||
if [ -f $dir/profile.tmp ]
|
||||
then
|
||||
cat $dir/profile.tmp | tail -n +2 >> profile.cov
|
||||
|
@ -26,4 +29,4 @@ go tool cover -func profile.cov
|
|||
|
||||
# To submit the test coverage result to coveralls.io,
|
||||
# use goveralls (https://github.com/mattn/goveralls)
|
||||
# goveralls -coverprofile=profile.cov -service=travis-ci
|
||||
goveralls -coverprofile=profile.cov -service=travis-ci -repotoken $COVERALLS_TOKEN
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
TEST_DEPS=$(go list -f '{{.Imports}} {{.TestImports}} {{.XTestImports}}' github.com/ethereum/go-ethereum/... | sed -e 's/\[//g' | sed -e 's/\]//g' | sed -e 's/C //g')
|
||||
if [ "$TEST_DEPS" ]; then
|
||||
go get -race $TEST_DEPS
|
||||
fi
|
|
@ -7,10 +7,10 @@ import (
|
|||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
|
@ -203,7 +203,7 @@ func (self *JSRE) addPeer(call otto.FunctionCall) otto.Value {
|
|||
if err != nil {
|
||||
return otto.FalseValue()
|
||||
}
|
||||
self.ethereum.ConnectToPeer(host)
|
||||
self.ethereum.SuggestPeer(host)
|
||||
|
||||
return otto.TrueValue()
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package javascript
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/state"
|
||||
"github.com/ethereum/go-ethereum/ui"
|
||||
|
@ -18,11 +18,11 @@ type JSStateObject struct {
|
|||
|
||||
func (self *JSStateObject) EachStorage(call otto.FunctionCall) otto.Value {
|
||||
cb := call.Argument(0)
|
||||
self.JSObject.EachStorage(func(key string, value *ethutil.Value) {
|
||||
value.Decode()
|
||||
|
||||
cb.Call(self.eth.toVal(self), self.eth.toVal(key), self.eth.toVal(ethutil.Bytes2Hex(value.Bytes())))
|
||||
})
|
||||
it := self.JSObject.Trie().Iterator()
|
||||
for it.Next() {
|
||||
cb.Call(self.eth.toVal(self), self.eth.toVal(ethutil.Bytes2Hex(it.Key)), self.eth.toVal(ethutil.Bytes2Hex(it.Value)))
|
||||
}
|
||||
|
||||
return otto.UndefinedValue()
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ import (
|
|||
"math/big"
|
||||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/pow/ezp"
|
||||
|
@ -36,7 +36,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
)
|
||||
|
||||
type LocalTx struct {
|
||||
|
@ -57,7 +56,7 @@ type Miner struct {
|
|||
eth *eth.Ethereum
|
||||
events event.Subscription
|
||||
|
||||
uncles types.Blocks
|
||||
uncles []*types.Header
|
||||
localTxs map[int]*LocalTx
|
||||
localTxId int
|
||||
|
||||
|
@ -185,15 +184,17 @@ func (self *Miner) mine() {
|
|||
block.SetUncles(self.uncles)
|
||||
}
|
||||
|
||||
parent := chainMan.GetBlock(block.PrevHash)
|
||||
coinbase := block.State().GetOrNewStateObject(block.Coinbase)
|
||||
coinbase.SetGasPool(block.CalcGasLimit(parent))
|
||||
parent := chainMan.GetBlock(block.ParentHash())
|
||||
coinbase := block.State().GetOrNewStateObject(block.Coinbase())
|
||||
coinbase.SetGasPool(core.CalcGasLimit(parent, block))
|
||||
|
||||
transactions := self.finiliseTxs()
|
||||
|
||||
state := block.State()
|
||||
|
||||
// Accumulate all valid transactions and apply them to the new state
|
||||
// Error may be ignored. It's not important during mining
|
||||
receipts, txs, _, erroneous, err := blockManager.ApplyTransactions(coinbase, block.State(), block, transactions, true)
|
||||
receipts, txs, _, erroneous, err := blockManager.ApplyTransactions(coinbase, state, block, transactions, true)
|
||||
if err != nil {
|
||||
minerlogger.Debugln(err)
|
||||
}
|
||||
|
@ -203,21 +204,22 @@ func (self *Miner) mine() {
|
|||
block.SetReceipts(receipts)
|
||||
|
||||
// Accumulate the rewards included for this block
|
||||
blockManager.AccumelateRewards(block.State(), block, parent)
|
||||
blockManager.AccumelateRewards(state, block, parent)
|
||||
|
||||
block.State().Update(ethutil.Big0)
|
||||
state.Update(ethutil.Big0)
|
||||
block.SetRoot(state.Root())
|
||||
|
||||
minerlogger.Infof("Mining on block. Includes %v transactions", len(transactions))
|
||||
|
||||
// Find a valid nonce
|
||||
nonce := self.pow.Search(block, self.powQuitCh)
|
||||
if nonce != nil {
|
||||
block.Nonce = nonce
|
||||
block.Header().Nonce = nonce
|
||||
err := chainMan.InsertChain(types.Blocks{block})
|
||||
if err != nil {
|
||||
minerlogger.Infoln(err)
|
||||
} else {
|
||||
self.eth.Broadcast(wire.MsgBlockTy, []interface{}{block.Value().Val})
|
||||
self.eth.EventMux().Post(core.NewMinedBlockEvent{block})
|
||||
|
||||
minerlogger.Infof("🔨 Mined block %x\n", block.Hash())
|
||||
minerlogger.Infoln(block)
|
||||
|
@ -237,8 +239,8 @@ func (self *Miner) finiliseTxs() types.Transactions {
|
|||
key := self.eth.KeyManager()
|
||||
for i, ltx := range self.localTxs {
|
||||
tx := types.NewTransactionMessage(ltx.To, ethutil.Big(ltx.Value), ethutil.Big(ltx.Gas), ethutil.Big(ltx.GasPrice), ltx.Data)
|
||||
tx.Nonce = state.GetNonce(self.Coinbase)
|
||||
state.SetNonce(self.Coinbase, tx.Nonce+1)
|
||||
tx.SetNonce(state.GetNonce(self.Coinbase))
|
||||
state.SetNonce(self.Coinbase, tx.Nonce()+1)
|
||||
|
||||
tx.Sign(key.PrivateKey())
|
||||
|
||||
|
@ -246,8 +248,8 @@ func (self *Miner) finiliseTxs() types.Transactions {
|
|||
}
|
||||
|
||||
// Faster than append
|
||||
for _, tx := range self.eth.TxPool().CurrentTransactions() {
|
||||
if tx.GasPrice.Cmp(self.MinAcceptedGasPrice) >= 0 {
|
||||
for _, tx := range self.eth.TxPool().GetTransactions() {
|
||||
if tx.GasPrice().Cmp(self.MinAcceptedGasPrice) >= 0 {
|
||||
txs[actualSize] = tx
|
||||
actualSize++
|
||||
}
|
||||
|
|
12
nat.go
12
nat.go
|
@ -1,12 +0,0 @@
|
|||
package eth
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// protocol is either "udp" or "tcp"
|
||||
type NAT interface {
|
||||
GetExternalAddress() (addr net.IP, err error)
|
||||
AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
|
||||
DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
|
||||
}
|
55
natpmp.go
55
natpmp.go
|
@ -1,55 +0,0 @@
|
|||
package eth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
natpmp "github.com/jackpal/go-nat-pmp"
|
||||
)
|
||||
|
||||
// Adapt the NAT-PMP protocol to the NAT interface
|
||||
|
||||
// TODO:
|
||||
// + Register for changes to the external address.
|
||||
// + Re-register port mapping when router reboots.
|
||||
// + A mechanism for keeping a port mapping registered.
|
||||
|
||||
type natPMPClient struct {
|
||||
client *natpmp.Client
|
||||
}
|
||||
|
||||
func NewNatPMP(gateway net.IP) (nat NAT) {
|
||||
return &natPMPClient{natpmp.NewClient(gateway)}
|
||||
}
|
||||
|
||||
func (n *natPMPClient) GetExternalAddress() (addr net.IP, err error) {
|
||||
response, err := n.client.GetExternalAddress()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ip := response.ExternalIPAddress
|
||||
addr = net.IPv4(ip[0], ip[1], ip[2], ip[3])
|
||||
return
|
||||
}
|
||||
|
||||
func (n *natPMPClient) AddPortMapping(protocol string, externalPort, internalPort int,
|
||||
description string, timeout int) (mappedExternalPort int, err error) {
|
||||
if timeout <= 0 {
|
||||
err = fmt.Errorf("timeout must not be <= 0")
|
||||
return
|
||||
}
|
||||
// Note order of port arguments is switched between our AddPortMapping and the client's AddPortMapping.
|
||||
response, err := n.client.AddPortMapping(protocol, internalPort, externalPort, timeout)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mappedExternalPort = int(response.MappedExternalPort)
|
||||
return
|
||||
}
|
||||
|
||||
func (n *natPMPClient) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
|
||||
// To destroy a mapping, send an add-port with
|
||||
// an internalPort of the internal port to destroy, an external port of zero and a time of zero.
|
||||
_, err = n.client.AddPortMapping(protocol, internalPort, 0, 0)
|
||||
return
|
||||
}
|
338
natupnp.go
338
natupnp.go
|
@ -1,338 +0,0 @@
|
|||
package eth
|
||||
|
||||
// Just enough UPnP to be able to forward ports
|
||||
//
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type upnpNAT struct {
|
||||
serviceURL string
|
||||
ourIP string
|
||||
}
|
||||
|
||||
func Discover() (nat NAT, err error) {
|
||||
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
conn, err := net.ListenPacket("udp4", ":0")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
socket := conn.(*net.UDPConn)
|
||||
defer socket.Close()
|
||||
|
||||
err = socket.SetDeadline(time.Now().Add(10 * time.Second))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
st := "ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
|
||||
buf := bytes.NewBufferString(
|
||||
"M-SEARCH * HTTP/1.1\r\n" +
|
||||
"HOST: 239.255.255.250:1900\r\n" +
|
||||
st +
|
||||
"MAN: \"ssdp:discover\"\r\n" +
|
||||
"MX: 2\r\n\r\n")
|
||||
message := buf.Bytes()
|
||||
answerBytes := make([]byte, 1024)
|
||||
for i := 0; i < 3; i++ {
|
||||
_, err = socket.WriteToUDP(message, ssdp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var n int
|
||||
n, _, err = socket.ReadFromUDP(answerBytes)
|
||||
if err != nil {
|
||||
continue
|
||||
// socket.Close()
|
||||
// return
|
||||
}
|
||||
answer := string(answerBytes[0:n])
|
||||
if strings.Index(answer, "\r\n"+st) < 0 {
|
||||
continue
|
||||
}
|
||||
// HTTP header field names are case-insensitive.
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
||||
locString := "\r\nlocation: "
|
||||
answer = strings.ToLower(answer)
|
||||
locIndex := strings.Index(answer, locString)
|
||||
if locIndex < 0 {
|
||||
continue
|
||||
}
|
||||
loc := answer[locIndex+len(locString):]
|
||||
endIndex := strings.Index(loc, "\r\n")
|
||||
if endIndex < 0 {
|
||||
continue
|
||||
}
|
||||
locURL := loc[0:endIndex]
|
||||
var serviceURL string
|
||||
serviceURL, err = getServiceURL(locURL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var ourIP string
|
||||
ourIP, err = getOurIP()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP}
|
||||
return
|
||||
}
|
||||
err = errors.New("UPnP port discovery failed.")
|
||||
return
|
||||
}
|
||||
|
||||
// service represents the Service type in an UPnP xml description.
|
||||
// Only the parts we care about are present and thus the xml may have more
|
||||
// fields than present in the structure.
|
||||
type service struct {
|
||||
ServiceType string `xml:"serviceType"`
|
||||
ControlURL string `xml:"controlURL"`
|
||||
}
|
||||
|
||||
// deviceList represents the deviceList type in an UPnP xml description.
|
||||
// Only the parts we care about are present and thus the xml may have more
|
||||
// fields than present in the structure.
|
||||
type deviceList struct {
|
||||
XMLName xml.Name `xml:"deviceList"`
|
||||
Device []device `xml:"device"`
|
||||
}
|
||||
|
||||
// serviceList represents the serviceList type in an UPnP xml description.
|
||||
// Only the parts we care about are present and thus the xml may have more
|
||||
// fields than present in the structure.
|
||||
type serviceList struct {
|
||||
XMLName xml.Name `xml:"serviceList"`
|
||||
Service []service `xml:"service"`
|
||||
}
|
||||
|
||||
// device represents the device type in an UPnP xml description.
|
||||
// Only the parts we care about are present and thus the xml may have more
|
||||
// fields than present in the structure.
|
||||
type device struct {
|
||||
XMLName xml.Name `xml:"device"`
|
||||
DeviceType string `xml:"deviceType"`
|
||||
DeviceList deviceList `xml:"deviceList"`
|
||||
ServiceList serviceList `xml:"serviceList"`
|
||||
}
|
||||
|
||||
// specVersion represents the specVersion in a UPnP xml description.
|
||||
// Only the parts we care about are present and thus the xml may have more
|
||||
// fields than present in the structure.
|
||||
type specVersion struct {
|
||||
XMLName xml.Name `xml:"specVersion"`
|
||||
Major int `xml:"major"`
|
||||
Minor int `xml:"minor"`
|
||||
}
|
||||
|
||||
// root represents the Root document for a UPnP xml description.
|
||||
// Only the parts we care about are present and thus the xml may have more
|
||||
// fields than present in the structure.
|
||||
type root struct {
|
||||
XMLName xml.Name `xml:"root"`
|
||||
SpecVersion specVersion
|
||||
Device device
|
||||
}
|
||||
|
||||
func getChildDevice(d *device, deviceType string) *device {
|
||||
dl := d.DeviceList.Device
|
||||
for i := 0; i < len(dl); i++ {
|
||||
if dl[i].DeviceType == deviceType {
|
||||
return &dl[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getChildService(d *device, serviceType string) *service {
|
||||
sl := d.ServiceList.Service
|
||||
for i := 0; i < len(sl); i++ {
|
||||
if sl[i].ServiceType == serviceType {
|
||||
return &sl[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getOurIP() (ip string, err error) {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p, err := net.LookupIP(hostname)
|
||||
if err != nil && len(p) > 0 {
|
||||
return
|
||||
}
|
||||
return p[0].String(), nil
|
||||
}
|
||||
|
||||
func getServiceURL(rootURL string) (url string, err error) {
|
||||
r, err := http.Get(rootURL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
if r.StatusCode >= 400 {
|
||||
err = errors.New(string(r.StatusCode))
|
||||
return
|
||||
}
|
||||
var root root
|
||||
err = xml.NewDecoder(r.Body).Decode(&root)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
a := &root.Device
|
||||
if a.DeviceType != "urn:schemas-upnp-org:device:InternetGatewayDevice:1" {
|
||||
err = errors.New("No InternetGatewayDevice")
|
||||
return
|
||||
}
|
||||
b := getChildDevice(a, "urn:schemas-upnp-org:device:WANDevice:1")
|
||||
if b == nil {
|
||||
err = errors.New("No WANDevice")
|
||||
return
|
||||
}
|
||||
c := getChildDevice(b, "urn:schemas-upnp-org:device:WANConnectionDevice:1")
|
||||
if c == nil {
|
||||
err = errors.New("No WANConnectionDevice")
|
||||
return
|
||||
}
|
||||
d := getChildService(c, "urn:schemas-upnp-org:service:WANIPConnection:1")
|
||||
if d == nil {
|
||||
err = errors.New("No WANIPConnection")
|
||||
return
|
||||
}
|
||||
url = combineURL(rootURL, d.ControlURL)
|
||||
return
|
||||
}
|
||||
|
||||
func combineURL(rootURL, subURL string) string {
|
||||
protocolEnd := "://"
|
||||
protoEndIndex := strings.Index(rootURL, protocolEnd)
|
||||
a := rootURL[protoEndIndex+len(protocolEnd):]
|
||||
rootIndex := strings.Index(a, "/")
|
||||
return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
|
||||
}
|
||||
|
||||
func soapRequest(url, function, message string) (r *http.Response, err error) {
|
||||
fullMessage := "<?xml version=\"1.0\" ?>" +
|
||||
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" +
|
||||
"<s:Body>" + message + "</s:Body></s:Envelope>"
|
||||
|
||||
req, err := http.NewRequest("POST", url, strings.NewReader(fullMessage))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "text/xml ; charset=\"utf-8\"")
|
||||
req.Header.Set("User-Agent", "Darwin/10.0.0, UPnP/1.0, MiniUPnPc/1.3")
|
||||
//req.Header.Set("Transfer-Encoding", "chunked")
|
||||
req.Header.Set("SOAPAction", "\"urn:schemas-upnp-org:service:WANIPConnection:1#"+function+"\"")
|
||||
req.Header.Set("Connection", "Close")
|
||||
req.Header.Set("Cache-Control", "no-cache")
|
||||
req.Header.Set("Pragma", "no-cache")
|
||||
|
||||
// log.Stderr("soapRequest ", req)
|
||||
//fmt.Println(fullMessage)
|
||||
|
||||
r, err = http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.Body != nil {
|
||||
defer r.Body.Close()
|
||||
}
|
||||
|
||||
if r.StatusCode >= 400 {
|
||||
// log.Stderr(function, r.StatusCode)
|
||||
err = errors.New("Error " + strconv.Itoa(r.StatusCode) + " for " + function)
|
||||
r = nil
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type statusInfo struct {
|
||||
externalIpAddress string
|
||||
}
|
||||
|
||||
func (n *upnpNAT) getStatusInfo() (info statusInfo, err error) {
|
||||
|
||||
message := "<u:GetStatusInfo xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
|
||||
"</u:GetStatusInfo>"
|
||||
|
||||
var response *http.Response
|
||||
response, err = soapRequest(n.serviceURL, "GetStatusInfo", message)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Write a soap reply parser. It has to eat the Body and envelope tags...
|
||||
|
||||
response.Body.Close()
|
||||
return
|
||||
}
|
||||
|
||||
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
|
||||
info, err := n.getStatusInfo()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
addr = net.ParseIP(info.externalIpAddress)
|
||||
return
|
||||
}
|
||||
|
||||
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
|
||||
// A single concatenation would break ARM compilation.
|
||||
message := "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
|
||||
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort)
|
||||
message += "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>"
|
||||
message += "<NewInternalPort>" + strconv.Itoa(internalPort) + "</NewInternalPort>" +
|
||||
"<NewInternalClient>" + n.ourIP + "</NewInternalClient>" +
|
||||
"<NewEnabled>1</NewEnabled><NewPortMappingDescription>"
|
||||
message += description +
|
||||
"</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) +
|
||||
"</NewLeaseDuration></u:AddPortMapping>"
|
||||
|
||||
var response *http.Response
|
||||
response, err = soapRequest(n.serviceURL, "AddPortMapping", message)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: check response to see if the port was forwarded
|
||||
// log.Println(message, response)
|
||||
mappedExternalPort = externalPort
|
||||
_ = response
|
||||
return
|
||||
}
|
||||
|
||||
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
|
||||
|
||||
message := "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
|
||||
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort) +
|
||||
"</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>" +
|
||||
"</u:DeletePortMapping>"
|
||||
|
||||
var response *http.Response
|
||||
response, err = soapRequest(n.serviceURL, "DeletePortMapping", message)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: check response to see if the port was deleted
|
||||
// log.Println(message, response)
|
||||
_ = response
|
||||
return
|
||||
}
|
|
@ -246,12 +246,7 @@ func (srv *Server) Stop() {
|
|||
|
||||
func (srv *Server) discLoop() {
|
||||
for peer := range srv.peerDisconnect {
|
||||
// peer has just disconnected. free up its slot.
|
||||
srvlog.Infof("%v is gone", peer)
|
||||
srv.peerSlots <- peer.slot
|
||||
srv.lock.Lock()
|
||||
srv.peers[peer.slot] = nil
|
||||
srv.lock.Unlock()
|
||||
srv.removePeer(peer)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -384,7 +379,7 @@ func (srv *Server) addPeer(conn net.Conn, desc *peerAddr, slot int) *Peer {
|
|||
func (srv *Server) removePeer(peer *Peer) {
|
||||
srv.lock.Lock()
|
||||
defer srv.lock.Unlock()
|
||||
srvlog.Debugf("Removing peer %v %v (slot %v)\n", peer, peer.slot)
|
||||
srvlog.Debugf("Removing %v (slot %v)\n", peer, peer.slot)
|
||||
if srv.peers[peer.slot] != peer {
|
||||
srvlog.Warnln("Invalid peer to remove:", peer)
|
||||
return
|
||||
|
@ -416,6 +411,7 @@ func (srv *Server) verifyPeer(addr *peerAddr) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// TODO replace with "Set"
|
||||
type Blacklist interface {
|
||||
Get([]byte) (bool, error)
|
||||
Put([]byte) error
|
||||
|
|
881
peer.go
881
peer.go
|
@ -1,881 +0,0 @@
|
|||
package eth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/wire"
|
||||
)
|
||||
|
||||
var peerlogger = logger.NewLogger("PEER")
|
||||
|
||||
const (
|
||||
// The size of the output buffer for writing messages
|
||||
outputBufferSize = 50
|
||||
// Current protocol version
|
||||
ProtocolVersion = 49
|
||||
// Current P2P version
|
||||
P2PVersion = 2
|
||||
// Ethereum network version
|
||||
NetVersion = 0
|
||||
// Interval for ping/pong message
|
||||
pingPongTimer = 2 * time.Second
|
||||
)
|
||||
|
||||
type DiscReason byte
|
||||
|
||||
const (
|
||||
// Values are given explicitly instead of by iota because these values are
|
||||
// defined by the wire protocol spec; it is easier for humans to ensure
|
||||
// correctness when values are explicit.
|
||||
DiscRequested DiscReason = iota
|
||||
DiscReTcpSysErr
|
||||
DiscBadProto
|
||||
DiscBadPeer
|
||||
DiscTooManyPeers
|
||||
DiscConnDup
|
||||
DiscGenesisErr
|
||||
DiscProtoErr
|
||||
DiscQuitting
|
||||
)
|
||||
|
||||
var discReasonToString = []string{
|
||||
"requested",
|
||||
"TCP sys error",
|
||||
"bad protocol",
|
||||
"useless peer",
|
||||
"too many peers",
|
||||
"already connected",
|
||||
"wrong genesis block",
|
||||
"incompatible network",
|
||||
"quitting",
|
||||
}
|
||||
|
||||
func (d DiscReason) String() string {
|
||||
if len(discReasonToString) < int(d) {
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
return discReasonToString[d]
|
||||
}
|
||||
|
||||
// Peer capabilities
|
||||
type Caps byte
|
||||
|
||||
const (
|
||||
CapPeerDiscTy Caps = 1 << iota
|
||||
CapTxTy
|
||||
CapChainTy
|
||||
|
||||
CapDefault = CapChainTy | CapTxTy | CapPeerDiscTy
|
||||
)
|
||||
|
||||
var capsToString = map[Caps]string{
|
||||
CapPeerDiscTy: "Peer discovery",
|
||||
CapTxTy: "Transaction relaying",
|
||||
CapChainTy: "Block chain relaying",
|
||||
}
|
||||
|
||||
func (c Caps) IsCap(cap Caps) bool {
|
||||
return c&cap > 0
|
||||
}
|
||||
|
||||
func (c Caps) String() string {
|
||||
var caps []string
|
||||
if c.IsCap(CapPeerDiscTy) {
|
||||
caps = append(caps, capsToString[CapPeerDiscTy])
|
||||
}
|
||||
if c.IsCap(CapChainTy) {
|
||||
caps = append(caps, capsToString[CapChainTy])
|
||||
}
|
||||
if c.IsCap(CapTxTy) {
|
||||
caps = append(caps, capsToString[CapTxTy])
|
||||
}
|
||||
|
||||
return strings.Join(caps, " | ")
|
||||
}
|
||||
|
||||
type Peer struct {
|
||||
// Ethereum interface
|
||||
ethereum *Ethereum
|
||||
// Net connection
|
||||
conn net.Conn
|
||||
// Output queue which is used to communicate and handle messages
|
||||
outputQueue chan *wire.Msg
|
||||
// Quit channel
|
||||
quit chan bool
|
||||
// Determines whether it's an inbound or outbound peer
|
||||
inbound bool
|
||||
// Flag for checking the peer's connectivity state
|
||||
connected int32
|
||||
disconnect int32
|
||||
// Last known message send
|
||||
lastSend time.Time
|
||||
// Indicated whether a verack has been send or not
|
||||
// This flag is used by writeMessage to check if messages are allowed
|
||||
// to be send or not. If no version is known all messages are ignored.
|
||||
versionKnown bool
|
||||
statusKnown bool
|
||||
|
||||
// Last received pong message
|
||||
lastPong int64
|
||||
lastBlockReceived time.Time
|
||||
doneFetchingHashes bool
|
||||
lastHashAt time.Time
|
||||
lastHashRequestedAt time.Time
|
||||
|
||||
host []byte
|
||||
port uint16
|
||||
caps Caps
|
||||
td *big.Int
|
||||
bestHash []byte
|
||||
lastReceivedHash []byte
|
||||
requestedHashes [][]byte
|
||||
|
||||
// This peer's public key
|
||||
pubkey []byte
|
||||
|
||||
// Indicated whether the node is catching up or not
|
||||
catchingUp bool
|
||||
diverted bool
|
||||
blocksRequested int
|
||||
|
||||
version string
|
||||
|
||||
// We use this to give some kind of pingtime to a node, not very accurate, could be improved.
|
||||
pingTime time.Duration
|
||||
pingStartTime time.Time
|
||||
|
||||
lastRequestedBlock *types.Block
|
||||
|
||||
protocolCaps *ethutil.Value
|
||||
}
|
||||
|
||||
func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer {
|
||||
pubkey := ethereum.KeyManager().PublicKey()[1:]
|
||||
|
||||
return &Peer{
|
||||
outputQueue: make(chan *wire.Msg, outputBufferSize),
|
||||
quit: make(chan bool),
|
||||
ethereum: ethereum,
|
||||
conn: conn,
|
||||
inbound: inbound,
|
||||
disconnect: 0,
|
||||
connected: 1,
|
||||
port: 30303,
|
||||
pubkey: pubkey,
|
||||
blocksRequested: 10,
|
||||
caps: ethereum.ServerCaps(),
|
||||
version: ethereum.ClientIdentity().String(),
|
||||
protocolCaps: ethutil.NewValue(nil),
|
||||
td: big.NewInt(0),
|
||||
doneFetchingHashes: true,
|
||||
}
|
||||
}
|
||||
|
||||
func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
|
||||
p := &Peer{
|
||||
outputQueue: make(chan *wire.Msg, outputBufferSize),
|
||||
quit: make(chan bool),
|
||||
ethereum: ethereum,
|
||||
inbound: false,
|
||||
connected: 0,
|
||||
disconnect: 0,
|
||||
port: 30303,
|
||||
caps: caps,
|
||||
version: ethereum.ClientIdentity().String(),
|
||||
protocolCaps: ethutil.NewValue(nil),
|
||||
td: big.NewInt(0),
|
||||
doneFetchingHashes: true,
|
||||
}
|
||||
|
||||
// Set up the connection in another goroutine so we don't block the main thread
|
||||
go func() {
|
||||
conn, err := p.Connect(addr)
|
||||
if err != nil {
|
||||
//peerlogger.Debugln("Connection to peer failed. Giving up.", err)
|
||||
p.Stop()
|
||||
return
|
||||
}
|
||||
p.conn = conn
|
||||
|
||||
// Atomically set the connection state
|
||||
atomic.StoreInt32(&p.connected, 1)
|
||||
atomic.StoreInt32(&p.disconnect, 0)
|
||||
|
||||
p.Start()
|
||||
}()
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (self *Peer) Connect(addr string) (conn net.Conn, err error) {
|
||||
const maxTries = 3
|
||||
for attempts := 0; attempts < maxTries; attempts++ {
|
||||
conn, err = net.DialTimeout("tcp", addr, 10*time.Second)
|
||||
if err != nil {
|
||||
time.Sleep(time.Duration(attempts*20) * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// Success
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Getters
|
||||
func (p *Peer) PingTime() string {
|
||||
return p.pingTime.String()
|
||||
}
|
||||
func (p *Peer) Inbound() bool {
|
||||
return p.inbound
|
||||
}
|
||||
func (p *Peer) LastSend() time.Time {
|
||||
return p.lastSend
|
||||
}
|
||||
func (p *Peer) LastPong() int64 {
|
||||
return p.lastPong
|
||||
}
|
||||
func (p *Peer) Host() []byte {
|
||||
return p.host
|
||||
}
|
||||
func (p *Peer) Port() uint16 {
|
||||
return p.port
|
||||
}
|
||||
func (p *Peer) Version() string {
|
||||
return p.version
|
||||
}
|
||||
func (p *Peer) Connected() *int32 {
|
||||
return &p.connected
|
||||
}
|
||||
|
||||
// Setters
|
||||
func (p *Peer) SetVersion(version string) {
|
||||
p.version = version
|
||||
}
|
||||
|
||||
// Outputs any RLP encoded data to the peer
|
||||
func (p *Peer) QueueMessage(msg *wire.Msg) {
|
||||
if atomic.LoadInt32(&p.connected) != 1 {
|
||||
return
|
||||
}
|
||||
p.outputQueue <- msg
|
||||
}
|
||||
|
||||
func (p *Peer) writeMessage(msg *wire.Msg) {
|
||||
// Ignore the write if we're not connected
|
||||
if atomic.LoadInt32(&p.connected) != 1 {
|
||||
return
|
||||
}
|
||||
|
||||
if !p.versionKnown {
|
||||
switch msg.Type {
|
||||
case wire.MsgHandshakeTy: // Ok
|
||||
default: // Anything but ack is allowed
|
||||
return
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
if !p.statusKnown {
|
||||
switch msg.Type {
|
||||
case wire.MsgStatusTy: // Ok
|
||||
default: // Anything but ack is allowed
|
||||
return
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
peerlogger.DebugDetailf("(%v) <= %v\n", p.conn.RemoteAddr(), formatMessage(msg))
|
||||
|
||||
err := wire.WriteMessage(p.conn, msg)
|
||||
if err != nil {
|
||||
peerlogger.Debugln(" Can't send message:", err)
|
||||
// Stop the client if there was an error writing to it
|
||||
p.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Outbound message handler. Outbound messages are handled here
|
||||
func (p *Peer) HandleOutbound() {
|
||||
// The ping timer. Makes sure that every 2 minutes a ping is send to the peer
|
||||
pingTimer := time.NewTicker(pingPongTimer)
|
||||
serviceTimer := time.NewTicker(10 * time.Second)
|
||||
|
||||
out:
|
||||
for {
|
||||
skip:
|
||||
select {
|
||||
// Main message queue. All outbound messages are processed through here
|
||||
case msg := <-p.outputQueue:
|
||||
if !p.statusKnown {
|
||||
switch msg.Type {
|
||||
case wire.MsgTxTy, wire.MsgGetBlockHashesTy, wire.MsgBlockHashesTy, wire.MsgGetBlocksTy, wire.MsgBlockTy:
|
||||
break skip
|
||||
}
|
||||
}
|
||||
|
||||
switch msg.Type {
|
||||
case wire.MsgGetBlockHashesTy:
|
||||
p.lastHashRequestedAt = time.Now()
|
||||
}
|
||||
|
||||
p.writeMessage(msg)
|
||||
p.lastSend = time.Now()
|
||||
|
||||
// Ping timer
|
||||
case <-pingTimer.C:
|
||||
p.writeMessage(wire.NewMessage(wire.MsgPingTy, ""))
|
||||
p.pingStartTime = time.Now()
|
||||
|
||||
// Service timer takes care of peer broadcasting, transaction
|
||||
// posting or block posting
|
||||
case <-serviceTimer.C:
|
||||
p.QueueMessage(wire.NewMessage(wire.MsgGetPeersTy, ""))
|
||||
|
||||
case <-p.quit:
|
||||
// Break out of the for loop if a quit message is posted
|
||||
break out
|
||||
}
|
||||
}
|
||||
|
||||
clean:
|
||||
// This loop is for draining the output queue and anybody waiting for us
|
||||
for {
|
||||
select {
|
||||
case <-p.outputQueue:
|
||||
// TODO
|
||||
default:
|
||||
break clean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func formatMessage(msg *wire.Msg) (ret string) {
|
||||
ret = fmt.Sprintf("%v %v", msg.Type, msg.Data)
|
||||
|
||||
/*
|
||||
XXX Commented out because I need the log level here to determine
|
||||
if i should or shouldn't generate this message
|
||||
*/
|
||||
/*
|
||||
switch msg.Type {
|
||||
case wire.MsgPeersTy:
|
||||
ret += fmt.Sprintf("(%d entries)", msg.Data.Len())
|
||||
case wire.MsgBlockTy:
|
||||
b1, b2 := chain.NewBlockFromRlpValue(msg.Data.Get(0)), ethchain.NewBlockFromRlpValue(msg.Data.Get(msg.Data.Len()-1))
|
||||
ret += fmt.Sprintf("(%d entries) %x - %x", msg.Data.Len(), b1.Hash()[0:4], b2.Hash()[0:4])
|
||||
case wire.MsgBlockHashesTy:
|
||||
h1, h2 := msg.Data.Get(0).Bytes(), msg.Data.Get(msg.Data.Len()-1).Bytes()
|
||||
ret += fmt.Sprintf("(%d entries) %x - %x", msg.Data.Len(), h1, h2)
|
||||
}
|
||||
*/
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Inbound handler. Inbound messages are received here and passed to the appropriate methods
|
||||
func (p *Peer) HandleInbound() {
|
||||
for atomic.LoadInt32(&p.disconnect) == 0 {
|
||||
|
||||
// HMM?
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
// Wait for a message from the peer
|
||||
msgs, err := wire.ReadMessages(p.conn)
|
||||
if err != nil {
|
||||
peerlogger.Debugln(err)
|
||||
}
|
||||
for _, msg := range msgs {
|
||||
peerlogger.DebugDetailf("(%v) => %v\n", p.conn.RemoteAddr(), formatMessage(msg))
|
||||
|
||||
switch msg.Type {
|
||||
case wire.MsgHandshakeTy:
|
||||
// Version message
|
||||
p.handleHandshake(msg)
|
||||
|
||||
//if p.caps.IsCap(CapPeerDiscTy) {
|
||||
p.QueueMessage(wire.NewMessage(wire.MsgGetPeersTy, ""))
|
||||
//}
|
||||
|
||||
case wire.MsgDiscTy:
|
||||
p.Stop()
|
||||
peerlogger.Infoln("Disconnect peer: ", DiscReason(msg.Data.Get(0).Uint()))
|
||||
case wire.MsgPingTy:
|
||||
// Respond back with pong
|
||||
p.QueueMessage(wire.NewMessage(wire.MsgPongTy, ""))
|
||||
case wire.MsgPongTy:
|
||||
// If we received a pong back from a peer we set the
|
||||
// last pong so the peer handler knows this peer is still
|
||||
// active.
|
||||
p.lastPong = time.Now().Unix()
|
||||
p.pingTime = time.Since(p.pingStartTime)
|
||||
case wire.MsgTxTy:
|
||||
// If the message was a transaction queue the transaction
|
||||
// in the TxPool where it will undergo validation and
|
||||
// processing when a new block is found
|
||||
for i := 0; i < msg.Data.Len(); i++ {
|
||||
tx := types.NewTransactionFromValue(msg.Data.Get(i))
|
||||
err := p.ethereum.TxPool().Add(tx)
|
||||
if err != nil {
|
||||
peerlogger.Infoln(err)
|
||||
} else {
|
||||
peerlogger.Infof("tx OK (%x)\n", tx.Hash()[0:4])
|
||||
}
|
||||
}
|
||||
case wire.MsgGetPeersTy:
|
||||
// Peer asked for list of connected peers
|
||||
//p.pushPeers()
|
||||
case wire.MsgPeersTy:
|
||||
// Received a list of peers (probably because MsgGetPeersTy was send)
|
||||
data := msg.Data
|
||||
// Create new list of possible peers for the ethereum to process
|
||||
peers := make([]string, data.Len())
|
||||
// Parse each possible peer
|
||||
for i := 0; i < data.Len(); i++ {
|
||||
value := data.Get(i)
|
||||
peers[i] = unpackAddr(value.Get(0), value.Get(1).Uint())
|
||||
}
|
||||
|
||||
// Connect to the list of peers
|
||||
p.ethereum.ProcessPeerList(peers)
|
||||
|
||||
case wire.MsgStatusTy:
|
||||
// Handle peer's status msg
|
||||
p.handleStatus(msg)
|
||||
}
|
||||
|
||||
// TMP
|
||||
if p.statusKnown {
|
||||
switch msg.Type {
|
||||
|
||||
case wire.MsgGetBlockHashesTy:
|
||||
if msg.Data.Len() < 2 {
|
||||
peerlogger.Debugln("err: argument length invalid ", msg.Data.Len())
|
||||
}
|
||||
|
||||
hash := msg.Data.Get(0).Bytes()
|
||||
amount := msg.Data.Get(1).Uint()
|
||||
|
||||
hashes := p.ethereum.ChainManager().GetChainHashesFromHash(hash, amount)
|
||||
|
||||
p.QueueMessage(wire.NewMessage(wire.MsgBlockHashesTy, ethutil.ByteSliceToInterface(hashes)))
|
||||
|
||||
case wire.MsgGetBlocksTy:
|
||||
// Limit to max 300 blocks
|
||||
max := int(math.Min(float64(msg.Data.Len()), 300.0))
|
||||
var blocks []interface{}
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
hash := msg.Data.Get(i).Bytes()
|
||||
block := p.ethereum.ChainManager().GetBlock(hash)
|
||||
if block != nil {
|
||||
blocks = append(blocks, block.Value().Raw())
|
||||
}
|
||||
}
|
||||
|
||||
p.QueueMessage(wire.NewMessage(wire.MsgBlockTy, blocks))
|
||||
|
||||
case wire.MsgBlockHashesTy:
|
||||
p.catchingUp = true
|
||||
|
||||
blockPool := p.ethereum.blockPool
|
||||
|
||||
foundCommonHash := false
|
||||
p.lastHashAt = time.Now()
|
||||
|
||||
it := msg.Data.NewIterator()
|
||||
for it.Next() {
|
||||
hash := it.Value().Bytes()
|
||||
p.lastReceivedHash = hash
|
||||
|
||||
if blockPool.HasCommonHash(hash) {
|
||||
foundCommonHash = true
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
blockPool.AddHash(hash, p)
|
||||
}
|
||||
|
||||
if !foundCommonHash {
|
||||
p.FetchHashes()
|
||||
} else {
|
||||
peerlogger.Infof("Found common hash (%x...)\n", p.lastReceivedHash[0:4])
|
||||
p.doneFetchingHashes = true
|
||||
}
|
||||
|
||||
case wire.MsgBlockTy:
|
||||
p.catchingUp = true
|
||||
|
||||
blockPool := p.ethereum.blockPool
|
||||
|
||||
it := msg.Data.NewIterator()
|
||||
for it.Next() {
|
||||
block := types.NewBlockFromRlpValue(it.Value())
|
||||
blockPool.Add(block, p)
|
||||
|
||||
p.lastBlockReceived = time.Now()
|
||||
}
|
||||
case wire.MsgNewBlockTy:
|
||||
var (
|
||||
blockPool = p.ethereum.blockPool
|
||||
block = types.NewBlockFromRlpValue(msg.Data.Get(0))
|
||||
td = msg.Data.Get(1).BigInt()
|
||||
)
|
||||
|
||||
if td.Cmp(blockPool.td) > 0 {
|
||||
p.ethereum.blockPool.AddNew(block, p)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.Stop()
|
||||
}
|
||||
|
||||
func (self *Peer) FetchBlocks(hashes [][]byte) {
|
||||
if len(hashes) > 0 {
|
||||
peerlogger.Debugf("Fetching blocks (%d)\n", len(hashes))
|
||||
|
||||
self.QueueMessage(wire.NewMessage(wire.MsgGetBlocksTy, ethutil.ByteSliceToInterface(hashes)))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Peer) FetchHashes() bool {
|
||||
blockPool := self.ethereum.blockPool
|
||||
|
||||
return blockPool.FetchHashes(self)
|
||||
}
|
||||
|
||||
func (self *Peer) FetchingHashes() bool {
|
||||
return !self.doneFetchingHashes
|
||||
}
|
||||
|
||||
// General update method
|
||||
func (self *Peer) update() {
|
||||
serviceTimer := time.NewTicker(100 * time.Millisecond)
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case <-serviceTimer.C:
|
||||
if self.IsCap("eth") {
|
||||
var (
|
||||
sinceBlock = time.Since(self.lastBlockReceived)
|
||||
)
|
||||
|
||||
if sinceBlock > 5*time.Second {
|
||||
self.catchingUp = false
|
||||
}
|
||||
}
|
||||
case <-self.quit:
|
||||
break out
|
||||
}
|
||||
}
|
||||
|
||||
serviceTimer.Stop()
|
||||
}
|
||||
|
||||
func (p *Peer) Start() {
|
||||
peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String())
|
||||
servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
|
||||
|
||||
if p.inbound {
|
||||
p.host, p.port = packAddr(peerHost, peerPort)
|
||||
} else {
|
||||
p.host, p.port = packAddr(servHost, servPort)
|
||||
}
|
||||
|
||||
err := p.pushHandshake()
|
||||
if err != nil {
|
||||
peerlogger.Debugln("Peer can't send outbound version ack", err)
|
||||
|
||||
p.Stop()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
go p.HandleOutbound()
|
||||
// Run the inbound handler in a new goroutine
|
||||
go p.HandleInbound()
|
||||
// Run the general update handler
|
||||
go p.update()
|
||||
|
||||
// Wait a few seconds for startup and then ask for an initial ping
|
||||
time.Sleep(2 * time.Second)
|
||||
p.writeMessage(wire.NewMessage(wire.MsgPingTy, ""))
|
||||
p.pingStartTime = time.Now()
|
||||
|
||||
}
|
||||
|
||||
func (p *Peer) Stop() {
|
||||
p.StopWithReason(DiscRequested)
|
||||
}
|
||||
|
||||
func (p *Peer) StopWithReason(reason DiscReason) {
|
||||
if atomic.AddInt32(&p.disconnect, 1) != 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// Pre-emptively remove the peer; don't wait for reaping. We already know it's dead if we are here
|
||||
p.ethereum.RemovePeer(p)
|
||||
|
||||
close(p.quit)
|
||||
if atomic.LoadInt32(&p.connected) != 0 {
|
||||
p.writeMessage(wire.NewMessage(wire.MsgDiscTy, reason))
|
||||
p.conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Peer) peersMessage() *wire.Msg {
|
||||
outPeers := make([]interface{}, len(p.ethereum.InOutPeers()))
|
||||
// Serialise each peer
|
||||
for i, peer := range p.ethereum.InOutPeers() {
|
||||
// Don't return localhost as valid peer
|
||||
if !net.ParseIP(peer.conn.RemoteAddr().String()).IsLoopback() {
|
||||
outPeers[i] = peer.RlpData()
|
||||
}
|
||||
}
|
||||
|
||||
// Return the message to the peer with the known list of connected clients
|
||||
return wire.NewMessage(wire.MsgPeersTy, outPeers)
|
||||
}
|
||||
|
||||
// Pushes the list of outbound peers to the client when requested
|
||||
func (p *Peer) pushPeers() {
|
||||
p.QueueMessage(p.peersMessage())
|
||||
}
|
||||
|
||||
func (self *Peer) pushStatus() {
|
||||
msg := wire.NewMessage(wire.MsgStatusTy, []interface{}{
|
||||
uint32(ProtocolVersion),
|
||||
uint32(NetVersion),
|
||||
self.ethereum.ChainManager().TD,
|
||||
self.ethereum.ChainManager().CurrentBlock.Hash(),
|
||||
self.ethereum.ChainManager().Genesis().Hash(),
|
||||
})
|
||||
|
||||
self.QueueMessage(msg)
|
||||
}
|
||||
|
||||
func (self *Peer) handleStatus(msg *wire.Msg) {
|
||||
c := msg.Data
|
||||
|
||||
var (
|
||||
//protoVersion = c.Get(0).Uint()
|
||||
netVersion = c.Get(1).Uint()
|
||||
td = c.Get(2).BigInt()
|
||||
bestHash = c.Get(3).Bytes()
|
||||
genesis = c.Get(4).Bytes()
|
||||
)
|
||||
|
||||
if bytes.Compare(self.ethereum.ChainManager().Genesis().Hash(), genesis) != 0 {
|
||||
loggerger.Warnf("Invalid genisis hash %x. Disabling [eth]\n", genesis)
|
||||
return
|
||||
}
|
||||
|
||||
if netVersion != NetVersion {
|
||||
loggerger.Warnf("Invalid network version %d. Disabling [eth]\n", netVersion)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
if protoVersion != ProtocolVersion {
|
||||
loggerger.Warnf("Invalid protocol version %d. Disabling [eth]\n", protoVersion)
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
// Get the td and last hash
|
||||
self.td = td
|
||||
self.bestHash = bestHash
|
||||
self.lastReceivedHash = bestHash
|
||||
|
||||
self.statusKnown = true
|
||||
|
||||
// Compare the total TD with the blockchain TD. If remote is higher
|
||||
// fetch hashes from highest TD node.
|
||||
self.FetchHashes()
|
||||
|
||||
loggerger.Infof("Peer is [eth] capable. (TD = %v ~ %x)", self.td, self.bestHash)
|
||||
|
||||
}
|
||||
|
||||
func (p *Peer) pushHandshake() error {
|
||||
pubkey := p.ethereum.KeyManager().PublicKey()
|
||||
msg := wire.NewMessage(wire.MsgHandshakeTy, []interface{}{
|
||||
P2PVersion, []byte(p.version), []interface{}{[]interface{}{"eth", ProtocolVersion}}, p.port, pubkey[1:],
|
||||
})
|
||||
|
||||
p.QueueMessage(msg)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Peer) handleHandshake(msg *wire.Msg) {
|
||||
c := msg.Data
|
||||
|
||||
var (
|
||||
p2pVersion = c.Get(0).Uint()
|
||||
clientId = c.Get(1).Str()
|
||||
caps = c.Get(2)
|
||||
port = c.Get(3).Uint()
|
||||
pub = c.Get(4).Bytes()
|
||||
)
|
||||
|
||||
// Check correctness of p2p protocol version
|
||||
if p2pVersion != P2PVersion {
|
||||
peerlogger.Debugf("Invalid P2P version. Require protocol %d, received %d\n", P2PVersion, p2pVersion)
|
||||
p.Stop()
|
||||
return
|
||||
}
|
||||
|
||||
// Handle the pub key (validation, uniqueness)
|
||||
if len(pub) == 0 {
|
||||
peerlogger.Warnln("Pubkey required, not supplied in handshake.")
|
||||
p.Stop()
|
||||
return
|
||||
}
|
||||
|
||||
// Self connect detection
|
||||
pubkey := p.ethereum.KeyManager().PublicKey()
|
||||
if bytes.Compare(pubkey[1:], pub) == 0 {
|
||||
p.Stop()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Check for blacklisting
|
||||
for _, pk := range p.ethereum.blacklist {
|
||||
if bytes.Compare(pk, pub) == 0 {
|
||||
peerlogger.Debugf("Blacklisted peer tried to connect (%x...)\n", pubkey[0:4])
|
||||
p.StopWithReason(DiscBadPeer)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
usedPub := 0
|
||||
// This peer is already added to the peerlist so we expect to find a double pubkey at least once
|
||||
eachPeer(p.ethereum.Peers(), func(peer *Peer, e *list.Element) {
|
||||
if bytes.Compare(pub, peer.pubkey) == 0 {
|
||||
usedPub++
|
||||
}
|
||||
})
|
||||
|
||||
if usedPub > 0 {
|
||||
peerlogger.Debugf("Pubkey %x found more then once. Already connected to client.", p.pubkey)
|
||||
p.Stop()
|
||||
return
|
||||
}
|
||||
p.pubkey = pub
|
||||
|
||||
// If this is an inbound connection send an ack back
|
||||
if p.inbound {
|
||||
p.port = uint16(port)
|
||||
}
|
||||
|
||||
p.SetVersion(clientId)
|
||||
|
||||
p.versionKnown = true
|
||||
|
||||
p.ethereum.PushPeer(p)
|
||||
p.ethereum.eventMux.Post(PeerListEvent{p.ethereum.Peers()})
|
||||
|
||||
p.protocolCaps = caps
|
||||
|
||||
it := caps.NewIterator()
|
||||
var capsStrs []string
|
||||
for it.Next() {
|
||||
cap := it.Value().Get(0).Str()
|
||||
ver := it.Value().Get(1).Uint()
|
||||
switch cap {
|
||||
case "eth":
|
||||
if ver != ProtocolVersion {
|
||||
loggerger.Warnf("Invalid protocol version %d. Disabling [eth]\n", ver)
|
||||
continue
|
||||
}
|
||||
p.pushStatus()
|
||||
}
|
||||
|
||||
capsStrs = append(capsStrs, fmt.Sprintf("%s/%d", cap, ver))
|
||||
}
|
||||
|
||||
peerlogger.Infof("Added peer (%s) %d / %d (%v)\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers, capsStrs)
|
||||
|
||||
peerlogger.Debugln(p)
|
||||
}
|
||||
|
||||
func (self *Peer) IsCap(cap string) bool {
|
||||
capsIt := self.protocolCaps.NewIterator()
|
||||
for capsIt.Next() {
|
||||
if capsIt.Value().Str() == cap {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *Peer) Caps() *ethutil.Value {
|
||||
return self.protocolCaps
|
||||
}
|
||||
|
||||
func (p *Peer) String() string {
|
||||
var strBoundType string
|
||||
if p.inbound {
|
||||
strBoundType = "inbound"
|
||||
} else {
|
||||
strBoundType = "outbound"
|
||||
}
|
||||
var strConnectType string
|
||||
if atomic.LoadInt32(&p.disconnect) == 0 {
|
||||
strConnectType = "connected"
|
||||
} else {
|
||||
strConnectType = "disconnected"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[%s] (%s) %v %s", strConnectType, strBoundType, p.conn.RemoteAddr(), p.version)
|
||||
|
||||
}
|
||||
|
||||
func (p *Peer) RlpData() []interface{} {
|
||||
return []interface{}{p.host, p.port, p.pubkey}
|
||||
}
|
||||
|
||||
func packAddr(address, _port string) (host []byte, port uint16) {
|
||||
p, _ := strconv.Atoi(_port)
|
||||
port = uint16(p)
|
||||
|
||||
h := net.ParseIP(address)
|
||||
if ip := h.To4(); ip != nil {
|
||||
host = []byte(ip)
|
||||
} else {
|
||||
host = []byte(h)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func unpackAddr(value *ethutil.Value, p uint64) string {
|
||||
host, _ := net.IP(value.Bytes()).MarshalText()
|
||||
prt := strconv.Itoa(int(p))
|
||||
|
||||
return net.JoinHostPort(string(host), prt)
|
||||
}
|
|
@ -3,7 +3,7 @@ package pow
|
|||
import "math/big"
|
||||
|
||||
type Block interface {
|
||||
Diff() *big.Int
|
||||
Difficulty() *big.Int
|
||||
HashNoNonce() []byte
|
||||
N() []byte
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ type EasyPow struct {
|
|||
}
|
||||
|
||||
func New() *EasyPow {
|
||||
return &EasyPow{}
|
||||
return &EasyPow{turbo: true}
|
||||
}
|
||||
|
||||
func (pow *EasyPow) GetHashrate() int64 {
|
||||
|
@ -35,7 +35,7 @@ func (pow *EasyPow) Turbo(on bool) {
|
|||
func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
hash := block.HashNoNonce()
|
||||
diff := block.Diff()
|
||||
diff := block.Difficulty()
|
||||
i := int64(0)
|
||||
start := time.Now().UnixNano()
|
||||
t := time.Now()
|
||||
|
@ -59,7 +59,7 @@ func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
|
|||
}
|
||||
|
||||
sha := crypto.Sha3(big.NewInt(r.Int63()).Bytes())
|
||||
if pow.verify(hash, diff, sha) {
|
||||
if verify(hash, diff, sha) {
|
||||
return sha
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,11 @@ func (pow *EasyPow) Search(block pow.Block, stop <-chan struct{}) []byte {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (pow *EasyPow) verify(hash []byte, diff *big.Int, nonce []byte) bool {
|
||||
func (pow *EasyPow) Verify(block pow.Block) bool {
|
||||
return Verify(block)
|
||||
}
|
||||
|
||||
func verify(hash []byte, diff *big.Int, nonce []byte) bool {
|
||||
sha := sha3.NewKeccak256()
|
||||
|
||||
d := append(hash, nonce...)
|
||||
|
@ -84,6 +88,6 @@ func (pow *EasyPow) verify(hash []byte, diff *big.Int, nonce []byte) bool {
|
|||
return res.Cmp(verification) <= 0
|
||||
}
|
||||
|
||||
func (pow *EasyPow) Verify(block pow.Block) bool {
|
||||
return pow.verify(block.HashNoNonce(), block.Diff(), block.N())
|
||||
func Verify(block pow.Block) bool {
|
||||
return verify(block.HashNoNonce(), block.Difficulty(), block.N())
|
||||
}
|
||||
|
|
3038
profile.cov
3038
profile.cov
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,7 @@
|
|||
package ptrie
|
||||
|
||||
import "fmt"
|
||||
|
||||
type FullNode struct {
|
||||
trie *Trie
|
||||
nodes [17]Node
|
||||
|
@ -21,7 +23,9 @@ func (self *FullNode) Branches() []Node {
|
|||
func (self *FullNode) Copy() Node {
|
||||
nnode := NewFullNode(self.trie)
|
||||
for i, node := range self.nodes {
|
||||
nnode.nodes[i] = node
|
||||
if node != nil {
|
||||
nnode.nodes[i] = node
|
||||
}
|
||||
}
|
||||
|
||||
return nnode
|
||||
|
@ -56,6 +60,10 @@ func (self *FullNode) RlpData() interface{} {
|
|||
}
|
||||
|
||||
func (self *FullNode) set(k byte, value Node) {
|
||||
if _, ok := value.(*ValueNode); ok && k != 16 {
|
||||
fmt.Println(value, k)
|
||||
}
|
||||
|
||||
self.nodes[int(k)] = value
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ type Iterator struct {
|
|||
}
|
||||
|
||||
func NewIterator(trie *Trie) *Iterator {
|
||||
return &Iterator{trie: trie, Key: []byte{0}}
|
||||
return &Iterator{trie: trie, Key: make([]byte, 32)}
|
||||
}
|
||||
|
||||
func (self *Iterator) Next() bool {
|
||||
|
|
|
@ -17,7 +17,7 @@ type Node interface {
|
|||
func (self *ValueNode) String() string { return self.fstring("") }
|
||||
func (self *FullNode) String() string { return self.fstring("") }
|
||||
func (self *ShortNode) String() string { return self.fstring("") }
|
||||
func (self *ValueNode) fstring(ind string) string { return fmt.Sprintf("%s ", self.data) }
|
||||
func (self *ValueNode) fstring(ind string) string { return fmt.Sprintf("%x ", self.data) }
|
||||
func (self *HashNode) fstring(ind string) string { return fmt.Sprintf("%x ", self.key) }
|
||||
|
||||
// Full node
|
||||
|
@ -36,5 +36,5 @@ func (self *FullNode) fstring(ind string) string {
|
|||
|
||||
// Short node
|
||||
func (self *ShortNode) fstring(ind string) string {
|
||||
return fmt.Sprintf("[ %s: %v ] ", self.key, self.value.fstring(ind+" "))
|
||||
return fmt.Sprintf("[ %x: %v ] ", self.key, self.value.fstring(ind+" "))
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue