OSDN Git Service

merge node_p2p and chain.
authorgguoss <1536310027@qq.com>
Fri, 14 Jul 2017 08:45:45 +0000 (16:45 +0800)
committergguoss <1536310027@qq.com>
Fri, 14 Jul 2017 08:45:45 +0000 (16:45 +0800)
148 files changed:
Makefile [new file with mode: 0644]
bcreactor/pool.go [new file with mode: 0644]
bcreactor/pool_test.go [new file with mode: 0644]
bcreactor/reactor.go [new file with mode: 0644]
bcreactor/store.go [new file with mode: 0644]
cmd/blockchain/commands/flags/log_level.go
cmd/blockchain/commands/root.go
config/config.go [new file with mode: 0644]
config/config_test.go [new file with mode: 0644]
config/toml.go [new file with mode: 0644]
config/toml_test.go [new file with mode: 0644]
crypto/ed25519/chainkd/bench_test.go [new file with mode: 0644]
crypto/ed25519/chainkd/chainkd.go [new file with mode: 0644]
crypto/ed25519/chainkd/chainkd_test.go [new file with mode: 0644]
crypto/ed25519/chainkd/serialize.go [new file with mode: 0644]
crypto/ed25519/chainkd/serialize_test.go [new file with mode: 0644]
crypto/ed25519/chainkd/util.go [new file with mode: 0644]
crypto/ed25519/ecmath/point.go [new file with mode: 0644]
crypto/ed25519/ecmath/point_test.go [new file with mode: 0644]
crypto/ed25519/ecmath/scalar.go [new file with mode: 0644]
crypto/ed25519/ed25519.go [new file with mode: 0644]
crypto/ed25519/ed25519_test.go [new file with mode: 0644]
crypto/ed25519/internal/edwards25519/chain_export.go [new file with mode: 0644]
crypto/ed25519/internal/edwards25519/const.go [new file with mode: 0644]
crypto/ed25519/internal/edwards25519/edwards25519.go [new file with mode: 0644]
crypto/ed25519/testdata/sign.input.gz [new file with mode: 0644]
crypto/sha3pool/pool.go [new file with mode: 0644]
glide.lock [new file with mode: 0644]
glide.yaml [new file with mode: 0644]
node/id.go [new file with mode: 0644]
node/node.go [new file with mode: 0644]
p2p/CHANGELOG.md [new file with mode: 0644]
p2p/Dockerfile [new file with mode: 0644]
p2p/README.md [new file with mode: 0644]
p2p/addrbook.go [new file with mode: 0644]
p2p/addrbook_test.go [new file with mode: 0644]
p2p/connection.go [new file with mode: 0644]
p2p/connection_test.go [new file with mode: 0644]
p2p/fuzz.go [new file with mode: 0644]
p2p/ip_range_counter.go [new file with mode: 0644]
p2p/listener.go [new file with mode: 0644]
p2p/listener_test.go [new file with mode: 0644]
p2p/netaddress.go [new file with mode: 0644]
p2p/netaddress_test.go [new file with mode: 0644]
p2p/peer.go [new file with mode: 0644]
p2p/peer_set.go [new file with mode: 0644]
p2p/peer_set_test.go [new file with mode: 0644]
p2p/peer_test.go [new file with mode: 0644]
p2p/pex_reactor.go [new file with mode: 0644]
p2p/pex_reactor_test.go [new file with mode: 0644]
p2p/secret_connection.go [new file with mode: 0644]
p2p/secret_connection_test.go [new file with mode: 0644]
p2p/switch.go [new file with mode: 0644]
p2p/switch_test.go [new file with mode: 0644]
p2p/types.go [new file with mode: 0644]
p2p/upnp/README.md [new file with mode: 0644]
p2p/upnp/probe.go [new file with mode: 0644]
p2p/upnp/upnp.go [new file with mode: 0644]
p2p/util.go [new file with mode: 0644]
p2p/version.go [new file with mode: 0644]
protocol/bc/asset.go
protocol/bc/bctest/tx.go
protocol/bc/entry.go
protocol/bc/legacy/bc_test.go
protocol/bc/legacy/block.go
protocol/bc/legacy/block_commitment.go
protocol/bc/legacy/block_header.go
protocol/bc/legacy/block_test.go
protocol/bc/legacy/block_witness.go
protocol/bc/legacy/issuance.go
protocol/bc/legacy/issuance_witness.go
protocol/bc/legacy/map.go
protocol/bc/legacy/map_test.go
protocol/bc/legacy/output_commitment.go
protocol/bc/legacy/spend.go
protocol/bc/legacy/transaction.go
protocol/bc/legacy/transaction_test.go
protocol/bc/legacy/tx_test.go
protocol/bc/legacy/txinput.go
protocol/bc/legacy/txoutput.go
protocol/bc/merkle.go
protocol/bc/merkle_test.go
protocol/bc/tx.go
protocol/block.go
protocol/block_test.go
protocol/patricia/patricia.go
protocol/patricia/patricia_test.go
protocol/protocol.go
protocol/prottest/block.go
protocol/prottest/memstore/memstore.go
protocol/recover.go
protocol/recover_test.go
protocol/state/snapshot.go
protocol/state/snapshot_test.go
protocol/tx.go
protocol/tx_test.go
protocol/validation/block_test.go
protocol/validation/fuzz_test.go
protocol/validation/validation.go
protocol/validation/validation_test.go
protocol/validation/vmcontext.go
protocol/validation/vmcontext_test.go
protocol/vm/assemble.go
protocol/vm/assemble_test.go
protocol/vm/bitwise_test.go
protocol/vm/control_test.go
protocol/vm/crypto.go
protocol/vm/crypto_test.go
protocol/vm/introspection_test.go
protocol/vm/numeric.go
protocol/vm/numeric_test.go
protocol/vm/ops.go
protocol/vm/ops_test.go
protocol/vm/pushdata_test.go
protocol/vm/splice.go
protocol/vm/splice_test.go
protocol/vm/stack.go
protocol/vm/stack_test.go
protocol/vm/vm.go
protocol/vm/vm_test.go
protocol/vm/vmutil/builder.go
protocol/vm/vmutil/builder_test.go
protocol/vm/vmutil/script.go
protocol/vm/vmutil/script_test.go
types/block.go [new file with mode: 0644]
types/block_meta.go [new file with mode: 0644]
types/canonical_json.go [new file with mode: 0644]
types/events.go [new file with mode: 0644]
types/genesis.go [new file with mode: 0644]
types/keys.go [new file with mode: 0644]
types/part_set.go [new file with mode: 0644]
types/part_set_test.go [new file with mode: 0644]
types/priv_validator.go [new file with mode: 0644]
types/priv_validator_test.go [new file with mode: 0644]
types/proposal.go [new file with mode: 0644]
types/proposal_test.go [new file with mode: 0644]
types/protobuf.go [new file with mode: 0644]
types/services.go [new file with mode: 0644]
types/signable.go [new file with mode: 0644]
types/tx.go [new file with mode: 0644]
types/tx_test.go [new file with mode: 0644]
types/validator.go [new file with mode: 0644]
types/validator_set.go [new file with mode: 0644]
types/validator_set_test.go [new file with mode: 0644]
types/vote.go [new file with mode: 0644]
types/vote_set.go [new file with mode: 0644]
types/vote_set_test.go [new file with mode: 0644]
types/vote_test.go [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..c5cfb86
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,79 @@
+GOTOOLS = \
+                                       github.com/mitchellh/gox \
+                                       github.com/Masterminds/glide
+PACKAGES=$(shell go list ./... | grep -v '/vendor/')
+BUILD_TAGS?=blockchain
+TMHOME = $${TMHOME:-$$HOME/.blockchain}
+
+all: install test
+
+install: get_vendor_deps copy
+       @go install --ldflags '-extldflags "-static"' \
+               --ldflags "-X github.com/Bytom/blockchain/version.GitCommit=`git rev-parse HEAD`" ./node/
+
+build: copy
+       go build \
+               --ldflags "-X github.com/Bytom/blockchain/version.GitCommit=`git rev-parse HEAD`"  -o build/node ./node/
+
+copy:
+       cp -r vendor/github.com/golang/crypto vendor/golang.org/x/crypto
+       cp -r vendor/github.com/golang/net vendor/golang.org/x/net
+       cp -r vendor/github.com/golang/text vendor/golang.org/x/text
+       cp -r vendor/github.com/golang/tools vendor/golang.org/x/tools
+
+# dist builds binaries for all platforms and packages them for distribution
+dist:
+       @BUILD_TAGS='$(BUILD_TAGS)' sh -c "'$(CURDIR)/scripts/dist.sh'"
+
+test:
+       @echo "--> Running go test"
+       @go test $(PACKAGES)
+
+test_race:
+       @echo "--> Running go test --race"
+       @go test -v -race $(PACKAGES)
+
+test_integrations:
+       @bash ./test/test.sh
+
+test100:
+       @for i in {1..100}; do make test; done
+
+draw_deps:
+       # requires brew install graphviz or apt-get install graphviz
+       go get github.com/RobotsAndPencils/goviz
+       @goviz -i github.com/tendermint/tendermint/cmd/tendermint -d 3 | dot -Tpng -o dependency-graph.png
+
+list_deps:
+       @go list -f '{{join .Deps "\n"}}' ./... | \
+               grep -v /vendor/ | sort | uniq | \
+               xargs go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}'
+
+get_deps:
+       @echo "--> Running go get"
+       @go get -v -d $(PACKAGES)
+       @go list -f '{{join .TestImports "\n"}}' ./... | \
+               grep -v /vendor/ | sort | uniq | \
+               xargs go get -v -d
+
+get_vendor_deps: ensure_tools
+       @rm -rf vendor/
+       @echo "--> Running glide install"
+       @glide install
+
+update_deps: tools
+       @echo "--> Updating dependencies"
+       @go get -d -u ./...
+
+revision:
+       -echo `git rev-parse --verify HEAD` > $(TMHOME)/revision
+       -echo `git rev-parse --verify HEAD` >> $(TMHOME)/revision_history
+
+tools:
+       go get -u -v $(GOTOOLS)
+
+ensure_tools:
+       go get $(GOTOOLS)
+
+
+.PHONY: install build build_race dist test test_race test_integrations test100 draw_deps list_deps get_deps get_vendor_deps update_deps revision tools
diff --git a/bcreactor/pool.go b/bcreactor/pool.go
new file mode 100644 (file)
index 0000000..551ca3f
--- /dev/null
@@ -0,0 +1,522 @@
+package blockchain
+
+import (
+       "math"
+       "sync"
+       "time"
+
+       "github.com/consensus/types"
+       . "github.com/tendermint/tmlibs/common"
+       flow "github.com/tendermint/tmlibs/flowrate"
+       "github.com/tendermint/tmlibs/log"
+)
+
+const (
+       requestIntervalMS         = 250
+       maxTotalRequesters        = 300
+       maxPendingRequests        = maxTotalRequesters
+       maxPendingRequestsPerPeer = 75
+       minRecvRate               = 10240 // 10Kb/s
+)
+
+var peerTimeoutSeconds = time.Duration(15) // not const so we can override with tests
+
+/*
+       Peers self report their heights when we join the block pool.
+       Starting from our latest pool.height, we request blocks
+       in sequence from peers that reported higher heights than ours.
+       Every so often we ask peers what height they're on so we can keep going.
+
+       Requests are continuously made for blocks of higher heights until
+       the limits. If most of the requests have no available peers, and we
+       are not at peer limits, we can probably switch to consensus reactor
+*/
+
+type BlockPool struct {
+       BaseService
+       startTime time.Time
+
+       mtx sync.Mutex
+       // block requests
+       requesters map[int]*bpRequester
+       height     int   // the lowest key in requesters.
+       numPending int32 // number of requests pending assignment or block response
+       // peers
+       peers map[string]*bpPeer
+
+       requestsCh chan<- BlockRequest
+       timeoutsCh chan<- string
+}
+
+func NewBlockPool(start int, requestsCh chan<- BlockRequest, timeoutsCh chan<- string) *BlockPool {
+       bp := &BlockPool{
+               peers: make(map[string]*bpPeer),
+
+               requesters: make(map[int]*bpRequester),
+               height:     start,
+               numPending: 0,
+
+               requestsCh: requestsCh,
+               timeoutsCh: timeoutsCh,
+       }
+       bp.BaseService = *NewBaseService(nil, "BlockPool", bp)
+       return bp
+}
+
+func (pool *BlockPool) OnStart() error {
+       go pool.makeRequestersRoutine()
+       pool.startTime = time.Now()
+       return nil
+}
+
+func (pool *BlockPool) OnStop() {
+       pool.BaseService.OnStop()
+}
+
+// Run spawns requesters as needed.
+func (pool *BlockPool) makeRequestersRoutine() {
+       for {
+               if !pool.IsRunning() {
+                       break
+               }
+               _, numPending, lenRequesters := pool.GetStatus()
+               if numPending >= maxPendingRequests {
+                       // sleep for a bit.
+                       time.Sleep(requestIntervalMS * time.Millisecond)
+                       // check for timed out peers
+                       pool.removeTimedoutPeers()
+               } else if lenRequesters >= maxTotalRequesters {
+                       // sleep for a bit.
+                       time.Sleep(requestIntervalMS * time.Millisecond)
+                       // check for timed out peers
+                       pool.removeTimedoutPeers()
+               } else {
+                       // request for more blocks.
+                       pool.makeNextRequester()
+               }
+       }
+}
+
+func (pool *BlockPool) removeTimedoutPeers() {
+       pool.mtx.Lock()
+       defer pool.mtx.Unlock()
+
+       for _, peer := range pool.peers {
+               if !peer.didTimeout && peer.numPending > 0 {
+                       curRate := peer.recvMonitor.Status().CurRate
+                       // XXX remove curRate != 0
+                       if curRate != 0 && curRate < minRecvRate {
+                               pool.sendTimeout(peer.id)
+                               pool.Logger.Error("SendTimeout", "peer", peer.id, "reason", "curRate too low")
+                               peer.didTimeout = true
+                       }
+               }
+               if peer.didTimeout {
+                       pool.removePeer(peer.id)
+               }
+       }
+}
+
+func (pool *BlockPool) GetStatus() (height int, numPending int32, lenRequesters int) {
+       pool.mtx.Lock()
+       defer pool.mtx.Unlock()
+
+       return pool.height, pool.numPending, len(pool.requesters)
+}
+
+// TODO: relax conditions, prevent abuse.
+func (pool *BlockPool) IsCaughtUp() bool {
+       pool.mtx.Lock()
+       defer pool.mtx.Unlock()
+
+       height := pool.height
+
+       // Need at least 1 peer to be considered caught up.
+       if len(pool.peers) == 0 {
+               pool.Logger.Debug("Blockpool has no peers")
+               return false
+       }
+
+       maxPeerHeight := 0
+       for _, peer := range pool.peers {
+               maxPeerHeight = MaxInt(maxPeerHeight, peer.height)
+       }
+
+       isCaughtUp := (height > 0 || time.Now().Sub(pool.startTime) > 5*time.Second) && (maxPeerHeight == 0 || height >= maxPeerHeight)
+       pool.Logger.Info(Fmt("IsCaughtUp: %v", isCaughtUp), "height", height, "maxPeerHeight", maxPeerHeight)
+       return isCaughtUp
+}
+
+// We need to see the second block's Commit to validate the first block.
+// So we peek two blocks at a time.
+// The caller will verify the commit.
+func (pool *BlockPool) PeekTwoBlocks() (first *types.Block, second *types.Block) {
+       pool.mtx.Lock()
+       defer pool.mtx.Unlock()
+
+       if r := pool.requesters[pool.height]; r != nil {
+               first = r.getBlock()
+       }
+       if r := pool.requesters[pool.height+1]; r != nil {
+               second = r.getBlock()
+       }
+       return
+}
+
+// Pop the first block at pool.height
+// It must have been validated by 'second'.Commit from PeekTwoBlocks().
+func (pool *BlockPool) PopRequest() {
+       pool.mtx.Lock()
+       defer pool.mtx.Unlock()
+
+       if r := pool.requesters[pool.height]; r != nil {
+               /*  The block can disappear at any time, due to removePeer().
+               if r := pool.requesters[pool.height]; r == nil || r.block == nil {
+                       PanicSanity("PopRequest() requires a valid block")
+               }
+               */
+               r.Stop()
+               delete(pool.requesters, pool.height)
+               pool.height++
+       } else {
+               PanicSanity(Fmt("Expected requester to pop, got nothing at height %v", pool.height))
+       }
+}
+
+// Invalidates the block at pool.height,
+// Remove the peer and redo request from others.
+func (pool *BlockPool) RedoRequest(height int) {
+       pool.mtx.Lock()
+       request := pool.requesters[height]
+       pool.mtx.Unlock()
+
+       if request.block == nil {
+               PanicSanity("Expected block to be non-nil")
+       }
+       // RemovePeer will redo all requesters associated with this peer.
+       // TODO: record this malfeasance
+       pool.RemovePeer(request.peerID)
+}
+
+// TODO: ensure that blocks come in order for each peer.
+func (pool *BlockPool) AddBlock(peerID string, block *types.Block, blockSize int) {
+       pool.mtx.Lock()
+       defer pool.mtx.Unlock()
+
+       requester := pool.requesters[block.Height]
+       if requester == nil {
+               return
+       }
+
+       if requester.setBlock(block, peerID) {
+               pool.numPending--
+               peer := pool.peers[peerID]
+               peer.decrPending(blockSize)
+       } else {
+               // Bad peer?
+       }
+}
+
+// Sets the peer's alleged blockchain height.
+func (pool *BlockPool) SetPeerHeight(peerID string, height int) {
+       pool.mtx.Lock()
+       defer pool.mtx.Unlock()
+
+       peer := pool.peers[peerID]
+       if peer != nil {
+               peer.height = height
+       } else {
+               peer = newBPPeer(pool, peerID, height)
+               peer.setLogger(pool.Logger.With("peer", peerID))
+               pool.peers[peerID] = peer
+       }
+}
+
+func (pool *BlockPool) RemovePeer(peerID string) {
+       pool.mtx.Lock()
+       defer pool.mtx.Unlock()
+
+       pool.removePeer(peerID)
+}
+
+func (pool *BlockPool) removePeer(peerID string) {
+       for _, requester := range pool.requesters {
+               if requester.getPeerID() == peerID {
+                       if requester.getBlock() != nil {
+                               pool.numPending++
+                       }
+                       go requester.redo() // pick another peer and ...
+               }
+       }
+       delete(pool.peers, peerID)
+}
+
+// Pick an available peer with at least the given minHeight.
+// If no peers are available, returns nil.
+func (pool *BlockPool) pickIncrAvailablePeer(minHeight int) *bpPeer {
+       pool.mtx.Lock()
+       defer pool.mtx.Unlock()
+
+       for _, peer := range pool.peers {
+               if peer.didTimeout {
+                       pool.removePeer(peer.id)
+                       continue
+               } else {
+               }
+               if peer.numPending >= maxPendingRequestsPerPeer {
+                       continue
+               }
+               if peer.height < minHeight {
+                       continue
+               }
+               peer.incrPending()
+               return peer
+       }
+       return nil
+}
+
+func (pool *BlockPool) makeNextRequester() {
+       pool.mtx.Lock()
+       defer pool.mtx.Unlock()
+
+       nextHeight := pool.height + len(pool.requesters)
+       request := newBPRequester(pool, nextHeight)
+       request.SetLogger(pool.Logger.With("height", nextHeight))
+
+       pool.requesters[nextHeight] = request
+       pool.numPending++
+
+       request.Start()
+}
+
+func (pool *BlockPool) sendRequest(height int, peerID string) {
+       if !pool.IsRunning() {
+               return
+       }
+       pool.requestsCh <- BlockRequest{height, peerID}
+}
+
+func (pool *BlockPool) sendTimeout(peerID string) {
+       if !pool.IsRunning() {
+               return
+       }
+       pool.timeoutsCh <- peerID
+}
+
+func (pool *BlockPool) debug() string {
+       pool.mtx.Lock() // Lock
+       defer pool.mtx.Unlock()
+
+       str := ""
+       for h := pool.height; h < pool.height+len(pool.requesters); h++ {
+               if pool.requesters[h] == nil {
+                       str += Fmt("H(%v):X ", h)
+               } else {
+                       str += Fmt("H(%v):", h)
+                       str += Fmt("B?(%v) ", pool.requesters[h].block != nil)
+               }
+       }
+       return str
+}
+
+//-------------------------------------
+
+type bpPeer struct {
+       pool        *BlockPool
+       id          string
+       recvMonitor *flow.Monitor
+
+       mtx        sync.Mutex
+       height     int
+       numPending int32
+       timeout    *time.Timer
+       didTimeout bool
+
+       logger log.Logger
+}
+
+func newBPPeer(pool *BlockPool, peerID string, height int) *bpPeer {
+       peer := &bpPeer{
+               pool:       pool,
+               id:         peerID,
+               height:     height,
+               numPending: 0,
+               logger:     log.NewNopLogger(),
+       }
+       return peer
+}
+
+func (peer *bpPeer) setLogger(l log.Logger) {
+       peer.logger = l
+}
+
+func (peer *bpPeer) resetMonitor() {
+       peer.recvMonitor = flow.New(time.Second, time.Second*40)
+       var initialValue = float64(minRecvRate) * math.E
+       peer.recvMonitor.SetREMA(initialValue)
+}
+
+func (peer *bpPeer) resetTimeout() {
+       if peer.timeout == nil {
+               peer.timeout = time.AfterFunc(time.Second*peerTimeoutSeconds, peer.onTimeout)
+       } else {
+               peer.timeout.Reset(time.Second * peerTimeoutSeconds)
+       }
+}
+
+func (peer *bpPeer) incrPending() {
+       if peer.numPending == 0 {
+               peer.resetMonitor()
+               peer.resetTimeout()
+       }
+       peer.numPending++
+}
+
+func (peer *bpPeer) decrPending(recvSize int) {
+       peer.numPending--
+       if peer.numPending == 0 {
+               peer.timeout.Stop()
+       } else {
+               peer.recvMonitor.Update(recvSize)
+               peer.resetTimeout()
+       }
+}
+
+func (peer *bpPeer) onTimeout() {
+       peer.pool.mtx.Lock()
+       defer peer.pool.mtx.Unlock()
+
+       peer.pool.sendTimeout(peer.id)
+       peer.logger.Error("SendTimeout", "reason", "onTimeout")
+       peer.didTimeout = true
+}
+
+//-------------------------------------
+
+type bpRequester struct {
+       BaseService
+       pool       *BlockPool
+       height     int
+       gotBlockCh chan struct{}
+       redoCh     chan struct{}
+
+       mtx    sync.Mutex
+       peerID string
+       block  *types.Block
+}
+
+func newBPRequester(pool *BlockPool, height int) *bpRequester {
+       bpr := &bpRequester{
+               pool:       pool,
+               height:     height,
+               gotBlockCh: make(chan struct{}),
+               redoCh:     make(chan struct{}),
+
+               peerID: "",
+               block:  nil,
+       }
+       bpr.BaseService = *NewBaseService(nil, "bpRequester", bpr)
+       return bpr
+}
+
+func (bpr *bpRequester) OnStart() error {
+       go bpr.requestRoutine()
+       return nil
+}
+
+// Returns true if the peer matches
+func (bpr *bpRequester) setBlock(block *types.Block, peerID string) bool {
+       bpr.mtx.Lock()
+       if bpr.block != nil || bpr.peerID != peerID {
+               bpr.mtx.Unlock()
+               return false
+       }
+       bpr.block = block
+       bpr.mtx.Unlock()
+
+       bpr.gotBlockCh <- struct{}{}
+       return true
+}
+
+func (bpr *bpRequester) getBlock() *types.Block {
+       bpr.mtx.Lock()
+       defer bpr.mtx.Unlock()
+       return bpr.block
+}
+
+func (bpr *bpRequester) getPeerID() string {
+       bpr.mtx.Lock()
+       defer bpr.mtx.Unlock()
+       return bpr.peerID
+}
+
+func (bpr *bpRequester) reset() {
+       bpr.mtx.Lock()
+       bpr.peerID = ""
+       bpr.block = nil
+       bpr.mtx.Unlock()
+}
+
+// Tells bpRequester to pick another peer and try again.
+// NOTE: blocking
+func (bpr *bpRequester) redo() {
+       bpr.redoCh <- struct{}{}
+}
+
+// Responsible for making more requests as necessary
+// Returns only when a block is found (e.g. AddBlock() is called)
+func (bpr *bpRequester) requestRoutine() {
+OUTER_LOOP:
+       for {
+               // Pick a peer to send request to.
+               var peer *bpPeer = nil
+       PICK_PEER_LOOP:
+               for {
+                       if !bpr.IsRunning() || !bpr.pool.IsRunning() {
+                               return
+                       }
+                       peer = bpr.pool.pickIncrAvailablePeer(bpr.height)
+                       if peer == nil {
+                               //log.Info("No peers available", "height", height)
+                               time.Sleep(requestIntervalMS * time.Millisecond)
+                               continue PICK_PEER_LOOP
+                       }
+                       break PICK_PEER_LOOP
+               }
+               bpr.mtx.Lock()
+               bpr.peerID = peer.id
+               bpr.mtx.Unlock()
+
+               // Send request and wait.
+               bpr.pool.sendRequest(bpr.height, peer.id)
+               select {
+               case <-bpr.pool.Quit:
+                       bpr.Stop()
+                       return
+               case <-bpr.Quit:
+                       return
+               case <-bpr.redoCh:
+                       bpr.reset()
+                       continue OUTER_LOOP // When peer is removed
+               case <-bpr.gotBlockCh:
+                       // We got the block, now see if it's good.
+                       select {
+                       case <-bpr.pool.Quit:
+                               bpr.Stop()
+                               return
+                       case <-bpr.Quit:
+                               return
+                       case <-bpr.redoCh:
+                               bpr.reset()
+                               continue OUTER_LOOP
+                       }
+               }
+       }
+}
+
+//-------------------------------------
+
+type BlockRequest struct {
+       Height int
+       PeerID string
+}
diff --git a/bcreactor/pool_test.go b/bcreactor/pool_test.go
new file mode 100644 (file)
index 0000000..54a0921
--- /dev/null
@@ -0,0 +1,137 @@
+package blockchain
+
+import (
+       "math/rand"
+       "testing"
+       "time"
+
+       "github.com/consensus/types"
+       . "github.com/tendermint/tmlibs/common"
+       "github.com/tendermint/tmlibs/log"
+)
+
+func init() {
+       peerTimeoutSeconds = time.Duration(2)
+}
+
+type testPeer struct {
+       id     string
+       height int
+}
+
+func makePeers(numPeers int, minHeight, maxHeight int) map[string]testPeer {
+       peers := make(map[string]testPeer, numPeers)
+       for i := 0; i < numPeers; i++ {
+               peerID := RandStr(12)
+               height := minHeight + rand.Intn(maxHeight-minHeight)
+               peers[peerID] = testPeer{peerID, height}
+       }
+       return peers
+}
+
+func TestBasic(t *testing.T) {
+       start := 42
+       peers := makePeers(10, start+1, 1000)
+       timeoutsCh := make(chan string, 100)
+       requestsCh := make(chan BlockRequest, 100)
+       pool := NewBlockPool(start, requestsCh, timeoutsCh)
+       pool.SetLogger(log.TestingLogger())
+       pool.Start()
+       defer pool.Stop()
+
+       // Introduce each peer.
+       go func() {
+               for _, peer := range peers {
+                       pool.SetPeerHeight(peer.id, peer.height)
+               }
+       }()
+
+       // Start a goroutine to pull blocks
+       go func() {
+               for {
+                       if !pool.IsRunning() {
+                               return
+                       }
+                       first, second := pool.PeekTwoBlocks()
+                       if first != nil && second != nil {
+                               pool.PopRequest()
+                       } else {
+                               time.Sleep(1 * time.Second)
+                       }
+               }
+       }()
+
+       // Pull from channels
+       for {
+               select {
+               case peerID := <-timeoutsCh:
+                       t.Errorf("timeout: %v", peerID)
+               case request := <-requestsCh:
+                       t.Logf("Pulled new BlockRequest %v", request)
+                       if request.Height == 300 {
+                               return // Done!
+                       }
+                       // Request desired, pretend like we got the block immediately.
+                       go func() {
+                               block := &types.Block{Header: &types.Header{Height: request.Height}}
+                               pool.AddBlock(request.PeerID, block, 123)
+                               t.Logf("Added block from peer %v (height: %v)", request.PeerID, request.Height)
+                       }()
+               }
+       }
+}
+
+func TestTimeout(t *testing.T) {
+       start := 42
+       peers := makePeers(10, start+1, 1000)
+       timeoutsCh := make(chan string, 100)
+       requestsCh := make(chan BlockRequest, 100)
+       pool := NewBlockPool(start, requestsCh, timeoutsCh)
+       pool.SetLogger(log.TestingLogger())
+       pool.Start()
+       defer pool.Stop()
+
+       for _, peer := range peers {
+               t.Logf("Peer %v", peer.id)
+       }
+
+       // Introduce each peer.
+       go func() {
+               for _, peer := range peers {
+                       pool.SetPeerHeight(peer.id, peer.height)
+               }
+       }()
+
+       // Start a goroutine to pull blocks
+       go func() {
+               for {
+                       if !pool.IsRunning() {
+                               return
+                       }
+                       first, second := pool.PeekTwoBlocks()
+                       if first != nil && second != nil {
+                               pool.PopRequest()
+                       } else {
+                               time.Sleep(1 * time.Second)
+                       }
+               }
+       }()
+
+       // Pull from channels
+       counter := 0
+       timedOut := map[string]struct{}{}
+       for {
+               select {
+               case peerID := <-timeoutsCh:
+                       t.Logf("Peer %v timeouted", peerID)
+                       if _, ok := timedOut[peerID]; !ok {
+                               counter++
+                               if counter == len(peers) {
+                                       return // Done!
+                               }
+                       }
+               case request := <-requestsCh:
+                       t.Logf("Pulled new BlockRequest %+v", request)
+               }
+       }
+}
diff --git a/bcreactor/reactor.go b/bcreactor/reactor.go
new file mode 100644 (file)
index 0000000..630b34a
--- /dev/null
@@ -0,0 +1,323 @@
+package blockchain
+
+import (
+       "bytes"
+       "errors"
+       "reflect"
+       "time"
+
+       wire "github.com/tendermint/go-wire"
+       "github.com/blockchain/p2p"
+       "github.com/blockchain/types"
+       cmn "github.com/tendermint/tmlibs/common"
+)
+
+const (
+       // BlockchainChannel is a channel for blocks and status updates (`BlockStore` height)
+       BlockchainChannel = byte(0x40)
+
+       defaultChannelCapacity = 100
+       defaultSleepIntervalMS = 500
+       trySyncIntervalMS      = 100
+       // stop syncing when last block's time is
+       // within this much of the system time.
+       // stopSyncingDurationMinutes = 10
+
+       // ask for best height every 10s
+       statusUpdateIntervalSeconds = 10
+       // check if we should switch to consensus reactor
+       switchToConsensusIntervalSeconds = 1
+       maxBlockchainResponseSize        = types.MaxBlockSize + 2
+)
+
+/*
+type consensusReactor interface {
+       // for when we switch from blockchain reactor and fast sync to
+       // the consensus machine
+       SwitchToConsensus(*sm.State)
+}
+*/
+
+// BlockchainReactor handles long-term catchup syncing.
+type BlockchainReactor struct {
+       p2p.BaseReactor
+
+//     state        *sm.State
+//     proxyAppConn proxy.AppConnConsensus // same as consensus.proxyAppConn
+//     store        *BlockStore
+//     pool         *BlockPool
+//     fastSync     bool
+       requestsCh   chan BlockRequest
+       timeoutsCh   chan string
+//     lastBlock    *types.Block
+
+       evsw types.EventSwitch
+}
+
+func NewBlockchainReactor() *BlockchainReactor {
+    requestsCh    make(chan BlockRequest, defeaultChannelCapacity)
+    timeoutsCh    make(chan string, defaultChannelCapacity)
+
+    bcR := &BlockchainReactor {
+        requestsCh:    requestCh,
+        timeoutsCh:    timeoutsCh,
+    }
+    bcR.BaseReactor = *p2p.NewBaseReactor("BlockchainReactor", bcR)
+    return bcR
+}
+
+// OnStart implements BaseService
+func (bcR *BlockchainReactor) OnStart() error {
+       bcR.BaseReactor.OnStart()
+       return nil
+}
+
+// OnStop implements BaseService
+func (bcR *BlockchainReactor) OnStop() {
+       bcR.BaseReactor.OnStop()
+}
+
+// GetChannels implements Reactor
+func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor {
+       return []*p2p.ChannelDescriptor{
+               &p2p.ChannelDescriptor{
+                       ID:                BlockchainChannel,
+                       Priority:          5,
+                       SendQueueCapacity: 100,
+               },
+       }
+}
+
+// AddPeer implements Reactor by sending our state to peer.
+func (bcR *BlockchainReactor) AddPeer(peer *p2p.Peer) {
+       /*if !peer.Send(BlockchainChannel, struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}}) {
+               // doing nothing, will try later in `poolRoutine`
+       }*/
+}
+
+// RemovePeer implements Reactor by removing peer from the pool.
+func (bcR *BlockchainReactor) RemovePeer(peer *p2p.Peer, reason interface{}) {
+       //bcR.pool.RemovePeer(peer.Key)
+}
+
+// Receive implements Reactor by handling 4 types of messages (look below).
+func (bcR *BlockchainReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) {
+       _, msg, err := DecodeMessage(msgBytes)
+       if err != nil {
+               bcR.Logger.Error("Error decoding message", "error", err)
+               return
+       }
+
+       bcR.Logger.Debug("Receive", "src", src, "chID", chID, "msg", msg)
+
+       switch msg := msg.(type) {
+       case *bcBlockRequestMessage:
+               // Got a request for a block. Respond with block if we have it.
+               /*block := bcR.store.LoadBlock(msg.Height)
+               if block != nil {
+                       msg := &bcBlockResponseMessage{Block: block}
+                       queued := src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
+                       if !queued {
+                               // queue is full, just ignore.
+                       }
+               } else {
+                       // TODO peer is asking for things we don't have.
+               }*/
+       case *bcBlockResponseMessage:
+               // Got a block.
+               //bcR.pool.AddBlock(src.Key, msg.Block, len(msgBytes))
+       case *bcStatusRequestMessage:
+               // Send peer our state.
+               /*queued := src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}})
+               if !queued {
+                       // sorry
+               }*/
+       case *bcStatusResponseMessage:
+               // Got a peer status. Unverified.
+               //bcR.pool.SetPeerHeight(src.Key, msg.Height)
+       default:
+               bcR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
+       }
+}
+
+/*
+// Handle messages from the poolReactor telling the reactor what to do.
+// NOTE: Don't sleep in the FOR_LOOP or otherwise slow it down!
+// (Except for the SYNC_LOOP, which is the primary purpose and must be synchronous.)
+func (bcR *BlockchainReactor) poolRoutine() {
+
+       trySyncTicker := time.NewTicker(trySyncIntervalMS * time.Millisecond)
+       statusUpdateTicker := time.NewTicker(statusUpdateIntervalSeconds * time.Second)
+       switchToConsensusTicker := time.NewTicker(switchToConsensusIntervalSeconds * time.Second)
+
+FOR_LOOP:
+       for {
+               select {
+               case request := <-bcR.requestsCh: // chan BlockRequest
+                       peer := bcR.Switch.Peers().Get(request.PeerID)
+                       if peer == nil {
+                               continue FOR_LOOP // Peer has since been disconnected.
+                       }
+                       msg := &bcBlockRequestMessage{request.Height}
+                       queued := peer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
+                       if !queued {
+                               // We couldn't make the request, send-queue full.
+                               // The pool handles timeouts, just let it go.
+                               continue FOR_LOOP
+                       }
+               case peerID := <-bcR.timeoutsCh: // chan string
+                       // Peer timed out.
+                       peer := bcR.Switch.Peers().Get(peerID)
+                       if peer != nil {
+                               bcR.Switch.StopPeerForError(peer, errors.New("BlockchainReactor Timeout"))
+                       }
+               case _ = <-statusUpdateTicker.C:
+                       // ask for status updates
+                       go bcR.BroadcastStatusRequest()
+               case _ = <-switchToConsensusTicker.C:
+                       height, numPending, _ := bcR.pool.GetStatus()
+                       outbound, inbound, _ := bcR.Switch.NumPeers()
+                       bcR.Logger.Info("Consensus ticker", "numPending", numPending, "total", len(bcR.pool.requesters),
+                               "outbound", outbound, "inbound", inbound)
+                       if bcR.pool.IsCaughtUp() {
+                               bcR.Logger.Info("Time to switch to consensus reactor!", "height", height)
+                               bcR.pool.Stop()
+
+                               conR := bcR.Switch.Reactor("CONSENSUS").(consensusReactor)
+                               conR.SwitchToConsensus(bcR.state)
+
+                               break FOR_LOOP
+                       }
+               case _ = <-trySyncTicker.C: // chan time
+                       // This loop can be slow as long as it's doing syncing work.
+               SYNC_LOOP:
+                       for i := 0; i < 10; i++ {
+                               // See if there are any blocks to sync.
+                               first, second := bcR.pool.PeekTwoBlocks()
+                               //bcR.Logger.Info("TrySync peeked", "first", first, "second", second)
+                               if first == nil || second == nil {
+                                       // We need both to sync the first block.
+                                       break SYNC_LOOP
+                               }
+                               firstParts := first.MakePartSet(types.DefaultBlockPartSize)
+                               firstPartsHeader := firstParts.Header()
+                               // Finally, verify the first block using the second's commit
+                               // NOTE: we can probably make this more efficient, but note that calling
+                               // first.Hash() doesn't verify the tx contents, so MakePartSet() is
+                               // currently necessary.
+                               err := bcR.state.Validators.VerifyCommit(
+                                       bcR.state.ChainID, types.BlockID{first.Hash(), firstPartsHeader}, first.Height, second.LastCommit)
+                               if err != nil {
+                                       bcR.Logger.Info("error in validation", "error", err)
+                                       bcR.pool.RedoRequest(first.Height)
+                                       break SYNC_LOOP
+                               } else {
+                                       bcR.pool.PopRequest()
+
+                                       bcR.store.SaveBlock(first, firstParts, second.LastCommit)
+
+                                       // TODO: should we be firing events? need to fire NewBlock events manually ...
+                                       // NOTE: we could improve performance if we
+                                       // didn't make the app commit to disk every block
+                                       // ... but we would need a way to get the hash without it persisting
+                                       err := bcR.state.ApplyBlock(bcR.evsw, bcR.proxyAppConn, first, firstPartsHeader, types.MockMempool{})
+                                       if err != nil {
+                                               // TODO This is bad, are we zombie?
+                                               cmn.PanicQ(cmn.Fmt("Failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err))
+                                       }
+                               }
+                       }
+                       continue FOR_LOOP
+               case <-bcR.Quit:
+                       break FOR_LOOP
+               }
+       }
+}
+
+// BroadcastStatusRequest broadcasts `BlockStore` height.
+func (bcR *BlockchainReactor) BroadcastStatusRequest() error {
+       bcR.Switch.Broadcast(BlockchainChannel, struct{ BlockchainMessage }{&bcStatusRequestMessage{bcR.store.Height()}})
+       return nil
+}
+
+*/
+
+// SetEventSwitch implements events.Eventable
+func (bcR *BlockchainReactor) SetEventSwitch(evsw types.EventSwitch) {
+       bcR.evsw = evsw
+}
+
+//-----------------------------------------------------------------------------
+// Messages
+
+const (
+       msgTypeBlockRequest   = byte(0x10)
+       msgTypeBlockResponse  = byte(0x11)
+       msgTypeStatusResponse = byte(0x20)
+       msgTypeStatusRequest  = byte(0x21)
+)
+
+// BlockchainMessage is a generic message for this reactor.
+type BlockchainMessage interface{}
+
+var _ = wire.RegisterInterface(
+       struct{ BlockchainMessage }{},
+       wire.ConcreteType{&bcBlockRequestMessage{}, msgTypeBlockRequest},
+       wire.ConcreteType{&bcBlockResponseMessage{}, msgTypeBlockResponse},
+       wire.ConcreteType{&bcStatusResponseMessage{}, msgTypeStatusResponse},
+       wire.ConcreteType{&bcStatusRequestMessage{}, msgTypeStatusRequest},
+)
+
+// DecodeMessage decodes BlockchainMessage.
+// TODO: ensure that bz is completely read.
+func DecodeMessage(bz []byte) (msgType byte, msg BlockchainMessage, err error) {
+       msgType = bz[0]
+       n := int(0)
+       r := bytes.NewReader(bz)
+       msg = wire.ReadBinary(struct{ BlockchainMessage }{}, r, maxBlockchainResponseSize, &n, &err).(struct{ BlockchainMessage }).BlockchainMessage
+       if err != nil && n != len(bz) {
+               err = errors.New("DecodeMessage() had bytes left over")
+       }
+       return
+}
+
+//-------------------------------------
+
+type bcBlockRequestMessage struct {
+       Height int
+}
+
+func (m *bcBlockRequestMessage) String() string {
+       return cmn.Fmt("[bcBlockRequestMessage %v]", m.Height)
+}
+
+//-------------------------------------
+
+// NOTE: keep up-to-date with maxBlockchainResponseSize
+type bcBlockResponseMessage struct {
+       Block *types.Block
+}
+
+func (m *bcBlockResponseMessage) String() string {
+       return cmn.Fmt("[bcBlockResponseMessage %v]", m.Block.Height)
+}
+
+//-------------------------------------
+
+type bcStatusRequestMessage struct {
+       Height int
+}
+
+func (m *bcStatusRequestMessage) String() string {
+       return cmn.Fmt("[bcStatusRequestMessage %v]", m.Height)
+}
+
+//-------------------------------------
+
+type bcStatusResponseMessage struct {
+       Height int
+}
+
+func (m *bcStatusResponseMessage) String() string {
+       return cmn.Fmt("[bcStatusResponseMessage %v]", m.Height)
+}
diff --git a/bcreactor/store.go b/bcreactor/store.go
new file mode 100644 (file)
index 0000000..f8c4f2e
--- /dev/null
@@ -0,0 +1,242 @@
+package blockchain
+
+import (
+       "bytes"
+       "encoding/json"
+       "fmt"
+       "io"
+       "sync"
+
+       . "github.com/tendermint/tmlibs/common"
+       dbm "github.com/tendermint/tmlibs/db"
+       "github.com/tendermint/go-wire"
+       "github.com/consensus/types"
+)
+
+/*
+Simple low level store for blocks.
+
+There are three types of information stored:
+ - BlockMeta:   Meta information about each block
+ - Block part:  Parts of each block, aggregated w/ PartSet
+ - Commit:      The commit part of each block, for gossiping precommit votes
+
+Currently the precommit signatures are duplicated in the Block parts as
+well as the Commit.  In the future this may change, perhaps by moving
+the Commit data outside the Block.
+
+Panics indicate probable corruption in the data
+*/
+type BlockStore struct {
+       db dbm.DB
+
+       mtx    sync.RWMutex
+       height int
+}
+
+func NewBlockStore(db dbm.DB) *BlockStore {
+       bsjson := LoadBlockStoreStateJSON(db)
+       return &BlockStore{
+               height: bsjson.Height,
+               db:     db,
+       }
+}
+
+// Height() returns the last known contiguous block height.
+func (bs *BlockStore) Height() int {
+       bs.mtx.RLock()
+       defer bs.mtx.RUnlock()
+       return bs.height
+}
+
+func (bs *BlockStore) GetReader(key []byte) io.Reader {
+       bytez := bs.db.Get(key)
+       if bytez == nil {
+               return nil
+       }
+       return bytes.NewReader(bytez)
+}
+
+func (bs *BlockStore) LoadBlock(height int) *types.Block {
+       var n int
+       var err error
+       r := bs.GetReader(calcBlockMetaKey(height))
+       if r == nil {
+               return nil
+       }
+       blockMeta := wire.ReadBinary(&types.BlockMeta{}, r, 0, &n, &err).(*types.BlockMeta)
+       if err != nil {
+               PanicCrisis(Fmt("Error reading block meta: %v", err))
+       }
+       bytez := []byte{}
+       for i := 0; i < blockMeta.BlockID.PartsHeader.Total; i++ {
+               part := bs.LoadBlockPart(height, i)
+               bytez = append(bytez, part.Bytes...)
+       }
+       block := wire.ReadBinary(&types.Block{}, bytes.NewReader(bytez), 0, &n, &err).(*types.Block)
+       if err != nil {
+               PanicCrisis(Fmt("Error reading block: %v", err))
+       }
+       return block
+}
+
+func (bs *BlockStore) LoadBlockPart(height int, index int) *types.Part {
+       var n int
+       var err error
+       r := bs.GetReader(calcBlockPartKey(height, index))
+       if r == nil {
+               return nil
+       }
+       part := wire.ReadBinary(&types.Part{}, r, 0, &n, &err).(*types.Part)
+       if err != nil {
+               PanicCrisis(Fmt("Error reading block part: %v", err))
+       }
+       return part
+}
+
+func (bs *BlockStore) LoadBlockMeta(height int) *types.BlockMeta {
+       var n int
+       var err error
+       r := bs.GetReader(calcBlockMetaKey(height))
+       if r == nil {
+               return nil
+       }
+       blockMeta := wire.ReadBinary(&types.BlockMeta{}, r, 0, &n, &err).(*types.BlockMeta)
+       if err != nil {
+               PanicCrisis(Fmt("Error reading block meta: %v", err))
+       }
+       return blockMeta
+}
+
+// The +2/3 and other Precommit-votes for block at `height`.
+// This Commit comes from block.LastCommit for `height+1`.
+func (bs *BlockStore) LoadBlockCommit(height int) *types.Commit {
+       var n int
+       var err error
+       r := bs.GetReader(calcBlockCommitKey(height))
+       if r == nil {
+               return nil
+       }
+       commit := wire.ReadBinary(&types.Commit{}, r, 0, &n, &err).(*types.Commit)
+       if err != nil {
+               PanicCrisis(Fmt("Error reading commit: %v", err))
+       }
+       return commit
+}
+
+// NOTE: the Precommit-vote heights are for the block at `height`
+func (bs *BlockStore) LoadSeenCommit(height int) *types.Commit {
+       var n int
+       var err error
+       r := bs.GetReader(calcSeenCommitKey(height))
+       if r == nil {
+               return nil
+       }
+       commit := wire.ReadBinary(&types.Commit{}, r, 0, &n, &err).(*types.Commit)
+       if err != nil {
+               PanicCrisis(Fmt("Error reading commit: %v", err))
+       }
+       return commit
+}
+
+// blockParts: Must be parts of the block
+// seenCommit: The +2/3 precommits that were seen which committed at height.
+//             If all the nodes restart after committing a block,
+//             we need this to reload the precommits to catch-up nodes to the
+//             most recent height.  Otherwise they'd stall at H-1.
+func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
+       height := block.Height
+       if height != bs.Height()+1 {
+               PanicSanity(Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height))
+       }
+       if !blockParts.IsComplete() {
+               PanicSanity(Fmt("BlockStore can only save complete block part sets"))
+       }
+
+       // Save block meta
+       blockMeta := types.NewBlockMeta(block, blockParts)
+       metaBytes := wire.BinaryBytes(blockMeta)
+       bs.db.Set(calcBlockMetaKey(height), metaBytes)
+
+       // Save block parts
+       for i := 0; i < blockParts.Total(); i++ {
+               bs.saveBlockPart(height, i, blockParts.GetPart(i))
+       }
+
+       // Save block commit (duplicate and separate from the Block)
+       blockCommitBytes := wire.BinaryBytes(block.LastCommit)
+       bs.db.Set(calcBlockCommitKey(height-1), blockCommitBytes)
+
+       // Save seen commit (seen +2/3 precommits for block)
+       // NOTE: we can delete this at a later height
+       seenCommitBytes := wire.BinaryBytes(seenCommit)
+       bs.db.Set(calcSeenCommitKey(height), seenCommitBytes)
+
+       // Save new BlockStoreStateJSON descriptor
+       BlockStoreStateJSON{Height: height}.Save(bs.db)
+
+       // Done!
+       bs.mtx.Lock()
+       bs.height = height
+       bs.mtx.Unlock()
+
+       // Flush
+       bs.db.SetSync(nil, nil)
+}
+
+func (bs *BlockStore) saveBlockPart(height int, index int, part *types.Part) {
+       if height != bs.Height()+1 {
+               PanicSanity(Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height))
+       }
+       partBytes := wire.BinaryBytes(part)
+       bs.db.Set(calcBlockPartKey(height, index), partBytes)
+}
+
+//-----------------------------------------------------------------------------
+
+func calcBlockMetaKey(height int) []byte {
+       return []byte(fmt.Sprintf("H:%v", height))
+}
+
+func calcBlockPartKey(height int, partIndex int) []byte {
+       return []byte(fmt.Sprintf("P:%v:%v", height, partIndex))
+}
+
+func calcBlockCommitKey(height int) []byte {
+       return []byte(fmt.Sprintf("C:%v", height))
+}
+
+func calcSeenCommitKey(height int) []byte {
+       return []byte(fmt.Sprintf("SC:%v", height))
+}
+
+//-----------------------------------------------------------------------------
+
+var blockStoreKey = []byte("blockStore")
+
+type BlockStoreStateJSON struct {
+       Height int
+}
+
+func (bsj BlockStoreStateJSON) Save(db dbm.DB) {
+       bytes, err := json.Marshal(bsj)
+       if err != nil {
+               PanicSanity(Fmt("Could not marshal state bytes: %v", err))
+       }
+       db.SetSync(blockStoreKey, bytes)
+}
+
+func LoadBlockStoreStateJSON(db dbm.DB) BlockStoreStateJSON {
+       bytes := db.Get(blockStoreKey)
+       if bytes == nil {
+               return BlockStoreStateJSON{
+                       Height: 0,
+               }
+       }
+       bsj := BlockStoreStateJSON{}
+       err := json.Unmarshal(bytes, &bsj)
+       if err != nil {
+               PanicCrisis(Fmt("Could not unmarshal bytes: %X", bytes))
+       }
+       return bsj
+}
index f9451ff..b673662 100644 (file)
@@ -6,7 +6,7 @@ import (
 
        "github.com/pkg/errors"
 
-       cfg "github.com/node_p2p/config"
+       cfg "github.com/blockchain/config"
        "github.com/tendermint/tmlibs/log"
 )
 
index 13026b8..d69afa0 100644 (file)
@@ -4,27 +4,36 @@ import (
        "os"
 
        "github.com/spf13/cobra"
+       "github.com/spf13/viper"
 
-//     tmflags "github.com/blockchain/cmd/blockchain/commands/flags"
+       tmflags "github.com/blockchain/cmd/blockchain/commands/flags"
+       cfg "github.com/blockchain/config"
        "github.com/tendermint/tmlibs/log"
 )
 
 var (
+       config = cfg.DefaultConfig()
        logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main")
 )
 
 func init() {
-       RootCmd.PersistentFlags().String("log_level", "*:info", "Log level")
+       RootCmd.PersistentFlags().String("log_level", config.LogLevel, "Log level")
 }
 
 var RootCmd = &cobra.Command{
-       Use:   "blockchain",
-       Short: "blockchain in Go",
+       Use:   "node_p2p",
+       Short: "node_p2p in Go",
        PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
-               /*logger, err := tmflags.ParseLogLevel("*:info", logger)
+               err := viper.Unmarshal(config)
                if err != nil {
                        return err
-               }*/
+               }
+               config.SetRoot(config.RootDir)
+               cfg.EnsureRoot(config.RootDir)
+               logger, err = tmflags.ParseLogLevel(config.LogLevel, logger)
+               if err != nil {
+                       return err
+               }
                return nil
        },
 }
diff --git a/config/config.go b/config/config.go
new file mode 100644 (file)
index 0000000..5ff5ae6
--- /dev/null
@@ -0,0 +1,345 @@
+package config
+
+import (
+       "fmt"
+       "path/filepath"
+       "time"
+
+       "github.com/blockchain/types"
+)
+
+type Config struct {
+       // Top level options use an anonymous struct
+       BaseConfig `mapstructure:",squash"`
+
+       // Options for services
+       RPC       *RPCConfig       `mapstructure:"rpc"`
+       P2P       *P2PConfig       `mapstructure:"p2p"`
+       Mempool   *MempoolConfig   `mapstructure:"mempool"`
+       Consensus *ConsensusConfig `mapstructure:"consensus"`
+}
+
+func DefaultConfig() *Config {
+       return &Config{
+               BaseConfig: DefaultBaseConfig(),
+               RPC:        DefaultRPCConfig(),
+               P2P:        DefaultP2PConfig(),
+               Mempool:    DefaultMempoolConfig(),
+               Consensus:  DefaultConsensusConfig(),
+       }
+}
+
+func TestConfig() *Config {
+       return &Config{
+               BaseConfig: TestBaseConfig(),
+               RPC:        TestRPCConfig(),
+               P2P:        TestP2PConfig(),
+               Mempool:    DefaultMempoolConfig(),
+               Consensus:  TestConsensusConfig(),
+       }
+}
+
+// Set the RootDir for all Config structs
+func (cfg *Config) SetRoot(root string) *Config {
+       cfg.BaseConfig.RootDir = root
+       cfg.RPC.RootDir = root
+       cfg.P2P.RootDir = root
+       cfg.Mempool.RootDir = root
+       cfg.Consensus.RootDir = root
+       return cfg
+}
+
+//-----------------------------------------------------------------------------
+// BaseConfig
+
+// BaseConfig struct for a Tendermint node
+type BaseConfig struct {
+       // The root directory for all data.
+       // This should be set in viper so it can unmarshal into this struct
+       RootDir string `mapstructure:"home"`
+
+       // The ID of the chain to join (should be signed with every transaction and vote)
+       ChainID string `mapstructure:"chain_id"`
+
+       // A JSON file containing the initial validator set and other meta data
+       Genesis string `mapstructure:"genesis_file"`
+
+       // A JSON file containing the private key to use as a validator in the consensus protocol
+       PrivValidator string `mapstructure:"priv_validator_file"`
+
+       // A custom human readable name for this node
+       Moniker string `mapstructure:"moniker"`
+
+       // TCP or UNIX socket address of the ABCI application,
+       // or the name of an ABCI application compiled in with the Tendermint binary
+       ProxyApp string `mapstructure:"proxy_app"`
+
+       // Mechanism to connect to the ABCI application: socket | grpc
+       ABCI string `mapstructure:"abci"`
+
+       // Output level for logging
+       LogLevel string `mapstructure:"log_level"`
+
+       // TCP or UNIX socket address for the profiling server to listen on
+       ProfListenAddress string `mapstructure:"prof_laddr"`
+
+       // If this node is many blocks behind the tip of the chain, FastSync
+       // allows them to catchup quickly by downloading blocks in parallel
+       // and verifying their commits
+       FastSync bool `mapstructure:"fast_sync"`
+
+       // If true, query the ABCI app on connecting to a new peer
+       // so the app can decide if we should keep the connection or not
+       FilterPeers bool `mapstructure:"filter_peers"` // false
+
+       // What indexer to use for transactions
+       TxIndex string `mapstructure:"tx_index"`
+
+       // Database backend: leveldb | memdb
+       DBBackend string `mapstructure:"db_backend"`
+
+       // Database directory
+       DBPath string `mapstructure:"db_dir"`
+}
+
+func DefaultBaseConfig() BaseConfig {
+       return BaseConfig{
+               Genesis:           "genesis.json",
+               PrivValidator:     "priv_validator.json",
+               Moniker:           "anonymous",
+               ProxyApp:          "tcp://127.0.0.1:46658",
+               ABCI:              "socket",
+               LogLevel:          DefaultPackageLogLevels(),
+               ProfListenAddress: "",
+               FastSync:          true,
+               FilterPeers:       false,
+               TxIndex:           "kv",
+               DBBackend:         "leveldb",
+               DBPath:            "data",
+       }
+}
+
+func TestBaseConfig() BaseConfig {
+       conf := DefaultBaseConfig()
+       conf.ChainID = "tendermint_test"
+       conf.ProxyApp = "dummy"
+       conf.FastSync = false
+       conf.DBBackend = "memdb"
+       return conf
+}
+
+func (b BaseConfig) GenesisFile() string {
+       return rootify(b.Genesis, b.RootDir)
+}
+
+func (b BaseConfig) PrivValidatorFile() string {
+       return rootify(b.PrivValidator, b.RootDir)
+}
+
+func (b BaseConfig) DBDir() string {
+       return rootify(b.DBPath, b.RootDir)
+}
+
+func DefaultLogLevel() string {
+       return "info"
+}
+
+func DefaultPackageLogLevels() string {
+       return fmt.Sprintf("state:info,*:%s", DefaultLogLevel())
+}
+
+//-----------------------------------------------------------------------------
+// RPCConfig
+
+type RPCConfig struct {
+       RootDir string `mapstructure:"home"`
+
+       // TCP or UNIX socket address for the RPC server to listen on
+       ListenAddress string `mapstructure:"laddr"`
+
+       // TCP or UNIX socket address for the gRPC server to listen on
+       // NOTE: This server only supports /broadcast_tx_commit
+       GRPCListenAddress string `mapstructure:"grpc_laddr"`
+
+       // Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool
+       Unsafe bool `mapstructure:"unsafe"`
+}
+
+func DefaultRPCConfig() *RPCConfig {
+       return &RPCConfig{
+               ListenAddress:     "tcp://0.0.0.0:46657",
+               GRPCListenAddress: "",
+               Unsafe:            false,
+       }
+}
+
+func TestRPCConfig() *RPCConfig {
+       conf := DefaultRPCConfig()
+       conf.ListenAddress = "tcp://0.0.0.0:36657"
+       conf.GRPCListenAddress = "tcp://0.0.0.0:36658"
+       conf.Unsafe = true
+       return conf
+}
+
+//-----------------------------------------------------------------------------
+// P2PConfig
+
+type P2PConfig struct {
+       RootDir        string `mapstructure:"home"`
+       ListenAddress  string `mapstructure:"laddr"`
+       Seeds          string `mapstructure:"seeds"`
+       SkipUPNP       bool   `mapstructure:"skip_upnp"`
+       AddrBook       string `mapstructure:"addr_book_file"`
+       AddrBookStrict bool   `mapstructure:"addr_book_strict"`
+       PexReactor     bool   `mapstructure:"pex"`
+       MaxNumPeers    int    `mapstructure:"max_num_peers"`
+}
+
+func DefaultP2PConfig() *P2PConfig {
+       return &P2PConfig{
+               ListenAddress:  "tcp://0.0.0.0:46656",
+               AddrBook:       "addrbook.json",
+               AddrBookStrict: true,
+               MaxNumPeers:    50,
+       }
+}
+
+func TestP2PConfig() *P2PConfig {
+       conf := DefaultP2PConfig()
+       conf.ListenAddress = "tcp://0.0.0.0:36656"
+       conf.SkipUPNP = true
+       return conf
+}
+
+func (p *P2PConfig) AddrBookFile() string {
+       return rootify(p.AddrBook, p.RootDir)
+}
+
+//-----------------------------------------------------------------------------
+// MempoolConfig
+
+type MempoolConfig struct {
+       RootDir      string `mapstructure:"home"`
+       Recheck      bool   `mapstructure:"recheck"`
+       RecheckEmpty bool   `mapstructure:"recheck_empty"`
+       Broadcast    bool   `mapstructure:"broadcast"`
+       WalPath      string `mapstructure:"wal_dir"`
+}
+
+func DefaultMempoolConfig() *MempoolConfig {
+       return &MempoolConfig{
+               Recheck:      true,
+               RecheckEmpty: true,
+               Broadcast:    true,
+               WalPath:      "data/mempool.wal",
+       }
+}
+
+func (m *MempoolConfig) WalDir() string {
+       return rootify(m.WalPath, m.RootDir)
+}
+
+//-----------------------------------------------------------------------------
+// ConsensusConfig
+
+// ConsensusConfig holds timeouts and details about the WAL, the block structure,
+// and timeouts in the consensus protocol.
+type ConsensusConfig struct {
+       RootDir  string `mapstructure:"home"`
+       WalPath  string `mapstructure:"wal_file"`
+       WalLight bool   `mapstructure:"wal_light"`
+       walFile  string // overrides WalPath if set
+
+       // All timeouts are in ms
+       TimeoutPropose        int `mapstructure:"timeout_propose"`
+       TimeoutProposeDelta   int `mapstructure:"timeout_propose_delta"`
+       TimeoutPrevote        int `mapstructure:"timeout_prevote"`
+       TimeoutPrevoteDelta   int `mapstructure:"timeout_prevote_delta"`
+       TimeoutPrecommit      int `mapstructure:"timeout_precommit"`
+       TimeoutPrecommitDelta int `mapstructure:"timeout_precommit_delta"`
+       TimeoutCommit         int `mapstructure:"timeout_commit"`
+
+       // Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)
+       SkipTimeoutCommit bool `mapstructure:"skip_timeout_commit"`
+
+       // BlockSize
+       MaxBlockSizeTxs   int `mapstructure:"max_block_size_txs"`
+       MaxBlockSizeBytes int `mapstructure:"max_block_size_bytes"`
+
+       // TODO: This probably shouldn't be exposed but it makes it
+       // easy to write tests for the wal/replay
+       BlockPartSize int `mapstructure:"block_part_size"`
+}
+
+// Wait this long for a proposal
+func (cfg *ConsensusConfig) Propose(round int) time.Duration {
+       return time.Duration(cfg.TimeoutPropose+cfg.TimeoutProposeDelta*round) * time.Millisecond
+}
+
+// After receiving any +2/3 prevote, wait this long for stragglers
+func (cfg *ConsensusConfig) Prevote(round int) time.Duration {
+       return time.Duration(cfg.TimeoutPrevote+cfg.TimeoutPrevoteDelta*round) * time.Millisecond
+}
+
+// After receiving any +2/3 precommits, wait this long for stragglers
+func (cfg *ConsensusConfig) Precommit(round int) time.Duration {
+       return time.Duration(cfg.TimeoutPrecommit+cfg.TimeoutPrecommitDelta*round) * time.Millisecond
+}
+
+// After receiving +2/3 precommits for a single block (a commit), wait this long for stragglers in the next height's RoundStepNewHeight
+func (cfg *ConsensusConfig) Commit(t time.Time) time.Time {
+       return t.Add(time.Duration(cfg.TimeoutCommit) * time.Millisecond)
+}
+
+func DefaultConsensusConfig() *ConsensusConfig {
+       return &ConsensusConfig{
+               WalPath:               "data/cs.wal/wal",
+               WalLight:              false,
+               TimeoutPropose:        3000,
+               TimeoutProposeDelta:   500,
+               TimeoutPrevote:        1000,
+               TimeoutPrevoteDelta:   500,
+               TimeoutPrecommit:      1000,
+               TimeoutPrecommitDelta: 500,
+               TimeoutCommit:         1000,
+               SkipTimeoutCommit:     false,
+               MaxBlockSizeTxs:       10000,
+               MaxBlockSizeBytes:     1,                          // TODO
+               BlockPartSize:         types.DefaultBlockPartSize, // TODO: we shouldnt be importing types
+       }
+}
+
+func TestConsensusConfig() *ConsensusConfig {
+       config := DefaultConsensusConfig()
+       config.TimeoutPropose = 2000
+       config.TimeoutProposeDelta = 1
+       config.TimeoutPrevote = 10
+       config.TimeoutPrevoteDelta = 1
+       config.TimeoutPrecommit = 10
+       config.TimeoutPrecommitDelta = 1
+       config.TimeoutCommit = 10
+       config.SkipTimeoutCommit = true
+       return config
+}
+
+func (c *ConsensusConfig) WalFile() string {
+       if c.walFile != "" {
+               return c.walFile
+       }
+       return rootify(c.WalPath, c.RootDir)
+}
+
+func (c *ConsensusConfig) SetWalFile(walFile string) {
+       c.walFile = walFile
+}
+
+//-----------------------------------------------------------------------------
+// Utils
+
+// helper function to make config creation independent of root dir
+func rootify(path, root string) string {
+       if filepath.IsAbs(path) {
+               return path
+       }
+       return filepath.Join(root, path)
+}
diff --git a/config/config_test.go b/config/config_test.go
new file mode 100644 (file)
index 0000000..6379960
--- /dev/null
@@ -0,0 +1,28 @@
+package config
+
+import (
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+)
+
+func TestDefaultConfig(t *testing.T) {
+       assert := assert.New(t)
+
+       // set up some defaults
+       cfg := DefaultConfig()
+       assert.NotNil(cfg.P2P)
+       assert.NotNil(cfg.Mempool)
+       assert.NotNil(cfg.Consensus)
+
+       // check the root dir stuff...
+       cfg.SetRoot("/foo")
+       cfg.Genesis = "bar"
+       cfg.DBPath = "/opt/data"
+       cfg.Mempool.WalPath = "wal/mem/"
+
+       assert.Equal("/foo/bar", cfg.GenesisFile())
+       assert.Equal("/opt/data", cfg.DBDir())
+       assert.Equal("/foo/wal/mem", cfg.Mempool.WalDir())
+
+}
diff --git a/config/toml.go b/config/toml.go
new file mode 100644 (file)
index 0000000..4a5a353
--- /dev/null
@@ -0,0 +1,142 @@
+package config
+
+import (
+       "os"
+       "path"
+       "path/filepath"
+       "strings"
+
+       cmn "github.com/tendermint/tmlibs/common"
+)
+
+/****** these are for production settings ***********/
+
+func EnsureRoot(rootDir string) {
+       cmn.EnsureDir(rootDir, 0700)
+       cmn.EnsureDir(rootDir+"/data", 0700)
+
+       configFilePath := path.Join(rootDir, "config.toml")
+
+       // Write default config file if missing.
+       if !cmn.FileExists(configFilePath) {
+               // Ask user for moniker
+               // moniker := cfg.Prompt("Type hostname: ", "anonymous")
+               cmn.MustWriteFile(configFilePath, []byte(defaultConfig("anonymous")), 0644)
+       }
+}
+
+var defaultConfigTmpl = `# This is a TOML config file.
+# For more information, see https://github.com/toml-lang/toml
+
+proxy_app = "tcp://127.0.0.1:46658"
+moniker = "__MONIKER__"
+fast_sync = true
+db_backend = "leveldb"
+log_level = "state:info,*:info"
+
+[rpc]
+laddr = "tcp://0.0.0.0:46657"
+
+[p2p]
+laddr = "tcp://0.0.0.0:46656"
+seeds = ""
+`
+
+func defaultConfig(moniker string) string {
+       return strings.Replace(defaultConfigTmpl, "__MONIKER__", moniker, -1)
+}
+
+/****** these are for test settings ***********/
+
+func ResetTestRoot(testName string) *Config {
+       rootDir := os.ExpandEnv("$HOME/.tendermint_test")
+       rootDir = filepath.Join(rootDir, testName)
+       // Remove ~/.tendermint_test_bak
+       if cmn.FileExists(rootDir + "_bak") {
+               err := os.RemoveAll(rootDir + "_bak")
+               if err != nil {
+                       cmn.PanicSanity(err.Error())
+               }
+       }
+       // Move ~/.tendermint_test to ~/.tendermint_test_bak
+       if cmn.FileExists(rootDir) {
+               err := os.Rename(rootDir, rootDir+"_bak")
+               if err != nil {
+                       cmn.PanicSanity(err.Error())
+               }
+       }
+       // Create new dir
+       cmn.EnsureDir(rootDir, 0700)
+       cmn.EnsureDir(rootDir+"/data", 0700)
+
+       configFilePath := path.Join(rootDir, "config.toml")
+       genesisFilePath := path.Join(rootDir, "genesis.json")
+       privFilePath := path.Join(rootDir, "priv_validator.json")
+
+       // Write default config file if missing.
+       if !cmn.FileExists(configFilePath) {
+               // Ask user for moniker
+               cmn.MustWriteFile(configFilePath, []byte(testConfig("anonymous")), 0644)
+       }
+       if !cmn.FileExists(genesisFilePath) {
+               cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644)
+       }
+       // we always overwrite the priv val
+       cmn.MustWriteFile(privFilePath, []byte(testPrivValidator), 0644)
+
+       config := TestConfig().SetRoot(rootDir)
+       return config
+}
+
+var testConfigTmpl = `# This is a TOML config file.
+# For more information, see https://github.com/toml-lang/toml
+
+proxy_app = "dummy"
+moniker = "__MONIKER__"
+fast_sync = false
+db_backend = "memdb"
+log_level = "info"
+
+[rpc]
+laddr = "tcp://0.0.0.0:36657"
+
+[p2p]
+laddr = "tcp://0.0.0.0:36656"
+seeds = ""
+`
+
+func testConfig(moniker string) (testConfig string) {
+       testConfig = strings.Replace(testConfigTmpl, "__MONIKER__", moniker, -1)
+       return
+}
+
+var testGenesis = `{
+  "genesis_time": "0001-01-01T00:00:00.000Z",
+  "chain_id": "tendermint_test",
+  "validators": [
+    {
+      "pub_key": {
+        "type": "ed25519",
+        "data":"3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
+      },
+      "amount": 10,
+      "name": ""
+    }
+  ],
+  "app_hash": ""
+}`
+
+var testPrivValidator = `{
+  "address": "D028C9981F7A87F3093672BF0D5B0E2A1B3ED456",
+  "pub_key": {
+    "type": "ed25519",
+    "data": "3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
+  },
+  "priv_key": {
+    "type": "ed25519",
+    "data": "27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
+  },
+  "last_height": 0,
+  "last_round": 0,
+  "last_step": 0
+}`
diff --git a/config/toml_test.go b/config/toml_test.go
new file mode 100644 (file)
index 0000000..d8f372a
--- /dev/null
@@ -0,0 +1,57 @@
+package config
+
+import (
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+)
+
+func ensureFiles(t *testing.T, rootDir string, files ...string) {
+       for _, f := range files {
+               p := rootify(rootDir, f)
+               _, err := os.Stat(p)
+               assert.Nil(t, err, p)
+       }
+}
+
+func TestEnsureRoot(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       // setup temp dir for test
+       tmpDir, err := ioutil.TempDir("", "config-test")
+       require.Nil(err)
+       defer os.RemoveAll(tmpDir)
+
+       // create root dir
+       EnsureRoot(tmpDir)
+
+       // make sure config is set properly
+       data, err := ioutil.ReadFile(filepath.Join(tmpDir, "config.toml"))
+       require.Nil(err)
+       assert.Equal([]byte(defaultConfig("anonymous")), data)
+
+       ensureFiles(t, tmpDir, "data")
+}
+
+func TestEnsureTestRoot(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       testName := "ensureTestRoot"
+
+       // create root dir
+       cfg := ResetTestRoot(testName)
+       rootDir := cfg.RootDir
+
+       // make sure config is set properly
+       data, err := ioutil.ReadFile(filepath.Join(rootDir, "config.toml"))
+       require.Nil(err)
+       assert.Equal([]byte(testConfig("anonymous")), data)
+
+       // TODO: make sure the cfg returned and testconfig are the same!
+
+       ensureFiles(t, rootDir, "data", "genesis.json", "priv_validator.json")
+}
diff --git a/crypto/ed25519/chainkd/bench_test.go b/crypto/ed25519/chainkd/bench_test.go
new file mode 100644 (file)
index 0000000..afadd56
--- /dev/null
@@ -0,0 +1,53 @@
+package chainkd
+
+import (
+       "log"
+       "testing"
+)
+
+var (
+       benchXprv XPrv
+       benchXpub XPub
+       benchMsg  = []byte("Hello, world!")
+       benchSig  []byte
+)
+
+func init() {
+       var err error
+       benchXprv, err = NewXPrv(nil)
+       if err != nil {
+               log.Fatalln(err)
+       }
+       benchXpub = benchXprv.XPub()
+       benchSig = benchXprv.Sign(benchMsg)
+}
+
+func BenchmarkXPrvChildNonHardened(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               benchXprv.Child(benchMsg, false)
+       }
+}
+
+func BenchmarkXPrvChildHardened(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               benchXprv.Child(benchMsg, true)
+       }
+}
+
+func BenchmarkXPubChild(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               benchXpub.Child(benchMsg)
+       }
+}
+
+func BenchmarkXPrvSign(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               benchXprv.Sign(benchMsg)
+       }
+}
+
+func BenchmarkXPubVerify(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               benchXpub.Verify(benchMsg, benchSig)
+       }
+}
diff --git a/crypto/ed25519/chainkd/chainkd.go b/crypto/ed25519/chainkd/chainkd.go
new file mode 100644 (file)
index 0000000..70619bc
--- /dev/null
@@ -0,0 +1,214 @@
+package chainkd
+
+import (
+       "crypto/rand"
+       "crypto/sha512"
+       "encoding/binary"
+       "hash"
+       "io"
+
+       "github.com/blockchain/crypto/ed25519"
+       "github.com/blockchain/crypto/ed25519/internal/edwards25519"
+)
+
+type (
+       // TODO(bobg): consider making these types opaque. See https://github.com/chain/chain/pull/1875#discussion_r80577736
+       XPrv [64]byte
+       XPub [64]byte
+)
+
+var one = [32]byte{1}
+
+// NewXPrv takes a source of random bytes and produces a new XPrv. If
+// r is nil, crypto/rand.Reader is used.
+func NewXPrv(r io.Reader) (xprv XPrv, err error) {
+       if r == nil {
+               r = rand.Reader
+       }
+       var entropy [32]byte
+       _, err = io.ReadFull(r, entropy[:])
+       if err != nil {
+               return xprv, err
+       }
+       hasher := sha512.New()
+       hasher.Write([]byte("Chain seed"))
+       hasher.Write(entropy[:])
+       hasher.Sum(xprv[:0])
+       modifyScalar(xprv[:32])
+       return xprv, nil
+}
+
+func (xprv XPrv) XPub() XPub {
+       var buf [32]byte
+       copy(buf[:], xprv[:32])
+
+       var P edwards25519.ExtendedGroupElement
+       edwards25519.GeScalarMultBase(&P, &buf)
+       P.ToBytes(&buf)
+
+       var xpub XPub
+       copy(xpub[:32], buf[:])
+       copy(xpub[32:], xprv[32:])
+
+       return xpub
+}
+
+func (xprv XPrv) Child(sel []byte, hardened bool) (res XPrv) {
+       if hardened {
+               hashKeySaltSelector(res[:], 0, xprv[:32], xprv[32:], sel)
+               return res
+       }
+
+       var s [32]byte
+       copy(s[:], xprv[:32])
+       var P edwards25519.ExtendedGroupElement
+       edwards25519.GeScalarMultBase(&P, &s)
+
+       var pubkey [32]byte
+       P.ToBytes(&pubkey)
+
+       hashKeySaltSelector(res[:], 1, pubkey[:], xprv[32:], sel)
+
+       var (
+               f  [32]byte
+               s2 [32]byte
+       )
+       copy(f[:], res[:32])
+       edwards25519.ScMulAdd(&s2, &one, &f, &s)
+       copy(res[:32], s2[:])
+
+       return res
+}
+
+func (xpub XPub) Child(sel []byte) (res XPub) {
+       hashKeySaltSelector(res[:], 1, xpub[:32], xpub[32:], sel)
+
+       var (
+               f [32]byte
+               F edwards25519.ExtendedGroupElement
+       )
+       copy(f[:], res[:32])
+       edwards25519.GeScalarMultBase(&F, &f)
+
+       var (
+               pubkey [32]byte
+               P      edwards25519.ExtendedGroupElement
+       )
+       copy(pubkey[:], xpub[:32])
+       P.FromBytes(&pubkey)
+
+       var (
+               P2 edwards25519.ExtendedGroupElement
+               R  edwards25519.CompletedGroupElement
+               Fc edwards25519.CachedGroupElement
+       )
+       F.ToCached(&Fc)
+       edwards25519.GeAdd(&R, &P, &Fc)
+       R.ToExtended(&P2)
+
+       P2.ToBytes(&pubkey)
+
+       copy(res[:32], pubkey[:])
+
+       return res
+}
+
+func (xprv XPrv) Derive(path [][]byte) XPrv {
+       res := xprv
+       for _, p := range path {
+               res = res.Child(p, false)
+       }
+       return res
+}
+
+func (xpub XPub) Derive(path [][]byte) XPub {
+       res := xpub
+       for _, p := range path {
+               res = res.Child(p)
+       }
+       return res
+}
+
+func (xprv XPrv) Sign(msg []byte) []byte {
+       var s [32]byte
+       copy(s[:], xprv[:32])
+
+       var h [64]byte
+       hashKeySalt(h[:], 2, xprv[:32], xprv[32:])
+
+       var P edwards25519.ExtendedGroupElement
+       edwards25519.GeScalarMultBase(&P, &s)
+
+       var pubkey [32]byte
+       P.ToBytes(&pubkey)
+
+       var r [64]byte
+       hasher := sha512.New()
+       hasher.Write(h[:32])
+       hasher.Write(msg)
+       hasher.Sum(r[:0])
+
+       var rReduced [32]byte
+       edwards25519.ScReduce(&rReduced, &r)
+
+       var rPoint edwards25519.ExtendedGroupElement
+       edwards25519.GeScalarMultBase(&rPoint, &rReduced)
+
+       var R [32]byte
+       rPoint.ToBytes(&R)
+
+       hasher.Reset()
+       hasher.Write(R[:])
+       hasher.Write(pubkey[:])
+       hasher.Write(msg)
+
+       var k [64]byte
+       hasher.Sum(k[:0])
+
+       var kReduced [32]byte
+       edwards25519.ScReduce(&kReduced, &k)
+
+       var S [32]byte
+       edwards25519.ScMulAdd(&S, &kReduced, &s, &rReduced)
+
+       return append(R[:], S[:]...)
+}
+
+func (xpub XPub) Verify(msg []byte, sig []byte) bool {
+       return ed25519.Verify(xpub.PublicKey(), msg, sig)
+}
+
+// PublicKey extracts the ed25519 public key from an xpub.
+func (xpub XPub) PublicKey() ed25519.PublicKey {
+       return ed25519.PublicKey(xpub[:32])
+}
+
+func hashKeySaltSelector(out []byte, version byte, key, salt, sel []byte) {
+       hasher := hashKeySaltHelper(version, key, salt)
+       var l [10]byte
+       n := binary.PutUvarint(l[:], uint64(len(sel)))
+       hasher.Write(l[:n])
+       hasher.Write(sel)
+       hasher.Sum(out[:0])
+       modifyScalar(out)
+}
+
+func hashKeySalt(out []byte, version byte, key, salt []byte) {
+       hasher := hashKeySaltHelper(version, key, salt)
+       hasher.Sum(out[:0])
+}
+
+func hashKeySaltHelper(version byte, key, salt []byte) hash.Hash {
+       hasher := sha512.New()
+       hasher.Write([]byte{version})
+       hasher.Write(key)
+       hasher.Write(salt)
+       return hasher
+}
+
+// s must be >= 32 bytes long and gets rewritten in place
+func modifyScalar(s []byte) {
+       s[0] &= 248
+       s[31] &= 127
+       s[31] |= 64
+}
diff --git a/crypto/ed25519/chainkd/chainkd_test.go b/crypto/ed25519/chainkd/chainkd_test.go
new file mode 100644 (file)
index 0000000..7818f2b
--- /dev/null
@@ -0,0 +1,110 @@
+package chainkd
+
+import (
+       "fmt"
+       "reflect"
+       "testing"
+)
+
+func TestChildKeys(t *testing.T) {
+       rootXPrv, err := NewXPrv(nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       rootXPub := rootXPrv.XPub()
+
+       msg := []byte("In the face of ignorance and resistance I wrote financial systems into existence")
+
+       sig := rootXPrv.Sign(msg)
+       doverify(t, rootXPub, msg, sig, "root xpub", "root xprv")
+
+       sel := []byte{1, 2, 3}
+       dprv := rootXPrv.Child(sel, false)
+       dpub := rootXPub.Child(sel)
+
+       sig = dprv.Sign(msg)
+       doverify(t, dpub, msg, sig, "derived xpub", "derived xprv")
+
+       dpub = dprv.XPub()
+       doverify(t, dpub, msg, sig, "xpub from derived xprv", "derived xprv")
+
+       dprv = dprv.Child(sel, false)
+       sig = dprv.Sign(msg)
+       dpub = dpub.Child(sel)
+       doverify(t, dpub, msg, sig, "double-derived xpub", "double-derived xprv")
+
+       for i := byte(0); i < 10; i++ {
+               sel := []byte{i}
+
+               // Non-hardened children
+               dprv := rootXPrv.Child(sel, false)
+               if reflect.DeepEqual(dprv, rootXPrv) {
+                       t.Errorf("derived private key %d is the same as the root", i)
+               }
+               dpub1 := rootXPub.Child(sel)
+               if reflect.DeepEqual(dpub1, rootXPub) {
+                       t.Errorf("derived public key %d is the same as the root", i)
+               }
+               sig := dprv.Sign(msg)
+               doverify(t, dpub1, msg, sig, fmt.Sprintf("derived pubkey (%d)", i), "derived xprv")
+
+               for j := byte(0); j < 10; j++ {
+                       sel2 := []byte{j}
+                       ddprv := dprv.Child(sel2, false)
+                       if reflect.DeepEqual(ddprv, dprv) {
+                               t.Errorf("rootXPrv.Child(%d).Child(%d) is the same as its parent", i, j)
+                       }
+                       ddpub1 := dpub1.Child(sel2)
+                       if reflect.DeepEqual(ddpub1, dpub1) {
+                               t.Errorf("rootXPub.Child(%d).Child(%d) is the same as its parent", i, j)
+                       }
+                       sig = ddprv.Sign(msg)
+                       doverify(t, ddpub1, msg, sig, fmt.Sprintf("double-derived pubkey (%d, %d)", i, j), "double-derived xprv")
+               }
+
+               // Hardened children
+               hdprv := rootXPrv.Child(sel, true)
+               if reflect.DeepEqual(hdprv, rootXPrv) {
+                       t.Errorf("derived hardened privkey %d is the same as the root", i)
+               }
+               if reflect.DeepEqual(hdprv, dprv) {
+                       t.Errorf("derived hardened privkey %d is the same as the unhardened derived privkey", i)
+               }
+               hdpub := hdprv.XPub()
+               if reflect.DeepEqual(hdpub, dpub1) {
+                       t.Errorf("pubkey of hardened child %d is the same as pubkey of non-hardened child", i)
+               }
+               sig = hdprv.Sign(msg)
+               doverify(t, hdpub, msg, sig, fmt.Sprintf("pubkey of hardened child %d", i), "derived xprv")
+       }
+}
+
+func doverify(t *testing.T, xpub XPub, msg, sig []byte, xpubdesc, xprvdesc string) {
+       if !xpub.Verify(msg, sig) {
+               t.Errorf("%s cannot verify signature from %s", xpubdesc, xprvdesc)
+       }
+
+       for i := 0; i < 32; i++ {
+               xpub[i] ^= 0xff
+               if xpub.Verify(msg, sig) {
+                       t.Fatalf("altered %s should not verify signature from %s", xpubdesc, xprvdesc)
+               }
+               xpub[i] ^= 0xff
+       }
+
+       for i := 0; i < len(msg); i++ {
+               msg[i] ^= 0xff
+               if xpub.Verify(msg, sig) {
+                       t.Fatalf("%s should not verify signature from %s against altered message", xpubdesc, xprvdesc)
+               }
+               msg[i] ^= 0xff
+       }
+
+       for i := 0; i < len(sig); i++ {
+               sig[i] ^= 0xff
+               if xpub.Verify(msg, sig) {
+                       t.Fatalf("%s should not verify altered signature from %s", xpubdesc, xprvdesc)
+               }
+               sig[i] ^= 0xff
+       }
+}
diff --git a/crypto/ed25519/chainkd/serialize.go b/crypto/ed25519/chainkd/serialize.go
new file mode 100644 (file)
index 0000000..81b435a
--- /dev/null
@@ -0,0 +1,60 @@
+package chainkd
+
+import (
+       "encoding/hex"
+       "errors"
+)
+
+const (
+       extendedPublicKeySize  = 64
+       extendedPrivateKeySize = 64
+)
+
+var (
+       ErrBadKeyLen = errors.New("bad key length")
+       ErrBadKeyStr = errors.New("bad key string")
+)
+
+func (xpub XPub) MarshalText() ([]byte, error) {
+       hexBytes := make([]byte, hex.EncodedLen(len(xpub.Bytes())))
+       hex.Encode(hexBytes, xpub.Bytes())
+       return hexBytes, nil
+}
+
+func (xpub XPub) Bytes() []byte {
+       return xpub[:]
+}
+
+func (xprv XPrv) MarshalText() ([]byte, error) {
+       hexBytes := make([]byte, hex.EncodedLen(len(xprv.Bytes())))
+       hex.Encode(hexBytes, xprv.Bytes())
+       return hexBytes, nil
+}
+
+func (xprv XPrv) Bytes() []byte {
+       return xprv[:]
+}
+
+func (xpub *XPub) UnmarshalText(inp []byte) error {
+       if len(inp) != 2*extendedPublicKeySize {
+               return ErrBadKeyStr
+       }
+       _, err := hex.Decode(xpub[:], inp)
+       return err
+}
+
+func (xpub XPub) String() string {
+       return hex.EncodeToString(xpub.Bytes())
+}
+
+func (xprv *XPrv) UnmarshalText(inp []byte) error {
+       if len(inp) != 2*extendedPrivateKeySize {
+               return ErrBadKeyStr
+       }
+       _, err := hex.Decode(xprv[:], inp)
+       return err
+}
+
+func (xprv XPrv) String() string {
+       return hex.EncodeToString(xprv.Bytes())
+}
diff --git a/crypto/ed25519/chainkd/serialize_test.go b/crypto/ed25519/chainkd/serialize_test.go
new file mode 100644 (file)
index 0000000..f58dbf3
--- /dev/null
@@ -0,0 +1,38 @@
+package chainkd
+
+import (
+       "bytes"
+       "reflect"
+       "testing"
+
+       "encoding/hex"
+       "encoding/json"
+)
+
+func TestMarshalingFuncs(t *testing.T) {
+       xprv, err := NewXPrv(nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       want := make([]byte, hex.EncodedLen(len(xprv.Bytes())))
+       hex.Encode(want, xprv.Bytes())
+
+       got, err := json.Marshal(xprv)
+       if err != nil {
+               t.Fatal(err)
+       }
+       // First and last bytes are "
+       if !reflect.DeepEqual(want, got[1:len(got)-1]) {
+               t.Errorf("marshaling error: want = %+v, got = %+v", want, got)
+       }
+
+       secXprv := new(XPrv)
+       err = json.Unmarshal(got, &secXprv)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !bytes.Equal(xprv[:], secXprv[:]) {
+               t.Errorf("unmarshaling error: want = %+v, got = %+v", xprv, secXprv)
+       }
+}
diff --git a/crypto/ed25519/chainkd/util.go b/crypto/ed25519/chainkd/util.go
new file mode 100644 (file)
index 0000000..c06e611
--- /dev/null
@@ -0,0 +1,34 @@
+package chainkd
+
+import (
+       "io"
+
+       "github.com/blockchain/crypto/ed25519"
+)
+
+// Utility functions
+
+func NewXKeys(r io.Reader) (xprv XPrv, xpub XPub, err error) {
+       xprv, err = NewXPrv(r)
+       if err != nil {
+               return
+       }
+       return xprv, xprv.XPub(), nil
+}
+
+func XPubKeys(xpubs []XPub) []ed25519.PublicKey {
+       res := make([]ed25519.PublicKey, 0, len(xpubs))
+       for _, xpub := range xpubs {
+               res = append(res, xpub.PublicKey())
+       }
+       return res
+}
+
+func DeriveXPubs(xpubs []XPub, path [][]byte) []XPub {
+       res := make([]XPub, 0, len(xpubs))
+       for _, xpub := range xpubs {
+               d := xpub.Derive(path)
+               res = append(res, d)
+       }
+       return res
+}
diff --git a/crypto/ed25519/ecmath/point.go b/crypto/ed25519/ecmath/point.go
new file mode 100644 (file)
index 0000000..23cfeae
--- /dev/null
@@ -0,0 +1,91 @@
+package ecmath
+
+import (
+       "crypto/subtle"
+
+       "github.com/blockchain/crypto/ed25519/internal/edwards25519"
+)
+
+// Point is a point on the ed25519 curve.
+type Point edwards25519.ExtendedGroupElement
+
+// ZeroPoint is the zero point on the ed25519 curve (not the zero value of Point).
+var ZeroPoint Point
+
+// Add adds the points in x and y, storing the result in z and
+// returning that. Any or all of x, y, and z may be the same pointers.
+func (z *Point) Add(x, y *Point) *Point {
+       var y2 edwards25519.CachedGroupElement
+       (*edwards25519.ExtendedGroupElement)(y).ToCached(&y2)
+
+       var z2 edwards25519.CompletedGroupElement
+       edwards25519.GeAdd(&z2, (*edwards25519.ExtendedGroupElement)(x), &y2)
+
+       z2.ToExtended((*edwards25519.ExtendedGroupElement)(z))
+       return z
+}
+
+// Sub subtracts y from x, storing the result in z and
+// returning that. Any or all of x, y, and z may be the same pointers.
+func (z *Point) Sub(x, y *Point) *Point {
+       var y2 edwards25519.CachedGroupElement
+       (*edwards25519.ExtendedGroupElement)(y).ToCached(&y2)
+
+       var z2 edwards25519.CompletedGroupElement
+       edwards25519.GeSub(&z2, (*edwards25519.ExtendedGroupElement)(x), &y2)
+
+       z2.ToExtended((*edwards25519.ExtendedGroupElement)(z))
+       return z
+}
+
+// ScMul multiplies the EC point x by the scalar y, placing the result
+// in z and returning that. X and z may be the same pointer.
+func (z *Point) ScMul(x *Point, y *Scalar) *Point {
+       return z.ScMulAdd(x, y, &Zero)
+}
+
+// ScMulBase multiplies the ed25519 base point by x and places the
+// result in z, returning that.
+func (z *Point) ScMulBase(x *Scalar) *Point {
+       edwards25519.GeScalarMultBase((*edwards25519.ExtendedGroupElement)(z), (*[32]byte)(x))
+       return z
+}
+
+// ScMulAdd computes xa+yB, where B is the ed25519 base point, and
+// places the result in z, returning that.
+func (z *Point) ScMulAdd(a *Point, x, y *Scalar) *Point {
+       // TODO: replace with constant-time implementation to avoid
+       // sidechannel attacks
+
+       var p edwards25519.ProjectiveGroupElement
+       edwards25519.GeDoubleScalarMultVartime(&p, (*[32]byte)(x), (*edwards25519.ExtendedGroupElement)(a), (*[32]byte)(y))
+
+       var buf [32]byte
+       p.ToBytes(&buf)
+       // TODO(bobg): double-check that it's OK to ignore the return value
+       // from ExtendedGroupElement.FromBytes here. (It's a bool indicating
+       // that its input represented a legal value.)
+       (*edwards25519.ExtendedGroupElement)(z).FromBytes(&buf)
+       return z
+}
+
+func (z *Point) Encode() [32]byte {
+       var e [32]byte
+       (*edwards25519.ExtendedGroupElement)(z).ToBytes(&e)
+       return e
+}
+
+func (z *Point) Decode(e [32]byte) (*Point, bool) {
+       ok := (*edwards25519.ExtendedGroupElement)(z).FromBytes(&e)
+       return z, ok
+}
+
+func (z *Point) ConstTimeEqual(x *Point) bool {
+       xe := x.Encode()
+       ze := z.Encode()
+       return subtle.ConstantTimeCompare(xe[:], ze[:]) == 1
+}
+
+func init() {
+       (*edwards25519.ExtendedGroupElement)(&ZeroPoint).Zero()
+}
diff --git a/crypto/ed25519/ecmath/point_test.go b/crypto/ed25519/ecmath/point_test.go
new file mode 100644 (file)
index 0000000..4de539a
--- /dev/null
@@ -0,0 +1,44 @@
+package ecmath
+
+import "testing"
+
+// base is the ed25519 base point
+var base Point
+
+func init() {
+       base.ScMulBase(&One)
+}
+
+func TestBasePointArith(t *testing.T) {
+       var base1 Point
+       base1.ScMul(&base, &One)
+       if !base.ConstTimeEqual(&base1) {
+               ebase := base.Encode()
+               ebase1 := base1.Encode()
+               t.Errorf("base [%x] != 1*base [%x]", ebase[:], ebase1[:])
+       }
+
+       Two := One
+       Two.Add(&Two, &One)
+
+       base2a := base
+       base2a.Add(&base2a, &base)
+
+       base2b := base
+       base2b.ScMul(&base2b, &Two)
+
+       if !base2a.ConstTimeEqual(&base2b) {
+               ebase2a := base2a.Encode()
+               ebase2b := base2b.Encode()
+               t.Errorf("base+base [%x] != 2*base [%x] (1)", ebase2a[:], ebase2b[:])
+       }
+
+       var base2c Point
+       base2c.ScMulBase(&Two)
+
+       if !base2a.ConstTimeEqual(&base2c) {
+               ebase2a := base2a.Encode()
+               ebase2c := base2c.Encode()
+               t.Errorf("base+base [%x] != 2*base [%x] (2)", ebase2a[:], ebase2c[:])
+       }
+}
diff --git a/crypto/ed25519/ecmath/scalar.go b/crypto/ed25519/ecmath/scalar.go
new file mode 100644 (file)
index 0000000..15634a1
--- /dev/null
@@ -0,0 +1,78 @@
+package ecmath
+
+import (
+       "crypto/subtle"
+
+       "github.com/blockchain/crypto/ed25519/internal/edwards25519"
+)
+
+// Scalar is a 256-bit little-endian scalar.
+type Scalar [32]byte
+
+var (
+       // Zero is the number 0.
+       Zero Scalar
+
+       // One is the number 1.
+       One = Scalar{1}
+
+       // NegOne is the number -1 mod L
+       NegOne = Scalar{
+               0xec, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
+               0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+       }
+
+       // L is the subgroup order:
+       // 2^252 + 27742317777372353535851937790883648493
+       L = Scalar{
+               0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
+               0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+       }
+)
+
+// Add computes x+y (mod L) and places the result in z, returning
+// that. Any or all of x, y, and z may be the same pointer.
+func (z *Scalar) Add(x, y *Scalar) *Scalar {
+       return z.MulAdd(x, &One, y)
+}
+
+// Sub computes x-y (mod L) and places the result in z, returning
+// that. Any or all of x, y, and z may be the same pointer.
+func (z *Scalar) Sub(x, y *Scalar) *Scalar {
+       return z.MulAdd(y, &NegOne, x)
+}
+
+// Neg negates x (mod L) and places the result in z, returning that. X
+// and z may be the same pointer.
+func (z *Scalar) Neg(x *Scalar) *Scalar {
+       return z.MulAdd(x, &NegOne, &Zero)
+}
+
+// MulAdd computes ab+c (mod L) and places the result in z, returning
+// that. Any or all of the pointers may be the same.
+func (z *Scalar) MulAdd(a, b, c *Scalar) *Scalar {
+       edwards25519.ScMulAdd((*[32]byte)(z), (*[32]byte)(a), (*[32]byte)(b), (*[32]byte)(c))
+       return z
+}
+
+func (z *Scalar) Equal(x *Scalar) bool {
+       return subtle.ConstantTimeCompare(x[:], z[:]) == 1
+}
+
+// Prune performs the pruning operation in-place.
+func (z *Scalar) Prune() {
+       z[0] &= 248
+       z[31] &= 127
+       z[31] |= 64
+}
+
+// Reduce takes a 512-bit scalar and reduces it mod L, placing the
+// result in z and returning that.
+func (z *Scalar) Reduce(x *[64]byte) *Scalar {
+       edwards25519.ScReduce((*[32]byte)(z), x)
+       return z
+}
diff --git a/crypto/ed25519/ed25519.go b/crypto/ed25519/ed25519.go
new file mode 100644 (file)
index 0000000..ba08abb
--- /dev/null
@@ -0,0 +1,182 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file at
+// https://github.com/golang/crypto/blob/master/LICENSE.
+
+// Package ed25519 implements the Ed25519 signature algorithm. See
+// http://ed25519.cr.yp.to/.
+//
+// These functions are also compatible with the “Ed25519” function defined in
+// https://tools.ietf.org/html/draft-irtf-cfrg-eddsa-05.
+package ed25519
+
+// This code is a port of the public domain, “ref10” implementation of ed25519
+// from SUPERCOP.
+
+import (
+       "crypto"
+       cryptorand "crypto/rand"
+       "crypto/sha512"
+       "crypto/subtle"
+       "errors"
+       "io"
+       "strconv"
+
+       "github.com/blockchain/crypto/ed25519/internal/edwards25519"
+)
+
+const (
+       // PublicKeySize is the size, in bytes, of public keys as used in this package.
+       PublicKeySize = 32
+       // PrivateKeySize is the size, in bytes, of private keys as used in this package.
+       PrivateKeySize = 64
+       // SignatureSize is the size, in bytes, of signatures generated and verified by this package.
+       SignatureSize = 64
+)
+
+// PublicKey is the type of Ed25519 public keys.
+type PublicKey []byte
+
+// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
+type PrivateKey []byte
+
+// Public returns the PublicKey corresponding to priv.
+func (priv PrivateKey) Public() crypto.PublicKey {
+       publicKey := make([]byte, PublicKeySize)
+       copy(publicKey, priv[32:])
+       return PublicKey(publicKey)
+}
+
+// Sign signs the given message with priv.
+// Ed25519 performs two passes over messages to be signed and therefore cannot
+// handle pre-hashed messages. Thus opts.HashFunc() must return zero to
+// indicate the message hasn't been hashed. This can be achieved by passing
+// crypto.Hash(0) as the value for opts.
+func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
+       if opts.HashFunc() != crypto.Hash(0) {
+               return nil, errors.New("ed25519: cannot sign hashed message")
+       }
+
+       return Sign(priv, message), nil
+}
+
+// GenerateKey generates a public/private key pair using entropy from rand.
+// If rand is nil, crypto/rand.Reader will be used.
+func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) {
+       if rand == nil {
+               rand = cryptorand.Reader
+       }
+
+       privateKey = make([]byte, PrivateKeySize)
+       publicKey = make([]byte, PublicKeySize)
+       _, err = io.ReadFull(rand, privateKey[:32])
+       if err != nil {
+               return nil, nil, err
+       }
+
+       digest := sha512.Sum512(privateKey[:32])
+       digest[0] &= 248
+       digest[31] &= 127
+       digest[31] |= 64
+
+       var A edwards25519.ExtendedGroupElement
+       var hBytes [32]byte
+       copy(hBytes[:], digest[:])
+       edwards25519.GeScalarMultBase(&A, &hBytes)
+       var publicKeyBytes [32]byte
+       A.ToBytes(&publicKeyBytes)
+
+       copy(privateKey[32:], publicKeyBytes[:])
+       copy(publicKey, publicKeyBytes[:])
+
+       return publicKey, privateKey, nil
+}
+
+// Sign signs the message with privateKey and returns a signature. It will
+// panic if len(privateKey) is not PrivateKeySize.
+func Sign(privateKey PrivateKey, message []byte) []byte {
+       if l := len(privateKey); l != PrivateKeySize {
+               panic("ed25519: bad private key length: " + strconv.Itoa(l))
+       }
+
+       h := sha512.New()
+       h.Write(privateKey[:32])
+
+       var digest1, messageDigest, hramDigest [64]byte
+       var expandedSecretKey [32]byte
+       h.Sum(digest1[:0])
+       copy(expandedSecretKey[:], digest1[:])
+       expandedSecretKey[0] &= 248
+       expandedSecretKey[31] &= 63
+       expandedSecretKey[31] |= 64
+
+       h.Reset()
+       h.Write(digest1[32:])
+       h.Write(message)
+       h.Sum(messageDigest[:0])
+
+       var messageDigestReduced [32]byte
+       edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
+       var R edwards25519.ExtendedGroupElement
+       edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
+
+       var encodedR [32]byte
+       R.ToBytes(&encodedR)
+
+       h.Reset()
+       h.Write(encodedR[:])
+       h.Write(privateKey[32:])
+       h.Write(message)
+       h.Sum(hramDigest[:0])
+       var hramDigestReduced [32]byte
+       edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
+
+       var s [32]byte
+       edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
+
+       signature := make([]byte, SignatureSize)
+       copy(signature[:], encodedR[:])
+       copy(signature[32:], s[:])
+
+       return signature
+}
+
+// Verify reports whether sig is a valid signature of message by publicKey. It
+// will panic if len(publicKey) is not PublicKeySize.
+func Verify(publicKey PublicKey, message, sig []byte) bool {
+       if l := len(publicKey); l != PublicKeySize {
+               panic("ed25519: bad public key length: " + strconv.Itoa(l))
+       }
+
+       if len(sig) != SignatureSize || sig[63]&224 != 0 {
+               return false
+       }
+
+       var A edwards25519.ExtendedGroupElement
+       var publicKeyBytes [32]byte
+       copy(publicKeyBytes[:], publicKey)
+       if !A.FromBytes(&publicKeyBytes) {
+               return false
+       }
+       edwards25519.FeNeg(&A.X, &A.X)
+       edwards25519.FeNeg(&A.T, &A.T)
+
+       h := sha512.New()
+       h.Write(sig[:32])
+       h.Write(publicKey[:])
+       h.Write(message)
+       var digest [64]byte
+       h.Sum(digest[:0])
+
+       var hReduced [32]byte
+       edwards25519.ScReduce(&hReduced, &digest)
+
+       var R edwards25519.ProjectiveGroupElement
+       var b [32]byte
+       copy(b[:], sig[32:])
+       edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b)
+
+       var checkR [32]byte
+       R.ToBytes(&checkR)
+       return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1
+}
diff --git a/crypto/ed25519/ed25519_test.go b/crypto/ed25519/ed25519_test.go
new file mode 100644 (file)
index 0000000..36ff657
--- /dev/null
@@ -0,0 +1,184 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file at
+// https://github.com/golang/crypto/blob/master/LICENSE.
+
+package ed25519
+
+import (
+       "bufio"
+       "bytes"
+       "compress/gzip"
+       "crypto"
+       "crypto/rand"
+       "encoding/hex"
+       "os"
+       "strings"
+       "testing"
+
+       "github.com/blockchain/crypto/ed25519/internal/edwards25519"
+)
+
+type zeroReader struct{}
+
+func (zeroReader) Read(buf []byte) (int, error) {
+       for i := range buf {
+               buf[i] = 0
+       }
+       return len(buf), nil
+}
+
+func TestUnmarshalMarshal(t *testing.T) {
+       pub, _, _ := GenerateKey(rand.Reader)
+
+       var A edwards25519.ExtendedGroupElement
+       var pubBytes [32]byte
+       copy(pubBytes[:], pub)
+       if !A.FromBytes(&pubBytes) {
+               t.Fatalf("ExtendedGroupElement.FromBytes failed")
+       }
+
+       var pub2 [32]byte
+       A.ToBytes(&pub2)
+
+       if pubBytes != pub2 {
+               t.Errorf("FromBytes(%v)->ToBytes does not round-trip, got %x\n", pubBytes, pub2)
+       }
+}
+
+func TestSignVerify(t *testing.T) {
+       var zero zeroReader
+       public, private, _ := GenerateKey(zero)
+
+       message := []byte("test message")
+       sig := Sign(private, message)
+       if !Verify(public, message, sig) {
+               t.Errorf("valid signature rejected")
+       }
+
+       wrongMessage := []byte("wrong message")
+       if Verify(public, wrongMessage, sig) {
+               t.Errorf("signature of different message accepted")
+       }
+}
+
+func TestCryptoSigner(t *testing.T) {
+       var zero zeroReader
+       public, private, _ := GenerateKey(zero)
+
+       signer := crypto.Signer(private)
+
+       publicInterface := signer.Public()
+       public2, ok := publicInterface.(PublicKey)
+       if !ok {
+               t.Fatalf("expected PublicKey from Public() but got %T", publicInterface)
+       }
+
+       if !bytes.Equal(public, public2) {
+               t.Errorf("public keys do not match: original:%x vs Public():%x", public, public2)
+       }
+
+       message := []byte("message")
+       var noHash crypto.Hash
+       signature, err := signer.Sign(zero, message, noHash)
+       if err != nil {
+               t.Fatalf("error from Sign(): %s", err)
+       }
+
+       if !Verify(public, message, signature) {
+               t.Errorf("Verify failed on signature from Sign()")
+       }
+}
+
+func TestGolden(t *testing.T) {
+       // sign.input.gz is a selection of test cases from
+       // http://ed25519.cr.yp.to/python/sign.input
+       testDataZ, err := os.Open("testdata/sign.input.gz")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer testDataZ.Close()
+       testData, err := gzip.NewReader(testDataZ)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer testData.Close()
+
+       scanner := bufio.NewScanner(testData)
+       lineNo := 0
+
+       for scanner.Scan() {
+               lineNo++
+
+               line := scanner.Text()
+               parts := strings.Split(line, ":")
+               if len(parts) != 5 {
+                       t.Fatalf("bad number of parts on line %d", lineNo)
+               }
+
+               privBytes, _ := hex.DecodeString(parts[0])
+               pubKey, _ := hex.DecodeString(parts[1])
+               msg, _ := hex.DecodeString(parts[2])
+               sig, _ := hex.DecodeString(parts[3])
+               // The signatures in the test vectors also include the message
+               // at the end, but we just want R and S.
+               sig = sig[:SignatureSize]
+
+               if l := len(pubKey); l != PublicKeySize {
+                       t.Fatalf("bad public key length on line %d: got %d bytes", lineNo, l)
+               }
+
+               var priv [PrivateKeySize]byte
+               copy(priv[:], privBytes)
+               copy(priv[32:], pubKey)
+
+               sig2 := Sign(priv[:], msg)
+               if !bytes.Equal(sig, sig2[:]) {
+                       t.Errorf("different signature result on line %d: %x vs %x", lineNo, sig, sig2)
+               }
+
+               if !Verify(pubKey, msg, sig2) {
+                       t.Errorf("signature failed to verify on line %d", lineNo)
+               }
+       }
+
+       if err := scanner.Err(); err != nil {
+               t.Fatalf("error reading test data: %s", err)
+       }
+}
+
+func BenchmarkKeyGeneration(b *testing.B) {
+       var zero zeroReader
+       for i := 0; i < b.N; i++ {
+               if _, _, err := GenerateKey(zero); err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
+func BenchmarkSigning(b *testing.B) {
+       var zero zeroReader
+       _, priv, err := GenerateKey(zero)
+       if err != nil {
+               b.Fatal(err)
+       }
+       message := []byte("Hello, world!")
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               Sign(priv, message)
+       }
+}
+
+func BenchmarkVerification(b *testing.B) {
+       var zero zeroReader
+       pub, priv, err := GenerateKey(zero)
+       if err != nil {
+               b.Fatal(err)
+       }
+       message := []byte("Hello, world!")
+       signature := Sign(priv, message)
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               Verify(pub, message, signature)
+       }
+}
diff --git a/crypto/ed25519/internal/edwards25519/chain_export.go b/crypto/ed25519/internal/edwards25519/chain_export.go
new file mode 100644 (file)
index 0000000..da88750
--- /dev/null
@@ -0,0 +1,6 @@
+package edwards25519
+
+var (
+       GeAdd = geAdd
+       GeSub = geSub
+)
diff --git a/crypto/ed25519/internal/edwards25519/const.go b/crypto/ed25519/internal/edwards25519/const.go
new file mode 100644 (file)
index 0000000..4ccdeb7
--- /dev/null
@@ -0,0 +1,1423 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file at
+// https://github.com/golang/crypto/blob/master/LICENSE.
+
+package edwards25519
+
+// These values are from the public domain, “ref10” implementation of ed25519
+// from SUPERCOP.
+
+// d is a constant in the Edwards curve equation.
+var d = FieldElement{
+       -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116,
+}
+
+// d2 is 2*d.
+var d2 = FieldElement{
+       -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199,
+}
+
+// SqrtM1 is the square-root of -1 in the field.
+var SqrtM1 = FieldElement{
+       -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482,
+}
+
+// A is a constant in the Montgomery-form of curve25519.
+var A = FieldElement{
+       486662, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+}
+
+// bi contains precomputed multiples of the base-point. See the Ed25519 paper
+// for a discussion about how these values are used.
+var bi = [8]PreComputedGroupElement{
+       {
+               FieldElement{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605},
+               FieldElement{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378},
+               FieldElement{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546},
+       },
+       {
+               FieldElement{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024},
+               FieldElement{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574},
+               FieldElement{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357},
+       },
+       {
+               FieldElement{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380},
+               FieldElement{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306},
+               FieldElement{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942},
+       },
+       {
+               FieldElement{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766},
+               FieldElement{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701},
+               FieldElement{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300},
+       },
+       {
+               FieldElement{-22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877},
+               FieldElement{-6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951},
+               FieldElement{4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784},
+       },
+       {
+               FieldElement{-25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436},
+               FieldElement{25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918},
+               FieldElement{23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877},
+       },
+       {
+               FieldElement{-33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800},
+               FieldElement{-25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305},
+               FieldElement{-13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300},
+       },
+       {
+               FieldElement{-3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876},
+               FieldElement{-24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619},
+               FieldElement{-3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683},
+       },
+}
+
+// base contains precomputed multiples of the base-point. See the Ed25519 paper
+// for a discussion about how these values are used.
+var base = [32][8]PreComputedGroupElement{
+       {
+               {
+                       FieldElement{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605},
+                       FieldElement{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378},
+                       FieldElement{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546},
+               },
+               {
+                       FieldElement{-12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303},
+                       FieldElement{-21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081},
+                       FieldElement{26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697},
+               },
+               {
+                       FieldElement{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024},
+                       FieldElement{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574},
+                       FieldElement{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357},
+               },
+               {
+                       FieldElement{-17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540},
+                       FieldElement{23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397},
+                       FieldElement{7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325},
+               },
+               {
+                       FieldElement{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380},
+                       FieldElement{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306},
+                       FieldElement{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942},
+               },
+               {
+                       FieldElement{-15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777},
+                       FieldElement{-8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737},
+                       FieldElement{-18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652},
+               },
+               {
+                       FieldElement{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766},
+                       FieldElement{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701},
+                       FieldElement{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300},
+               },
+               {
+                       FieldElement{14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726},
+                       FieldElement{-7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955},
+                       FieldElement{27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425},
+               },
+       },
+       {
+               {
+                       FieldElement{-13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171},
+                       FieldElement{27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510},
+                       FieldElement{17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660},
+               },
+               {
+                       FieldElement{-10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639},
+                       FieldElement{29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963},
+                       FieldElement{5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950},
+               },
+               {
+                       FieldElement{-27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568},
+                       FieldElement{12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335},
+                       FieldElement{25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628},
+               },
+               {
+                       FieldElement{-26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007},
+                       FieldElement{-2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772},
+                       FieldElement{-22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653},
+               },
+               {
+                       FieldElement{2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567},
+                       FieldElement{13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686},
+                       FieldElement{21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372},
+               },
+               {
+                       FieldElement{-13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887},
+                       FieldElement{-23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954},
+                       FieldElement{-29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953},
+               },
+               {
+                       FieldElement{24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833},
+                       FieldElement{-16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532},
+                       FieldElement{-22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876},
+               },
+               {
+                       FieldElement{2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268},
+                       FieldElement{33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214},
+                       FieldElement{1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038},
+               },
+       },
+       {
+               {
+                       FieldElement{6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800},
+                       FieldElement{4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645},
+                       FieldElement{-4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664},
+               },
+               {
+                       FieldElement{1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933},
+                       FieldElement{-25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182},
+                       FieldElement{-17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222},
+               },
+               {
+                       FieldElement{-18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991},
+                       FieldElement{20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880},
+                       FieldElement{9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092},
+               },
+               {
+                       FieldElement{-16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295},
+                       FieldElement{19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788},
+                       FieldElement{8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553},
+               },
+               {
+                       FieldElement{-15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026},
+                       FieldElement{11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347},
+                       FieldElement{-18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033},
+               },
+               {
+                       FieldElement{-23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395},
+                       FieldElement{-27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278},
+                       FieldElement{1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890},
+               },
+               {
+                       FieldElement{32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995},
+                       FieldElement{-30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596},
+                       FieldElement{-11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891},
+               },
+               {
+                       FieldElement{31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060},
+                       FieldElement{11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608},
+                       FieldElement{-20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606},
+               },
+       },
+       {
+               {
+                       FieldElement{7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389},
+                       FieldElement{-19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016},
+                       FieldElement{-11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341},
+               },
+               {
+                       FieldElement{-22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505},
+                       FieldElement{14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553},
+                       FieldElement{-28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655},
+               },
+               {
+                       FieldElement{15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220},
+                       FieldElement{12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631},
+                       FieldElement{-4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099},
+               },
+               {
+                       FieldElement{26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556},
+                       FieldElement{14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749},
+                       FieldElement{236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930},
+               },
+               {
+                       FieldElement{1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391},
+                       FieldElement{5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253},
+                       FieldElement{20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066},
+               },
+               {
+                       FieldElement{24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958},
+                       FieldElement{-11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082},
+                       FieldElement{-28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383},
+               },
+               {
+                       FieldElement{-30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521},
+                       FieldElement{-11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807},
+                       FieldElement{23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948},
+               },
+               {
+                       FieldElement{9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134},
+                       FieldElement{-32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455},
+                       FieldElement{27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629},
+               },
+       },
+       {
+               {
+                       FieldElement{-8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069},
+                       FieldElement{-32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746},
+                       FieldElement{24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919},
+               },
+               {
+                       FieldElement{11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837},
+                       FieldElement{8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906},
+                       FieldElement{-28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771},
+               },
+               {
+                       FieldElement{-25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817},
+                       FieldElement{10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098},
+                       FieldElement{10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409},
+               },
+               {
+                       FieldElement{-12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504},
+                       FieldElement{-26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727},
+                       FieldElement{28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420},
+               },
+               {
+                       FieldElement{-32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003},
+                       FieldElement{-1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605},
+                       FieldElement{-30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384},
+               },
+               {
+                       FieldElement{-26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701},
+                       FieldElement{-23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683},
+                       FieldElement{29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708},
+               },
+               {
+                       FieldElement{-3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563},
+                       FieldElement{-19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260},
+                       FieldElement{-5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387},
+               },
+               {
+                       FieldElement{-19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672},
+                       FieldElement{23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686},
+                       FieldElement{-24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665},
+               },
+       },
+       {
+               {
+                       FieldElement{11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182},
+                       FieldElement{-31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277},
+                       FieldElement{14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628},
+               },
+               {
+                       FieldElement{-4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474},
+                       FieldElement{-26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539},
+                       FieldElement{-25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822},
+               },
+               {
+                       FieldElement{-10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970},
+                       FieldElement{19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756},
+                       FieldElement{-24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508},
+               },
+               {
+                       FieldElement{-26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683},
+                       FieldElement{-10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655},
+                       FieldElement{-20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158},
+               },
+               {
+                       FieldElement{-4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125},
+                       FieldElement{-15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839},
+                       FieldElement{-20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664},
+               },
+               {
+                       FieldElement{27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294},
+                       FieldElement{-18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899},
+                       FieldElement{-11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070},
+               },
+               {
+                       FieldElement{3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294},
+                       FieldElement{-15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949},
+                       FieldElement{-21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083},
+               },
+               {
+                       FieldElement{31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420},
+                       FieldElement{-5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940},
+                       FieldElement{29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396},
+               },
+       },
+       {
+               {
+                       FieldElement{-12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567},
+                       FieldElement{20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127},
+                       FieldElement{-16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294},
+               },
+               {
+                       FieldElement{-12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887},
+                       FieldElement{22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964},
+                       FieldElement{16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195},
+               },
+               {
+                       FieldElement{9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244},
+                       FieldElement{24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999},
+                       FieldElement{-1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762},
+               },
+               {
+                       FieldElement{-18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274},
+                       FieldElement{-33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236},
+                       FieldElement{-16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605},
+               },
+               {
+                       FieldElement{-13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761},
+                       FieldElement{-22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884},
+                       FieldElement{-6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482},
+               },
+               {
+                       FieldElement{-24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638},
+                       FieldElement{-11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490},
+                       FieldElement{-32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170},
+               },
+               {
+                       FieldElement{5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736},
+                       FieldElement{10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124},
+                       FieldElement{-17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392},
+               },
+               {
+                       FieldElement{8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029},
+                       FieldElement{6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048},
+                       FieldElement{28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958},
+               },
+       },
+       {
+               {
+                       FieldElement{24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593},
+                       FieldElement{26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071},
+                       FieldElement{-11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692},
+               },
+               {
+                       FieldElement{11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687},
+                       FieldElement{-160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441},
+                       FieldElement{-20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001},
+               },
+               {
+                       FieldElement{-938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460},
+                       FieldElement{-19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007},
+                       FieldElement{-21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762},
+               },
+               {
+                       FieldElement{15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005},
+                       FieldElement{-9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674},
+                       FieldElement{4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035},
+               },
+               {
+                       FieldElement{7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590},
+                       FieldElement{-2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957},
+                       FieldElement{-30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812},
+               },
+               {
+                       FieldElement{33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740},
+                       FieldElement{-18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122},
+                       FieldElement{-27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158},
+               },
+               {
+                       FieldElement{8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885},
+                       FieldElement{26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140},
+                       FieldElement{19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857},
+               },
+               {
+                       FieldElement{801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155},
+                       FieldElement{19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260},
+                       FieldElement{19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483},
+               },
+       },
+       {
+               {
+                       FieldElement{-3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677},
+                       FieldElement{32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815},
+                       FieldElement{22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751},
+               },
+               {
+                       FieldElement{-16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203},
+                       FieldElement{-11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208},
+                       FieldElement{1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230},
+               },
+               {
+                       FieldElement{16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850},
+                       FieldElement{-21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389},
+                       FieldElement{-9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968},
+               },
+               {
+                       FieldElement{-11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689},
+                       FieldElement{14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880},
+                       FieldElement{5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304},
+               },
+               {
+                       FieldElement{30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632},
+                       FieldElement{-3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412},
+                       FieldElement{20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566},
+               },
+               {
+                       FieldElement{-20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038},
+                       FieldElement{-26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232},
+                       FieldElement{-1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943},
+               },
+               {
+                       FieldElement{17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856},
+                       FieldElement{23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738},
+                       FieldElement{15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971},
+               },
+               {
+                       FieldElement{-27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718},
+                       FieldElement{-13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697},
+                       FieldElement{-11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883},
+               },
+       },
+       {
+               {
+                       FieldElement{5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912},
+                       FieldElement{-26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358},
+                       FieldElement{3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849},
+               },
+               {
+                       FieldElement{29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307},
+                       FieldElement{-14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977},
+                       FieldElement{-6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335},
+               },
+               {
+                       FieldElement{-29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644},
+                       FieldElement{-22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616},
+                       FieldElement{-27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735},
+               },
+               {
+                       FieldElement{-21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099},
+                       FieldElement{29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341},
+                       FieldElement{-936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336},
+               },
+               {
+                       FieldElement{-23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646},
+                       FieldElement{31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425},
+                       FieldElement{-17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388},
+               },
+               {
+                       FieldElement{-31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743},
+                       FieldElement{-16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822},
+                       FieldElement{-8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462},
+               },
+               {
+                       FieldElement{18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985},
+                       FieldElement{9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702},
+                       FieldElement{-22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797},
+               },
+               {
+                       FieldElement{21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293},
+                       FieldElement{27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100},
+                       FieldElement{19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688},
+               },
+       },
+       {
+               {
+                       FieldElement{12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186},
+                       FieldElement{2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610},
+                       FieldElement{-2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707},
+               },
+               {
+                       FieldElement{7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220},
+                       FieldElement{915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025},
+                       FieldElement{32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044},
+               },
+               {
+                       FieldElement{32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992},
+                       FieldElement{-4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027},
+                       FieldElement{21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197},
+               },
+               {
+                       FieldElement{8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901},
+                       FieldElement{31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952},
+                       FieldElement{19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878},
+               },
+               {
+                       FieldElement{-28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390},
+                       FieldElement{32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730},
+                       FieldElement{2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730},
+               },
+               {
+                       FieldElement{-19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180},
+                       FieldElement{-30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272},
+                       FieldElement{-15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715},
+               },
+               {
+                       FieldElement{-22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970},
+                       FieldElement{-31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772},
+                       FieldElement{-17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865},
+               },
+               {
+                       FieldElement{15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750},
+                       FieldElement{20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373},
+                       FieldElement{32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348},
+               },
+       },
+       {
+               {
+                       FieldElement{9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144},
+                       FieldElement{-22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195},
+                       FieldElement{5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086},
+               },
+               {
+                       FieldElement{-13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684},
+                       FieldElement{-8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518},
+                       FieldElement{-2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233},
+               },
+               {
+                       FieldElement{-5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793},
+                       FieldElement{-2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794},
+                       FieldElement{580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435},
+               },
+               {
+                       FieldElement{23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921},
+                       FieldElement{13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518},
+                       FieldElement{2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563},
+               },
+               {
+                       FieldElement{14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278},
+                       FieldElement{-27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024},
+                       FieldElement{4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030},
+               },
+               {
+                       FieldElement{10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783},
+                       FieldElement{27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717},
+                       FieldElement{6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844},
+               },
+               {
+                       FieldElement{14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333},
+                       FieldElement{16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048},
+                       FieldElement{22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760},
+               },
+               {
+                       FieldElement{-4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760},
+                       FieldElement{-15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757},
+                       FieldElement{-2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112},
+               },
+       },
+       {
+               {
+                       FieldElement{-19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468},
+                       FieldElement{3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184},
+                       FieldElement{10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289},
+               },
+               {
+                       FieldElement{15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066},
+                       FieldElement{24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882},
+                       FieldElement{13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226},
+               },
+               {
+                       FieldElement{16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101},
+                       FieldElement{29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279},
+                       FieldElement{-6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811},
+               },
+               {
+                       FieldElement{27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709},
+                       FieldElement{20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714},
+                       FieldElement{-2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121},
+               },
+               {
+                       FieldElement{9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464},
+                       FieldElement{12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847},
+                       FieldElement{13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400},
+               },
+               {
+                       FieldElement{4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414},
+                       FieldElement{-15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158},
+                       FieldElement{17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045},
+               },
+               {
+                       FieldElement{-461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415},
+                       FieldElement{-5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459},
+                       FieldElement{-31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079},
+               },
+               {
+                       FieldElement{21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412},
+                       FieldElement{-20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743},
+                       FieldElement{-14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836},
+               },
+       },
+       {
+               {
+                       FieldElement{12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022},
+                       FieldElement{18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429},
+                       FieldElement{-6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065},
+               },
+               {
+                       FieldElement{30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861},
+                       FieldElement{10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000},
+                       FieldElement{-33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101},
+               },
+               {
+                       FieldElement{32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815},
+                       FieldElement{29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642},
+                       FieldElement{10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966},
+               },
+               {
+                       FieldElement{25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574},
+                       FieldElement{-21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742},
+                       FieldElement{-18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689},
+               },
+               {
+                       FieldElement{12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020},
+                       FieldElement{-10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772},
+                       FieldElement{3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982},
+               },
+               {
+                       FieldElement{-14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953},
+                       FieldElement{-16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218},
+                       FieldElement{-17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265},
+               },
+               {
+                       FieldElement{29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073},
+                       FieldElement{-3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325},
+                       FieldElement{-11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798},
+               },
+               {
+                       FieldElement{-4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870},
+                       FieldElement{-7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863},
+                       FieldElement{-13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927},
+               },
+       },
+       {
+               {
+                       FieldElement{-2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267},
+                       FieldElement{-9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663},
+                       FieldElement{22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862},
+               },
+               {
+                       FieldElement{-25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673},
+                       FieldElement{15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943},
+                       FieldElement{15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020},
+               },
+               {
+                       FieldElement{-4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238},
+                       FieldElement{11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064},
+                       FieldElement{14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795},
+               },
+               {
+                       FieldElement{15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052},
+                       FieldElement{-10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904},
+                       FieldElement{29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531},
+               },
+               {
+                       FieldElement{-13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979},
+                       FieldElement{-5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841},
+                       FieldElement{10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431},
+               },
+               {
+                       FieldElement{10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324},
+                       FieldElement{-31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940},
+                       FieldElement{10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320},
+               },
+               {
+                       FieldElement{-15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184},
+                       FieldElement{14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114},
+                       FieldElement{30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878},
+               },
+               {
+                       FieldElement{12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784},
+                       FieldElement{-2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091},
+                       FieldElement{-16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585},
+               },
+       },
+       {
+               {
+                       FieldElement{-8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208},
+                       FieldElement{10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864},
+                       FieldElement{17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661},
+               },
+               {
+                       FieldElement{7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233},
+                       FieldElement{26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212},
+                       FieldElement{-12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525},
+               },
+               {
+                       FieldElement{-24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068},
+                       FieldElement{9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397},
+                       FieldElement{-8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988},
+               },
+               {
+                       FieldElement{5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889},
+                       FieldElement{32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038},
+                       FieldElement{14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697},
+               },
+               {
+                       FieldElement{20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875},
+                       FieldElement{-25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905},
+                       FieldElement{-25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656},
+               },
+               {
+                       FieldElement{11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818},
+                       FieldElement{27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714},
+                       FieldElement{10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203},
+               },
+               {
+                       FieldElement{20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931},
+                       FieldElement{-30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024},
+                       FieldElement{-23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084},
+               },
+               {
+                       FieldElement{-1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204},
+                       FieldElement{20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817},
+                       FieldElement{27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667},
+               },
+       },
+       {
+               {
+                       FieldElement{11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504},
+                       FieldElement{-12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768},
+                       FieldElement{-19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255},
+               },
+               {
+                       FieldElement{6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790},
+                       FieldElement{1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438},
+                       FieldElement{-22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333},
+               },
+               {
+                       FieldElement{17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971},
+                       FieldElement{31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905},
+                       FieldElement{29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409},
+               },
+               {
+                       FieldElement{12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409},
+                       FieldElement{6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499},
+                       FieldElement{-8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363},
+               },
+               {
+                       FieldElement{28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664},
+                       FieldElement{-11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324},
+                       FieldElement{-21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940},
+               },
+               {
+                       FieldElement{13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990},
+                       FieldElement{-17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914},
+                       FieldElement{-25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290},
+               },
+               {
+                       FieldElement{24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257},
+                       FieldElement{-6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433},
+                       FieldElement{-16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236},
+               },
+               {
+                       FieldElement{-12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045},
+                       FieldElement{11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093},
+                       FieldElement{-1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347},
+               },
+       },
+       {
+               {
+                       FieldElement{-28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191},
+                       FieldElement{-15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507},
+                       FieldElement{-12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906},
+               },
+               {
+                       FieldElement{3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018},
+                       FieldElement{-16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109},
+                       FieldElement{-23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926},
+               },
+               {
+                       FieldElement{-24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528},
+                       FieldElement{8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625},
+                       FieldElement{-32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286},
+               },
+               {
+                       FieldElement{2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033},
+                       FieldElement{27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866},
+                       FieldElement{21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896},
+               },
+               {
+                       FieldElement{30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075},
+                       FieldElement{26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347},
+                       FieldElement{-22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437},
+               },
+               {
+                       FieldElement{-5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165},
+                       FieldElement{-18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588},
+                       FieldElement{-32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193},
+               },
+               {
+                       FieldElement{-19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017},
+                       FieldElement{-28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883},
+                       FieldElement{21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961},
+               },
+               {
+                       FieldElement{8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043},
+                       FieldElement{29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663},
+                       FieldElement{-20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362},
+               },
+       },
+       {
+               {
+                       FieldElement{-33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860},
+                       FieldElement{2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466},
+                       FieldElement{-24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063},
+               },
+               {
+                       FieldElement{-26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997},
+                       FieldElement{-1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295},
+                       FieldElement{-13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369},
+               },
+               {
+                       FieldElement{9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385},
+                       FieldElement{18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109},
+                       FieldElement{2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906},
+               },
+               {
+                       FieldElement{4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424},
+                       FieldElement{-19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185},
+                       FieldElement{7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962},
+               },
+               {
+                       FieldElement{-7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325},
+                       FieldElement{10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593},
+                       FieldElement{696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404},
+               },
+               {
+                       FieldElement{-11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644},
+                       FieldElement{17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801},
+                       FieldElement{26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804},
+               },
+               {
+                       FieldElement{-31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884},
+                       FieldElement{-586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577},
+                       FieldElement{-9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849},
+               },
+               {
+                       FieldElement{32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473},
+                       FieldElement{-8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644},
+                       FieldElement{-2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319},
+               },
+       },
+       {
+               {
+                       FieldElement{-11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599},
+                       FieldElement{-9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768},
+                       FieldElement{-27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084},
+               },
+               {
+                       FieldElement{-27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328},
+                       FieldElement{-15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369},
+                       FieldElement{20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920},
+               },
+               {
+                       FieldElement{12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815},
+                       FieldElement{-32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025},
+                       FieldElement{-21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397},
+               },
+               {
+                       FieldElement{-20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448},
+                       FieldElement{6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981},
+                       FieldElement{30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165},
+               },
+               {
+                       FieldElement{32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501},
+                       FieldElement{17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073},
+                       FieldElement{-1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861},
+               },
+               {
+                       FieldElement{14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845},
+                       FieldElement{-1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211},
+                       FieldElement{18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870},
+               },
+               {
+                       FieldElement{10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096},
+                       FieldElement{33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803},
+                       FieldElement{-32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168},
+               },
+               {
+                       FieldElement{30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965},
+                       FieldElement{-14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505},
+                       FieldElement{18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598},
+               },
+       },
+       {
+               {
+                       FieldElement{5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782},
+                       FieldElement{5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900},
+                       FieldElement{-31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479},
+               },
+               {
+                       FieldElement{-12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208},
+                       FieldElement{8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232},
+                       FieldElement{17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719},
+               },
+               {
+                       FieldElement{16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271},
+                       FieldElement{-4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326},
+                       FieldElement{-8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132},
+               },
+               {
+                       FieldElement{14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300},
+                       FieldElement{8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570},
+                       FieldElement{15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670},
+               },
+               {
+                       FieldElement{-2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994},
+                       FieldElement{-12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913},
+                       FieldElement{31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317},
+               },
+               {
+                       FieldElement{-25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730},
+                       FieldElement{842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096},
+                       FieldElement{-4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078},
+               },
+               {
+                       FieldElement{-15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411},
+                       FieldElement{-19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905},
+                       FieldElement{-9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654},
+               },
+               {
+                       FieldElement{-28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870},
+                       FieldElement{-23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498},
+                       FieldElement{12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579},
+               },
+       },
+       {
+               {
+                       FieldElement{14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677},
+                       FieldElement{10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647},
+                       FieldElement{-2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743},
+               },
+               {
+                       FieldElement{-25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468},
+                       FieldElement{21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375},
+                       FieldElement{-25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155},
+               },
+               {
+                       FieldElement{6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725},
+                       FieldElement{-12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612},
+                       FieldElement{-10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943},
+               },
+               {
+                       FieldElement{-30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944},
+                       FieldElement{30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928},
+                       FieldElement{9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406},
+               },
+               {
+                       FieldElement{22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139},
+                       FieldElement{-8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963},
+                       FieldElement{-31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693},
+               },
+               {
+                       FieldElement{1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734},
+                       FieldElement{-448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680},
+                       FieldElement{-24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410},
+               },
+               {
+                       FieldElement{-9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931},
+                       FieldElement{-16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654},
+                       FieldElement{22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710},
+               },
+               {
+                       FieldElement{29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180},
+                       FieldElement{-26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684},
+                       FieldElement{-10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895},
+               },
+       },
+       {
+               {
+                       FieldElement{22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501},
+                       FieldElement{-11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413},
+                       FieldElement{6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880},
+               },
+               {
+                       FieldElement{-8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874},
+                       FieldElement{22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962},
+                       FieldElement{-7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899},
+               },
+               {
+                       FieldElement{21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152},
+                       FieldElement{9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063},
+                       FieldElement{7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080},
+               },
+               {
+                       FieldElement{-9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146},
+                       FieldElement{-17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183},
+                       FieldElement{-19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133},
+               },
+               {
+                       FieldElement{-32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421},
+                       FieldElement{-3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622},
+                       FieldElement{-4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197},
+               },
+               {
+                       FieldElement{2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663},
+                       FieldElement{31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753},
+                       FieldElement{4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755},
+               },
+               {
+                       FieldElement{-9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862},
+                       FieldElement{-26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118},
+                       FieldElement{26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171},
+               },
+               {
+                       FieldElement{15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380},
+                       FieldElement{16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824},
+                       FieldElement{28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270},
+               },
+       },
+       {
+               {
+                       FieldElement{-817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438},
+                       FieldElement{-31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584},
+                       FieldElement{-594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562},
+               },
+               {
+                       FieldElement{30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471},
+                       FieldElement{18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610},
+                       FieldElement{19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269},
+               },
+               {
+                       FieldElement{-30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650},
+                       FieldElement{14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369},
+                       FieldElement{19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461},
+               },
+               {
+                       FieldElement{30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462},
+                       FieldElement{-5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793},
+                       FieldElement{-2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218},
+               },
+               {
+                       FieldElement{-24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226},
+                       FieldElement{18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019},
+                       FieldElement{-15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037},
+               },
+               {
+                       FieldElement{31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171},
+                       FieldElement{-17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132},
+                       FieldElement{-28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841},
+               },
+               {
+                       FieldElement{21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181},
+                       FieldElement{-33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210},
+                       FieldElement{-1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040},
+               },
+               {
+                       FieldElement{3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935},
+                       FieldElement{24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105},
+                       FieldElement{-28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814},
+               },
+       },
+       {
+               {
+                       FieldElement{793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852},
+                       FieldElement{5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581},
+                       FieldElement{-4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646},
+               },
+               {
+                       FieldElement{10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844},
+                       FieldElement{10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025},
+                       FieldElement{27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453},
+               },
+               {
+                       FieldElement{-23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068},
+                       FieldElement{4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192},
+                       FieldElement{-17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921},
+               },
+               {
+                       FieldElement{-9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259},
+                       FieldElement{-12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426},
+                       FieldElement{-5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072},
+               },
+               {
+                       FieldElement{-17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305},
+                       FieldElement{13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832},
+                       FieldElement{28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943},
+               },
+               {
+                       FieldElement{-16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011},
+                       FieldElement{24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447},
+                       FieldElement{17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494},
+               },
+               {
+                       FieldElement{-28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245},
+                       FieldElement{-20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859},
+                       FieldElement{28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915},
+               },
+               {
+                       FieldElement{16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707},
+                       FieldElement{10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848},
+                       FieldElement{-11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224},
+               },
+       },
+       {
+               {
+                       FieldElement{-25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391},
+                       FieldElement{15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215},
+                       FieldElement{-23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101},
+               },
+               {
+                       FieldElement{23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713},
+                       FieldElement{21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849},
+                       FieldElement{-7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930},
+               },
+               {
+                       FieldElement{-29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940},
+                       FieldElement{-21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031},
+                       FieldElement{-17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404},
+               },
+               {
+                       FieldElement{-25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243},
+                       FieldElement{-23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116},
+                       FieldElement{-24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525},
+               },
+               {
+                       FieldElement{-23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509},
+                       FieldElement{-10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883},
+                       FieldElement{15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865},
+               },
+               {
+                       FieldElement{-3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660},
+                       FieldElement{4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273},
+                       FieldElement{-28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138},
+               },
+               {
+                       FieldElement{-25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560},
+                       FieldElement{-10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135},
+                       FieldElement{2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941},
+               },
+               {
+                       FieldElement{-4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739},
+                       FieldElement{18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756},
+                       FieldElement{-30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819},
+               },
+       },
+       {
+               {
+                       FieldElement{-6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347},
+                       FieldElement{-27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028},
+                       FieldElement{21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075},
+               },
+               {
+                       FieldElement{16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799},
+                       FieldElement{-2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609},
+                       FieldElement{-25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817},
+               },
+               {
+                       FieldElement{-23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989},
+                       FieldElement{-30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523},
+                       FieldElement{4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278},
+               },
+               {
+                       FieldElement{31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045},
+                       FieldElement{19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377},
+                       FieldElement{24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480},
+               },
+               {
+                       FieldElement{17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016},
+                       FieldElement{510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426},
+                       FieldElement{18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525},
+               },
+               {
+                       FieldElement{13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396},
+                       FieldElement{9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080},
+                       FieldElement{12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892},
+               },
+               {
+                       FieldElement{15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275},
+                       FieldElement{11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074},
+                       FieldElement{20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140},
+               },
+               {
+                       FieldElement{-16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717},
+                       FieldElement{-1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101},
+                       FieldElement{24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127},
+               },
+       },
+       {
+               {
+                       FieldElement{-12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632},
+                       FieldElement{-26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415},
+                       FieldElement{-31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160},
+               },
+               {
+                       FieldElement{31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876},
+                       FieldElement{22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625},
+                       FieldElement{-15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478},
+               },
+               {
+                       FieldElement{27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164},
+                       FieldElement{26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595},
+                       FieldElement{-7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248},
+               },
+               {
+                       FieldElement{-16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858},
+                       FieldElement{15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193},
+                       FieldElement{8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184},
+               },
+               {
+                       FieldElement{-18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942},
+                       FieldElement{-1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635},
+                       FieldElement{21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948},
+               },
+               {
+                       FieldElement{11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935},
+                       FieldElement{-25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415},
+                       FieldElement{-15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416},
+               },
+               {
+                       FieldElement{-7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018},
+                       FieldElement{4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778},
+                       FieldElement{366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659},
+               },
+               {
+                       FieldElement{-24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385},
+                       FieldElement{18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503},
+                       FieldElement{476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329},
+               },
+       },
+       {
+               {
+                       FieldElement{20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056},
+                       FieldElement{-13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838},
+                       FieldElement{24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948},
+               },
+               {
+                       FieldElement{-3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691},
+                       FieldElement{-15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118},
+                       FieldElement{-23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517},
+               },
+               {
+                       FieldElement{-20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269},
+                       FieldElement{-6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904},
+                       FieldElement{-23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589},
+               },
+               {
+                       FieldElement{-28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193},
+                       FieldElement{-7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910},
+                       FieldElement{-30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930},
+               },
+               {
+                       FieldElement{-7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667},
+                       FieldElement{25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481},
+                       FieldElement{-9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876},
+               },
+               {
+                       FieldElement{22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640},
+                       FieldElement{-8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278},
+                       FieldElement{-21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112},
+               },
+               {
+                       FieldElement{26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272},
+                       FieldElement{17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012},
+                       FieldElement{-10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221},
+               },
+               {
+                       FieldElement{30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046},
+                       FieldElement{13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345},
+                       FieldElement{-19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310},
+               },
+       },
+       {
+               {
+                       FieldElement{19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937},
+                       FieldElement{31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636},
+                       FieldElement{-9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008},
+               },
+               {
+                       FieldElement{-2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429},
+                       FieldElement{-15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576},
+                       FieldElement{31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066},
+               },
+               {
+                       FieldElement{-9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490},
+                       FieldElement{-12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104},
+                       FieldElement{33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053},
+               },
+               {
+                       FieldElement{31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275},
+                       FieldElement{-20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511},
+                       FieldElement{22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095},
+               },
+               {
+                       FieldElement{-28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439},
+                       FieldElement{23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939},
+                       FieldElement{-23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424},
+               },
+               {
+                       FieldElement{2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310},
+                       FieldElement{3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608},
+                       FieldElement{-32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079},
+               },
+               {
+                       FieldElement{-23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101},
+                       FieldElement{21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418},
+                       FieldElement{18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576},
+               },
+               {
+                       FieldElement{30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356},
+                       FieldElement{9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996},
+                       FieldElement{-26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099},
+               },
+       },
+       {
+               {
+                       FieldElement{-26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728},
+                       FieldElement{-13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658},
+                       FieldElement{-10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242},
+               },
+               {
+                       FieldElement{-21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001},
+                       FieldElement{-4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766},
+                       FieldElement{18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373},
+               },
+               {
+                       FieldElement{26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458},
+                       FieldElement{-17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628},
+                       FieldElement{-13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657},
+               },
+               {
+                       FieldElement{-23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062},
+                       FieldElement{25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616},
+                       FieldElement{31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014},
+               },
+               {
+                       FieldElement{24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383},
+                       FieldElement{-25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814},
+                       FieldElement{-20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718},
+               },
+               {
+                       FieldElement{30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417},
+                       FieldElement{2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222},
+                       FieldElement{33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444},
+               },
+               {
+                       FieldElement{-20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597},
+                       FieldElement{23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970},
+                       FieldElement{1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799},
+               },
+               {
+                       FieldElement{-5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647},
+                       FieldElement{13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511},
+                       FieldElement{-29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032},
+               },
+       },
+       {
+               {
+                       FieldElement{9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834},
+                       FieldElement{-23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461},
+                       FieldElement{29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062},
+               },
+               {
+                       FieldElement{-25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516},
+                       FieldElement{-20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547},
+                       FieldElement{-24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240},
+               },
+               {
+                       FieldElement{-17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038},
+                       FieldElement{-33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741},
+                       FieldElement{16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103},
+               },
+               {
+                       FieldElement{-19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747},
+                       FieldElement{-1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323},
+                       FieldElement{31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016},
+               },
+               {
+                       FieldElement{-14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373},
+                       FieldElement{15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228},
+                       FieldElement{-2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141},
+               },
+               {
+                       FieldElement{16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399},
+                       FieldElement{11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831},
+                       FieldElement{-185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376},
+               },
+               {
+                       FieldElement{-32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313},
+                       FieldElement{-18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958},
+                       FieldElement{-6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577},
+               },
+               {
+                       FieldElement{-22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743},
+                       FieldElement{29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684},
+                       FieldElement{-20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476},
+               },
+       },
+}
diff --git a/crypto/ed25519/internal/edwards25519/edwards25519.go b/crypto/ed25519/internal/edwards25519/edwards25519.go
new file mode 100644 (file)
index 0000000..fe1be3e
--- /dev/null
@@ -0,0 +1,1772 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file at
+// https://github.com/golang/crypto/blob/master/LICENSE.
+
+package edwards25519
+
+// This code is a port of the public domain, “ref10” implementation of ed25519
+// from SUPERCOP.
+
+// FieldElement represents an element of the field GF(2^255 - 19).  An element
+// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
+// t[3]+2^102 t[4]+...+2^230 t[9].  Bounds on each t[i] vary depending on
+// context.
+type FieldElement [10]int32
+
+var zero FieldElement
+
+func FeZero(fe *FieldElement) {
+       copy(fe[:], zero[:])
+}
+
+func FeOne(fe *FieldElement) {
+       FeZero(fe)
+       fe[0] = 1
+}
+
+func FeAdd(dst, a, b *FieldElement) {
+       dst[0] = a[0] + b[0]
+       dst[1] = a[1] + b[1]
+       dst[2] = a[2] + b[2]
+       dst[3] = a[3] + b[3]
+       dst[4] = a[4] + b[4]
+       dst[5] = a[5] + b[5]
+       dst[6] = a[6] + b[6]
+       dst[7] = a[7] + b[7]
+       dst[8] = a[8] + b[8]
+       dst[9] = a[9] + b[9]
+}
+
+func FeSub(dst, a, b *FieldElement) {
+       dst[0] = a[0] - b[0]
+       dst[1] = a[1] - b[1]
+       dst[2] = a[2] - b[2]
+       dst[3] = a[3] - b[3]
+       dst[4] = a[4] - b[4]
+       dst[5] = a[5] - b[5]
+       dst[6] = a[6] - b[6]
+       dst[7] = a[7] - b[7]
+       dst[8] = a[8] - b[8]
+       dst[9] = a[9] - b[9]
+}
+
+func FeCopy(dst, src *FieldElement) {
+       copy(dst[:], src[:])
+}
+
+// Replace (f,g) with (g,g) if b == 1;
+// replace (f,g) with (f,g) if b == 0.
+//
+// Preconditions: b in {0,1}.
+func FeCMove(f, g *FieldElement, b int32) {
+       b = -b
+       f[0] ^= b & (f[0] ^ g[0])
+       f[1] ^= b & (f[1] ^ g[1])
+       f[2] ^= b & (f[2] ^ g[2])
+       f[3] ^= b & (f[3] ^ g[3])
+       f[4] ^= b & (f[4] ^ g[4])
+       f[5] ^= b & (f[5] ^ g[5])
+       f[6] ^= b & (f[6] ^ g[6])
+       f[7] ^= b & (f[7] ^ g[7])
+       f[8] ^= b & (f[8] ^ g[8])
+       f[9] ^= b & (f[9] ^ g[9])
+}
+
+func load3(in []byte) int64 {
+       var r int64
+       r = int64(in[0])
+       r |= int64(in[1]) << 8
+       r |= int64(in[2]) << 16
+       return r
+}
+
+func load4(in []byte) int64 {
+       var r int64
+       r = int64(in[0])
+       r |= int64(in[1]) << 8
+       r |= int64(in[2]) << 16
+       r |= int64(in[3]) << 24
+       return r
+}
+
+func FeFromBytes(dst *FieldElement, src *[32]byte) {
+       h0 := load4(src[:])
+       h1 := load3(src[4:]) << 6
+       h2 := load3(src[7:]) << 5
+       h3 := load3(src[10:]) << 3
+       h4 := load3(src[13:]) << 2
+       h5 := load4(src[16:])
+       h6 := load3(src[20:]) << 7
+       h7 := load3(src[23:]) << 5
+       h8 := load3(src[26:]) << 4
+       h9 := (load3(src[29:]) & 8388607) << 2
+
+       FeCombine(dst, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+// FeToBytes marshals h to s.
+// Preconditions:
+//   |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Write p=2^255-19; q=floor(h/p).
+// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
+//
+// Proof:
+//   Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
+//   Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4.
+//
+//   Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
+//   Then 0<y<1.
+//
+//   Write r=h-pq.
+//   Have 0<=r<=p-1=2^255-20.
+//   Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
+//
+//   Write x=r+19(2^-255)r+y.
+//   Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
+//
+//   Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
+//   so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
+func FeToBytes(s *[32]byte, h *FieldElement) {
+       var carry [10]int32
+
+       q := (19*h[9] + (1 << 24)) >> 25
+       q = (h[0] + q) >> 26
+       q = (h[1] + q) >> 25
+       q = (h[2] + q) >> 26
+       q = (h[3] + q) >> 25
+       q = (h[4] + q) >> 26
+       q = (h[5] + q) >> 25
+       q = (h[6] + q) >> 26
+       q = (h[7] + q) >> 25
+       q = (h[8] + q) >> 26
+       q = (h[9] + q) >> 25
+
+       // Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20.
+       h[0] += 19 * q
+       // Goal: Output h-2^255 q, which is between 0 and 2^255-20.
+
+       carry[0] = h[0] >> 26
+       h[1] += carry[0]
+       h[0] -= carry[0] << 26
+       carry[1] = h[1] >> 25
+       h[2] += carry[1]
+       h[1] -= carry[1] << 25
+       carry[2] = h[2] >> 26
+       h[3] += carry[2]
+       h[2] -= carry[2] << 26
+       carry[3] = h[3] >> 25
+       h[4] += carry[3]
+       h[3] -= carry[3] << 25
+       carry[4] = h[4] >> 26
+       h[5] += carry[4]
+       h[4] -= carry[4] << 26
+       carry[5] = h[5] >> 25
+       h[6] += carry[5]
+       h[5] -= carry[5] << 25
+       carry[6] = h[6] >> 26
+       h[7] += carry[6]
+       h[6] -= carry[6] << 26
+       carry[7] = h[7] >> 25
+       h[8] += carry[7]
+       h[7] -= carry[7] << 25
+       carry[8] = h[8] >> 26
+       h[9] += carry[8]
+       h[8] -= carry[8] << 26
+       carry[9] = h[9] >> 25
+       h[9] -= carry[9] << 25
+       // h10 = carry9
+
+       // Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
+       // Have h[0]+...+2^230 h[9] between 0 and 2^255-1;
+       // evidently 2^255 h10-2^255 q = 0.
+       // Goal: Output h[0]+...+2^230 h[9].
+
+       s[0] = byte(h[0] >> 0)
+       s[1] = byte(h[0] >> 8)
+       s[2] = byte(h[0] >> 16)
+       s[3] = byte((h[0] >> 24) | (h[1] << 2))
+       s[4] = byte(h[1] >> 6)
+       s[5] = byte(h[1] >> 14)
+       s[6] = byte((h[1] >> 22) | (h[2] << 3))
+       s[7] = byte(h[2] >> 5)
+       s[8] = byte(h[2] >> 13)
+       s[9] = byte((h[2] >> 21) | (h[3] << 5))
+       s[10] = byte(h[3] >> 3)
+       s[11] = byte(h[3] >> 11)
+       s[12] = byte((h[3] >> 19) | (h[4] << 6))
+       s[13] = byte(h[4] >> 2)
+       s[14] = byte(h[4] >> 10)
+       s[15] = byte(h[4] >> 18)
+       s[16] = byte(h[5] >> 0)
+       s[17] = byte(h[5] >> 8)
+       s[18] = byte(h[5] >> 16)
+       s[19] = byte((h[5] >> 24) | (h[6] << 1))
+       s[20] = byte(h[6] >> 7)
+       s[21] = byte(h[6] >> 15)
+       s[22] = byte((h[6] >> 23) | (h[7] << 3))
+       s[23] = byte(h[7] >> 5)
+       s[24] = byte(h[7] >> 13)
+       s[25] = byte((h[7] >> 21) | (h[8] << 4))
+       s[26] = byte(h[8] >> 4)
+       s[27] = byte(h[8] >> 12)
+       s[28] = byte((h[8] >> 20) | (h[9] << 6))
+       s[29] = byte(h[9] >> 2)
+       s[30] = byte(h[9] >> 10)
+       s[31] = byte(h[9] >> 18)
+}
+
+func FeIsNegative(f *FieldElement) byte {
+       var s [32]byte
+       FeToBytes(&s, f)
+       return s[0] & 1
+}
+
+func FeIsNonZero(f *FieldElement) int32 {
+       var s [32]byte
+       FeToBytes(&s, f)
+       var x uint8
+       for _, b := range s {
+               x |= b
+       }
+       x |= x >> 4
+       x |= x >> 2
+       x |= x >> 1
+       return int32(x & 1)
+}
+
+// FeNeg sets h = -f
+//
+// Preconditions:
+//    |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Postconditions:
+//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+func FeNeg(h, f *FieldElement) {
+       h[0] = -f[0]
+       h[1] = -f[1]
+       h[2] = -f[2]
+       h[3] = -f[3]
+       h[4] = -f[4]
+       h[5] = -f[5]
+       h[6] = -f[6]
+       h[7] = -f[7]
+       h[8] = -f[8]
+       h[9] = -f[9]
+}
+
+func FeCombine(h *FieldElement, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 int64) {
+       var c0, c1, c2, c3, c4, c5, c6, c7, c8, c9 int64
+
+       /*
+         |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38))
+           i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8
+         |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19))
+           i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9
+       */
+
+       c0 = (h0 + (1 << 25)) >> 26
+       h1 += c0
+       h0 -= c0 << 26
+       c4 = (h4 + (1 << 25)) >> 26
+       h5 += c4
+       h4 -= c4 << 26
+       /* |h0| <= 2^25 */
+       /* |h4| <= 2^25 */
+       /* |h1| <= 1.51*2^58 */
+       /* |h5| <= 1.51*2^58 */
+
+       c1 = (h1 + (1 << 24)) >> 25
+       h2 += c1
+       h1 -= c1 << 25
+       c5 = (h5 + (1 << 24)) >> 25
+       h6 += c5
+       h5 -= c5 << 25
+       /* |h1| <= 2^24; from now on fits into int32 */
+       /* |h5| <= 2^24; from now on fits into int32 */
+       /* |h2| <= 1.21*2^59 */
+       /* |h6| <= 1.21*2^59 */
+
+       c2 = (h2 + (1 << 25)) >> 26
+       h3 += c2
+       h2 -= c2 << 26
+       c6 = (h6 + (1 << 25)) >> 26
+       h7 += c6
+       h6 -= c6 << 26
+       /* |h2| <= 2^25; from now on fits into int32 unchanged */
+       /* |h6| <= 2^25; from now on fits into int32 unchanged */
+       /* |h3| <= 1.51*2^58 */
+       /* |h7| <= 1.51*2^58 */
+
+       c3 = (h3 + (1 << 24)) >> 25
+       h4 += c3
+       h3 -= c3 << 25
+       c7 = (h7 + (1 << 24)) >> 25
+       h8 += c7
+       h7 -= c7 << 25
+       /* |h3| <= 2^24; from now on fits into int32 unchanged */
+       /* |h7| <= 2^24; from now on fits into int32 unchanged */
+       /* |h4| <= 1.52*2^33 */
+       /* |h8| <= 1.52*2^33 */
+
+       c4 = (h4 + (1 << 25)) >> 26
+       h5 += c4
+       h4 -= c4 << 26
+       c8 = (h8 + (1 << 25)) >> 26
+       h9 += c8
+       h8 -= c8 << 26
+       /* |h4| <= 2^25; from now on fits into int32 unchanged */
+       /* |h8| <= 2^25; from now on fits into int32 unchanged */
+       /* |h5| <= 1.01*2^24 */
+       /* |h9| <= 1.51*2^58 */
+
+       c9 = (h9 + (1 << 24)) >> 25
+       h0 += c9 * 19
+       h9 -= c9 << 25
+       /* |h9| <= 2^24; from now on fits into int32 unchanged */
+       /* |h0| <= 1.8*2^37 */
+
+       c0 = (h0 + (1 << 25)) >> 26
+       h1 += c0
+       h0 -= c0 << 26
+       /* |h0| <= 2^25; from now on fits into int32 unchanged */
+       /* |h1| <= 1.01*2^24 */
+
+       h[0] = int32(h0)
+       h[1] = int32(h1)
+       h[2] = int32(h2)
+       h[3] = int32(h3)
+       h[4] = int32(h4)
+       h[5] = int32(h5)
+       h[6] = int32(h6)
+       h[7] = int32(h7)
+       h[8] = int32(h8)
+       h[9] = int32(h9)
+}
+
+// FeMul calculates h = f * g
+// Can overlap h with f or g.
+//
+// Preconditions:
+//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//    |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Notes on implementation strategy:
+//
+// Using schoolbook multiplication.
+// Karatsuba would save a little in some cost models.
+//
+// Most multiplications by 2 and 19 are 32-bit precomputations;
+// cheaper than 64-bit postcomputations.
+//
+// There is one remaining multiplication by 19 in the carry chain;
+// one *19 precomputation can be merged into this,
+// but the resulting data flow is considerably less clean.
+//
+// There are 12 carries below.
+// 10 of them are 2-way parallelizable and vectorizable.
+// Can get away with 11 carries, but then data flow is much deeper.
+//
+// With tighter constraints on inputs, can squeeze carries into int32.
+func FeMul(h, f, g *FieldElement) {
+       f0 := int64(f[0])
+       f1 := int64(f[1])
+       f2 := int64(f[2])
+       f3 := int64(f[3])
+       f4 := int64(f[4])
+       f5 := int64(f[5])
+       f6 := int64(f[6])
+       f7 := int64(f[7])
+       f8 := int64(f[8])
+       f9 := int64(f[9])
+
+       f1_2 := int64(2 * f[1])
+       f3_2 := int64(2 * f[3])
+       f5_2 := int64(2 * f[5])
+       f7_2 := int64(2 * f[7])
+       f9_2 := int64(2 * f[9])
+
+       g0 := int64(g[0])
+       g1 := int64(g[1])
+       g2 := int64(g[2])
+       g3 := int64(g[3])
+       g4 := int64(g[4])
+       g5 := int64(g[5])
+       g6 := int64(g[6])
+       g7 := int64(g[7])
+       g8 := int64(g[8])
+       g9 := int64(g[9])
+
+       g1_19 := int64(19 * g[1]) /* 1.4*2^29 */
+       g2_19 := int64(19 * g[2]) /* 1.4*2^30; still ok */
+       g3_19 := int64(19 * g[3])
+       g4_19 := int64(19 * g[4])
+       g5_19 := int64(19 * g[5])
+       g6_19 := int64(19 * g[6])
+       g7_19 := int64(19 * g[7])
+       g8_19 := int64(19 * g[8])
+       g9_19 := int64(19 * g[9])
+
+       h0 := f0*g0 + f1_2*g9_19 + f2*g8_19 + f3_2*g7_19 + f4*g6_19 + f5_2*g5_19 + f6*g4_19 + f7_2*g3_19 + f8*g2_19 + f9_2*g1_19
+       h1 := f0*g1 + f1*g0 + f2*g9_19 + f3*g8_19 + f4*g7_19 + f5*g6_19 + f6*g5_19 + f7*g4_19 + f8*g3_19 + f9*g2_19
+       h2 := f0*g2 + f1_2*g1 + f2*g0 + f3_2*g9_19 + f4*g8_19 + f5_2*g7_19 + f6*g6_19 + f7_2*g5_19 + f8*g4_19 + f9_2*g3_19
+       h3 := f0*g3 + f1*g2 + f2*g1 + f3*g0 + f4*g9_19 + f5*g8_19 + f6*g7_19 + f7*g6_19 + f8*g5_19 + f9*g4_19
+       h4 := f0*g4 + f1_2*g3 + f2*g2 + f3_2*g1 + f4*g0 + f5_2*g9_19 + f6*g8_19 + f7_2*g7_19 + f8*g6_19 + f9_2*g5_19
+       h5 := f0*g5 + f1*g4 + f2*g3 + f3*g2 + f4*g1 + f5*g0 + f6*g9_19 + f7*g8_19 + f8*g7_19 + f9*g6_19
+       h6 := f0*g6 + f1_2*g5 + f2*g4 + f3_2*g3 + f4*g2 + f5_2*g1 + f6*g0 + f7_2*g9_19 + f8*g8_19 + f9_2*g7_19
+       h7 := f0*g7 + f1*g6 + f2*g5 + f3*g4 + f4*g3 + f5*g2 + f6*g1 + f7*g0 + f8*g9_19 + f9*g8_19
+       h8 := f0*g8 + f1_2*g7 + f2*g6 + f3_2*g5 + f4*g4 + f5_2*g3 + f6*g2 + f7_2*g1 + f8*g0 + f9_2*g9_19
+       h9 := f0*g9 + f1*g8 + f2*g7 + f3*g6 + f4*g5 + f5*g4 + f6*g3 + f7*g2 + f8*g1 + f9*g0
+
+       FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+func feSquare(f *FieldElement) (h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 int64) {
+       f0 := int64(f[0])
+       f1 := int64(f[1])
+       f2 := int64(f[2])
+       f3 := int64(f[3])
+       f4 := int64(f[4])
+       f5 := int64(f[5])
+       f6 := int64(f[6])
+       f7 := int64(f[7])
+       f8 := int64(f[8])
+       f9 := int64(f[9])
+       f0_2 := int64(2 * f[0])
+       f1_2 := int64(2 * f[1])
+       f2_2 := int64(2 * f[2])
+       f3_2 := int64(2 * f[3])
+       f4_2 := int64(2 * f[4])
+       f5_2 := int64(2 * f[5])
+       f6_2 := int64(2 * f[6])
+       f7_2 := int64(2 * f[7])
+       f5_38 := 38 * f5 // 1.31*2^30
+       f6_19 := 19 * f6 // 1.31*2^30
+       f7_38 := 38 * f7 // 1.31*2^30
+       f8_19 := 19 * f8 // 1.31*2^30
+       f9_38 := 38 * f9 // 1.31*2^30
+
+       h0 = f0*f0 + f1_2*f9_38 + f2_2*f8_19 + f3_2*f7_38 + f4_2*f6_19 + f5*f5_38
+       h1 = f0_2*f1 + f2*f9_38 + f3_2*f8_19 + f4*f7_38 + f5_2*f6_19
+       h2 = f0_2*f2 + f1_2*f1 + f3_2*f9_38 + f4_2*f8_19 + f5_2*f7_38 + f6*f6_19
+       h3 = f0_2*f3 + f1_2*f2 + f4*f9_38 + f5_2*f8_19 + f6*f7_38
+       h4 = f0_2*f4 + f1_2*f3_2 + f2*f2 + f5_2*f9_38 + f6_2*f8_19 + f7*f7_38
+       h5 = f0_2*f5 + f1_2*f4 + f2_2*f3 + f6*f9_38 + f7_2*f8_19
+       h6 = f0_2*f6 + f1_2*f5_2 + f2_2*f4 + f3_2*f3 + f7_2*f9_38 + f8*f8_19
+       h7 = f0_2*f7 + f1_2*f6 + f2_2*f5 + f3_2*f4 + f8*f9_38
+       h8 = f0_2*f8 + f1_2*f7_2 + f2_2*f6 + f3_2*f5_2 + f4*f4 + f9*f9_38
+       h9 = f0_2*f9 + f1_2*f8 + f2_2*f7 + f3_2*f6 + f4_2*f5
+
+       return
+}
+
+// FeSquare calculates h = f*f. Can overlap h with f.
+//
+// Preconditions:
+//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+func FeSquare(h, f *FieldElement) {
+       h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 := feSquare(f)
+       FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+// FeSquare2 sets h = 2 * f * f
+//
+// Can overlap h with f.
+//
+// Preconditions:
+//    |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+//
+// Postconditions:
+//    |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+// See fe_mul.c for discussion of implementation strategy.
+func FeSquare2(h, f *FieldElement) {
+       h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 := feSquare(f)
+
+       h0 += h0
+       h1 += h1
+       h2 += h2
+       h3 += h3
+       h4 += h4
+       h5 += h5
+       h6 += h6
+       h7 += h7
+       h8 += h8
+       h9 += h9
+
+       FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+func FeInvert(out, z *FieldElement) {
+       var t0, t1, t2, t3 FieldElement
+       var i int
+
+       FeSquare(&t0, z)        // 2^1
+       FeSquare(&t1, &t0)      // 2^2
+       for i = 1; i < 2; i++ { // 2^3
+               FeSquare(&t1, &t1)
+       }
+       FeMul(&t1, z, &t1)      // 2^3 + 2^0
+       FeMul(&t0, &t0, &t1)    // 2^3 + 2^1 + 2^0
+       FeSquare(&t2, &t0)      // 2^4 + 2^2 + 2^1
+       FeMul(&t1, &t1, &t2)    // 2^4 + 2^3 + 2^2 + 2^1 + 2^0
+       FeSquare(&t2, &t1)      // 5,4,3,2,1
+       for i = 1; i < 5; i++ { // 9,8,7,6,5
+               FeSquare(&t2, &t2)
+       }
+       FeMul(&t1, &t2, &t1)     // 9,8,7,6,5,4,3,2,1,0
+       FeSquare(&t2, &t1)       // 10..1
+       for i = 1; i < 10; i++ { // 19..10
+               FeSquare(&t2, &t2)
+       }
+       FeMul(&t2, &t2, &t1)     // 19..0
+       FeSquare(&t3, &t2)       // 20..1
+       for i = 1; i < 20; i++ { // 39..20
+               FeSquare(&t3, &t3)
+       }
+       FeMul(&t2, &t3, &t2)     // 39..0
+       FeSquare(&t2, &t2)       // 40..1
+       for i = 1; i < 10; i++ { // 49..10
+               FeSquare(&t2, &t2)
+       }
+       FeMul(&t1, &t2, &t1)     // 49..0
+       FeSquare(&t2, &t1)       // 50..1
+       for i = 1; i < 50; i++ { // 99..50
+               FeSquare(&t2, &t2)
+       }
+       FeMul(&t2, &t2, &t1)      // 99..0
+       FeSquare(&t3, &t2)        // 100..1
+       for i = 1; i < 100; i++ { // 199..100
+               FeSquare(&t3, &t3)
+       }
+       FeMul(&t2, &t3, &t2)     // 199..0
+       FeSquare(&t2, &t2)       // 200..1
+       for i = 1; i < 50; i++ { // 249..50
+               FeSquare(&t2, &t2)
+       }
+       FeMul(&t1, &t2, &t1)    // 249..0
+       FeSquare(&t1, &t1)      // 250..1
+       for i = 1; i < 5; i++ { // 254..5
+               FeSquare(&t1, &t1)
+       }
+       FeMul(out, &t1, &t0) // 254..5,3,1,0
+}
+
+func fePow22523(out, z *FieldElement) {
+       var t0, t1, t2 FieldElement
+       var i int
+
+       FeSquare(&t0, z)
+       for i = 1; i < 1; i++ {
+               FeSquare(&t0, &t0)
+       }
+       FeSquare(&t1, &t0)
+       for i = 1; i < 2; i++ {
+               FeSquare(&t1, &t1)
+       }
+       FeMul(&t1, z, &t1)
+       FeMul(&t0, &t0, &t1)
+       FeSquare(&t0, &t0)
+       for i = 1; i < 1; i++ {
+               FeSquare(&t0, &t0)
+       }
+       FeMul(&t0, &t1, &t0)
+       FeSquare(&t1, &t0)
+       for i = 1; i < 5; i++ {
+               FeSquare(&t1, &t1)
+       }
+       FeMul(&t0, &t1, &t0)
+       FeSquare(&t1, &t0)
+       for i = 1; i < 10; i++ {
+               FeSquare(&t1, &t1)
+       }
+       FeMul(&t1, &t1, &t0)
+       FeSquare(&t2, &t1)
+       for i = 1; i < 20; i++ {
+               FeSquare(&t2, &t2)
+       }
+       FeMul(&t1, &t2, &t1)
+       FeSquare(&t1, &t1)
+       for i = 1; i < 10; i++ {
+               FeSquare(&t1, &t1)
+       }
+       FeMul(&t0, &t1, &t0)
+       FeSquare(&t1, &t0)
+       for i = 1; i < 50; i++ {
+               FeSquare(&t1, &t1)
+       }
+       FeMul(&t1, &t1, &t0)
+       FeSquare(&t2, &t1)
+       for i = 1; i < 100; i++ {
+               FeSquare(&t2, &t2)
+       }
+       FeMul(&t1, &t2, &t1)
+       FeSquare(&t1, &t1)
+       for i = 1; i < 50; i++ {
+               FeSquare(&t1, &t1)
+       }
+       FeMul(&t0, &t1, &t0)
+       FeSquare(&t0, &t0)
+       for i = 1; i < 2; i++ {
+               FeSquare(&t0, &t0)
+       }
+       FeMul(out, &t0, z)
+}
+
+// Group elements are members of the elliptic curve -x^2 + y^2 = 1 + d * x^2 *
+// y^2 where d = -121665/121666.
+//
+// Several representations are used:
+//   ProjectiveGroupElement: (X:Y:Z) satisfying x=X/Z, y=Y/Z
+//   ExtendedGroupElement: (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
+//   CompletedGroupElement: ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
+//   PreComputedGroupElement: (y+x,y-x,2dxy)
+
+type ProjectiveGroupElement struct {
+       X, Y, Z FieldElement
+}
+
+type ExtendedGroupElement struct {
+       X, Y, Z, T FieldElement
+}
+
+type CompletedGroupElement struct {
+       X, Y, Z, T FieldElement
+}
+
+type PreComputedGroupElement struct {
+       yPlusX, yMinusX, xy2d FieldElement
+}
+
+type CachedGroupElement struct {
+       yPlusX, yMinusX, Z, T2d FieldElement
+}
+
+func (p *ProjectiveGroupElement) Zero() {
+       FeZero(&p.X)
+       FeOne(&p.Y)
+       FeOne(&p.Z)
+}
+
+func (p *ProjectiveGroupElement) Double(r *CompletedGroupElement) {
+       var t0 FieldElement
+
+       FeSquare(&r.X, &p.X)
+       FeSquare(&r.Z, &p.Y)
+       FeSquare2(&r.T, &p.Z)
+       FeAdd(&r.Y, &p.X, &p.Y)
+       FeSquare(&t0, &r.Y)
+       FeAdd(&r.Y, &r.Z, &r.X)
+       FeSub(&r.Z, &r.Z, &r.X)
+       FeSub(&r.X, &t0, &r.Y)
+       FeSub(&r.T, &r.T, &r.Z)
+}
+
+func (p *ProjectiveGroupElement) ToBytes(s *[32]byte) {
+       var recip, x, y FieldElement
+
+       FeInvert(&recip, &p.Z)
+       FeMul(&x, &p.X, &recip)
+       FeMul(&y, &p.Y, &recip)
+       FeToBytes(s, &y)
+       s[31] ^= FeIsNegative(&x) << 7
+}
+
+func (p *ExtendedGroupElement) Zero() {
+       FeZero(&p.X)
+       FeOne(&p.Y)
+       FeOne(&p.Z)
+       FeZero(&p.T)
+}
+
+func (p *ExtendedGroupElement) Double(r *CompletedGroupElement) {
+       var q ProjectiveGroupElement
+       p.ToProjective(&q)
+       q.Double(r)
+}
+
+func (p *ExtendedGroupElement) ToCached(r *CachedGroupElement) {
+       FeAdd(&r.yPlusX, &p.Y, &p.X)
+       FeSub(&r.yMinusX, &p.Y, &p.X)
+       FeCopy(&r.Z, &p.Z)
+       FeMul(&r.T2d, &p.T, &d2)
+}
+
+func (p *ExtendedGroupElement) ToProjective(r *ProjectiveGroupElement) {
+       FeCopy(&r.X, &p.X)
+       FeCopy(&r.Y, &p.Y)
+       FeCopy(&r.Z, &p.Z)
+}
+
+func (p *ExtendedGroupElement) ToBytes(s *[32]byte) {
+       var recip, x, y FieldElement
+
+       FeInvert(&recip, &p.Z)
+       FeMul(&x, &p.X, &recip)
+       FeMul(&y, &p.Y, &recip)
+       FeToBytes(s, &y)
+       s[31] ^= FeIsNegative(&x) << 7
+}
+
+func (p *ExtendedGroupElement) FromBytes(s *[32]byte) bool {
+       var u, v, v3, vxx, check FieldElement
+
+       FeFromBytes(&p.Y, s)
+       FeOne(&p.Z)
+       FeSquare(&u, &p.Y)
+       FeMul(&v, &u, &d)
+       FeSub(&u, &u, &p.Z) // y = y^2-1
+       FeAdd(&v, &v, &p.Z) // v = dy^2+1
+
+       FeSquare(&v3, &v)
+       FeMul(&v3, &v3, &v) // v3 = v^3
+       FeSquare(&p.X, &v3)
+       FeMul(&p.X, &p.X, &v)
+       FeMul(&p.X, &p.X, &u) // x = uv^7
+
+       fePow22523(&p.X, &p.X) // x = (uv^7)^((q-5)/8)
+       FeMul(&p.X, &p.X, &v3)
+       FeMul(&p.X, &p.X, &u) // x = uv^3(uv^7)^((q-5)/8)
+
+       var tmpX, tmp2 [32]byte
+
+       FeSquare(&vxx, &p.X)
+       FeMul(&vxx, &vxx, &v)
+       FeSub(&check, &vxx, &u) // vx^2-u
+       if FeIsNonZero(&check) == 1 {
+               FeAdd(&check, &vxx, &u) // vx^2+u
+               if FeIsNonZero(&check) == 1 {
+                       return false
+               }
+               FeMul(&p.X, &p.X, &SqrtM1)
+
+               FeToBytes(&tmpX, &p.X)
+               for i, v := range tmpX {
+                       tmp2[31-i] = v
+               }
+       }
+
+       if FeIsNegative(&p.X) != (s[31] >> 7) {
+               FeNeg(&p.X, &p.X)
+       }
+
+       FeMul(&p.T, &p.X, &p.Y)
+       return true
+}
+
+func (p *CompletedGroupElement) ToProjective(r *ProjectiveGroupElement) {
+       FeMul(&r.X, &p.X, &p.T)
+       FeMul(&r.Y, &p.Y, &p.Z)
+       FeMul(&r.Z, &p.Z, &p.T)
+}
+
+func (p *CompletedGroupElement) ToExtended(r *ExtendedGroupElement) {
+       FeMul(&r.X, &p.X, &p.T)
+       FeMul(&r.Y, &p.Y, &p.Z)
+       FeMul(&r.Z, &p.Z, &p.T)
+       FeMul(&r.T, &p.X, &p.Y)
+}
+
+func (p *PreComputedGroupElement) Zero() {
+       FeOne(&p.yPlusX)
+       FeOne(&p.yMinusX)
+       FeZero(&p.xy2d)
+}
+
+func geAdd(r *CompletedGroupElement, p *ExtendedGroupElement, q *CachedGroupElement) {
+       var t0 FieldElement
+
+       FeAdd(&r.X, &p.Y, &p.X)
+       FeSub(&r.Y, &p.Y, &p.X)
+       FeMul(&r.Z, &r.X, &q.yPlusX)
+       FeMul(&r.Y, &r.Y, &q.yMinusX)
+       FeMul(&r.T, &q.T2d, &p.T)
+       FeMul(&r.X, &p.Z, &q.Z)
+       FeAdd(&t0, &r.X, &r.X)
+       FeSub(&r.X, &r.Z, &r.Y)
+       FeAdd(&r.Y, &r.Z, &r.Y)
+       FeAdd(&r.Z, &t0, &r.T)
+       FeSub(&r.T, &t0, &r.T)
+}
+
+func geSub(r *CompletedGroupElement, p *ExtendedGroupElement, q *CachedGroupElement) {
+       var t0 FieldElement
+
+       FeAdd(&r.X, &p.Y, &p.X)
+       FeSub(&r.Y, &p.Y, &p.X)
+       FeMul(&r.Z, &r.X, &q.yMinusX)
+       FeMul(&r.Y, &r.Y, &q.yPlusX)
+       FeMul(&r.T, &q.T2d, &p.T)
+       FeMul(&r.X, &p.Z, &q.Z)
+       FeAdd(&t0, &r.X, &r.X)
+       FeSub(&r.X, &r.Z, &r.Y)
+       FeAdd(&r.Y, &r.Z, &r.Y)
+       FeSub(&r.Z, &t0, &r.T)
+       FeAdd(&r.T, &t0, &r.T)
+}
+
+func geMixedAdd(r *CompletedGroupElement, p *ExtendedGroupElement, q *PreComputedGroupElement) {
+       var t0 FieldElement
+
+       FeAdd(&r.X, &p.Y, &p.X)
+       FeSub(&r.Y, &p.Y, &p.X)
+       FeMul(&r.Z, &r.X, &q.yPlusX)
+       FeMul(&r.Y, &r.Y, &q.yMinusX)
+       FeMul(&r.T, &q.xy2d, &p.T)
+       FeAdd(&t0, &p.Z, &p.Z)
+       FeSub(&r.X, &r.Z, &r.Y)
+       FeAdd(&r.Y, &r.Z, &r.Y)
+       FeAdd(&r.Z, &t0, &r.T)
+       FeSub(&r.T, &t0, &r.T)
+}
+
+func geMixedSub(r *CompletedGroupElement, p *ExtendedGroupElement, q *PreComputedGroupElement) {
+       var t0 FieldElement
+
+       FeAdd(&r.X, &p.Y, &p.X)
+       FeSub(&r.Y, &p.Y, &p.X)
+       FeMul(&r.Z, &r.X, &q.yMinusX)
+       FeMul(&r.Y, &r.Y, &q.yPlusX)
+       FeMul(&r.T, &q.xy2d, &p.T)
+       FeAdd(&t0, &p.Z, &p.Z)
+       FeSub(&r.X, &r.Z, &r.Y)
+       FeAdd(&r.Y, &r.Z, &r.Y)
+       FeSub(&r.Z, &t0, &r.T)
+       FeAdd(&r.T, &t0, &r.T)
+}
+
+func slide(r *[256]int8, a *[32]byte) {
+       for i := range r {
+               r[i] = int8(1 & (a[i>>3] >> uint(i&7)))
+       }
+
+       for i := range r {
+               if r[i] != 0 {
+                       for b := 1; b <= 6 && i+b < 256; b++ {
+                               if r[i+b] != 0 {
+                                       if r[i]+(r[i+b]<<uint(b)) <= 15 {
+                                               r[i] += r[i+b] << uint(b)
+                                               r[i+b] = 0
+                                       } else if r[i]-(r[i+b]<<uint(b)) >= -15 {
+                                               r[i] -= r[i+b] << uint(b)
+                                               for k := i + b; k < 256; k++ {
+                                                       if r[k] == 0 {
+                                                               r[k] = 1
+                                                               break
+                                                       }
+                                                       r[k] = 0
+                                               }
+                                       } else {
+                                               break
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+// GeDoubleScalarMultVartime sets r = a*A + b*B
+// where a = a[0]+256*a[1]+...+256^31 a[31].
+// and b = b[0]+256*b[1]+...+256^31 b[31].
+// B is the Ed25519 base point (x,4/5) with x positive.
+func GeDoubleScalarMultVartime(r *ProjectiveGroupElement, a *[32]byte, A *ExtendedGroupElement, b *[32]byte) {
+       var aSlide, bSlide [256]int8
+       var Ai [8]CachedGroupElement // A,3A,5A,7A,9A,11A,13A,15A
+       var t CompletedGroupElement
+       var u, A2 ExtendedGroupElement
+       var i int
+
+       slide(&aSlide, a)
+       slide(&bSlide, b)
+
+       A.ToCached(&Ai[0])
+       A.Double(&t)
+       t.ToExtended(&A2)
+
+       for i := 0; i < 7; i++ {
+               geAdd(&t, &A2, &Ai[i])
+               t.ToExtended(&u)
+               u.ToCached(&Ai[i+1])
+       }
+
+       r.Zero()
+
+       for i = 255; i >= 0; i-- {
+               if aSlide[i] != 0 || bSlide[i] != 0 {
+                       break
+               }
+       }
+
+       for ; i >= 0; i-- {
+               r.Double(&t)
+
+               if aSlide[i] > 0 {
+                       t.ToExtended(&u)
+                       geAdd(&t, &u, &Ai[aSlide[i]/2])
+               } else if aSlide[i] < 0 {
+                       t.ToExtended(&u)
+                       geSub(&t, &u, &Ai[(-aSlide[i])/2])
+               }
+
+               if bSlide[i] > 0 {
+                       t.ToExtended(&u)
+                       geMixedAdd(&t, &u, &bi[bSlide[i]/2])
+               } else if bSlide[i] < 0 {
+                       t.ToExtended(&u)
+                       geMixedSub(&t, &u, &bi[(-bSlide[i])/2])
+               }
+
+               t.ToProjective(r)
+       }
+}
+
+// equal returns 1 if b == c and 0 otherwise, assuming that b and c are
+// non-negative.
+func equal(b, c int32) int32 {
+       x := uint32(b ^ c)
+       x--
+       return int32(x >> 31)
+}
+
+// negative returns 1 if b < 0 and 0 otherwise.
+func negative(b int32) int32 {
+       return (b >> 31) & 1
+}
+
+func PreComputedGroupElementCMove(t, u *PreComputedGroupElement, b int32) {
+       FeCMove(&t.yPlusX, &u.yPlusX, b)
+       FeCMove(&t.yMinusX, &u.yMinusX, b)
+       FeCMove(&t.xy2d, &u.xy2d, b)
+}
+
+func selectPoint(t *PreComputedGroupElement, pos int32, b int32) {
+       var minusT PreComputedGroupElement
+       bNegative := negative(b)
+       bAbs := b - (((-bNegative) & b) << 1)
+
+       t.Zero()
+       for i := int32(0); i < 8; i++ {
+               PreComputedGroupElementCMove(t, &base[pos][i], equal(bAbs, i+1))
+       }
+       FeCopy(&minusT.yPlusX, &t.yMinusX)
+       FeCopy(&minusT.yMinusX, &t.yPlusX)
+       FeNeg(&minusT.xy2d, &t.xy2d)
+       PreComputedGroupElementCMove(t, &minusT, bNegative)
+}
+
+// GeScalarMultBase computes h = a*B, where
+//   a = a[0]+256*a[1]+...+256^31 a[31]
+//   B is the Ed25519 base point (x,4/5) with x positive.
+//
+// Preconditions:
+//   a[31] <= 127
+func GeScalarMultBase(h *ExtendedGroupElement, a *[32]byte) {
+       var e [64]int8
+
+       for i, v := range a {
+               e[2*i] = int8(v & 15)
+               e[2*i+1] = int8((v >> 4) & 15)
+       }
+
+       // each e[i] is between 0 and 15 and e[63] is between 0 and 7.
+
+       carry := int8(0)
+       for i := 0; i < 63; i++ {
+               e[i] += carry
+               carry = (e[i] + 8) >> 4
+               e[i] -= carry << 4
+       }
+       e[63] += carry
+       // each e[i] is between -8 and 8.
+
+       h.Zero()
+       var t PreComputedGroupElement
+       var r CompletedGroupElement
+       for i := int32(1); i < 64; i += 2 {
+               selectPoint(&t, i/2, int32(e[i]))
+               geMixedAdd(&r, h, &t)
+               r.ToExtended(h)
+       }
+
+       var s ProjectiveGroupElement
+
+       h.Double(&r)
+       r.ToProjective(&s)
+       s.Double(&r)
+       r.ToProjective(&s)
+       s.Double(&r)
+       r.ToProjective(&s)
+       s.Double(&r)
+       r.ToExtended(h)
+
+       for i := int32(0); i < 64; i += 2 {
+               selectPoint(&t, i/2, int32(e[i]))
+               geMixedAdd(&r, h, &t)
+               r.ToExtended(h)
+       }
+}
+
+// The scalars are GF(2^252 + 27742317777372353535851937790883648493).
+
+// Input:
+//   a[0]+256*a[1]+...+256^31*a[31] = a
+//   b[0]+256*b[1]+...+256^31*b[31] = b
+//   c[0]+256*c[1]+...+256^31*c[31] = c
+//
+// Output:
+//   s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
+//   where l = 2^252 + 27742317777372353535851937790883648493.
+func ScMulAdd(s, a, b, c *[32]byte) {
+       a0 := 2097151 & load3(a[:])
+       a1 := 2097151 & (load4(a[2:]) >> 5)
+       a2 := 2097151 & (load3(a[5:]) >> 2)
+       a3 := 2097151 & (load4(a[7:]) >> 7)
+       a4 := 2097151 & (load4(a[10:]) >> 4)
+       a5 := 2097151 & (load3(a[13:]) >> 1)
+       a6 := 2097151 & (load4(a[15:]) >> 6)
+       a7 := 2097151 & (load3(a[18:]) >> 3)
+       a8 := 2097151 & load3(a[21:])
+       a9 := 2097151 & (load4(a[23:]) >> 5)
+       a10 := 2097151 & (load3(a[26:]) >> 2)
+       a11 := (load4(a[28:]) >> 7)
+       b0 := 2097151 & load3(b[:])
+       b1 := 2097151 & (load4(b[2:]) >> 5)
+       b2 := 2097151 & (load3(b[5:]) >> 2)
+       b3 := 2097151 & (load4(b[7:]) >> 7)
+       b4 := 2097151 & (load4(b[10:]) >> 4)
+       b5 := 2097151 & (load3(b[13:]) >> 1)
+       b6 := 2097151 & (load4(b[15:]) >> 6)
+       b7 := 2097151 & (load3(b[18:]) >> 3)
+       b8 := 2097151 & load3(b[21:])
+       b9 := 2097151 & (load4(b[23:]) >> 5)
+       b10 := 2097151 & (load3(b[26:]) >> 2)
+       b11 := (load4(b[28:]) >> 7)
+       c0 := 2097151 & load3(c[:])
+       c1 := 2097151 & (load4(c[2:]) >> 5)
+       c2 := 2097151 & (load3(c[5:]) >> 2)
+       c3 := 2097151 & (load4(c[7:]) >> 7)
+       c4 := 2097151 & (load4(c[10:]) >> 4)
+       c5 := 2097151 & (load3(c[13:]) >> 1)
+       c6 := 2097151 & (load4(c[15:]) >> 6)
+       c7 := 2097151 & (load3(c[18:]) >> 3)
+       c8 := 2097151 & load3(c[21:])
+       c9 := 2097151 & (load4(c[23:]) >> 5)
+       c10 := 2097151 & (load3(c[26:]) >> 2)
+       c11 := (load4(c[28:]) >> 7)
+       var carry [23]int64
+
+       s0 := c0 + a0*b0
+       s1 := c1 + a0*b1 + a1*b0
+       s2 := c2 + a0*b2 + a1*b1 + a2*b0
+       s3 := c3 + a0*b3 + a1*b2 + a2*b1 + a3*b0
+       s4 := c4 + a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0
+       s5 := c5 + a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0
+       s6 := c6 + a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0
+       s7 := c7 + a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0
+       s8 := c8 + a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0
+       s9 := c9 + a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0
+       s10 := c10 + a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0
+       s11 := c11 + a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0
+       s12 := a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1
+       s13 := a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2
+       s14 := a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3
+       s15 := a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4
+       s16 := a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5
+       s17 := a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6
+       s18 := a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7
+       s19 := a8*b11 + a9*b10 + a10*b9 + a11*b8
+       s20 := a9*b11 + a10*b10 + a11*b9
+       s21 := a10*b11 + a11*b10
+       s22 := a11 * b11
+       s23 := int64(0)
+
+       carry[0] = (s0 + (1 << 20)) >> 21
+       s1 += carry[0]
+       s0 -= carry[0] << 21
+       carry[2] = (s2 + (1 << 20)) >> 21
+       s3 += carry[2]
+       s2 -= carry[2] << 21
+       carry[4] = (s4 + (1 << 20)) >> 21
+       s5 += carry[4]
+       s4 -= carry[4] << 21
+       carry[6] = (s6 + (1 << 20)) >> 21
+       s7 += carry[6]
+       s6 -= carry[6] << 21
+       carry[8] = (s8 + (1 << 20)) >> 21
+       s9 += carry[8]
+       s8 -= carry[8] << 21
+       carry[10] = (s10 + (1 << 20)) >> 21
+       s11 += carry[10]
+       s10 -= carry[10] << 21
+       carry[12] = (s12 + (1 << 20)) >> 21
+       s13 += carry[12]
+       s12 -= carry[12] << 21
+       carry[14] = (s14 + (1 << 20)) >> 21
+       s15 += carry[14]
+       s14 -= carry[14] << 21
+       carry[16] = (s16 + (1 << 20)) >> 21
+       s17 += carry[16]
+       s16 -= carry[16] << 21
+       carry[18] = (s18 + (1 << 20)) >> 21
+       s19 += carry[18]
+       s18 -= carry[18] << 21
+       carry[20] = (s20 + (1 << 20)) >> 21
+       s21 += carry[20]
+       s20 -= carry[20] << 21
+       carry[22] = (s22 + (1 << 20)) >> 21
+       s23 += carry[22]
+       s22 -= carry[22] << 21
+
+       carry[1] = (s1 + (1 << 20)) >> 21
+       s2 += carry[1]
+       s1 -= carry[1] << 21
+       carry[3] = (s3 + (1 << 20)) >> 21
+       s4 += carry[3]
+       s3 -= carry[3] << 21
+       carry[5] = (s5 + (1 << 20)) >> 21
+       s6 += carry[5]
+       s5 -= carry[5] << 21
+       carry[7] = (s7 + (1 << 20)) >> 21
+       s8 += carry[7]
+       s7 -= carry[7] << 21
+       carry[9] = (s9 + (1 << 20)) >> 21
+       s10 += carry[9]
+       s9 -= carry[9] << 21
+       carry[11] = (s11 + (1 << 20)) >> 21
+       s12 += carry[11]
+       s11 -= carry[11] << 21
+       carry[13] = (s13 + (1 << 20)) >> 21
+       s14 += carry[13]
+       s13 -= carry[13] << 21
+       carry[15] = (s15 + (1 << 20)) >> 21
+       s16 += carry[15]
+       s15 -= carry[15] << 21
+       carry[17] = (s17 + (1 << 20)) >> 21
+       s18 += carry[17]
+       s17 -= carry[17] << 21
+       carry[19] = (s19 + (1 << 20)) >> 21
+       s20 += carry[19]
+       s19 -= carry[19] << 21
+       carry[21] = (s21 + (1 << 20)) >> 21
+       s22 += carry[21]
+       s21 -= carry[21] << 21
+
+       s11 += s23 * 666643
+       s12 += s23 * 470296
+       s13 += s23 * 654183
+       s14 -= s23 * 997805
+       s15 += s23 * 136657
+       s16 -= s23 * 683901
+       s23 = 0
+
+       s10 += s22 * 666643
+       s11 += s22 * 470296
+       s12 += s22 * 654183
+       s13 -= s22 * 997805
+       s14 += s22 * 136657
+       s15 -= s22 * 683901
+       s22 = 0
+
+       s9 += s21 * 666643
+       s10 += s21 * 470296
+       s11 += s21 * 654183
+       s12 -= s21 * 997805
+       s13 += s21 * 136657
+       s14 -= s21 * 683901
+       s21 = 0
+
+       s8 += s20 * 666643
+       s9 += s20 * 470296
+       s10 += s20 * 654183
+       s11 -= s20 * 997805
+       s12 += s20 * 136657
+       s13 -= s20 * 683901
+       s20 = 0
+
+       s7 += s19 * 666643
+       s8 += s19 * 470296
+       s9 += s19 * 654183
+       s10 -= s19 * 997805
+       s11 += s19 * 136657
+       s12 -= s19 * 683901
+       s19 = 0
+
+       s6 += s18 * 666643
+       s7 += s18 * 470296
+       s8 += s18 * 654183
+       s9 -= s18 * 997805
+       s10 += s18 * 136657
+       s11 -= s18 * 683901
+       s18 = 0
+
+       carry[6] = (s6 + (1 << 20)) >> 21
+       s7 += carry[6]
+       s6 -= carry[6] << 21
+       carry[8] = (s8 + (1 << 20)) >> 21
+       s9 += carry[8]
+       s8 -= carry[8] << 21
+       carry[10] = (s10 + (1 << 20)) >> 21
+       s11 += carry[10]
+       s10 -= carry[10] << 21
+       carry[12] = (s12 + (1 << 20)) >> 21
+       s13 += carry[12]
+       s12 -= carry[12] << 21
+       carry[14] = (s14 + (1 << 20)) >> 21
+       s15 += carry[14]
+       s14 -= carry[14] << 21
+       carry[16] = (s16 + (1 << 20)) >> 21
+       s17 += carry[16]
+       s16 -= carry[16] << 21
+
+       carry[7] = (s7 + (1 << 20)) >> 21
+       s8 += carry[7]
+       s7 -= carry[7] << 21
+       carry[9] = (s9 + (1 << 20)) >> 21
+       s10 += carry[9]
+       s9 -= carry[9] << 21
+       carry[11] = (s11 + (1 << 20)) >> 21
+       s12 += carry[11]
+       s11 -= carry[11] << 21
+       carry[13] = (s13 + (1 << 20)) >> 21
+       s14 += carry[13]
+       s13 -= carry[13] << 21
+       carry[15] = (s15 + (1 << 20)) >> 21
+       s16 += carry[15]
+       s15 -= carry[15] << 21
+
+       s5 += s17 * 666643
+       s6 += s17 * 470296
+       s7 += s17 * 654183
+       s8 -= s17 * 997805
+       s9 += s17 * 136657
+       s10 -= s17 * 683901
+       s17 = 0
+
+       s4 += s16 * 666643
+       s5 += s16 * 470296
+       s6 += s16 * 654183
+       s7 -= s16 * 997805
+       s8 += s16 * 136657
+       s9 -= s16 * 683901
+       s16 = 0
+
+       s3 += s15 * 666643
+       s4 += s15 * 470296
+       s5 += s15 * 654183
+       s6 -= s15 * 997805
+       s7 += s15 * 136657
+       s8 -= s15 * 683901
+       s15 = 0
+
+       s2 += s14 * 666643
+       s3 += s14 * 470296
+       s4 += s14 * 654183
+       s5 -= s14 * 997805
+       s6 += s14 * 136657
+       s7 -= s14 * 683901
+       s14 = 0
+
+       s1 += s13 * 666643
+       s2 += s13 * 470296
+       s3 += s13 * 654183
+       s4 -= s13 * 997805
+       s5 += s13 * 136657
+       s6 -= s13 * 683901
+       s13 = 0
+
+       s0 += s12 * 666643
+       s1 += s12 * 470296
+       s2 += s12 * 654183
+       s3 -= s12 * 997805
+       s4 += s12 * 136657
+       s5 -= s12 * 683901
+       s12 = 0
+
+       carry[0] = (s0 + (1 << 20)) >> 21
+       s1 += carry[0]
+       s0 -= carry[0] << 21
+       carry[2] = (s2 + (1 << 20)) >> 21
+       s3 += carry[2]
+       s2 -= carry[2] << 21
+       carry[4] = (s4 + (1 << 20)) >> 21
+       s5 += carry[4]
+       s4 -= carry[4] << 21
+       carry[6] = (s6 + (1 << 20)) >> 21
+       s7 += carry[6]
+       s6 -= carry[6] << 21
+       carry[8] = (s8 + (1 << 20)) >> 21
+       s9 += carry[8]
+       s8 -= carry[8] << 21
+       carry[10] = (s10 + (1 << 20)) >> 21
+       s11 += carry[10]
+       s10 -= carry[10] << 21
+
+       carry[1] = (s1 + (1 << 20)) >> 21
+       s2 += carry[1]
+       s1 -= carry[1] << 21
+       carry[3] = (s3 + (1 << 20)) >> 21
+       s4 += carry[3]
+       s3 -= carry[3] << 21
+       carry[5] = (s5 + (1 << 20)) >> 21
+       s6 += carry[5]
+       s5 -= carry[5] << 21
+       carry[7] = (s7 + (1 << 20)) >> 21
+       s8 += carry[7]
+       s7 -= carry[7] << 21
+       carry[9] = (s9 + (1 << 20)) >> 21
+       s10 += carry[9]
+       s9 -= carry[9] << 21
+       carry[11] = (s11 + (1 << 20)) >> 21
+       s12 += carry[11]
+       s11 -= carry[11] << 21
+
+       s0 += s12 * 666643
+       s1 += s12 * 470296
+       s2 += s12 * 654183
+       s3 -= s12 * 997805
+       s4 += s12 * 136657
+       s5 -= s12 * 683901
+       s12 = 0
+
+       carry[0] = s0 >> 21
+       s1 += carry[0]
+       s0 -= carry[0] << 21
+       carry[1] = s1 >> 21
+       s2 += carry[1]
+       s1 -= carry[1] << 21
+       carry[2] = s2 >> 21
+       s3 += carry[2]
+       s2 -= carry[2] << 21
+       carry[3] = s3 >> 21
+       s4 += carry[3]
+       s3 -= carry[3] << 21
+       carry[4] = s4 >> 21
+       s5 += carry[4]
+       s4 -= carry[4] << 21
+       carry[5] = s5 >> 21
+       s6 += carry[5]
+       s5 -= carry[5] << 21
+       carry[6] = s6 >> 21
+       s7 += carry[6]
+       s6 -= carry[6] << 21
+       carry[7] = s7 >> 21
+       s8 += carry[7]
+       s7 -= carry[7] << 21
+       carry[8] = s8 >> 21
+       s9 += carry[8]
+       s8 -= carry[8] << 21
+       carry[9] = s9 >> 21
+       s10 += carry[9]
+       s9 -= carry[9] << 21
+       carry[10] = s10 >> 21
+       s11 += carry[10]
+       s10 -= carry[10] << 21
+       carry[11] = s11 >> 21
+       s12 += carry[11]
+       s11 -= carry[11] << 21
+
+       s0 += s12 * 666643
+       s1 += s12 * 470296
+       s2 += s12 * 654183
+       s3 -= s12 * 997805
+       s4 += s12 * 136657
+       s5 -= s12 * 683901
+       s12 = 0
+
+       carry[0] = s0 >> 21
+       s1 += carry[0]
+       s0 -= carry[0] << 21
+       carry[1] = s1 >> 21
+       s2 += carry[1]
+       s1 -= carry[1] << 21
+       carry[2] = s2 >> 21
+       s3 += carry[2]
+       s2 -= carry[2] << 21
+       carry[3] = s3 >> 21
+       s4 += carry[3]
+       s3 -= carry[3] << 21
+       carry[4] = s4 >> 21
+       s5 += carry[4]
+       s4 -= carry[4] << 21
+       carry[5] = s5 >> 21
+       s6 += carry[5]
+       s5 -= carry[5] << 21
+       carry[6] = s6 >> 21
+       s7 += carry[6]
+       s6 -= carry[6] << 21
+       carry[7] = s7 >> 21
+       s8 += carry[7]
+       s7 -= carry[7] << 21
+       carry[8] = s8 >> 21
+       s9 += carry[8]
+       s8 -= carry[8] << 21
+       carry[9] = s9 >> 21
+       s10 += carry[9]
+       s9 -= carry[9] << 21
+       carry[10] = s10 >> 21
+       s11 += carry[10]
+       s10 -= carry[10] << 21
+
+       s[0] = byte(s0 >> 0)
+       s[1] = byte(s0 >> 8)
+       s[2] = byte((s0 >> 16) | (s1 << 5))
+       s[3] = byte(s1 >> 3)
+       s[4] = byte(s1 >> 11)
+       s[5] = byte((s1 >> 19) | (s2 << 2))
+       s[6] = byte(s2 >> 6)
+       s[7] = byte((s2 >> 14) | (s3 << 7))
+       s[8] = byte(s3 >> 1)
+       s[9] = byte(s3 >> 9)
+       s[10] = byte((s3 >> 17) | (s4 << 4))
+       s[11] = byte(s4 >> 4)
+       s[12] = byte(s4 >> 12)
+       s[13] = byte((s4 >> 20) | (s5 << 1))
+       s[14] = byte(s5 >> 7)
+       s[15] = byte((s5 >> 15) | (s6 << 6))
+       s[16] = byte(s6 >> 2)
+       s[17] = byte(s6 >> 10)
+       s[18] = byte((s6 >> 18) | (s7 << 3))
+       s[19] = byte(s7 >> 5)
+       s[20] = byte(s7 >> 13)
+       s[21] = byte(s8 >> 0)
+       s[22] = byte(s8 >> 8)
+       s[23] = byte((s8 >> 16) | (s9 << 5))
+       s[24] = byte(s9 >> 3)
+       s[25] = byte(s9 >> 11)
+       s[26] = byte((s9 >> 19) | (s10 << 2))
+       s[27] = byte(s10 >> 6)
+       s[28] = byte((s10 >> 14) | (s11 << 7))
+       s[29] = byte(s11 >> 1)
+       s[30] = byte(s11 >> 9)
+       s[31] = byte(s11 >> 17)
+}
+
+// Input:
+//   s[0]+256*s[1]+...+256^63*s[63] = s
+//
+// Output:
+//   s[0]+256*s[1]+...+256^31*s[31] = s mod l
+//   where l = 2^252 + 27742317777372353535851937790883648493.
+func ScReduce(out *[32]byte, s *[64]byte) {
+       s0 := 2097151 & load3(s[:])
+       s1 := 2097151 & (load4(s[2:]) >> 5)
+       s2 := 2097151 & (load3(s[5:]) >> 2)
+       s3 := 2097151 & (load4(s[7:]) >> 7)
+       s4 := 2097151 & (load4(s[10:]) >> 4)
+       s5 := 2097151 & (load3(s[13:]) >> 1)
+       s6 := 2097151 & (load4(s[15:]) >> 6)
+       s7 := 2097151 & (load3(s[18:]) >> 3)
+       s8 := 2097151 & load3(s[21:])
+       s9 := 2097151 & (load4(s[23:]) >> 5)
+       s10 := 2097151 & (load3(s[26:]) >> 2)
+       s11 := 2097151 & (load4(s[28:]) >> 7)
+       s12 := 2097151 & (load4(s[31:]) >> 4)
+       s13 := 2097151 & (load3(s[34:]) >> 1)
+       s14 := 2097151 & (load4(s[36:]) >> 6)
+       s15 := 2097151 & (load3(s[39:]) >> 3)
+       s16 := 2097151 & load3(s[42:])
+       s17 := 2097151 & (load4(s[44:]) >> 5)
+       s18 := 2097151 & (load3(s[47:]) >> 2)
+       s19 := 2097151 & (load4(s[49:]) >> 7)
+       s20 := 2097151 & (load4(s[52:]) >> 4)
+       s21 := 2097151 & (load3(s[55:]) >> 1)
+       s22 := 2097151 & (load4(s[57:]) >> 6)
+       s23 := (load4(s[60:]) >> 3)
+
+       s11 += s23 * 666643
+       s12 += s23 * 470296
+       s13 += s23 * 654183
+       s14 -= s23 * 997805
+       s15 += s23 * 136657
+       s16 -= s23 * 683901
+       s23 = 0
+
+       s10 += s22 * 666643
+       s11 += s22 * 470296
+       s12 += s22 * 654183
+       s13 -= s22 * 997805
+       s14 += s22 * 136657
+       s15 -= s22 * 683901
+       s22 = 0
+
+       s9 += s21 * 666643
+       s10 += s21 * 470296
+       s11 += s21 * 654183
+       s12 -= s21 * 997805
+       s13 += s21 * 136657
+       s14 -= s21 * 683901
+       s21 = 0
+
+       s8 += s20 * 666643
+       s9 += s20 * 470296
+       s10 += s20 * 654183
+       s11 -= s20 * 997805
+       s12 += s20 * 136657
+       s13 -= s20 * 683901
+       s20 = 0
+
+       s7 += s19 * 666643
+       s8 += s19 * 470296
+       s9 += s19 * 654183
+       s10 -= s19 * 997805
+       s11 += s19 * 136657
+       s12 -= s19 * 683901
+       s19 = 0
+
+       s6 += s18 * 666643
+       s7 += s18 * 470296
+       s8 += s18 * 654183
+       s9 -= s18 * 997805
+       s10 += s18 * 136657
+       s11 -= s18 * 683901
+       s18 = 0
+
+       var carry [17]int64
+
+       carry[6] = (s6 + (1 << 20)) >> 21
+       s7 += carry[6]
+       s6 -= carry[6] << 21
+       carry[8] = (s8 + (1 << 20)) >> 21
+       s9 += carry[8]
+       s8 -= carry[8] << 21
+       carry[10] = (s10 + (1 << 20)) >> 21
+       s11 += carry[10]
+       s10 -= carry[10] << 21
+       carry[12] = (s12 + (1 << 20)) >> 21
+       s13 += carry[12]
+       s12 -= carry[12] << 21
+       carry[14] = (s14 + (1 << 20)) >> 21
+       s15 += carry[14]
+       s14 -= carry[14] << 21
+       carry[16] = (s16 + (1 << 20)) >> 21
+       s17 += carry[16]
+       s16 -= carry[16] << 21
+
+       carry[7] = (s7 + (1 << 20)) >> 21
+       s8 += carry[7]
+       s7 -= carry[7] << 21
+       carry[9] = (s9 + (1 << 20)) >> 21
+       s10 += carry[9]
+       s9 -= carry[9] << 21
+       carry[11] = (s11 + (1 << 20)) >> 21
+       s12 += carry[11]
+       s11 -= carry[11] << 21
+       carry[13] = (s13 + (1 << 20)) >> 21
+       s14 += carry[13]
+       s13 -= carry[13] << 21
+       carry[15] = (s15 + (1 << 20)) >> 21
+       s16 += carry[15]
+       s15 -= carry[15] << 21
+
+       s5 += s17 * 666643
+       s6 += s17 * 470296
+       s7 += s17 * 654183
+       s8 -= s17 * 997805
+       s9 += s17 * 136657
+       s10 -= s17 * 683901
+       s17 = 0
+
+       s4 += s16 * 666643
+       s5 += s16 * 470296
+       s6 += s16 * 654183
+       s7 -= s16 * 997805
+       s8 += s16 * 136657
+       s9 -= s16 * 683901
+       s16 = 0
+
+       s3 += s15 * 666643
+       s4 += s15 * 470296
+       s5 += s15 * 654183
+       s6 -= s15 * 997805
+       s7 += s15 * 136657
+       s8 -= s15 * 683901
+       s15 = 0
+
+       s2 += s14 * 666643
+       s3 += s14 * 470296
+       s4 += s14 * 654183
+       s5 -= s14 * 997805
+       s6 += s14 * 136657
+       s7 -= s14 * 683901
+       s14 = 0
+
+       s1 += s13 * 666643
+       s2 += s13 * 470296
+       s3 += s13 * 654183
+       s4 -= s13 * 997805
+       s5 += s13 * 136657
+       s6 -= s13 * 683901
+       s13 = 0
+
+       s0 += s12 * 666643
+       s1 += s12 * 470296
+       s2 += s12 * 654183
+       s3 -= s12 * 997805
+       s4 += s12 * 136657
+       s5 -= s12 * 683901
+       s12 = 0
+
+       carry[0] = (s0 + (1 << 20)) >> 21
+       s1 += carry[0]
+       s0 -= carry[0] << 21
+       carry[2] = (s2 + (1 << 20)) >> 21
+       s3 += carry[2]
+       s2 -= carry[2] << 21
+       carry[4] = (s4 + (1 << 20)) >> 21
+       s5 += carry[4]
+       s4 -= carry[4] << 21
+       carry[6] = (s6 + (1 << 20)) >> 21
+       s7 += carry[6]
+       s6 -= carry[6] << 21
+       carry[8] = (s8 + (1 << 20)) >> 21
+       s9 += carry[8]
+       s8 -= carry[8] << 21
+       carry[10] = (s10 + (1 << 20)) >> 21
+       s11 += carry[10]
+       s10 -= carry[10] << 21
+
+       carry[1] = (s1 + (1 << 20)) >> 21
+       s2 += carry[1]
+       s1 -= carry[1] << 21
+       carry[3] = (s3 + (1 << 20)) >> 21
+       s4 += carry[3]
+       s3 -= carry[3] << 21
+       carry[5] = (s5 + (1 << 20)) >> 21
+       s6 += carry[5]
+       s5 -= carry[5] << 21
+       carry[7] = (s7 + (1 << 20)) >> 21
+       s8 += carry[7]
+       s7 -= carry[7] << 21
+       carry[9] = (s9 + (1 << 20)) >> 21
+       s10 += carry[9]
+       s9 -= carry[9] << 21
+       carry[11] = (s11 + (1 << 20)) >> 21
+       s12 += carry[11]
+       s11 -= carry[11] << 21
+
+       s0 += s12 * 666643
+       s1 += s12 * 470296
+       s2 += s12 * 654183
+       s3 -= s12 * 997805
+       s4 += s12 * 136657
+       s5 -= s12 * 683901
+       s12 = 0
+
+       carry[0] = s0 >> 21
+       s1 += carry[0]
+       s0 -= carry[0] << 21
+       carry[1] = s1 >> 21
+       s2 += carry[1]
+       s1 -= carry[1] << 21
+       carry[2] = s2 >> 21
+       s3 += carry[2]
+       s2 -= carry[2] << 21
+       carry[3] = s3 >> 21
+       s4 += carry[3]
+       s3 -= carry[3] << 21
+       carry[4] = s4 >> 21
+       s5 += carry[4]
+       s4 -= carry[4] << 21
+       carry[5] = s5 >> 21
+       s6 += carry[5]
+       s5 -= carry[5] << 21
+       carry[6] = s6 >> 21
+       s7 += carry[6]
+       s6 -= carry[6] << 21
+       carry[7] = s7 >> 21
+       s8 += carry[7]
+       s7 -= carry[7] << 21
+       carry[8] = s8 >> 21
+       s9 += carry[8]
+       s8 -= carry[8] << 21
+       carry[9] = s9 >> 21
+       s10 += carry[9]
+       s9 -= carry[9] << 21
+       carry[10] = s10 >> 21
+       s11 += carry[10]
+       s10 -= carry[10] << 21
+       carry[11] = s11 >> 21
+       s12 += carry[11]
+       s11 -= carry[11] << 21
+
+       s0 += s12 * 666643
+       s1 += s12 * 470296
+       s2 += s12 * 654183
+       s3 -= s12 * 997805
+       s4 += s12 * 136657
+       s5 -= s12 * 683901
+       s12 = 0
+
+       carry[0] = s0 >> 21
+       s1 += carry[0]
+       s0 -= carry[0] << 21
+       carry[1] = s1 >> 21
+       s2 += carry[1]
+       s1 -= carry[1] << 21
+       carry[2] = s2 >> 21
+       s3 += carry[2]
+       s2 -= carry[2] << 21
+       carry[3] = s3 >> 21
+       s4 += carry[3]
+       s3 -= carry[3] << 21
+       carry[4] = s4 >> 21
+       s5 += carry[4]
+       s4 -= carry[4] << 21
+       carry[5] = s5 >> 21
+       s6 += carry[5]
+       s5 -= carry[5] << 21
+       carry[6] = s6 >> 21
+       s7 += carry[6]
+       s6 -= carry[6] << 21
+       carry[7] = s7 >> 21
+       s8 += carry[7]
+       s7 -= carry[7] << 21
+       carry[8] = s8 >> 21
+       s9 += carry[8]
+       s8 -= carry[8] << 21
+       carry[9] = s9 >> 21
+       s10 += carry[9]
+       s9 -= carry[9] << 21
+       carry[10] = s10 >> 21
+       s11 += carry[10]
+       s10 -= carry[10] << 21
+
+       out[0] = byte(s0 >> 0)
+       out[1] = byte(s0 >> 8)
+       out[2] = byte((s0 >> 16) | (s1 << 5))
+       out[3] = byte(s1 >> 3)
+       out[4] = byte(s1 >> 11)
+       out[5] = byte((s1 >> 19) | (s2 << 2))
+       out[6] = byte(s2 >> 6)
+       out[7] = byte((s2 >> 14) | (s3 << 7))
+       out[8] = byte(s3 >> 1)
+       out[9] = byte(s3 >> 9)
+       out[10] = byte((s3 >> 17) | (s4 << 4))
+       out[11] = byte(s4 >> 4)
+       out[12] = byte(s4 >> 12)
+       out[13] = byte((s4 >> 20) | (s5 << 1))
+       out[14] = byte(s5 >> 7)
+       out[15] = byte((s5 >> 15) | (s6 << 6))
+       out[16] = byte(s6 >> 2)
+       out[17] = byte(s6 >> 10)
+       out[18] = byte((s6 >> 18) | (s7 << 3))
+       out[19] = byte(s7 >> 5)
+       out[20] = byte(s7 >> 13)
+       out[21] = byte(s8 >> 0)
+       out[22] = byte(s8 >> 8)
+       out[23] = byte((s8 >> 16) | (s9 << 5))
+       out[24] = byte(s9 >> 3)
+       out[25] = byte(s9 >> 11)
+       out[26] = byte((s9 >> 19) | (s10 << 2))
+       out[27] = byte(s10 >> 6)
+       out[28] = byte((s10 >> 14) | (s11 << 7))
+       out[29] = byte(s11 >> 1)
+       out[30] = byte(s11 >> 9)
+       out[31] = byte(s11 >> 17)
+}
diff --git a/crypto/ed25519/testdata/sign.input.gz b/crypto/ed25519/testdata/sign.input.gz
new file mode 100644 (file)
index 0000000..4103069
Binary files /dev/null and b/crypto/ed25519/testdata/sign.input.gz differ
diff --git a/crypto/sha3pool/pool.go b/crypto/sha3pool/pool.go
new file mode 100644 (file)
index 0000000..ca69b6a
--- /dev/null
@@ -0,0 +1,31 @@
+// Package sha3pool is a freelist for SHA3-256 hash objects.
+package sha3pool
+
+import (
+       "sync"
+
+       "golang.org/x/crypto/sha3"
+)
+
+var pool = &sync.Pool{New: func() interface{} { return sha3.New256() }}
+
+// Get256 returns an initialized SHA3-256 hash ready to use.
+// It is like sha3.New256 except it uses the freelist.
+// The caller should call Put256 when finished with the returned object.
+func Get256() sha3.ShakeHash {
+       return pool.Get().(sha3.ShakeHash)
+}
+
+// Put256 resets h and puts it in the freelist.
+func Put256(h sha3.ShakeHash) {
+       h.Reset()
+       pool.Put(h)
+}
+
+// Sum256 uses a ShakeHash from the pool to sum into hash.
+func Sum256(hash, data []byte) {
+       h := Get256()
+       h.Write(data)
+       h.Read(hash)
+       Put256(h)
+}
diff --git a/glide.lock b/glide.lock
new file mode 100644 (file)
index 0000000..80a7733
--- /dev/null
@@ -0,0 +1,174 @@
+hash: 93f15c9766ea826c29a91f545c42172eafd8c61e39c1d81617114ad1a9c9eaf2
+updated: 2017-05-18T06:13:24.295793122-04:00
+imports:
+- name: github.com/btcsuite/btcd
+  version: 53f55a46349aa8f44b90895047e843666991cf24
+  subpackages:
+  - btcec
+- name: github.com/davecgh/go-spew
+  version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9
+  subpackages:
+  - spew
+- name: github.com/ebuchman/fail-test
+  version: 95f809107225be108efcf10a3509e4ea6ceef3c4
+- name: github.com/fsnotify/fsnotify
+  version: 4da3e2cfbabc9f751898f250b49f2439785783a1
+- name: github.com/go-kit/kit
+  version: 6964666de57c88f7d93da127e900d201b632f561
+  subpackages:
+  - log
+  - log/level
+  - log/term
+- name: github.com/go-logfmt/logfmt
+  version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5
+- name: github.com/go-stack/stack
+  version: 7a2f19628aabfe68f0766b59e74d6315f8347d22
+- name: github.com/gogo/protobuf
+  version: 9df9efe4c742f1a2bfdedf1c3b6902fc6e814c6b
+  subpackages:
+  - proto
+- name: github.com/golang/protobuf
+  version: fec3b39b059c0f88fa6b20f5ed012b1aa203a8b4
+  subpackages:
+  - proto
+  - ptypes/any
+- name: github.com/golang/snappy
+  version: 553a641470496b2327abcac10b36396bd98e45c9
+- name: github.com/gorilla/websocket
+  version: a91eba7f97777409bc2c443f5534d41dd20c5720
+- name: github.com/hashicorp/hcl
+  version: 392dba7d905ed5d04a5794ba89f558b27e2ba1ca
+  subpackages:
+  - hcl/ast
+  - hcl/parser
+  - hcl/scanner
+  - hcl/strconv
+  - hcl/token
+  - json/parser
+  - json/scanner
+  - json/token
+- name: github.com/inconshreveable/mousetrap
+  version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
+- name: github.com/jmhodges/levigo
+  version: c42d9e0ca023e2198120196f842701bb4c55d7b9
+- name: github.com/kr/logfmt
+  version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
+- name: github.com/magiconair/properties
+  version: 51463bfca2576e06c62a8504b5c0f06d61312647
+- name: github.com/mitchellh/mapstructure
+  version: cc8532a8e9a55ea36402aa21efdf403a60d34096
+- name: github.com/pelletier/go-buffruneio
+  version: c37440a7cf42ac63b919c752ca73a85067e05992
+- name: github.com/pelletier/go-toml
+  version: 5c26a6ff6fd178719e15decac1c8196da0d7d6d1
+- name: github.com/pkg/errors
+  version: c605e284fe17294bda444b34710735b29d1a9d90
+- name: github.com/pmezard/go-difflib
+  version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
+  subpackages:
+  - difflib
+- name: github.com/spf13/afero
+  version: 9be650865eab0c12963d8753212f4f9c66cdcf12
+  subpackages:
+  - mem
+- name: github.com/spf13/cast
+  version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4
+- name: github.com/spf13/cobra
+  version: 4cdb38c072b86bf795d2c81de50784d9fdd6eb77
+- name: github.com/spf13/jwalterweatherman
+  version: 8f07c835e5cc1450c082fe3a439cf87b0cbb2d99
+- name: github.com/spf13/pflag
+  version: e57e3eeb33f795204c1ca35f56c44f83227c6e66
+- name: github.com/spf13/viper
+  version: 0967fc9aceab2ce9da34061253ac10fb99bba5b2
+- name: github.com/stretchr/testify
+  version: 4d4bfba8f1d1027c4fdbe371823030df51419987
+  subpackages:
+  - assert
+  - require
+- name: github.com/syndtr/goleveldb
+  version: 8c81ea47d4c41a385645e133e15510fc6a2a74b4
+  subpackages:
+  - leveldb
+  - leveldb/cache
+  - leveldb/comparer
+  - leveldb/errors
+  - leveldb/filter
+  - leveldb/iterator
+  - leveldb/journal
+  - leveldb/memdb
+  - leveldb/opt
+  - leveldb/storage
+  - leveldb/table
+  - leveldb/util
+- name: github.com/tendermint/abci
+  version: 864d1f80b36b440bde030a5c18d8ac3aa8c2949d
+  subpackages:
+  - client
+  - example/counter
+  - example/dummy
+  - server
+  - types
+- name: github.com/tendermint/ed25519
+  version: 1f52c6f8b8a5c7908aff4497c186af344b428925
+  subpackages:
+  - edwards25519
+  - extra25519
+- name: github.com/tendermint/go-crypto
+  version: 7dff40942a64cdeefefa9446b2d104750b349f8a
+- name: github.com/tendermint/go-wire
+  version: 5f88da3dbc1a72844e6dfaf274ce87f851d488eb
+  subpackages:
+  - data
+  - data/base58
+- name: github.com/tendermint/merkleeyes
+  version: a0e73e1ac3e18e12a007520a4ea2c9822256e307
+  subpackages:
+  - app
+  - client
+  - iavl
+  - testutil
+- name: github.com/tendermint/tmlibs
+  version: 306795ae1d8e4f4a10dcc8bdb32a00455843c9d5
+  subpackages:
+  - autofile
+  - cli
+  - clist
+  - common
+  - db
+  - events
+  - flowrate
+  - log
+  - merkle
+  - test
+- name: github.com/golang/crypto
+- name: github.com/golang/net
+- name: github.com/golang/text
+- name: github.com/golang/tools
+- name: golang.org/x/sys
+  version: e62c3de784db939836898e5c19ffd41bece347da
+  subpackages:
+  - unix
+- name: google.golang.org/genproto
+  version: bb3573be0c484136831138976d444b8754777aff
+  subpackages:
+  - googleapis/rpc/status
+- name: google.golang.org/grpc
+  version: 11d93ecdb918872ee841ba3a2dc391aa6d4f57c3
+  subpackages:
+  - codes
+  - credentials
+  - grpclb/grpc_lb_v1
+  - grpclog
+  - internal
+  - keepalive
+  - metadata
+  - naming
+  - peer
+  - stats
+  - status
+  - tap
+  - transport
+- name: gopkg.in/yaml.v2
+  version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b
+testImports: []
diff --git a/glide.yaml b/glide.yaml
new file mode 100644 (file)
index 0000000..e44b500
--- /dev/null
@@ -0,0 +1,56 @@
+package: github.com/consensus
+import:
+- package: github.com/ebuchman/fail-test
+- package: github.com/gogo/protobuf
+  subpackages:
+  - proto
+- package: github.com/golang/protobuf
+  subpackages:
+  - proto
+- package: github.com/gorilla/websocket
+- package: github.com/pkg/errors
+- package: github.com/spf13/cobra
+- package: github.com/spf13/viper
+- package: github.com/stretchr/testify
+  subpackages:
+  - require
+- package: github.com/tendermint/abci
+  version: v0.5.0
+  subpackages:
+  - client
+  - example/dummy
+  - types
+- package: github.com/tendermint/go-crypto
+  version: v0.2.0
+- package: github.com/tendermint/go-wire
+  version: v0.6.2
+  subpackages:
+  - data
+- package: github.com/tendermint/tmlibs
+  version: v0.2.0
+  subpackages:
+  - autofile
+  - cli
+  - clist
+  - common
+  - db
+  - events
+  - flowrate
+  - log
+  - merkle
+- package: github.com/golang/crypto
+  subpackages:
+  - nacl/box
+  - nacl/secretbox
+  - ripemd160
+- package: github.com/golang/net
+  subpackages:
+  - context
+- package: google.golang.org/grpc
+testImport:
+- package: github.com/tendermint/merkleeyes
+  version: develop
+  subpackages:
+  - app
+  - iavl
+  - testutil
diff --git a/node/id.go b/node/id.go
new file mode 100644 (file)
index 0000000..c521aa4
--- /dev/null
@@ -0,0 +1,34 @@
+package node
+
+import (
+       "github.com/tendermint/go-crypto"
+       "time"
+)
+
+type NodeID struct {
+       Name   string
+       PubKey crypto.PubKey
+}
+
+type PrivNodeID struct {
+       NodeID
+       PrivKey crypto.PrivKey
+}
+
+type NodeGreeting struct {
+       NodeID
+       Version string
+       ChainID string
+       Message string
+       Time    time.Time
+}
+
+type SignedNodeGreeting struct {
+       NodeGreeting
+       Signature crypto.Signature
+}
+
+func (pnid *PrivNodeID) SignGreeting() *SignedNodeGreeting {
+       //greeting := NodeGreeting{}
+       return nil
+}
diff --git a/node/node.go b/node/node.go
new file mode 100644 (file)
index 0000000..6cdccbc
--- /dev/null
@@ -0,0 +1,217 @@
+package node
+
+import (
+       "net/http"
+       "strings"
+
+       crypto "github.com/tendermint/go-crypto"
+       wire "github.com/tendermint/go-wire"
+       cfg "github.com/blockchain/config"
+       p2p "github.com/blockchain/p2p"
+       "github.com/blockchain/types"
+       "github.com/blockchain/version"
+       cmn "github.com/tendermint/tmlibs/common"
+       "github.com/tendermint/tmlibs/log"
+
+       _ "net/http/pprof"
+)
+
+type Node struct {
+       cmn.BaseService
+
+       // config
+       config        *cfg.Config
+       privValidator *types.PrivValidator // local node's validator key
+
+       // network
+       privKey  crypto.PrivKeyEd25519 // local node's p2p key
+       sw       *p2p.Switch           // p2p connections
+       addrBook *p2p.AddrBook         // known peers
+
+       // services
+       evsw             types.EventSwitch           // pub/sub for services
+}
+
+func NewNodeDefault(config *cfg.Config, logger log.Logger) *Node {
+       // Get PrivValidator
+       privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile(), logger)
+       return NewNode(config, privValidator, logger)
+}
+
+func NewNode(config *cfg.Config, privValidator *types.PrivValidator, logger log.Logger) *Node {
+       // Get BlockStore
+
+       // Generate node PrivKey
+       privKey := crypto.GenPrivKeyEd25519()
+
+       // Make event switch
+       eventSwitch := types.NewEventSwitch()
+       eventSwitch.SetLogger(logger.With("module", "types"))
+       _, err := eventSwitch.Start()
+       if err != nil {
+               cmn.Exit(cmn.Fmt("Failed to start switch: %v", err))
+       }
+
+
+       p2pLogger := logger.With("module", "p2p")
+
+       sw := p2p.NewSwitch(config.P2P)
+       sw.SetLogger(p2pLogger)
+
+       // Optionally, start the pex reactor
+       var addrBook *p2p.AddrBook
+       if config.P2P.PexReactor {
+               addrBook = p2p.NewAddrBook(config.P2P.AddrBookFile(), config.P2P.AddrBookStrict)
+               addrBook.SetLogger(p2pLogger.With("book", config.P2P.AddrBookFile()))
+               pexReactor := p2p.NewPEXReactor(addrBook)
+               pexReactor.SetLogger(p2pLogger)
+               sw.AddReactor("PEX", pexReactor)
+       }
+
+       // add the event switch to all services
+       // they should all satisfy events.Eventable
+       //SetEventSwitch(eventSwitch, bcReactor, mempoolReactor, consensusReactor)
+
+       // run the profile server
+       profileHost := config.ProfListenAddress
+       if profileHost != "" {
+
+               go func() {
+                       logger.Error("Profile server", "error", http.ListenAndServe(profileHost, nil))
+               }()
+       }
+
+       node := &Node{
+               config:        config,
+               privValidator: privValidator,
+
+               privKey:  privKey,
+               sw:       sw,
+               addrBook: addrBook,
+
+               evsw:             eventSwitch,
+       }
+       node.BaseService = *cmn.NewBaseService(logger, "Node", node)
+       return node
+}
+
+func (n *Node) OnStart() error {
+       // Create & add listener
+       protocol, address := ProtocolAndAddress(n.config.P2P.ListenAddress)
+       l := p2p.NewDefaultListener(protocol, address, n.config.P2P.SkipUPNP, n.Logger.With("module", "p2p"))
+       n.sw.AddListener(l)
+
+       // Start the switch
+       n.sw.SetNodeInfo(n.makeNodeInfo())
+       n.sw.SetNodePrivKey(n.privKey)
+       _, err := n.sw.Start()
+       if err != nil {
+               return err
+       }
+
+       // If seeds exist, add them to the address book and dial out
+       if n.config.P2P.Seeds != "" {
+               // dial out
+               seeds := strings.Split(n.config.P2P.Seeds, ",")
+               if err := n.DialSeeds(seeds); err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
+func (n *Node) OnStop() {
+       n.BaseService.OnStop()
+
+       n.Logger.Info("Stopping Node")
+       // TODO: gracefully disconnect from peers.
+       n.sw.Stop()
+}
+
+func (n *Node) RunForever() {
+       // Sleep forever and then...
+       cmn.TrapSignal(func() {
+               n.Stop()
+       })
+}
+
+// Add the event switch to reactors, mempool, etc.
+func SetEventSwitch(evsw types.EventSwitch, eventables ...types.Eventable) {
+       for _, e := range eventables {
+               e.SetEventSwitch(evsw)
+       }
+}
+
+// Add a Listener to accept inbound peer connections.
+// Add listeners before starting the Node.
+// The first listener is the primary listener (in NodeInfo)
+func (n *Node) AddListener(l p2p.Listener) {
+       n.sw.AddListener(l)
+}
+
+
+func (n *Node) Switch() *p2p.Switch {
+       return n.sw
+}
+
+
+func (n *Node) EventSwitch() types.EventSwitch {
+       return n.evsw
+}
+
+// XXX: for convenience
+func (n *Node) PrivValidator() *types.PrivValidator {
+       return n.privValidator
+}
+
+func (n *Node) makeNodeInfo() *p2p.NodeInfo {
+       nodeInfo := &p2p.NodeInfo{
+               PubKey:  n.privKey.PubKey().Unwrap().(crypto.PubKeyEd25519),
+               Moniker: n.config.Moniker,
+               Network: "chain0",
+               Version: version.Version,
+               Other: []string{
+                       cmn.Fmt("wire_version=%v", wire.Version),
+                       cmn.Fmt("p2p_version=%v", p2p.Version),
+               },
+       }
+
+       if !n.sw.IsListening() {
+               return nodeInfo
+       }
+
+       p2pListener := n.sw.Listeners()[0]
+       p2pHost := p2pListener.ExternalAddress().IP.String()
+       p2pPort := p2pListener.ExternalAddress().Port
+       rpcListenAddr := n.config.RPC.ListenAddress
+
+       // We assume that the rpcListener has the same ExternalAddress.
+       // This is probably true because both P2P and RPC listeners use UPnP,
+       // except of course if the rpc is only bound to localhost
+       nodeInfo.ListenAddr = cmn.Fmt("%v:%v", p2pHost, p2pPort)
+       nodeInfo.Other = append(nodeInfo.Other, cmn.Fmt("rpc_addr=%v", rpcListenAddr))
+       return nodeInfo
+}
+
+//------------------------------------------------------------------------------
+
+func (n *Node) NodeInfo() *p2p.NodeInfo {
+       return n.sw.NodeInfo()
+}
+
+func (n *Node) DialSeeds(seeds []string) error {
+       return n.sw.DialSeeds(n.addrBook, seeds)
+}
+
+// Defaults to tcp
+func ProtocolAndAddress(listenAddr string) (string, string) {
+       protocol, address := "tcp", listenAddr
+       parts := strings.SplitN(address, "://", 2)
+       if len(parts) == 2 {
+               protocol, address = parts[0], parts[1]
+       }
+       return protocol, address
+}
+
+//------------------------------------------------------------------------------
diff --git a/p2p/CHANGELOG.md b/p2p/CHANGELOG.md
new file mode 100644 (file)
index 0000000..cae2f4c
--- /dev/null
@@ -0,0 +1,78 @@
+# Changelog
+
+## 0.5.0 (April 21, 2017)
+
+BREAKING CHANGES: 
+
+- Remove or unexport methods from FuzzedConnection: Active, Mode, ProbDropRW, ProbDropConn, ProbSleep, MaxDelayMilliseconds, Fuzz
+- switch.AddPeerWithConnection is unexported and replaced by switch.AddPeer
+- switch.DialPeerWithAddress takes a bool, setting the peer as persistent or not
+
+FEATURES:
+
+- Persistent peers: any peer considered a "seed" will be reconnected to when the connection is dropped
+
+
+IMPROVEMENTS:
+
+- Many more tests and comments
+- Refactor configurations for less dependence on go-config. Introduces new structs PeerConfig, MConnConfig, FuzzConnConfig
+- New methods on peer: CloseConn, HandshakeTimeout, IsPersistent, Addr, PubKey
+- NewNetAddress supports a testing mode where the address defaults to 0.0.0.0:0
+
+
+## 0.4.0 (March 6, 2017)
+
+BREAKING CHANGES: 
+
+- DialSeeds now takes an AddrBook and returns an error: `DialSeeds(*AddrBook, []string) error`
+- NewNetAddressString now returns an error: `NewNetAddressString(string) (*NetAddress, error)`
+
+FEATURES:
+
+- `NewNetAddressStrings([]string) ([]*NetAddress, error)`
+- `AddrBook.Save()`
+
+IMPROVEMENTS:
+
+- PexReactor responsible for starting and stopping the AddrBook
+
+BUG FIXES:
+
+- DialSeeds returns an error instead of panicking on bad addresses
+
+## 0.3.5 (January 12, 2017)
+
+FEATURES
+
+- Toggle strict routability in the AddrBook 
+
+BUG FIXES
+
+- Close filtered out connections
+- Fixes for MakeConnectedSwitches and Connect2Switches
+
+## 0.3.4 (August 10, 2016)
+
+FEATURES:
+
+- Optionally filter connections by address or public key
+
+## 0.3.3 (May 12, 2016)
+
+FEATURES:
+
+- FuzzConn
+
+## 0.3.2 (March 12, 2016)
+
+IMPROVEMENTS:
+
+- Memory optimizations
+
+## 0.3.1 ()
+
+FEATURES: 
+
+- Configurable parameters
+
diff --git a/p2p/Dockerfile b/p2p/Dockerfile
new file mode 100644 (file)
index 0000000..6c71b2f
--- /dev/null
@@ -0,0 +1,13 @@
+FROM golang:latest
+
+RUN curl https://glide.sh/get | sh
+
+RUN mkdir -p /go/src/github.com/tendermint/tendermint/p2p
+WORKDIR /go/src/github.com/tendermint/tendermint/p2p
+
+COPY glide.yaml /go/src/github.com/tendermint/tendermint/p2p/
+COPY glide.lock /go/src/github.com/tendermint/tendermint/p2p/
+
+RUN glide install
+
+COPY . /go/src/github.com/tendermint/tendermint/p2p
diff --git a/p2p/README.md b/p2p/README.md
new file mode 100644 (file)
index 0000000..bf0a5c4
--- /dev/null
@@ -0,0 +1,79 @@
+# `tendermint/tendermint/p2p`
+
+[![CircleCI](https://circleci.com/gh/tendermint/tendermint/p2p.svg?style=svg)](https://circleci.com/gh/tendermint/tendermint/p2p)
+
+`tendermint/tendermint/p2p` provides an abstraction around peer-to-peer communication.<br/>
+
+## Peer/MConnection/Channel
+
+Each peer has one `MConnection` (multiplex connection) instance.
+
+__multiplex__ *noun* a system or signal involving simultaneous transmission of
+several messages along a single channel of communication.
+
+Each `MConnection` handles message transmission on multiple abstract communication
+`Channel`s.  Each channel has a globally unique byte id.
+The byte id and the relative priorities of each `Channel` are configured upon
+initialization of the connection.
+
+There are two methods for sending messages:
+```go
+func (m MConnection) Send(chID byte, msg interface{}) bool {}
+func (m MConnection) TrySend(chID byte, msg interface{}) bool {}
+```
+
+`Send(chID, msg)` is a blocking call that waits until `msg` is successfully queued
+for the channel with the given id byte `chID`.  The message `msg` is serialized
+using the `tendermint/wire` submodule's `WriteBinary()` reflection routine.
+
+`TrySend(chID, msg)` is a nonblocking call that returns false if the channel's
+queue is full.
+
+`Send()` and `TrySend()` are also exposed for each `Peer`.
+
+## Switch/Reactor
+
+The `Switch` handles peer connections and exposes an API to receive incoming messages
+on `Reactors`.  Each `Reactor` is responsible for handling incoming messages of one
+or more `Channels`.  So while sending outgoing messages is typically performed on the peer,
+incoming messages are received on the reactor.
+
+```go
+// Declare a MyReactor reactor that handles messages on MyChannelID.
+type MyReactor struct{}
+
+func (reactor MyReactor) GetChannels() []*ChannelDescriptor {
+    return []*ChannelDescriptor{ChannelDescriptor{ID:MyChannelID, Priority: 1}}
+}
+
+func (reactor MyReactor) Receive(chID byte, peer *Peer, msgBytes []byte) {
+    r, n, err := bytes.NewBuffer(msgBytes), new(int64), new(error)
+    msgString := ReadString(r, n, err)
+    fmt.Println(msgString)
+}
+
+// Other Reactor methods omitted for brevity
+...
+
+switch := NewSwitch([]Reactor{MyReactor{}})
+
+...
+
+// Send a random message to all outbound connections
+for _, peer := range switch.Peers().List() {
+    if peer.IsOutbound() {
+        peer.Send(MyChannelID, "Here's a random message")
+    }
+}
+```
+
+### PexReactor/AddrBook
+
+A `PEXReactor` reactor implementation is provided to automate peer discovery.
+
+```go
+book := p2p.NewAddrBook(addrBookFilePath)
+pexReactor := p2p.NewPEXReactor(book)
+...
+switch := NewSwitch([]Reactor{pexReactor, myReactor, ...})
+```
diff --git a/p2p/addrbook.go b/p2p/addrbook.go
new file mode 100644 (file)
index 0000000..1df0817
--- /dev/null
@@ -0,0 +1,841 @@
+// Modified for Tendermint
+// Originally Copyright (c) 2013-2014 Conformal Systems LLC.
+// https://github.com/conformal/btcd/blob/master/LICENSE
+
+package p2p
+
+import (
+       "encoding/binary"
+       "encoding/json"
+       "math"
+       "math/rand"
+       "net"
+       "os"
+       "sync"
+       "time"
+
+       crypto "github.com/tendermint/go-crypto"
+       cmn "github.com/tendermint/tmlibs/common"
+)
+
+const (
+       // addresses under which the address manager will claim to need more addresses.
+       needAddressThreshold = 1000
+
+       // interval used to dump the address cache to disk for future use.
+       dumpAddressInterval = time.Minute * 2
+
+       // max addresses in each old address bucket.
+       oldBucketSize = 64
+
+       // buckets we split old addresses over.
+       oldBucketCount = 64
+
+       // max addresses in each new address bucket.
+       newBucketSize = 64
+
+       // buckets that we spread new addresses over.
+       newBucketCount = 256
+
+       // old buckets over which an address group will be spread.
+       oldBucketsPerGroup = 4
+
+       // new buckets over which an source address group will be spread.
+       newBucketsPerGroup = 32
+
+       // buckets a frequently seen new address may end up in.
+       maxNewBucketsPerAddress = 4
+
+       // days before which we assume an address has vanished
+       // if we have not seen it announced in that long.
+       numMissingDays = 30
+
+       // tries without a single success before we assume an address is bad.
+       numRetries = 3
+
+       // max failures we will accept without a success before considering an address bad.
+       maxFailures = 10
+
+       // days since the last success before we will consider evicting an address.
+       minBadDays = 7
+
+       // % of total addresses known returned by GetSelection.
+       getSelectionPercent = 23
+
+       // min addresses that must be returned by GetSelection. Useful for bootstrapping.
+       minGetSelection = 32
+
+       // max addresses returned by GetSelection
+       // NOTE: this must match "maxPexMessageSize"
+       maxGetSelection = 250
+
+       // current version of the on-disk format.
+       serializationVersion = 1
+)
+
+const (
+       bucketTypeNew = 0x01
+       bucketTypeOld = 0x02
+)
+
+// AddrBook - concurrency safe peer address manager.
+type AddrBook struct {
+       cmn.BaseService
+
+       mtx               sync.Mutex
+       filePath          string
+       routabilityStrict bool
+       rand              *rand.Rand
+       key               string
+       ourAddrs          map[string]*NetAddress
+       addrLookup        map[string]*knownAddress // new & old
+       addrNew           []map[string]*knownAddress
+       addrOld           []map[string]*knownAddress
+       wg                sync.WaitGroup
+       nOld              int
+       nNew              int
+}
+
+// NewAddrBook creates a new address book.
+// Use Start to begin processing asynchronous address updates.
+func NewAddrBook(filePath string, routabilityStrict bool) *AddrBook {
+       am := &AddrBook{
+               rand:              rand.New(rand.NewSource(time.Now().UnixNano())),
+               ourAddrs:          make(map[string]*NetAddress),
+               addrLookup:        make(map[string]*knownAddress),
+               filePath:          filePath,
+               routabilityStrict: routabilityStrict,
+       }
+       am.init()
+       am.BaseService = *cmn.NewBaseService(nil, "AddrBook", am)
+       return am
+}
+
+// When modifying this, don't forget to update loadFromFile()
+func (a *AddrBook) init() {
+       a.key = crypto.CRandHex(24) // 24/2 * 8 = 96 bits
+       // New addr buckets
+       a.addrNew = make([]map[string]*knownAddress, newBucketCount)
+       for i := range a.addrNew {
+               a.addrNew[i] = make(map[string]*knownAddress)
+       }
+       // Old addr buckets
+       a.addrOld = make([]map[string]*knownAddress, oldBucketCount)
+       for i := range a.addrOld {
+               a.addrOld[i] = make(map[string]*knownAddress)
+       }
+}
+
+// OnStart implements Service.
+func (a *AddrBook) OnStart() error {
+       a.BaseService.OnStart()
+       a.loadFromFile(a.filePath)
+       a.wg.Add(1)
+       go a.saveRoutine()
+       return nil
+}
+
+// OnStop implements Service.
+func (a *AddrBook) OnStop() {
+       a.BaseService.OnStop()
+}
+
+func (a *AddrBook) Wait() {
+       a.wg.Wait()
+}
+
+func (a *AddrBook) AddOurAddress(addr *NetAddress) {
+       a.mtx.Lock()
+       defer a.mtx.Unlock()
+       a.Logger.Info("Add our address to book", "addr", addr)
+       a.ourAddrs[addr.String()] = addr
+}
+
+func (a *AddrBook) OurAddresses() []*NetAddress {
+       addrs := []*NetAddress{}
+       for _, addr := range a.ourAddrs {
+               addrs = append(addrs, addr)
+       }
+       return addrs
+}
+
+// NOTE: addr must not be nil
+func (a *AddrBook) AddAddress(addr *NetAddress, src *NetAddress) {
+       a.mtx.Lock()
+       defer a.mtx.Unlock()
+       a.Logger.Info("Add address to book", "addr", addr, "src", src)
+       a.addAddress(addr, src)
+}
+
+func (a *AddrBook) NeedMoreAddrs() bool {
+       return a.Size() < needAddressThreshold
+}
+
+func (a *AddrBook) Size() int {
+       a.mtx.Lock()
+       defer a.mtx.Unlock()
+       return a.size()
+}
+
+func (a *AddrBook) size() int {
+       return a.nNew + a.nOld
+}
+
+// Pick an address to connect to with new/old bias.
+func (a *AddrBook) PickAddress(newBias int) *NetAddress {
+       a.mtx.Lock()
+       defer a.mtx.Unlock()
+
+       if a.size() == 0 {
+               return nil
+       }
+       if newBias > 100 {
+               newBias = 100
+       }
+       if newBias < 0 {
+               newBias = 0
+       }
+
+       // Bias between new and old addresses.
+       oldCorrelation := math.Sqrt(float64(a.nOld)) * (100.0 - float64(newBias))
+       newCorrelation := math.Sqrt(float64(a.nNew)) * float64(newBias)
+
+       if (newCorrelation+oldCorrelation)*a.rand.Float64() < oldCorrelation {
+               // pick random Old bucket.
+               var bucket map[string]*knownAddress = nil
+               for len(bucket) == 0 {
+                       bucket = a.addrOld[a.rand.Intn(len(a.addrOld))]
+               }
+               // pick a random ka from bucket.
+               randIndex := a.rand.Intn(len(bucket))
+               for _, ka := range bucket {
+                       if randIndex == 0 {
+                               return ka.Addr
+                       }
+                       randIndex--
+               }
+               cmn.PanicSanity("Should not happen")
+       } else {
+               // pick random New bucket.
+               var bucket map[string]*knownAddress = nil
+               for len(bucket) == 0 {
+                       bucket = a.addrNew[a.rand.Intn(len(a.addrNew))]
+               }
+               // pick a random ka from bucket.
+               randIndex := a.rand.Intn(len(bucket))
+               for _, ka := range bucket {
+                       if randIndex == 0 {
+                               return ka.Addr
+                       }
+                       randIndex--
+               }
+               cmn.PanicSanity("Should not happen")
+       }
+       return nil
+}
+
+func (a *AddrBook) MarkGood(addr *NetAddress) {
+       a.mtx.Lock()
+       defer a.mtx.Unlock()
+       ka := a.addrLookup[addr.String()]
+       if ka == nil {
+               return
+       }
+       ka.markGood()
+       if ka.isNew() {
+               a.moveToOld(ka)
+       }
+}
+
+func (a *AddrBook) MarkAttempt(addr *NetAddress) {
+       a.mtx.Lock()
+       defer a.mtx.Unlock()
+       ka := a.addrLookup[addr.String()]
+       if ka == nil {
+               return
+       }
+       ka.markAttempt()
+}
+
+// MarkBad currently just ejects the address. In the future, consider
+// blacklisting.
+func (a *AddrBook) MarkBad(addr *NetAddress) {
+       a.RemoveAddress(addr)
+}
+
+// RemoveAddress removes the address from the book.
+func (a *AddrBook) RemoveAddress(addr *NetAddress) {
+       a.mtx.Lock()
+       defer a.mtx.Unlock()
+       ka := a.addrLookup[addr.String()]
+       if ka == nil {
+               return
+       }
+       a.Logger.Info("Remove address from book", "addr", addr)
+       a.removeFromAllBuckets(ka)
+}
+
+/* Peer exchange */
+
+// GetSelection randomly selects some addresses (old & new). Suitable for peer-exchange protocols.
+func (a *AddrBook) GetSelection() []*NetAddress {
+       a.mtx.Lock()
+       defer a.mtx.Unlock()
+
+       if a.size() == 0 {
+               return nil
+       }
+
+       allAddr := make([]*NetAddress, a.size())
+       i := 0
+       for _, v := range a.addrLookup {
+               allAddr[i] = v.Addr
+               i++
+       }
+
+       numAddresses := cmn.MaxInt(
+               cmn.MinInt(minGetSelection, len(allAddr)),
+               len(allAddr)*getSelectionPercent/100)
+       numAddresses = cmn.MinInt(maxGetSelection, numAddresses)
+
+       // Fisher-Yates shuffle the array. We only need to do the first
+       // `numAddresses' since we are throwing the rest.
+       for i := 0; i < numAddresses; i++ {
+               // pick a number between current index and the end
+               j := rand.Intn(len(allAddr)-i) + i
+               allAddr[i], allAddr[j] = allAddr[j], allAddr[i]
+       }
+
+       // slice off the limit we are willing to share.
+       return allAddr[:numAddresses]
+}
+
+/* Loading & Saving */
+
+type addrBookJSON struct {
+       Key   string
+       Addrs []*knownAddress
+}
+
+func (a *AddrBook) saveToFile(filePath string) {
+       a.Logger.Info("Saving AddrBook to file", "size", a.Size())
+
+       a.mtx.Lock()
+       defer a.mtx.Unlock()
+       // Compile Addrs
+       addrs := []*knownAddress{}
+       for _, ka := range a.addrLookup {
+               addrs = append(addrs, ka)
+       }
+
+       aJSON := &addrBookJSON{
+               Key:   a.key,
+               Addrs: addrs,
+       }
+
+       jsonBytes, err := json.MarshalIndent(aJSON, "", "\t")
+       if err != nil {
+               a.Logger.Error("Failed to save AddrBook to file", "err", err)
+               return
+       }
+       err = cmn.WriteFileAtomic(filePath, jsonBytes, 0644)
+       if err != nil {
+               a.Logger.Error("Failed to save AddrBook to file", "file", filePath, "error", err)
+       }
+}
+
+// Returns false if file does not exist.
+// cmn.Panics if file is corrupt.
+func (a *AddrBook) loadFromFile(filePath string) bool {
+       // If doesn't exist, do nothing.
+       _, err := os.Stat(filePath)
+       if os.IsNotExist(err) {
+               return false
+       }
+
+       // Load addrBookJSON{}
+       r, err := os.Open(filePath)
+       if err != nil {
+               cmn.PanicCrisis(cmn.Fmt("Error opening file %s: %v", filePath, err))
+       }
+       defer r.Close()
+       aJSON := &addrBookJSON{}
+       dec := json.NewDecoder(r)
+       err = dec.Decode(aJSON)
+       if err != nil {
+               cmn.PanicCrisis(cmn.Fmt("Error reading file %s: %v", filePath, err))
+       }
+
+       // Restore all the fields...
+       // Restore the key
+       a.key = aJSON.Key
+       // Restore .addrNew & .addrOld
+       for _, ka := range aJSON.Addrs {
+               for _, bucketIndex := range ka.Buckets {
+                       bucket := a.getBucket(ka.BucketType, bucketIndex)
+                       bucket[ka.Addr.String()] = ka
+               }
+               a.addrLookup[ka.Addr.String()] = ka
+               if ka.BucketType == bucketTypeNew {
+                       a.nNew++
+               } else {
+                       a.nOld++
+               }
+       }
+       return true
+}
+
+// Save saves the book.
+func (a *AddrBook) Save() {
+       a.Logger.Info("Saving AddrBook to file", "size", a.Size())
+       a.saveToFile(a.filePath)
+}
+
+/* Private methods */
+
+func (a *AddrBook) saveRoutine() {
+       dumpAddressTicker := time.NewTicker(dumpAddressInterval)
+out:
+       for {
+               select {
+               case <-dumpAddressTicker.C:
+                       a.saveToFile(a.filePath)
+               case <-a.Quit:
+                       break out
+               }
+       }
+       dumpAddressTicker.Stop()
+       a.saveToFile(a.filePath)
+       a.wg.Done()
+       a.Logger.Info("Address handler done")
+}
+
+func (a *AddrBook) getBucket(bucketType byte, bucketIdx int) map[string]*knownAddress {
+       switch bucketType {
+       case bucketTypeNew:
+               return a.addrNew[bucketIdx]
+       case bucketTypeOld:
+               return a.addrOld[bucketIdx]
+       default:
+               cmn.PanicSanity("Should not happen")
+               return nil
+       }
+}
+
+// Adds ka to new bucket. Returns false if it couldn't do it cuz buckets full.
+// NOTE: currently it always returns true.
+func (a *AddrBook) addToNewBucket(ka *knownAddress, bucketIdx int) bool {
+       // Sanity check
+       if ka.isOld() {
+               a.Logger.Error(cmn.Fmt("Cannot add address already in old bucket to a new bucket: %v", ka))
+               return false
+       }
+
+       addrStr := ka.Addr.String()
+       bucket := a.getBucket(bucketTypeNew, bucketIdx)
+
+       // Already exists?
+       if _, ok := bucket[addrStr]; ok {
+               return true
+       }
+
+       // Enforce max addresses.
+       if len(bucket) > newBucketSize {
+               a.Logger.Info("new bucket is full, expiring old ")
+               a.expireNew(bucketIdx)
+       }
+
+       // Add to bucket.
+       bucket[addrStr] = ka
+       if ka.addBucketRef(bucketIdx) == 1 {
+               a.nNew++
+       }
+
+       // Ensure in addrLookup
+       a.addrLookup[addrStr] = ka
+
+       return true
+}
+
+// Adds ka to old bucket. Returns false if it couldn't do it cuz buckets full.
+func (a *AddrBook) addToOldBucket(ka *knownAddress, bucketIdx int) bool {
+       // Sanity check
+       if ka.isNew() {
+               a.Logger.Error(cmn.Fmt("Cannot add new address to old bucket: %v", ka))
+               return false
+       }
+       if len(ka.Buckets) != 0 {
+               a.Logger.Error(cmn.Fmt("Cannot add already old address to another old bucket: %v", ka))
+               return false
+       }
+
+       addrStr := ka.Addr.String()
+       bucket := a.getBucket(bucketTypeNew, bucketIdx)
+
+       // Already exists?
+       if _, ok := bucket[addrStr]; ok {
+               return true
+       }
+
+       // Enforce max addresses.
+       if len(bucket) > oldBucketSize {
+               return false
+       }
+
+       // Add to bucket.
+       bucket[addrStr] = ka
+       if ka.addBucketRef(bucketIdx) == 1 {
+               a.nOld++
+       }
+
+       // Ensure in addrLookup
+       a.addrLookup[addrStr] = ka
+
+       return true
+}
+
+func (a *AddrBook) removeFromBucket(ka *knownAddress, bucketType byte, bucketIdx int) {
+       if ka.BucketType != bucketType {
+               a.Logger.Error(cmn.Fmt("Bucket type mismatch: %v", ka))
+               return
+       }
+       bucket := a.getBucket(bucketType, bucketIdx)
+       delete(bucket, ka.Addr.String())
+       if ka.removeBucketRef(bucketIdx) == 0 {
+               if bucketType == bucketTypeNew {
+                       a.nNew--
+               } else {
+                       a.nOld--
+               }
+               delete(a.addrLookup, ka.Addr.String())
+       }
+}
+
+func (a *AddrBook) removeFromAllBuckets(ka *knownAddress) {
+       for _, bucketIdx := range ka.Buckets {
+               bucket := a.getBucket(ka.BucketType, bucketIdx)
+               delete(bucket, ka.Addr.String())
+       }
+       ka.Buckets = nil
+       if ka.BucketType == bucketTypeNew {
+               a.nNew--
+       } else {
+               a.nOld--
+       }
+       delete(a.addrLookup, ka.Addr.String())
+}
+
+func (a *AddrBook) pickOldest(bucketType byte, bucketIdx int) *knownAddress {
+       bucket := a.getBucket(bucketType, bucketIdx)
+       var oldest *knownAddress
+       for _, ka := range bucket {
+               if oldest == nil || ka.LastAttempt.Before(oldest.LastAttempt) {
+                       oldest = ka
+               }
+       }
+       return oldest
+}
+
+func (a *AddrBook) addAddress(addr, src *NetAddress) {
+       if a.routabilityStrict && !addr.Routable() {
+               a.Logger.Error(cmn.Fmt("Cannot add non-routable address %v", addr))
+               return
+       }
+       if _, ok := a.ourAddrs[addr.String()]; ok {
+               // Ignore our own listener address.
+               return
+       }
+
+       ka := a.addrLookup[addr.String()]
+
+       if ka != nil {
+               // Already old.
+               if ka.isOld() {
+                       return
+               }
+               // Already in max new buckets.
+               if len(ka.Buckets) == maxNewBucketsPerAddress {
+                       return
+               }
+               // The more entries we have, the less likely we are to add more.
+               factor := int32(2 * len(ka.Buckets))
+               if a.rand.Int31n(factor) != 0 {
+                       return
+               }
+       } else {
+               ka = newKnownAddress(addr, src)
+       }
+
+       bucket := a.calcNewBucket(addr, src)
+       a.addToNewBucket(ka, bucket)
+
+       a.Logger.Info("Added new address", "address", addr, "total", a.size())
+}
+
+// Make space in the new buckets by expiring the really bad entries.
+// If no bad entries are available we remove the oldest.
+func (a *AddrBook) expireNew(bucketIdx int) {
+       for addrStr, ka := range a.addrNew[bucketIdx] {
+               // If an entry is bad, throw it away
+               if ka.isBad() {
+                       a.Logger.Info(cmn.Fmt("expiring bad address %v", addrStr))
+                       a.removeFromBucket(ka, bucketTypeNew, bucketIdx)
+                       return
+               }
+       }
+
+       // If we haven't thrown out a bad entry, throw out the oldest entry
+       oldest := a.pickOldest(bucketTypeNew, bucketIdx)
+       a.removeFromBucket(oldest, bucketTypeNew, bucketIdx)
+}
+
+// Promotes an address from new to old.
+// TODO: Move to old probabilistically.
+// The better a node is, the less likely it should be evicted from an old bucket.
+func (a *AddrBook) moveToOld(ka *knownAddress) {
+       // Sanity check
+       if ka.isOld() {
+               a.Logger.Error(cmn.Fmt("Cannot promote address that is already old %v", ka))
+               return
+       }
+       if len(ka.Buckets) == 0 {
+               a.Logger.Error(cmn.Fmt("Cannot promote address that isn't in any new buckets %v", ka))
+               return
+       }
+
+       // Remember one of the buckets in which ka is in.
+       freedBucket := ka.Buckets[0]
+       // Remove from all (new) buckets.
+       a.removeFromAllBuckets(ka)
+       // It's officially old now.
+       ka.BucketType = bucketTypeOld
+
+       // Try to add it to its oldBucket destination.
+       oldBucketIdx := a.calcOldBucket(ka.Addr)
+       added := a.addToOldBucket(ka, oldBucketIdx)
+       if !added {
+               // No room, must evict something
+               oldest := a.pickOldest(bucketTypeOld, oldBucketIdx)
+               a.removeFromBucket(oldest, bucketTypeOld, oldBucketIdx)
+               // Find new bucket to put oldest in
+               newBucketIdx := a.calcNewBucket(oldest.Addr, oldest.Src)
+               added := a.addToNewBucket(oldest, newBucketIdx)
+               // No space in newBucket either, just put it in freedBucket from above.
+               if !added {
+                       added := a.addToNewBucket(oldest, freedBucket)
+                       if !added {
+                               a.Logger.Error(cmn.Fmt("Could not migrate oldest %v to freedBucket %v", oldest, freedBucket))
+                       }
+               }
+               // Finally, add to bucket again.
+               added = a.addToOldBucket(ka, oldBucketIdx)
+               if !added {
+                       a.Logger.Error(cmn.Fmt("Could not re-add ka %v to oldBucketIdx %v", ka, oldBucketIdx))
+               }
+       }
+}
+
+// doublesha256(  key + sourcegroup +
+//                int64(doublesha256(key + group + sourcegroup))%bucket_per_group  ) % num_new_buckets
+func (a *AddrBook) calcNewBucket(addr, src *NetAddress) int {
+       data1 := []byte{}
+       data1 = append(data1, []byte(a.key)...)
+       data1 = append(data1, []byte(a.groupKey(addr))...)
+       data1 = append(data1, []byte(a.groupKey(src))...)
+       hash1 := doubleSha256(data1)
+       hash64 := binary.BigEndian.Uint64(hash1)
+       hash64 %= newBucketsPerGroup
+       var hashbuf [8]byte
+       binary.BigEndian.PutUint64(hashbuf[:], hash64)
+       data2 := []byte{}
+       data2 = append(data2, []byte(a.key)...)
+       data2 = append(data2, a.groupKey(src)...)
+       data2 = append(data2, hashbuf[:]...)
+
+       hash2 := doubleSha256(data2)
+       return int(binary.BigEndian.Uint64(hash2) % newBucketCount)
+}
+
+// doublesha256(  key + group +
+//                int64(doublesha256(key + addr))%buckets_per_group  ) % num_old_buckets
+func (a *AddrBook) calcOldBucket(addr *NetAddress) int {
+       data1 := []byte{}
+       data1 = append(data1, []byte(a.key)...)
+       data1 = append(data1, []byte(addr.String())...)
+       hash1 := doubleSha256(data1)
+       hash64 := binary.BigEndian.Uint64(hash1)
+       hash64 %= oldBucketsPerGroup
+       var hashbuf [8]byte
+       binary.BigEndian.PutUint64(hashbuf[:], hash64)
+       data2 := []byte{}
+       data2 = append(data2, []byte(a.key)...)
+       data2 = append(data2, a.groupKey(addr)...)
+       data2 = append(data2, hashbuf[:]...)
+
+       hash2 := doubleSha256(data2)
+       return int(binary.BigEndian.Uint64(hash2) % oldBucketCount)
+}
+
+// Return a string representing the network group of this address.
+// This is the /16 for IPv6, the /32 (/36 for he.net) for IPv6, the string
+// "local" for a local address and the string "unroutable for an unroutable
+// address.
+func (a *AddrBook) groupKey(na *NetAddress) string {
+       if a.routabilityStrict && na.Local() {
+               return "local"
+       }
+       if a.routabilityStrict && !na.Routable() {
+               return "unroutable"
+       }
+
+       if ipv4 := na.IP.To4(); ipv4 != nil {
+               return (&net.IPNet{IP: na.IP, Mask: net.CIDRMask(16, 32)}).String()
+       }
+       if na.RFC6145() || na.RFC6052() {
+               // last four bytes are the ip address
+               ip := net.IP(na.IP[12:16])
+               return (&net.IPNet{IP: ip, Mask: net.CIDRMask(16, 32)}).String()
+       }
+
+       if na.RFC3964() {
+               ip := net.IP(na.IP[2:7])
+               return (&net.IPNet{IP: ip, Mask: net.CIDRMask(16, 32)}).String()
+
+       }
+       if na.RFC4380() {
+               // teredo tunnels have the last 4 bytes as the v4 address XOR
+               // 0xff.
+               ip := net.IP(make([]byte, 4))
+               for i, byte := range na.IP[12:16] {
+                       ip[i] = byte ^ 0xff
+               }
+               return (&net.IPNet{IP: ip, Mask: net.CIDRMask(16, 32)}).String()
+       }
+
+       // OK, so now we know ourselves to be a IPv6 address.
+       // bitcoind uses /32 for everything, except for Hurricane Electric's
+       // (he.net) IP range, which it uses /36 for.
+       bits := 32
+       heNet := &net.IPNet{IP: net.ParseIP("2001:470::"),
+               Mask: net.CIDRMask(32, 128)}
+       if heNet.Contains(na.IP) {
+               bits = 36
+       }
+
+       return (&net.IPNet{IP: na.IP, Mask: net.CIDRMask(bits, 128)}).String()
+}
+
+//-----------------------------------------------------------------------------
+
+/*
+   knownAddress
+
+   tracks information about a known network address that is used
+   to determine how viable an address is.
+*/
+type knownAddress struct {
+       Addr        *NetAddress
+       Src         *NetAddress
+       Attempts    int32
+       LastAttempt time.Time
+       LastSuccess time.Time
+       BucketType  byte
+       Buckets     []int
+}
+
+func newKnownAddress(addr *NetAddress, src *NetAddress) *knownAddress {
+       return &knownAddress{
+               Addr:        addr,
+               Src:         src,
+               Attempts:    0,
+               LastAttempt: time.Now(),
+               BucketType:  bucketTypeNew,
+               Buckets:     nil,
+       }
+}
+
+func (ka *knownAddress) isOld() bool {
+       return ka.BucketType == bucketTypeOld
+}
+
+func (ka *knownAddress) isNew() bool {
+       return ka.BucketType == bucketTypeNew
+}
+
+func (ka *knownAddress) markAttempt() {
+       now := time.Now()
+       ka.LastAttempt = now
+       ka.Attempts += 1
+}
+
+func (ka *knownAddress) markGood() {
+       now := time.Now()
+       ka.LastAttempt = now
+       ka.Attempts = 0
+       ka.LastSuccess = now
+}
+
+func (ka *knownAddress) addBucketRef(bucketIdx int) int {
+       for _, bucket := range ka.Buckets {
+               if bucket == bucketIdx {
+                       // TODO refactor to return error?
+                       // log.Warn(Fmt("Bucket already exists in ka.Buckets: %v", ka))
+                       return -1
+               }
+       }
+       ka.Buckets = append(ka.Buckets, bucketIdx)
+       return len(ka.Buckets)
+}
+
+func (ka *knownAddress) removeBucketRef(bucketIdx int) int {
+       buckets := []int{}
+       for _, bucket := range ka.Buckets {
+               if bucket != bucketIdx {
+                       buckets = append(buckets, bucket)
+               }
+       }
+       if len(buckets) != len(ka.Buckets)-1 {
+               // TODO refactor to return error?
+               // log.Warn(Fmt("bucketIdx not found in ka.Buckets: %v", ka))
+               return -1
+       }
+       ka.Buckets = buckets
+       return len(ka.Buckets)
+}
+
+/*
+   An address is bad if the address in question has not been tried in the last
+   minute and meets one of the following criteria:
+
+   1) It claims to be from the future
+   2) It hasn't been seen in over a month
+   3) It has failed at least three times and never succeeded
+   4) It has failed ten times in the last week
+
+   All addresses that meet these criteria are assumed to be worthless and not
+   worth keeping hold of.
+*/
+func (ka *knownAddress) isBad() bool {
+       // Has been attempted in the last minute --> good
+       if ka.LastAttempt.Before(time.Now().Add(-1 * time.Minute)) {
+               return false
+       }
+
+       // Over a month old?
+       if ka.LastAttempt.After(time.Now().Add(-1 * numMissingDays * time.Hour * 24)) {
+               return true
+       }
+
+       // Never succeeded?
+       if ka.LastSuccess.IsZero() && ka.Attempts >= numRetries {
+               return true
+       }
+
+       // Hasn't succeeded in too long?
+       if ka.LastSuccess.Before(time.Now().Add(-1*minBadDays*time.Hour*24)) &&
+               ka.Attempts >= maxFailures {
+               return true
+       }
+
+       return false
+}
diff --git a/p2p/addrbook_test.go b/p2p/addrbook_test.go
new file mode 100644 (file)
index 0000000..9b83be1
--- /dev/null
@@ -0,0 +1,174 @@
+package p2p
+
+import (
+       "fmt"
+       "io/ioutil"
+       "math/rand"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/tendermint/tmlibs/log"
+)
+
+func createTempFileName(prefix string) string {
+       f, err := ioutil.TempFile("", prefix)
+       if err != nil {
+               panic(err)
+       }
+       fname := f.Name()
+       err = f.Close()
+       if err != nil {
+               panic(err)
+       }
+       return fname
+}
+
+func TestAddrBookSaveLoad(t *testing.T) {
+       fname := createTempFileName("addrbook_test")
+
+       // 0 addresses
+       book := NewAddrBook(fname, true)
+       book.SetLogger(log.TestingLogger())
+       book.saveToFile(fname)
+
+       book = NewAddrBook(fname, true)
+       book.SetLogger(log.TestingLogger())
+       book.loadFromFile(fname)
+
+       assert.Zero(t, book.Size())
+
+       // 100 addresses
+       randAddrs := randNetAddressPairs(t, 100)
+
+       for _, addrSrc := range randAddrs {
+               book.AddAddress(addrSrc.addr, addrSrc.src)
+       }
+
+       assert.Equal(t, 100, book.Size())
+       book.saveToFile(fname)
+
+       book = NewAddrBook(fname, true)
+       book.SetLogger(log.TestingLogger())
+       book.loadFromFile(fname)
+
+       assert.Equal(t, 100, book.Size())
+}
+
+func TestAddrBookLookup(t *testing.T) {
+       fname := createTempFileName("addrbook_test")
+
+       randAddrs := randNetAddressPairs(t, 100)
+
+       book := NewAddrBook(fname, true)
+       book.SetLogger(log.TestingLogger())
+       for _, addrSrc := range randAddrs {
+               addr := addrSrc.addr
+               src := addrSrc.src
+               book.AddAddress(addr, src)
+
+               ka := book.addrLookup[addr.String()]
+               assert.NotNil(t, ka, "Expected to find KnownAddress %v but wasn't there.", addr)
+
+               if !(ka.Addr.Equals(addr) && ka.Src.Equals(src)) {
+                       t.Fatalf("KnownAddress doesn't match addr & src")
+               }
+       }
+}
+
+func TestAddrBookPromoteToOld(t *testing.T) {
+       fname := createTempFileName("addrbook_test")
+
+       randAddrs := randNetAddressPairs(t, 100)
+
+       book := NewAddrBook(fname, true)
+       book.SetLogger(log.TestingLogger())
+       for _, addrSrc := range randAddrs {
+               book.AddAddress(addrSrc.addr, addrSrc.src)
+       }
+
+       // Attempt all addresses.
+       for _, addrSrc := range randAddrs {
+               book.MarkAttempt(addrSrc.addr)
+       }
+
+       // Promote half of them
+       for i, addrSrc := range randAddrs {
+               if i%2 == 0 {
+                       book.MarkGood(addrSrc.addr)
+               }
+       }
+
+       // TODO: do more testing :)
+
+       selection := book.GetSelection()
+       t.Logf("selection: %v", selection)
+
+       if len(selection) > book.Size() {
+               t.Errorf("selection could not be bigger than the book")
+       }
+}
+
+func TestAddrBookHandlesDuplicates(t *testing.T) {
+       fname := createTempFileName("addrbook_test")
+
+       book := NewAddrBook(fname, true)
+       book.SetLogger(log.TestingLogger())
+
+       randAddrs := randNetAddressPairs(t, 100)
+
+       differentSrc := randIPv4Address(t)
+       for _, addrSrc := range randAddrs {
+               book.AddAddress(addrSrc.addr, addrSrc.src)
+               book.AddAddress(addrSrc.addr, addrSrc.src)  // duplicate
+               book.AddAddress(addrSrc.addr, differentSrc) // different src
+       }
+
+       assert.Equal(t, 100, book.Size())
+}
+
+type netAddressPair struct {
+       addr *NetAddress
+       src  *NetAddress
+}
+
+func randNetAddressPairs(t *testing.T, n int) []netAddressPair {
+       randAddrs := make([]netAddressPair, n)
+       for i := 0; i < n; i++ {
+               randAddrs[i] = netAddressPair{addr: randIPv4Address(t), src: randIPv4Address(t)}
+       }
+       return randAddrs
+}
+
+func randIPv4Address(t *testing.T) *NetAddress {
+       for {
+               ip := fmt.Sprintf("%v.%v.%v.%v",
+                       rand.Intn(254)+1,
+                       rand.Intn(255),
+                       rand.Intn(255),
+                       rand.Intn(255),
+               )
+               port := rand.Intn(65535-1) + 1
+               addr, err := NewNetAddressString(fmt.Sprintf("%v:%v", ip, port))
+               assert.Nil(t, err, "error generating rand network address")
+               if addr.Routable() {
+                       return addr
+               }
+       }
+}
+
+func TestAddrBookRemoveAddress(t *testing.T) {
+       fname := createTempFileName("addrbook_test")
+       book := NewAddrBook(fname, true)
+       book.SetLogger(log.TestingLogger())
+
+       addr := randIPv4Address(t)
+       book.AddAddress(addr, addr)
+       assert.Equal(t, 1, book.Size())
+
+       book.RemoveAddress(addr)
+       assert.Equal(t, 0, book.Size())
+
+       nonExistingAddr := randIPv4Address(t)
+       book.RemoveAddress(nonExistingAddr)
+       assert.Equal(t, 0, book.Size())
+}
diff --git a/p2p/connection.go b/p2p/connection.go
new file mode 100644 (file)
index 0000000..36f15ab
--- /dev/null
@@ -0,0 +1,686 @@
+package p2p
+
+import (
+       "bufio"
+       "fmt"
+       "io"
+       "math"
+       "net"
+       "runtime/debug"
+       "sync/atomic"
+       "time"
+
+       wire "github.com/tendermint/go-wire"
+       cmn "github.com/tendermint/tmlibs/common"
+       flow "github.com/tendermint/tmlibs/flowrate"
+)
+
+const (
+       numBatchMsgPackets = 10
+       minReadBufferSize  = 1024
+       minWriteBufferSize = 65536
+       updateState        = 2 * time.Second
+       pingTimeout        = 40 * time.Second
+       flushThrottle      = 100 * time.Millisecond
+
+       defaultSendQueueCapacity   = 1
+       defaultSendRate            = int64(512000) // 500KB/s
+       defaultRecvBufferCapacity  = 4096
+       defaultRecvMessageCapacity = 22020096      // 21MB
+       defaultRecvRate            = int64(512000) // 500KB/s
+       defaultSendTimeout         = 10 * time.Second
+)
+
+type receiveCbFunc func(chID byte, msgBytes []byte)
+type errorCbFunc func(interface{})
+
+/*
+Each peer has one `MConnection` (multiplex connection) instance.
+
+__multiplex__ *noun* a system or signal involving simultaneous transmission of
+several messages along a single channel of communication.
+
+Each `MConnection` handles message transmission on multiple abstract communication
+`Channel`s.  Each channel has a globally unique byte id.
+The byte id and the relative priorities of each `Channel` are configured upon
+initialization of the connection.
+
+There are two methods for sending messages:
+       func (m MConnection) Send(chID byte, msg interface{}) bool {}
+       func (m MConnection) TrySend(chID byte, msg interface{}) bool {}
+
+`Send(chID, msg)` is a blocking call that waits until `msg` is successfully queued
+for the channel with the given id byte `chID`, or until the request times out.
+The message `msg` is serialized using the `tendermint/wire` submodule's
+`WriteBinary()` reflection routine.
+
+`TrySend(chID, msg)` is a nonblocking call that returns false if the channel's
+queue is full.
+
+Inbound message bytes are handled with an onReceive callback function.
+*/
+type MConnection struct {
+       cmn.BaseService
+
+       conn        net.Conn
+       bufReader   *bufio.Reader
+       bufWriter   *bufio.Writer
+       sendMonitor *flow.Monitor
+       recvMonitor *flow.Monitor
+       send        chan struct{}
+       pong        chan struct{}
+       channels    []*Channel
+       channelsIdx map[byte]*Channel
+       onReceive   receiveCbFunc
+       onError     errorCbFunc
+       errored     uint32
+       config      *MConnConfig
+
+       quit         chan struct{}
+       flushTimer   *cmn.ThrottleTimer // flush writes as necessary but throttled.
+       pingTimer    *cmn.RepeatTimer   // send pings periodically
+       chStatsTimer *cmn.RepeatTimer   // update channel stats periodically
+
+       LocalAddress  *NetAddress
+       RemoteAddress *NetAddress
+}
+
+// MConnConfig is a MConnection configuration.
+type MConnConfig struct {
+       SendRate int64 `mapstructure:"send_rate"`
+       RecvRate int64 `mapstructure:"recv_rate"`
+}
+
+// DefaultMConnConfig returns the default config.
+func DefaultMConnConfig() *MConnConfig {
+       return &MConnConfig{
+               SendRate: defaultSendRate,
+               RecvRate: defaultRecvRate,
+       }
+}
+
+// NewMConnection wraps net.Conn and creates multiplex connection
+func NewMConnection(conn net.Conn, chDescs []*ChannelDescriptor, onReceive receiveCbFunc, onError errorCbFunc) *MConnection {
+       return NewMConnectionWithConfig(
+               conn,
+               chDescs,
+               onReceive,
+               onError,
+               DefaultMConnConfig())
+}
+
+// NewMConnectionWithConfig wraps net.Conn and creates multiplex connection with a config
+func NewMConnectionWithConfig(conn net.Conn, chDescs []*ChannelDescriptor, onReceive receiveCbFunc, onError errorCbFunc, config *MConnConfig) *MConnection {
+       mconn := &MConnection{
+               conn:        conn,
+               bufReader:   bufio.NewReaderSize(conn, minReadBufferSize),
+               bufWriter:   bufio.NewWriterSize(conn, minWriteBufferSize),
+               sendMonitor: flow.New(0, 0),
+               recvMonitor: flow.New(0, 0),
+               send:        make(chan struct{}, 1),
+               pong:        make(chan struct{}),
+               onReceive:   onReceive,
+               onError:     onError,
+               config:      config,
+
+               LocalAddress:  NewNetAddress(conn.LocalAddr()),
+               RemoteAddress: NewNetAddress(conn.RemoteAddr()),
+       }
+
+       // Create channels
+       var channelsIdx = map[byte]*Channel{}
+       var channels = []*Channel{}
+
+       for _, desc := range chDescs {
+               descCopy := *desc // copy the desc else unsafe access across connections
+               channel := newChannel(mconn, &descCopy)
+               channelsIdx[channel.id] = channel
+               channels = append(channels, channel)
+       }
+       mconn.channels = channels
+       mconn.channelsIdx = channelsIdx
+
+       mconn.BaseService = *cmn.NewBaseService(nil, "MConnection", mconn)
+
+       return mconn
+}
+
+func (c *MConnection) OnStart() error {
+       c.BaseService.OnStart()
+       c.quit = make(chan struct{})
+       c.flushTimer = cmn.NewThrottleTimer("flush", flushThrottle)
+       c.pingTimer = cmn.NewRepeatTimer("ping", pingTimeout)
+       c.chStatsTimer = cmn.NewRepeatTimer("chStats", updateState)
+       go c.sendRoutine()
+       go c.recvRoutine()
+       return nil
+}
+
+func (c *MConnection) OnStop() {
+       c.BaseService.OnStop()
+       c.flushTimer.Stop()
+       c.pingTimer.Stop()
+       c.chStatsTimer.Stop()
+       if c.quit != nil {
+               close(c.quit)
+       }
+       c.conn.Close()
+       // We can't close pong safely here because
+       // recvRoutine may write to it after we've stopped.
+       // Though it doesn't need to get closed at all,
+       // we close it @ recvRoutine.
+       // close(c.pong)
+}
+
+func (c *MConnection) String() string {
+       return fmt.Sprintf("MConn{%v}", c.conn.RemoteAddr())
+}
+
+func (c *MConnection) flush() {
+       c.Logger.Debug("Flush", "conn", c)
+       err := c.bufWriter.Flush()
+       if err != nil {
+               c.Logger.Error("MConnection flush failed", "error", err)
+       }
+}
+
+// Catch panics, usually caused by remote disconnects.
+func (c *MConnection) _recover() {
+       if r := recover(); r != nil {
+               stack := debug.Stack()
+               err := cmn.StackError{r, stack}
+               c.stopForError(err)
+       }
+}
+
+func (c *MConnection) stopForError(r interface{}) {
+       c.Stop()
+       if atomic.CompareAndSwapUint32(&c.errored, 0, 1) {
+               if c.onError != nil {
+                       c.onError(r)
+               }
+       }
+}
+
+// Queues a message to be sent to channel.
+func (c *MConnection) Send(chID byte, msg interface{}) bool {
+       if !c.IsRunning() {
+               return false
+       }
+
+       c.Logger.Debug("Send", "channel", chID, "conn", c, "msg", msg) //, "bytes", wire.BinaryBytes(msg))
+
+       // Send message to channel.
+       channel, ok := c.channelsIdx[chID]
+       if !ok {
+               c.Logger.Error(cmn.Fmt("Cannot send bytes, unknown channel %X", chID))
+               return false
+       }
+
+       success := channel.sendBytes(wire.BinaryBytes(msg))
+       if success {
+               // Wake up sendRoutine if necessary
+               select {
+               case c.send <- struct{}{}:
+               default:
+               }
+       } else {
+               c.Logger.Error("Send failed", "channel", chID, "conn", c, "msg", msg)
+       }
+       return success
+}
+
+// Queues a message to be sent to channel.
+// Nonblocking, returns true if successful.
+func (c *MConnection) TrySend(chID byte, msg interface{}) bool {
+       if !c.IsRunning() {
+               return false
+       }
+
+       c.Logger.Debug("TrySend", "channel", chID, "conn", c, "msg", msg)
+
+       // Send message to channel.
+       channel, ok := c.channelsIdx[chID]
+       if !ok {
+               c.Logger.Error(cmn.Fmt("Cannot send bytes, unknown channel %X", chID))
+               return false
+       }
+
+       ok = channel.trySendBytes(wire.BinaryBytes(msg))
+       if ok {
+               // Wake up sendRoutine if necessary
+               select {
+               case c.send <- struct{}{}:
+               default:
+               }
+       }
+
+       return ok
+}
+
+// CanSend returns true if you can send more data onto the chID, false
+// otherwise. Use only as a heuristic.
+func (c *MConnection) CanSend(chID byte) bool {
+       if !c.IsRunning() {
+               return false
+       }
+
+       channel, ok := c.channelsIdx[chID]
+       if !ok {
+               c.Logger.Error(cmn.Fmt("Unknown channel %X", chID))
+               return false
+       }
+       return channel.canSend()
+}
+
+// sendRoutine polls for packets to send from channels.
+func (c *MConnection) sendRoutine() {
+       defer c._recover()
+
+FOR_LOOP:
+       for {
+               var n int
+               var err error
+               select {
+               case <-c.flushTimer.Ch:
+                       // NOTE: flushTimer.Set() must be called every time
+                       // something is written to .bufWriter.
+                       c.flush()
+               case <-c.chStatsTimer.Ch:
+                       for _, channel := range c.channels {
+                               channel.updateStats()
+                       }
+               case <-c.pingTimer.Ch:
+                       c.Logger.Debug("Send Ping")
+                       wire.WriteByte(packetTypePing, c.bufWriter, &n, &err)
+                       c.sendMonitor.Update(int(n))
+                       c.flush()
+               case <-c.pong:
+                       c.Logger.Debug("Send Pong")
+                       wire.WriteByte(packetTypePong, c.bufWriter, &n, &err)
+                       c.sendMonitor.Update(int(n))
+                       c.flush()
+               case <-c.quit:
+                       break FOR_LOOP
+               case <-c.send:
+                       // Send some msgPackets
+                       eof := c.sendSomeMsgPackets()
+                       if !eof {
+                               // Keep sendRoutine awake.
+                               select {
+                               case c.send <- struct{}{}:
+                               default:
+                               }
+                       }
+               }
+
+               if !c.IsRunning() {
+                       break FOR_LOOP
+               }
+               if err != nil {
+                       c.Logger.Error("Connection failed @ sendRoutine", "conn", c, "error", err)
+                       c.stopForError(err)
+                       break FOR_LOOP
+               }
+       }
+
+       // Cleanup
+}
+
+// Returns true if messages from channels were exhausted.
+// Blocks in accordance to .sendMonitor throttling.
+func (c *MConnection) sendSomeMsgPackets() bool {
+       // Block until .sendMonitor says we can write.
+       // Once we're ready we send more than we asked for,
+       // but amortized it should even out.
+       c.sendMonitor.Limit(maxMsgPacketTotalSize, atomic.LoadInt64(&c.config.SendRate), true)
+
+       // Now send some msgPackets.
+       for i := 0; i < numBatchMsgPackets; i++ {
+               if c.sendMsgPacket() {
+                       return true
+               }
+       }
+       return false
+}
+
+// Returns true if messages from channels were exhausted.
+func (c *MConnection) sendMsgPacket() bool {
+       // Choose a channel to create a msgPacket from.
+       // The chosen channel will be the one whose recentlySent/priority is the least.
+       var leastRatio float32 = math.MaxFloat32
+       var leastChannel *Channel
+       for _, channel := range c.channels {
+               // If nothing to send, skip this channel
+               if !channel.isSendPending() {
+                       continue
+               }
+               // Get ratio, and keep track of lowest ratio.
+               ratio := float32(channel.recentlySent) / float32(channel.priority)
+               if ratio < leastRatio {
+                       leastRatio = ratio
+                       leastChannel = channel
+               }
+       }
+
+       // Nothing to send?
+       if leastChannel == nil {
+               return true
+       } else {
+               // c.Logger.Info("Found a msgPacket to send")
+       }
+
+       // Make & send a msgPacket from this channel
+       n, err := leastChannel.writeMsgPacketTo(c.bufWriter)
+       if err != nil {
+               c.Logger.Error("Failed to write msgPacket", "error", err)
+               c.stopForError(err)
+               return true
+       }
+       c.sendMonitor.Update(int(n))
+       c.flushTimer.Set()
+       return false
+}
+
+// recvRoutine reads msgPackets and reconstructs the message using the channels' "recving" buffer.
+// After a whole message has been assembled, it's pushed to onReceive().
+// Blocks depending on how the connection is throttled.
+func (c *MConnection) recvRoutine() {
+       defer c._recover()
+
+FOR_LOOP:
+       for {
+               // Block until .recvMonitor says we can read.
+               c.recvMonitor.Limit(maxMsgPacketTotalSize, atomic.LoadInt64(&c.config.RecvRate), true)
+
+               /*
+                       // Peek into bufReader for debugging
+                       if numBytes := c.bufReader.Buffered(); numBytes > 0 {
+                               log.Info("Peek connection buffer", "numBytes", numBytes, "bytes", log15.Lazy{func() []byte {
+                                       bytes, err := c.bufReader.Peek(MinInt(numBytes, 100))
+                                       if err == nil {
+                                               return bytes
+                                       } else {
+                                               log.Warn("Error peeking connection buffer", "error", err)
+                                               return nil
+                                       }
+                               }})
+                       }
+               */
+
+               // Read packet type
+               var n int
+               var err error
+               pktType := wire.ReadByte(c.bufReader, &n, &err)
+               c.recvMonitor.Update(int(n))
+               if err != nil {
+                       if c.IsRunning() {
+                               c.Logger.Error("Connection failed @ recvRoutine (reading byte)", "conn", c, "error", err)
+                               c.stopForError(err)
+                       }
+                       break FOR_LOOP
+               }
+
+               // Read more depending on packet type.
+               switch pktType {
+               case packetTypePing:
+                       // TODO: prevent abuse, as they cause flush()'s.
+                       c.Logger.Debug("Receive Ping")
+                       c.pong <- struct{}{}
+               case packetTypePong:
+                       // do nothing
+                       c.Logger.Debug("Receive Pong")
+               case packetTypeMsg:
+                       pkt, n, err := msgPacket{}, int(0), error(nil)
+                       wire.ReadBinaryPtr(&pkt, c.bufReader, maxMsgPacketTotalSize, &n, &err)
+                       c.recvMonitor.Update(int(n))
+                       if err != nil {
+                               if c.IsRunning() {
+                                       c.Logger.Error("Connection failed @ recvRoutine", "conn", c, "error", err)
+                                       c.stopForError(err)
+                               }
+                               break FOR_LOOP
+                       }
+                       channel, ok := c.channelsIdx[pkt.ChannelID]
+                       if !ok || channel == nil {
+                               cmn.PanicQ(cmn.Fmt("Unknown channel %X", pkt.ChannelID))
+                       }
+                       msgBytes, err := channel.recvMsgPacket(pkt)
+                       if err != nil {
+                               if c.IsRunning() {
+                                       c.Logger.Error("Connection failed @ recvRoutine", "conn", c, "error", err)
+                                       c.stopForError(err)
+                               }
+                               break FOR_LOOP
+                       }
+                       if msgBytes != nil {
+                               c.Logger.Debug("Received bytes", "chID", pkt.ChannelID, "msgBytes", msgBytes)
+                               c.onReceive(pkt.ChannelID, msgBytes)
+                       }
+               default:
+                       cmn.PanicSanity(cmn.Fmt("Unknown message type %X", pktType))
+               }
+
+               // TODO: shouldn't this go in the sendRoutine?
+               // Better to send a ping packet when *we* haven't sent anything for a while.
+               c.pingTimer.Reset()
+       }
+
+       // Cleanup
+       close(c.pong)
+       for _ = range c.pong {
+               // Drain
+       }
+}
+
+type ConnectionStatus struct {
+       SendMonitor flow.Status
+       RecvMonitor flow.Status
+       Channels    []ChannelStatus
+}
+
+type ChannelStatus struct {
+       ID                byte
+       SendQueueCapacity int
+       SendQueueSize     int
+       Priority          int
+       RecentlySent      int64
+}
+
+func (c *MConnection) Status() ConnectionStatus {
+       var status ConnectionStatus
+       status.SendMonitor = c.sendMonitor.Status()
+       status.RecvMonitor = c.recvMonitor.Status()
+       status.Channels = make([]ChannelStatus, len(c.channels))
+       for i, channel := range c.channels {
+               status.Channels[i] = ChannelStatus{
+                       ID:                channel.id,
+                       SendQueueCapacity: cap(channel.sendQueue),
+                       SendQueueSize:     int(channel.sendQueueSize), // TODO use atomic
+                       Priority:          channel.priority,
+                       RecentlySent:      channel.recentlySent,
+               }
+       }
+       return status
+}
+
+//-----------------------------------------------------------------------------
+
+type ChannelDescriptor struct {
+       ID                  byte
+       Priority            int
+       SendQueueCapacity   int
+       RecvBufferCapacity  int
+       RecvMessageCapacity int
+}
+
+func (chDesc *ChannelDescriptor) FillDefaults() {
+       if chDesc.SendQueueCapacity == 0 {
+               chDesc.SendQueueCapacity = defaultSendQueueCapacity
+       }
+       if chDesc.RecvBufferCapacity == 0 {
+               chDesc.RecvBufferCapacity = defaultRecvBufferCapacity
+       }
+       if chDesc.RecvMessageCapacity == 0 {
+               chDesc.RecvMessageCapacity = defaultRecvMessageCapacity
+       }
+}
+
+// TODO: lowercase.
+// NOTE: not goroutine-safe.
+type Channel struct {
+       conn          *MConnection
+       desc          *ChannelDescriptor
+       id            byte
+       sendQueue     chan []byte
+       sendQueueSize int32 // atomic.
+       recving       []byte
+       sending       []byte
+       priority      int
+       recentlySent  int64 // exponential moving average
+}
+
+func newChannel(conn *MConnection, desc *ChannelDescriptor) *Channel {
+       desc.FillDefaults()
+       if desc.Priority <= 0 {
+               cmn.PanicSanity("Channel default priority must be a postive integer")
+       }
+       return &Channel{
+               conn:      conn,
+               desc:      desc,
+               id:        desc.ID,
+               sendQueue: make(chan []byte, desc.SendQueueCapacity),
+               recving:   make([]byte, 0, desc.RecvBufferCapacity),
+               priority:  desc.Priority,
+       }
+}
+
+// Queues message to send to this channel.
+// Goroutine-safe
+// Times out (and returns false) after defaultSendTimeout
+func (ch *Channel) sendBytes(bytes []byte) bool {
+       select {
+       case ch.sendQueue <- bytes:
+               atomic.AddInt32(&ch.sendQueueSize, 1)
+               return true
+       case <-time.After(defaultSendTimeout):
+               return false
+       }
+}
+
+// Queues message to send to this channel.
+// Nonblocking, returns true if successful.
+// Goroutine-safe
+func (ch *Channel) trySendBytes(bytes []byte) bool {
+       select {
+       case ch.sendQueue <- bytes:
+               atomic.AddInt32(&ch.sendQueueSize, 1)
+               return true
+       default:
+               return false
+       }
+}
+
+// Goroutine-safe
+func (ch *Channel) loadSendQueueSize() (size int) {
+       return int(atomic.LoadInt32(&ch.sendQueueSize))
+}
+
+// Goroutine-safe
+// Use only as a heuristic.
+func (ch *Channel) canSend() bool {
+       return ch.loadSendQueueSize() < defaultSendQueueCapacity
+}
+
+// Returns true if any msgPackets are pending to be sent.
+// Call before calling nextMsgPacket()
+// Goroutine-safe
+func (ch *Channel) isSendPending() bool {
+       if len(ch.sending) == 0 {
+               if len(ch.sendQueue) == 0 {
+                       return false
+               }
+               ch.sending = <-ch.sendQueue
+       }
+       return true
+}
+
+// Creates a new msgPacket to send.
+// Not goroutine-safe
+func (ch *Channel) nextMsgPacket() msgPacket {
+       packet := msgPacket{}
+       packet.ChannelID = byte(ch.id)
+       packet.Bytes = ch.sending[:cmn.MinInt(maxMsgPacketPayloadSize, len(ch.sending))]
+       if len(ch.sending) <= maxMsgPacketPayloadSize {
+               packet.EOF = byte(0x01)
+               ch.sending = nil
+               atomic.AddInt32(&ch.sendQueueSize, -1) // decrement sendQueueSize
+       } else {
+               packet.EOF = byte(0x00)
+               ch.sending = ch.sending[cmn.MinInt(maxMsgPacketPayloadSize, len(ch.sending)):]
+       }
+       return packet
+}
+
+// Writes next msgPacket to w.
+// Not goroutine-safe
+func (ch *Channel) writeMsgPacketTo(w io.Writer) (n int, err error) {
+       packet := ch.nextMsgPacket()
+       // log.Debug("Write Msg Packet", "conn", ch.conn, "packet", packet)
+       wire.WriteByte(packetTypeMsg, w, &n, &err)
+       wire.WriteBinary(packet, w, &n, &err)
+       if err == nil {
+               ch.recentlySent += int64(n)
+       }
+       return
+}
+
+// Handles incoming msgPackets. Returns a msg bytes if msg is complete.
+// Not goroutine-safe
+func (ch *Channel) recvMsgPacket(packet msgPacket) ([]byte, error) {
+       // log.Debug("Read Msg Packet", "conn", ch.conn, "packet", packet)
+       if ch.desc.RecvMessageCapacity < len(ch.recving)+len(packet.Bytes) {
+               return nil, wire.ErrBinaryReadOverflow
+       }
+       ch.recving = append(ch.recving, packet.Bytes...)
+       if packet.EOF == byte(0x01) {
+               msgBytes := ch.recving
+               // clear the slice without re-allocating.
+               // http://stackoverflow.com/questions/16971741/how-do-you-clear-a-slice-in-go
+               //   suggests this could be a memory leak, but we might as well keep the memory for the channel until it closes,
+               //      at which point the recving slice stops being used and should be garbage collected
+               ch.recving = ch.recving[:0] // make([]byte, 0, ch.desc.RecvBufferCapacity)
+               return msgBytes, nil
+       }
+       return nil, nil
+}
+
+// Call this periodically to update stats for throttling purposes.
+// Not goroutine-safe
+func (ch *Channel) updateStats() {
+       // Exponential decay of stats.
+       // TODO: optimize.
+       ch.recentlySent = int64(float64(ch.recentlySent) * 0.8)
+}
+
+//-----------------------------------------------------------------------------
+
+const (
+       maxMsgPacketPayloadSize  = 1024
+       maxMsgPacketOverheadSize = 10 // It's actually lower but good enough
+       maxMsgPacketTotalSize    = maxMsgPacketPayloadSize + maxMsgPacketOverheadSize
+       packetTypePing           = byte(0x01)
+       packetTypePong           = byte(0x02)
+       packetTypeMsg            = byte(0x03)
+)
+
+// Messages in channels are chopped into smaller msgPackets for multiplexing.
+type msgPacket struct {
+       ChannelID byte
+       EOF       byte // 1 means message ends here.
+       Bytes     []byte
+}
+
+func (p msgPacket) String() string {
+       return fmt.Sprintf("MsgPacket{%X:%X T:%X}", p.ChannelID, p.Bytes, p.EOF)
+}
diff --git a/p2p/connection_test.go b/p2p/connection_test.go
new file mode 100644 (file)
index 0000000..47d33c9
--- /dev/null
@@ -0,0 +1,144 @@
+package p2p_test
+
+import (
+       "net"
+       "testing"
+       "time"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+       p2p "github.com/blockchain/p2p"
+       "github.com/tendermint/tmlibs/log"
+)
+
+func createMConnection(conn net.Conn) *p2p.MConnection {
+       onReceive := func(chID byte, msgBytes []byte) {
+       }
+       onError := func(r interface{}) {
+       }
+       c := createMConnectionWithCallbacks(conn, onReceive, onError)
+       c.SetLogger(log.TestingLogger())
+       return c
+}
+
+func createMConnectionWithCallbacks(conn net.Conn, onReceive func(chID byte, msgBytes []byte), onError func(r interface{})) *p2p.MConnection {
+       chDescs := []*p2p.ChannelDescriptor{&p2p.ChannelDescriptor{ID: 0x01, Priority: 1, SendQueueCapacity: 1}}
+       c := p2p.NewMConnection(conn, chDescs, onReceive, onError)
+       c.SetLogger(log.TestingLogger())
+       return c
+}
+
+func TestMConnectionSend(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       server, client := net.Pipe()
+       defer server.Close()
+       defer client.Close()
+
+       mconn := createMConnection(client)
+       _, err := mconn.Start()
+       require.Nil(err)
+       defer mconn.Stop()
+
+       msg := "Ant-Man"
+       assert.True(mconn.Send(0x01, msg))
+       // Note: subsequent Send/TrySend calls could pass because we are reading from
+       // the send queue in a separate goroutine.
+       server.Read(make([]byte, len(msg)))
+       assert.True(mconn.CanSend(0x01))
+
+       msg = "Spider-Man"
+       assert.True(mconn.TrySend(0x01, msg))
+       server.Read(make([]byte, len(msg)))
+
+       assert.False(mconn.CanSend(0x05), "CanSend should return false because channel is unknown")
+       assert.False(mconn.Send(0x05, "Absorbing Man"), "Send should return false because channel is unknown")
+}
+
+func TestMConnectionReceive(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       server, client := net.Pipe()
+       defer server.Close()
+       defer client.Close()
+
+       receivedCh := make(chan []byte)
+       errorsCh := make(chan interface{})
+       onReceive := func(chID byte, msgBytes []byte) {
+               receivedCh <- msgBytes
+       }
+       onError := func(r interface{}) {
+               errorsCh <- r
+       }
+       mconn1 := createMConnectionWithCallbacks(client, onReceive, onError)
+       _, err := mconn1.Start()
+       require.Nil(err)
+       defer mconn1.Stop()
+
+       mconn2 := createMConnection(server)
+       _, err = mconn2.Start()
+       require.Nil(err)
+       defer mconn2.Stop()
+
+       msg := "Cyclops"
+       assert.True(mconn2.Send(0x01, msg))
+
+       select {
+       case receivedBytes := <-receivedCh:
+               assert.Equal([]byte(msg), receivedBytes[2:]) // first 3 bytes are internal
+       case err := <-errorsCh:
+               t.Fatalf("Expected %s, got %+v", msg, err)
+       case <-time.After(500 * time.Millisecond):
+               t.Fatalf("Did not receive %s message in 500ms", msg)
+       }
+}
+
+func TestMConnectionStatus(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       server, client := net.Pipe()
+       defer server.Close()
+       defer client.Close()
+
+       mconn := createMConnection(client)
+       _, err := mconn.Start()
+       require.Nil(err)
+       defer mconn.Stop()
+
+       status := mconn.Status()
+       assert.NotNil(status)
+       assert.Zero(status.Channels[0].SendQueueSize)
+}
+
+func TestMConnectionStopsAndReturnsError(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       server, client := net.Pipe()
+       defer server.Close()
+       defer client.Close()
+
+       receivedCh := make(chan []byte)
+       errorsCh := make(chan interface{})
+       onReceive := func(chID byte, msgBytes []byte) {
+               receivedCh <- msgBytes
+       }
+       onError := func(r interface{}) {
+               errorsCh <- r
+       }
+       mconn := createMConnectionWithCallbacks(client, onReceive, onError)
+       _, err := mconn.Start()
+       require.Nil(err)
+       defer mconn.Stop()
+
+       client.Close()
+
+       select {
+       case receivedBytes := <-receivedCh:
+               t.Fatalf("Expected error, got %v", receivedBytes)
+       case err := <-errorsCh:
+               assert.NotNil(err)
+               assert.False(mconn.IsRunning())
+       case <-time.After(500 * time.Millisecond):
+               t.Fatal("Did not receive error in 500ms")
+       }
+}
diff --git a/p2p/fuzz.go b/p2p/fuzz.go
new file mode 100644 (file)
index 0000000..aefac98
--- /dev/null
@@ -0,0 +1,173 @@
+package p2p
+
+import (
+       "math/rand"
+       "net"
+       "sync"
+       "time"
+)
+
+const (
+       // FuzzModeDrop is a mode in which we randomly drop reads/writes, connections or sleep
+       FuzzModeDrop = iota
+       // FuzzModeDelay is a mode in which we randomly sleep
+       FuzzModeDelay
+)
+
+// FuzzedConnection wraps any net.Conn and depending on the mode either delays
+// reads/writes or randomly drops reads/writes/connections.
+type FuzzedConnection struct {
+       conn net.Conn
+
+       mtx    sync.Mutex
+       start  <-chan time.Time
+       active bool
+
+       config *FuzzConnConfig
+}
+
+// FuzzConnConfig is a FuzzedConnection configuration.
+type FuzzConnConfig struct {
+       Mode         int
+       MaxDelay     time.Duration
+       ProbDropRW   float64
+       ProbDropConn float64
+       ProbSleep    float64
+}
+
+// DefaultFuzzConnConfig returns the default config.
+func DefaultFuzzConnConfig() *FuzzConnConfig {
+       return &FuzzConnConfig{
+               Mode:         FuzzModeDrop,
+               MaxDelay:     3 * time.Second,
+               ProbDropRW:   0.2,
+               ProbDropConn: 0.00,
+               ProbSleep:    0.00,
+       }
+}
+
+// FuzzConn creates a new FuzzedConnection. Fuzzing starts immediately.
+func FuzzConn(conn net.Conn) net.Conn {
+       return FuzzConnFromConfig(conn, DefaultFuzzConnConfig())
+}
+
+// FuzzConnFromConfig creates a new FuzzedConnection from a config. Fuzzing
+// starts immediately.
+func FuzzConnFromConfig(conn net.Conn, config *FuzzConnConfig) net.Conn {
+       return &FuzzedConnection{
+               conn:   conn,
+               start:  make(<-chan time.Time),
+               active: true,
+               config: config,
+       }
+}
+
+// FuzzConnAfter creates a new FuzzedConnection. Fuzzing starts when the
+// duration elapses.
+func FuzzConnAfter(conn net.Conn, d time.Duration) net.Conn {
+       return FuzzConnAfterFromConfig(conn, d, DefaultFuzzConnConfig())
+}
+
+// FuzzConnAfterFromConfig creates a new FuzzedConnection from a config.
+// Fuzzing starts when the duration elapses.
+func FuzzConnAfterFromConfig(conn net.Conn, d time.Duration, config *FuzzConnConfig) net.Conn {
+       return &FuzzedConnection{
+               conn:   conn,
+               start:  time.After(d),
+               active: false,
+               config: config,
+       }
+}
+
+// Config returns the connection's config.
+func (fc *FuzzedConnection) Config() *FuzzConnConfig {
+       return fc.config
+}
+
+// Read implements net.Conn.
+func (fc *FuzzedConnection) Read(data []byte) (n int, err error) {
+       if fc.fuzz() {
+               return 0, nil
+       }
+       return fc.conn.Read(data)
+}
+
+// Write implements net.Conn.
+func (fc *FuzzedConnection) Write(data []byte) (n int, err error) {
+       if fc.fuzz() {
+               return 0, nil
+       }
+       return fc.conn.Write(data)
+}
+
+// Close implements net.Conn.
+func (fc *FuzzedConnection) Close() error { return fc.conn.Close() }
+
+// LocalAddr implements net.Conn.
+func (fc *FuzzedConnection) LocalAddr() net.Addr { return fc.conn.LocalAddr() }
+
+// RemoteAddr implements net.Conn.
+func (fc *FuzzedConnection) RemoteAddr() net.Addr { return fc.conn.RemoteAddr() }
+
+// SetDeadline implements net.Conn.
+func (fc *FuzzedConnection) SetDeadline(t time.Time) error { return fc.conn.SetDeadline(t) }
+
+// SetReadDeadline implements net.Conn.
+func (fc *FuzzedConnection) SetReadDeadline(t time.Time) error {
+       return fc.conn.SetReadDeadline(t)
+}
+
+// SetWriteDeadline implements net.Conn.
+func (fc *FuzzedConnection) SetWriteDeadline(t time.Time) error {
+       return fc.conn.SetWriteDeadline(t)
+}
+
+func (fc *FuzzedConnection) randomDuration() time.Duration {
+       maxDelayMillis := int(fc.config.MaxDelay.Nanoseconds() / 1000)
+       return time.Millisecond * time.Duration(rand.Int()%maxDelayMillis)
+}
+
+// implements the fuzz (delay, kill conn)
+// and returns whether or not the read/write should be ignored
+func (fc *FuzzedConnection) fuzz() bool {
+       if !fc.shouldFuzz() {
+               return false
+       }
+
+       switch fc.config.Mode {
+       case FuzzModeDrop:
+               // randomly drop the r/w, drop the conn, or sleep
+               r := rand.Float64()
+               if r <= fc.config.ProbDropRW {
+                       return true
+               } else if r < fc.config.ProbDropRW+fc.config.ProbDropConn {
+                       // XXX: can't this fail because machine precision?
+                       // XXX: do we need an error?
+                       fc.Close()
+                       return true
+               } else if r < fc.config.ProbDropRW+fc.config.ProbDropConn+fc.config.ProbSleep {
+                       time.Sleep(fc.randomDuration())
+               }
+       case FuzzModeDelay:
+               // sleep a bit
+               time.Sleep(fc.randomDuration())
+       }
+       return false
+}
+
+func (fc *FuzzedConnection) shouldFuzz() bool {
+       if fc.active {
+               return true
+       }
+
+       fc.mtx.Lock()
+       defer fc.mtx.Unlock()
+
+       select {
+       case <-fc.start:
+               fc.active = true
+               return true
+       default:
+               return false
+       }
+}
diff --git a/p2p/ip_range_counter.go b/p2p/ip_range_counter.go
new file mode 100644 (file)
index 0000000..85d9d40
--- /dev/null
@@ -0,0 +1,29 @@
+package p2p
+
+import (
+       "strings"
+)
+
+// TODO Test
+func AddToIPRangeCounts(counts map[string]int, ip string) map[string]int {
+       changes := make(map[string]int)
+       ipParts := strings.Split(ip, ":")
+       for i := 1; i < len(ipParts); i++ {
+               prefix := strings.Join(ipParts[:i], ":")
+               counts[prefix] += 1
+               changes[prefix] = counts[prefix]
+       }
+       return changes
+}
+
+// TODO Test
+func CheckIPRangeCounts(counts map[string]int, limits []int) bool {
+       for prefix, count := range counts {
+               ipParts := strings.Split(prefix, ":")
+               numParts := len(ipParts)
+               if limits[numParts] < count {
+                       return false
+               }
+       }
+       return true
+}
diff --git a/p2p/listener.go b/p2p/listener.go
new file mode 100644 (file)
index 0000000..4e897af
--- /dev/null
@@ -0,0 +1,218 @@
+package p2p
+
+import (
+       "fmt"
+       "net"
+       "strconv"
+       "time"
+
+       "github.com/blockchain/p2p/upnp"
+       cmn "github.com/tendermint/tmlibs/common"
+       "github.com/tendermint/tmlibs/log"
+)
+
+type Listener interface {
+       Connections() <-chan net.Conn
+       InternalAddress() *NetAddress
+       ExternalAddress() *NetAddress
+       String() string
+       Stop() bool
+}
+
+// Implements Listener
+type DefaultListener struct {
+       cmn.BaseService
+
+       listener    net.Listener
+       intAddr     *NetAddress
+       extAddr     *NetAddress
+       connections chan net.Conn
+}
+
+const (
+       numBufferedConnections = 10
+       defaultExternalPort    = 8770
+       tryListenSeconds       = 5
+)
+
+func splitHostPort(addr string) (host string, port int) {
+       host, portStr, err := net.SplitHostPort(addr)
+       if err != nil {
+               cmn.PanicSanity(err)
+       }
+       port, err = strconv.Atoi(portStr)
+       if err != nil {
+               cmn.PanicSanity(err)
+       }
+       return host, port
+}
+
+// skipUPNP: If true, does not try getUPNPExternalAddress()
+func NewDefaultListener(protocol string, lAddr string, skipUPNP bool, logger log.Logger) Listener {
+       // Local listen IP & port
+       lAddrIP, lAddrPort := splitHostPort(lAddr)
+
+       // Create listener
+       var listener net.Listener
+       var err error
+       for i := 0; i < tryListenSeconds; i++ {
+               listener, err = net.Listen(protocol, lAddr)
+               if err == nil {
+                       break
+               } else if i < tryListenSeconds-1 {
+                       time.Sleep(time.Second * 1)
+               }
+       }
+       if err != nil {
+               cmn.PanicCrisis(err)
+       }
+       // Actual listener local IP & port
+       listenerIP, listenerPort := splitHostPort(listener.Addr().String())
+       logger.Info("Local listener", "ip", listenerIP, "port", listenerPort)
+
+       // Determine internal address...
+       var intAddr *NetAddress
+       intAddr, err = NewNetAddressString(lAddr)
+       if err != nil {
+               cmn.PanicCrisis(err)
+       }
+
+       // Determine external address...
+       var extAddr *NetAddress
+       if !skipUPNP {
+               // If the lAddrIP is INADDR_ANY, try UPnP
+               if lAddrIP == "" || lAddrIP == "0.0.0.0" {
+                       extAddr = getUPNPExternalAddress(lAddrPort, listenerPort, logger)
+               }
+       }
+       // Otherwise just use the local address...
+       if extAddr == nil {
+               extAddr = getNaiveExternalAddress(listenerPort)
+       }
+       if extAddr == nil {
+               cmn.PanicCrisis("Could not determine external address!")
+       }
+
+       dl := &DefaultListener{
+               listener:    listener,
+               intAddr:     intAddr,
+               extAddr:     extAddr,
+               connections: make(chan net.Conn, numBufferedConnections),
+       }
+       dl.BaseService = *cmn.NewBaseService(logger, "DefaultListener", dl)
+       dl.Start() // Started upon construction
+       return dl
+}
+
+func (l *DefaultListener) OnStart() error {
+       l.BaseService.OnStart()
+       go l.listenRoutine()
+       return nil
+}
+
+func (l *DefaultListener) OnStop() {
+       l.BaseService.OnStop()
+       l.listener.Close()
+}
+
+// Accept connections and pass on the channel
+func (l *DefaultListener) listenRoutine() {
+       for {
+               conn, err := l.listener.Accept()
+
+               if !l.IsRunning() {
+                       break // Go to cleanup
+               }
+
+               // listener wasn't stopped,
+               // yet we encountered an error.
+               if err != nil {
+                       cmn.PanicCrisis(err)
+               }
+
+               l.connections <- conn
+       }
+
+       // Cleanup
+       close(l.connections)
+       for _ = range l.connections {
+               // Drain
+       }
+}
+
+// A channel of inbound connections.
+// It gets closed when the listener closes.
+func (l *DefaultListener) Connections() <-chan net.Conn {
+       return l.connections
+}
+
+func (l *DefaultListener) InternalAddress() *NetAddress {
+       return l.intAddr
+}
+
+func (l *DefaultListener) ExternalAddress() *NetAddress {
+       return l.extAddr
+}
+
+// NOTE: The returned listener is already Accept()'ing.
+// So it's not suitable to pass into http.Serve().
+func (l *DefaultListener) NetListener() net.Listener {
+       return l.listener
+}
+
+func (l *DefaultListener) String() string {
+       return fmt.Sprintf("Listener(@%v)", l.extAddr)
+}
+
+/* external address helpers */
+
+// UPNP external address discovery & port mapping
+func getUPNPExternalAddress(externalPort, internalPort int, logger log.Logger) *NetAddress {
+       logger.Info("Getting UPNP external address")
+       nat, err := upnp.Discover()
+       if err != nil {
+               logger.Info("Could not perform UPNP discover", "error", err)
+               return nil
+       }
+
+       ext, err := nat.GetExternalAddress()
+       if err != nil {
+               logger.Info("Could not get UPNP external address", "error", err)
+               return nil
+       }
+
+       // UPnP can't seem to get the external port, so let's just be explicit.
+       if externalPort == 0 {
+               externalPort = defaultExternalPort
+       }
+
+       externalPort, err = nat.AddPortMapping("tcp", externalPort, internalPort, "tendermint", 0)
+       if err != nil {
+               logger.Info("Could not add UPNP port mapping", "error", err)
+               return nil
+       }
+
+       logger.Info("Got UPNP external address", "address", ext)
+       return NewNetAddressIPPort(ext, uint16(externalPort))
+}
+
+// TODO: use syscalls: http://pastebin.com/9exZG4rh
+func getNaiveExternalAddress(port int) *NetAddress {
+       addrs, err := net.InterfaceAddrs()
+       if err != nil {
+               cmn.PanicCrisis(cmn.Fmt("Could not fetch interface addresses: %v", err))
+       }
+
+       for _, a := range addrs {
+               ipnet, ok := a.(*net.IPNet)
+               if !ok {
+                       continue
+               }
+               v4 := ipnet.IP.To4()
+               if v4 == nil || v4[0] == 127 {
+                       continue
+               } // loopback
+               return NewNetAddressIPPort(ipnet.IP, uint16(port))
+       }
+       return nil
+}
diff --git a/p2p/listener_test.go b/p2p/listener_test.go
new file mode 100644 (file)
index 0000000..c3d33a9
--- /dev/null
@@ -0,0 +1,42 @@
+package p2p
+
+import (
+       "bytes"
+       "testing"
+
+       "github.com/tendermint/tmlibs/log"
+)
+
+func TestListener(t *testing.T) {
+       // Create a listener
+       l := NewDefaultListener("tcp", ":8001", true, log.TestingLogger())
+
+       // Dial the listener
+       lAddr := l.ExternalAddress()
+       connOut, err := lAddr.Dial()
+       if err != nil {
+               t.Fatalf("Could not connect to listener address %v", lAddr)
+       } else {
+               t.Logf("Created a connection to listener address %v", lAddr)
+       }
+       connIn, ok := <-l.Connections()
+       if !ok {
+               t.Fatalf("Could not get inbound connection from listener")
+       }
+
+       msg := []byte("hi!")
+       go connIn.Write(msg)
+       b := make([]byte, 32)
+       n, err := connOut.Read(b)
+       if err != nil {
+               t.Fatalf("Error reading off connection: %v", err)
+       }
+
+       b = b[:n]
+       if !bytes.Equal(msg, b) {
+               t.Fatalf("Got %s, expected %s", b, msg)
+       }
+
+       // Close the server, no longer needed.
+       l.Stop()
+}
diff --git a/p2p/netaddress.go b/p2p/netaddress.go
new file mode 100644 (file)
index 0000000..0978748
--- /dev/null
@@ -0,0 +1,253 @@
+// Modified for Tendermint
+// Originally Copyright (c) 2013-2014 Conformal Systems LLC.
+// https://github.com/conformal/btcd/blob/master/LICENSE
+
+package p2p
+
+import (
+       "errors"
+       "flag"
+       "net"
+       "strconv"
+       "time"
+
+       cmn "github.com/tendermint/tmlibs/common"
+)
+
+// NetAddress defines information about a peer on the network
+// including its IP address, and port.
+type NetAddress struct {
+       IP   net.IP
+       Port uint16
+       str  string
+}
+
+// NewNetAddress returns a new NetAddress using the provided TCP
+// address. When testing, other net.Addr (except TCP) will result in
+// using 0.0.0.0:0. When normal run, other net.Addr (except TCP) will
+// panic.
+// TODO: socks proxies?
+func NewNetAddress(addr net.Addr) *NetAddress {
+       tcpAddr, ok := addr.(*net.TCPAddr)
+       if !ok {
+               if flag.Lookup("test.v") == nil { // normal run
+                       cmn.PanicSanity(cmn.Fmt("Only TCPAddrs are supported. Got: %v", addr))
+               } else { // in testing
+                       return NewNetAddressIPPort(net.IP("0.0.0.0"), 0)
+               }
+       }
+       ip := tcpAddr.IP
+       port := uint16(tcpAddr.Port)
+       return NewNetAddressIPPort(ip, port)
+}
+
+// NewNetAddressString returns a new NetAddress using the provided
+// address in the form of "IP:Port". Also resolves the host if host
+// is not an IP.
+func NewNetAddressString(addr string) (*NetAddress, error) {
+
+       host, portStr, err := net.SplitHostPort(addr)
+       if err != nil {
+               return nil, err
+       }
+
+       ip := net.ParseIP(host)
+       if ip == nil {
+               if len(host) > 0 {
+                       ips, err := net.LookupIP(host)
+                       if err != nil {
+                               return nil, err
+                       }
+                       ip = ips[0]
+               }
+       }
+
+       port, err := strconv.ParseUint(portStr, 10, 16)
+       if err != nil {
+               return nil, err
+       }
+
+       na := NewNetAddressIPPort(ip, uint16(port))
+       return na, nil
+}
+
+// NewNetAddressStrings returns an array of NetAddress'es build using
+// the provided strings.
+func NewNetAddressStrings(addrs []string) ([]*NetAddress, error) {
+       netAddrs := make([]*NetAddress, len(addrs))
+       for i, addr := range addrs {
+               netAddr, err := NewNetAddressString(addr)
+               if err != nil {
+                       return nil, errors.New(cmn.Fmt("Error in address %s: %v", addr, err))
+               }
+               netAddrs[i] = netAddr
+       }
+       return netAddrs, nil
+}
+
+// NewNetAddressIPPort returns a new NetAddress using the provided IP
+// and port number.
+func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
+       na := &NetAddress{
+               IP:   ip,
+               Port: port,
+               str: net.JoinHostPort(
+                       ip.String(),
+                       strconv.FormatUint(uint64(port), 10),
+               ),
+       }
+       return na
+}
+
+// Equals reports whether na and other are the same addresses.
+func (na *NetAddress) Equals(other interface{}) bool {
+       if o, ok := other.(*NetAddress); ok {
+               return na.String() == o.String()
+       }
+
+       return false
+}
+
+func (na *NetAddress) Less(other interface{}) bool {
+       if o, ok := other.(*NetAddress); ok {
+               return na.String() < o.String()
+       }
+
+       cmn.PanicSanity("Cannot compare unequal types")
+       return false
+}
+
+// String representation.
+func (na *NetAddress) String() string {
+       if na.str == "" {
+               na.str = net.JoinHostPort(
+                       na.IP.String(),
+                       strconv.FormatUint(uint64(na.Port), 10),
+               )
+       }
+       return na.str
+}
+
+// Dial calls net.Dial on the address.
+func (na *NetAddress) Dial() (net.Conn, error) {
+       conn, err := net.Dial("tcp", na.String())
+       if err != nil {
+               return nil, err
+       }
+       return conn, nil
+}
+
+// DialTimeout calls net.DialTimeout on the address.
+func (na *NetAddress) DialTimeout(timeout time.Duration) (net.Conn, error) {
+       conn, err := net.DialTimeout("tcp", na.String(), timeout)
+       if err != nil {
+               return nil, err
+       }
+       return conn, nil
+}
+
+// Routable returns true if the address is routable.
+func (na *NetAddress) Routable() bool {
+       // TODO(oga) bitcoind doesn't include RFC3849 here, but should we?
+       return na.Valid() && !(na.RFC1918() || na.RFC3927() || na.RFC4862() ||
+               na.RFC4193() || na.RFC4843() || na.Local())
+}
+
+// For IPv4 these are either a 0 or all bits set address. For IPv6 a zero
+// address or one that matches the RFC3849 documentation address format.
+func (na *NetAddress) Valid() bool {
+       return na.IP != nil && !(na.IP.IsUnspecified() || na.RFC3849() ||
+               na.IP.Equal(net.IPv4bcast))
+}
+
+// Local returns true if it is a local address.
+func (na *NetAddress) Local() bool {
+       return na.IP.IsLoopback() || zero4.Contains(na.IP)
+}
+
+// ReachabilityTo checks whenever o can be reached from na.
+func (na *NetAddress) ReachabilityTo(o *NetAddress) int {
+       const (
+               Unreachable = 0
+               Default     = iota
+               Teredo
+               Ipv6_weak
+               Ipv4
+               Ipv6_strong
+               Private
+       )
+       if !na.Routable() {
+               return Unreachable
+       } else if na.RFC4380() {
+               if !o.Routable() {
+                       return Default
+               } else if o.RFC4380() {
+                       return Teredo
+               } else if o.IP.To4() != nil {
+                       return Ipv4
+               } else { // ipv6
+                       return Ipv6_weak
+               }
+       } else if na.IP.To4() != nil {
+               if o.Routable() && o.IP.To4() != nil {
+                       return Ipv4
+               }
+               return Default
+       } else /* ipv6 */ {
+               var tunnelled bool
+               // Is our v6 is tunnelled?
+               if o.RFC3964() || o.RFC6052() || o.RFC6145() {
+                       tunnelled = true
+               }
+               if !o.Routable() {
+                       return Default
+               } else if o.RFC4380() {
+                       return Teredo
+               } else if o.IP.To4() != nil {
+                       return Ipv4
+               } else if tunnelled {
+                       // only prioritise ipv6 if we aren't tunnelling it.
+                       return Ipv6_weak
+               }
+               return Ipv6_strong
+       }
+}
+
+// RFC1918: IPv4 Private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12)
+// RFC3849: IPv6 Documentation address  (2001:0DB8::/32)
+// RFC3927: IPv4 Autoconfig (169.254.0.0/16)
+// RFC3964: IPv6 6to4 (2002::/16)
+// RFC4193: IPv6 unique local (FC00::/7)
+// RFC4380: IPv6 Teredo tunneling (2001::/32)
+// RFC4843: IPv6 ORCHID: (2001:10::/28)
+// RFC4862: IPv6 Autoconfig (FE80::/64)
+// RFC6052: IPv6 well known prefix (64:FF9B::/96)
+// RFC6145: IPv6 IPv4 translated address ::FFFF:0:0:0/96
+var rfc1918_10 = net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(8, 32)}
+var rfc1918_192 = net.IPNet{IP: net.ParseIP("192.168.0.0"), Mask: net.CIDRMask(16, 32)}
+var rfc1918_172 = net.IPNet{IP: net.ParseIP("172.16.0.0"), Mask: net.CIDRMask(12, 32)}
+var rfc3849 = net.IPNet{IP: net.ParseIP("2001:0DB8::"), Mask: net.CIDRMask(32, 128)}
+var rfc3927 = net.IPNet{IP: net.ParseIP("169.254.0.0"), Mask: net.CIDRMask(16, 32)}
+var rfc3964 = net.IPNet{IP: net.ParseIP("2002::"), Mask: net.CIDRMask(16, 128)}
+var rfc4193 = net.IPNet{IP: net.ParseIP("FC00::"), Mask: net.CIDRMask(7, 128)}
+var rfc4380 = net.IPNet{IP: net.ParseIP("2001::"), Mask: net.CIDRMask(32, 128)}
+var rfc4843 = net.IPNet{IP: net.ParseIP("2001:10::"), Mask: net.CIDRMask(28, 128)}
+var rfc4862 = net.IPNet{IP: net.ParseIP("FE80::"), Mask: net.CIDRMask(64, 128)}
+var rfc6052 = net.IPNet{IP: net.ParseIP("64:FF9B::"), Mask: net.CIDRMask(96, 128)}
+var rfc6145 = net.IPNet{IP: net.ParseIP("::FFFF:0:0:0"), Mask: net.CIDRMask(96, 128)}
+var zero4 = net.IPNet{IP: net.ParseIP("0.0.0.0"), Mask: net.CIDRMask(8, 32)}
+
+func (na *NetAddress) RFC1918() bool {
+       return rfc1918_10.Contains(na.IP) ||
+               rfc1918_192.Contains(na.IP) ||
+               rfc1918_172.Contains(na.IP)
+}
+func (na *NetAddress) RFC3849() bool { return rfc3849.Contains(na.IP) }
+func (na *NetAddress) RFC3927() bool { return rfc3927.Contains(na.IP) }
+func (na *NetAddress) RFC3964() bool { return rfc3964.Contains(na.IP) }
+func (na *NetAddress) RFC4193() bool { return rfc4193.Contains(na.IP) }
+func (na *NetAddress) RFC4380() bool { return rfc4380.Contains(na.IP) }
+func (na *NetAddress) RFC4843() bool { return rfc4843.Contains(na.IP) }
+func (na *NetAddress) RFC4862() bool { return rfc4862.Contains(na.IP) }
+func (na *NetAddress) RFC6052() bool { return rfc6052.Contains(na.IP) }
+func (na *NetAddress) RFC6145() bool { return rfc6145.Contains(na.IP) }
diff --git a/p2p/netaddress_test.go b/p2p/netaddress_test.go
new file mode 100644 (file)
index 0000000..8c60da2
--- /dev/null
@@ -0,0 +1,114 @@
+package p2p
+
+import (
+       "net"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+)
+
+func TestNewNetAddress(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
+       require.Nil(err)
+       addr := NewNetAddress(tcpAddr)
+
+       assert.Equal("127.0.0.1:8080", addr.String())
+
+       assert.NotPanics(func() {
+               NewNetAddress(&net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8000})
+       }, "Calling NewNetAddress with UDPAddr should not panic in testing")
+}
+
+func TestNewNetAddressString(t *testing.T) {
+       assert := assert.New(t)
+
+       tests := []struct {
+               addr    string
+               correct bool
+       }{
+               {"127.0.0.1:8080", true},
+               // {"127.0.0:8080", false},
+               {"a", false},
+               {"127.0.0.1:a", false},
+               {"a:8080", false},
+               {"8082", false},
+               {"127.0.0:8080000", false},
+       }
+
+       for _, t := range tests {
+               addr, err := NewNetAddressString(t.addr)
+               if t.correct {
+                       if assert.Nil(err, t.addr) {
+                               assert.Equal(t.addr, addr.String())
+                       }
+               } else {
+                       assert.NotNil(err, t.addr)
+               }
+       }
+}
+
+func TestNewNetAddressStrings(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+       addrs, err := NewNetAddressStrings([]string{"127.0.0.1:8080", "127.0.0.2:8080"})
+       require.Nil(err)
+
+       assert.Equal(2, len(addrs))
+}
+
+func TestNewNetAddressIPPort(t *testing.T) {
+       assert := assert.New(t)
+       addr := NewNetAddressIPPort(net.ParseIP("127.0.0.1"), 8080)
+
+       assert.Equal("127.0.0.1:8080", addr.String())
+}
+
+func TestNetAddressProperties(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       // TODO add more test cases
+       tests := []struct {
+               addr     string
+               valid    bool
+               local    bool
+               routable bool
+       }{
+               {"127.0.0.1:8080", true, true, false},
+               {"ya.ru:80", true, false, true},
+       }
+
+       for _, t := range tests {
+               addr, err := NewNetAddressString(t.addr)
+               require.Nil(err)
+
+               assert.Equal(t.valid, addr.Valid())
+               assert.Equal(t.local, addr.Local())
+               assert.Equal(t.routable, addr.Routable())
+       }
+}
+
+func TestNetAddressReachabilityTo(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       // TODO add more test cases
+       tests := []struct {
+               addr         string
+               other        string
+               reachability int
+       }{
+               {"127.0.0.1:8080", "127.0.0.1:8081", 0},
+               {"ya.ru:80", "127.0.0.1:8080", 1},
+       }
+
+       for _, t := range tests {
+               addr, err := NewNetAddressString(t.addr)
+               require.Nil(err)
+
+               other, err := NewNetAddressString(t.other)
+               require.Nil(err)
+
+               assert.Equal(t.reachability, addr.ReachabilityTo(other))
+       }
+}
diff --git a/p2p/peer.go b/p2p/peer.go
new file mode 100644 (file)
index 0000000..2602206
--- /dev/null
@@ -0,0 +1,303 @@
+package p2p
+
+import (
+       "fmt"
+       "io"
+       "net"
+       "time"
+
+       "github.com/pkg/errors"
+       crypto "github.com/tendermint/go-crypto"
+       wire "github.com/tendermint/go-wire"
+       cmn "github.com/tendermint/tmlibs/common"
+)
+
+// Peer could be marked as persistent, in which case you can use
+// Redial function to reconnect. Note that inbound peers can't be
+// made persistent. They should be made persistent on the other end.
+//
+// Before using a peer, you will need to perform a handshake on connection.
+type Peer struct {
+       cmn.BaseService
+
+       outbound bool
+
+       conn  net.Conn     // source connection
+       mconn *MConnection // multiplex connection
+
+       persistent bool
+       config     *PeerConfig
+
+       *NodeInfo
+       Key  string
+       Data *cmn.CMap // User data.
+}
+
+// PeerConfig is a Peer configuration.
+type PeerConfig struct {
+       AuthEnc bool `mapstructure:"auth_enc"` // authenticated encryption
+
+       // times are in seconds
+       HandshakeTimeout time.Duration `mapstructure:"handshake_timeout"`
+       DialTimeout      time.Duration `mapstructure:"dial_timeout"`
+
+       MConfig *MConnConfig `mapstructure:"connection"`
+
+       Fuzz       bool            `mapstructure:"fuzz"` // fuzz connection (for testing)
+       FuzzConfig *FuzzConnConfig `mapstructure:"fuzz_config"`
+}
+
+// DefaultPeerConfig returns the default config.
+func DefaultPeerConfig() *PeerConfig {
+       return &PeerConfig{
+               AuthEnc:          true,
+               HandshakeTimeout: 20, // * time.Second,
+               DialTimeout:      3,  // * time.Second,
+               MConfig:          DefaultMConnConfig(),
+               Fuzz:             false,
+               FuzzConfig:       DefaultFuzzConnConfig(),
+       }
+}
+
+func newOutboundPeer(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519) (*Peer, error) {
+       return newOutboundPeerWithConfig(addr, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, DefaultPeerConfig())
+}
+
+func newOutboundPeerWithConfig(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*Peer, error) {
+       conn, err := dial(addr, config)
+       if err != nil {
+               return nil, errors.Wrap(err, "Error creating peer")
+       }
+
+       peer, err := newPeerFromConnAndConfig(conn, true, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, config)
+       if err != nil {
+               conn.Close()
+               return nil, err
+       }
+       return peer, nil
+}
+
+func newInboundPeer(conn net.Conn, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519) (*Peer, error) {
+       return newInboundPeerWithConfig(conn, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, DefaultPeerConfig())
+}
+
+func newInboundPeerWithConfig(conn net.Conn, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*Peer, error) {
+       return newPeerFromConnAndConfig(conn, false, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, config)
+}
+
+func newPeerFromConnAndConfig(rawConn net.Conn, outbound bool, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*Peer, error) {
+       conn := rawConn
+
+       // Fuzz connection
+       if config.Fuzz {
+               // so we have time to do peer handshakes and get set up
+               conn = FuzzConnAfterFromConfig(conn, 10*time.Second, config.FuzzConfig)
+       }
+
+       // Encrypt connection
+       if config.AuthEnc {
+               conn.SetDeadline(time.Now().Add(config.HandshakeTimeout * time.Second))
+
+               var err error
+               conn, err = MakeSecretConnection(conn, ourNodePrivKey)
+               if err != nil {
+                       return nil, errors.Wrap(err, "Error creating peer")
+               }
+       }
+
+       // Key and NodeInfo are set after Handshake
+       p := &Peer{
+               outbound: outbound,
+               conn:     conn,
+               config:   config,
+               Data:     cmn.NewCMap(),
+       }
+
+       p.mconn = createMConnection(conn, p, reactorsByCh, chDescs, onPeerError, config.MConfig)
+
+       p.BaseService = *cmn.NewBaseService(nil, "Peer", p)
+
+       return p, nil
+}
+
+// CloseConn should be used when the peer was created, but never started.
+func (p *Peer) CloseConn() {
+       p.conn.Close()
+}
+
+// makePersistent marks the peer as persistent.
+func (p *Peer) makePersistent() {
+       if !p.outbound {
+               panic("inbound peers can't be made persistent")
+       }
+
+       p.persistent = true
+}
+
+// IsPersistent returns true if the peer is persitent, false otherwise.
+func (p *Peer) IsPersistent() bool {
+       return p.persistent
+}
+
+// HandshakeTimeout performs a handshake between a given node and the peer.
+// NOTE: blocking
+func (p *Peer) HandshakeTimeout(ourNodeInfo *NodeInfo, timeout time.Duration) error {
+       // Set deadline for handshake so we don't block forever on conn.ReadFull
+       p.conn.SetDeadline(time.Now().Add(timeout))
+
+       var peerNodeInfo = new(NodeInfo)
+       var err1 error
+       var err2 error
+       cmn.Parallel(
+               func() {
+                       var n int
+                       wire.WriteBinary(ourNodeInfo, p.conn, &n, &err1)
+               },
+               func() {
+                       var n int
+                       wire.ReadBinary(peerNodeInfo, p.conn, maxNodeInfoSize, &n, &err2)
+                       p.Logger.Info("Peer handshake", "peerNodeInfo", peerNodeInfo)
+               })
+       if err1 != nil {
+               return errors.Wrap(err1, "Error during handshake/write")
+       }
+       if err2 != nil {
+               return errors.Wrap(err2, "Error during handshake/read")
+       }
+
+       if p.config.AuthEnc {
+               // Check that the professed PubKey matches the sconn's.
+               if !peerNodeInfo.PubKey.Equals(p.PubKey().Wrap()) {
+                       return fmt.Errorf("Ignoring connection with unmatching pubkey: %v vs %v",
+                               peerNodeInfo.PubKey, p.PubKey())
+               }
+       }
+
+       // Remove deadline
+       p.conn.SetDeadline(time.Time{})
+
+       peerNodeInfo.RemoteAddr = p.Addr().String()
+
+       p.NodeInfo = peerNodeInfo
+       p.Key = peerNodeInfo.PubKey.KeyString()
+
+       return nil
+}
+
+// Addr returns peer's remote network address.
+func (p *Peer) Addr() net.Addr {
+       return p.conn.RemoteAddr()
+}
+
+// PubKey returns peer's public key.
+func (p *Peer) PubKey() crypto.PubKeyEd25519 {
+       if p.config.AuthEnc {
+               return p.conn.(*SecretConnection).RemotePubKey()
+       }
+       if p.NodeInfo == nil {
+               panic("Attempt to get peer's PubKey before calling Handshake")
+       }
+       return p.PubKey()
+}
+
+// OnStart implements BaseService.
+func (p *Peer) OnStart() error {
+       p.BaseService.OnStart()
+       _, err := p.mconn.Start()
+       return err
+}
+
+// OnStop implements BaseService.
+func (p *Peer) OnStop() {
+       p.BaseService.OnStop()
+       p.mconn.Stop()
+}
+
+// Connection returns underlying MConnection.
+func (p *Peer) Connection() *MConnection {
+       return p.mconn
+}
+
+// IsOutbound returns true if the connection is outbound, false otherwise.
+func (p *Peer) IsOutbound() bool {
+       return p.outbound
+}
+
+// Send msg to the channel identified by chID byte. Returns false if the send
+// queue is full after timeout, specified by MConnection.
+func (p *Peer) Send(chID byte, msg interface{}) bool {
+       if !p.IsRunning() {
+               // see Switch#Broadcast, where we fetch the list of peers and loop over
+               // them - while we're looping, one peer may be removed and stopped.
+               return false
+       }
+       return p.mconn.Send(chID, msg)
+}
+
+// TrySend msg to the channel identified by chID byte. Immediately returns
+// false if the send queue is full.
+func (p *Peer) TrySend(chID byte, msg interface{}) bool {
+       if !p.IsRunning() {
+               return false
+       }
+       return p.mconn.TrySend(chID, msg)
+}
+
+// CanSend returns true if the send queue is not full, false otherwise.
+func (p *Peer) CanSend(chID byte) bool {
+       if !p.IsRunning() {
+               return false
+       }
+       return p.mconn.CanSend(chID)
+}
+
+// WriteTo writes the peer's public key to w.
+func (p *Peer) WriteTo(w io.Writer) (n int64, err error) {
+       var n_ int
+       wire.WriteString(p.Key, w, &n_, &err)
+       n += int64(n_)
+       return
+}
+
+// String representation.
+func (p *Peer) String() string {
+       if p.outbound {
+               return fmt.Sprintf("Peer{%v %v out}", p.mconn, p.Key[:12])
+       }
+
+       return fmt.Sprintf("Peer{%v %v in}", p.mconn, p.Key[:12])
+}
+
+// Equals reports whenever 2 peers are actually represent the same node.
+func (p *Peer) Equals(other *Peer) bool {
+       return p.Key == other.Key
+}
+
+// Get the data for a given key.
+func (p *Peer) Get(key string) interface{} {
+       return p.Data.Get(key)
+}
+
+func dial(addr *NetAddress, config *PeerConfig) (net.Conn, error) {
+       conn, err := addr.DialTimeout(config.DialTimeout * time.Second)
+       if err != nil {
+               return nil, err
+       }
+       return conn, nil
+}
+
+func createMConnection(conn net.Conn, p *Peer, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), config *MConnConfig) *MConnection {
+       onReceive := func(chID byte, msgBytes []byte) {
+               reactor := reactorsByCh[chID]
+               if reactor == nil {
+                       cmn.PanicSanity(cmn.Fmt("Unknown channel %X", chID))
+               }
+               reactor.Receive(chID, p, msgBytes)
+       }
+
+       onError := func(r interface{}) {
+               onPeerError(p, r)
+       }
+
+       return NewMConnectionWithConfig(conn, chDescs, onReceive, onError, config)
+}
diff --git a/p2p/peer_set.go b/p2p/peer_set.go
new file mode 100644 (file)
index 0000000..c5206d2
--- /dev/null
@@ -0,0 +1,113 @@
+package p2p
+
+import (
+       "sync"
+)
+
+// IPeerSet has a (immutable) subset of the methods of PeerSet.
+type IPeerSet interface {
+       Has(key string) bool
+       Get(key string) *Peer
+       List() []*Peer
+       Size() int
+}
+
+//-----------------------------------------------------------------------------
+
+// PeerSet is a special structure for keeping a table of peers.
+// Iteration over the peers is super fast and thread-safe.
+type PeerSet struct {
+       mtx    sync.Mutex
+       lookup map[string]*peerSetItem
+       list   []*Peer
+}
+
+type peerSetItem struct {
+       peer  *Peer
+       index int
+}
+
+func NewPeerSet() *PeerSet {
+       return &PeerSet{
+               lookup: make(map[string]*peerSetItem),
+               list:   make([]*Peer, 0, 256),
+       }
+}
+
+// Returns false if peer with key (PubKeyEd25519) is already set
+func (ps *PeerSet) Add(peer *Peer) error {
+       ps.mtx.Lock()
+       defer ps.mtx.Unlock()
+       if ps.lookup[peer.Key] != nil {
+               return ErrSwitchDuplicatePeer
+       }
+
+       index := len(ps.list)
+       // Appending is safe even with other goroutines
+       // iterating over the ps.list slice.
+       ps.list = append(ps.list, peer)
+       ps.lookup[peer.Key] = &peerSetItem{peer, index}
+       return nil
+}
+
+func (ps *PeerSet) Has(peerKey string) bool {
+       ps.mtx.Lock()
+       defer ps.mtx.Unlock()
+       _, ok := ps.lookup[peerKey]
+       return ok
+}
+
+func (ps *PeerSet) Get(peerKey string) *Peer {
+       ps.mtx.Lock()
+       defer ps.mtx.Unlock()
+       item, ok := ps.lookup[peerKey]
+       if ok {
+               return item.peer
+       } else {
+               return nil
+       }
+}
+
+func (ps *PeerSet) Remove(peer *Peer) {
+       ps.mtx.Lock()
+       defer ps.mtx.Unlock()
+       item := ps.lookup[peer.Key]
+       if item == nil {
+               return
+       }
+
+       index := item.index
+       // Copy the list but without the last element.
+       // (we must copy because we're mutating the list)
+       newList := make([]*Peer, len(ps.list)-1)
+       copy(newList, ps.list)
+       // If it's the last peer, that's an easy special case.
+       if index == len(ps.list)-1 {
+               ps.list = newList
+               delete(ps.lookup, peer.Key)
+               return
+       }
+
+       // Move the last item from ps.list to "index" in list.
+       lastPeer := ps.list[len(ps.list)-1]
+       lastPeerKey := lastPeer.Key
+       lastPeerItem := ps.lookup[lastPeerKey]
+       newList[index] = lastPeer
+       lastPeerItem.index = index
+       ps.list = newList
+       delete(ps.lookup, peer.Key)
+
+}
+
+func (ps *PeerSet) Size() int {
+       ps.mtx.Lock()
+       defer ps.mtx.Unlock()
+       return len(ps.list)
+}
+
+// threadsafe list of peers.
+func (ps *PeerSet) List() []*Peer {
+       ps.mtx.Lock()
+       defer ps.mtx.Unlock()
+       return ps.list
+}
diff --git a/p2p/peer_set_test.go b/p2p/peer_set_test.go
new file mode 100644 (file)
index 0000000..9214b2e
--- /dev/null
@@ -0,0 +1,67 @@
+package p2p
+
+import (
+       "math/rand"
+       "testing"
+
+       cmn "github.com/tendermint/tmlibs/common"
+)
+
+// Returns an empty dummy peer
+func randPeer() *Peer {
+       return &Peer{
+               Key: cmn.RandStr(12),
+               NodeInfo: &NodeInfo{
+                       RemoteAddr: cmn.Fmt("%v.%v.%v.%v:46656", rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256),
+                       ListenAddr: cmn.Fmt("%v.%v.%v.%v:46656", rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256),
+               },
+       }
+}
+
+func TestAddRemoveOne(t *testing.T) {
+       peerSet := NewPeerSet()
+
+       peer := randPeer()
+       err := peerSet.Add(peer)
+       if err != nil {
+               t.Errorf("Failed to add new peer")
+       }
+       if peerSet.Size() != 1 {
+               t.Errorf("Failed to add new peer and increment size")
+       }
+
+       peerSet.Remove(peer)
+       if peerSet.Has(peer.Key) {
+               t.Errorf("Failed to remove peer")
+       }
+       if peerSet.Size() != 0 {
+               t.Errorf("Failed to remove peer and decrement size")
+       }
+}
+
+func TestAddRemoveMany(t *testing.T) {
+       peerSet := NewPeerSet()
+
+       peers := []*Peer{}
+       N := 100
+       for i := 0; i < N; i++ {
+               peer := randPeer()
+               if err := peerSet.Add(peer); err != nil {
+                       t.Errorf("Failed to add new peer")
+               }
+               if peerSet.Size() != i+1 {
+                       t.Errorf("Failed to add new peer and increment size")
+               }
+               peers = append(peers, peer)
+       }
+
+       for i, peer := range peers {
+               peerSet.Remove(peer)
+               if peerSet.Has(peer.Key) {
+                       t.Errorf("Failed to remove peer")
+               }
+               if peerSet.Size() != len(peers)-i-1 {
+                       t.Errorf("Failed to remove peer and decrement size")
+               }
+       }
+}
diff --git a/p2p/peer_test.go b/p2p/peer_test.go
new file mode 100644 (file)
index 0000000..0ac7763
--- /dev/null
@@ -0,0 +1,156 @@
+package p2p
+
+import (
+       golog "log"
+       "net"
+       "testing"
+       "time"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+
+       crypto "github.com/tendermint/go-crypto"
+)
+
+func TestPeerBasic(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       // simulate remote peer
+       rp := &remotePeer{PrivKey: crypto.GenPrivKeyEd25519(), Config: DefaultPeerConfig()}
+       rp.Start()
+       defer rp.Stop()
+
+       p, err := createOutboundPeerAndPerformHandshake(rp.Addr(), DefaultPeerConfig())
+       require.Nil(err)
+
+       p.Start()
+       defer p.Stop()
+
+       assert.True(p.IsRunning())
+       assert.True(p.IsOutbound())
+       assert.False(p.IsPersistent())
+       p.makePersistent()
+       assert.True(p.IsPersistent())
+       assert.Equal(rp.Addr().String(), p.Addr().String())
+       assert.Equal(rp.PubKey(), p.PubKey())
+}
+
+func TestPeerWithoutAuthEnc(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       config := DefaultPeerConfig()
+       config.AuthEnc = false
+
+       // simulate remote peer
+       rp := &remotePeer{PrivKey: crypto.GenPrivKeyEd25519(), Config: config}
+       rp.Start()
+       defer rp.Stop()
+
+       p, err := createOutboundPeerAndPerformHandshake(rp.Addr(), config)
+       require.Nil(err)
+
+       p.Start()
+       defer p.Stop()
+
+       assert.True(p.IsRunning())
+}
+
+func TestPeerSend(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       config := DefaultPeerConfig()
+       config.AuthEnc = false
+
+       // simulate remote peer
+       rp := &remotePeer{PrivKey: crypto.GenPrivKeyEd25519(), Config: config}
+       rp.Start()
+       defer rp.Stop()
+
+       p, err := createOutboundPeerAndPerformHandshake(rp.Addr(), config)
+       require.Nil(err)
+
+       p.Start()
+       defer p.Stop()
+
+       assert.True(p.CanSend(0x01))
+       assert.True(p.Send(0x01, "Asylum"))
+}
+
+func createOutboundPeerAndPerformHandshake(addr *NetAddress, config *PeerConfig) (*Peer, error) {
+       chDescs := []*ChannelDescriptor{
+               &ChannelDescriptor{ID: 0x01, Priority: 1},
+       }
+       reactorsByCh := map[byte]Reactor{0x01: NewTestReactor(chDescs, true)}
+       pk := crypto.GenPrivKeyEd25519()
+       p, err := newOutboundPeerWithConfig(addr, reactorsByCh, chDescs, func(p *Peer, r interface{}) {}, pk, config)
+       if err != nil {
+               return nil, err
+       }
+       err = p.HandshakeTimeout(&NodeInfo{
+               PubKey:  pk.PubKey().Unwrap().(crypto.PubKeyEd25519),
+               Moniker: "host_peer",
+               Network: "testing",
+               Version: "123.123.123",
+       }, 1*time.Second)
+       if err != nil {
+               return nil, err
+       }
+       return p, nil
+}
+
+type remotePeer struct {
+       PrivKey crypto.PrivKeyEd25519
+       Config  *PeerConfig
+       addr    *NetAddress
+       quit    chan struct{}
+}
+
+func (p *remotePeer) Addr() *NetAddress {
+       return p.addr
+}
+
+func (p *remotePeer) PubKey() crypto.PubKeyEd25519 {
+       return p.PrivKey.PubKey().Unwrap().(crypto.PubKeyEd25519)
+}
+
+func (p *remotePeer) Start() {
+       l, e := net.Listen("tcp", "127.0.0.1:0") // any available address
+       if e != nil {
+               golog.Fatalf("net.Listen tcp :0: %+v", e)
+       }
+       p.addr = NewNetAddress(l.Addr())
+       p.quit = make(chan struct{})
+       go p.accept(l)
+}
+
+func (p *remotePeer) Stop() {
+       close(p.quit)
+}
+
+func (p *remotePeer) accept(l net.Listener) {
+       for {
+               conn, err := l.Accept()
+               if err != nil {
+                       golog.Fatalf("Failed to accept conn: %+v", err)
+               }
+               peer, err := newInboundPeerWithConfig(conn, make(map[byte]Reactor), make([]*ChannelDescriptor, 0), func(p *Peer, r interface{}) {}, p.PrivKey, p.Config)
+               if err != nil {
+                       golog.Fatalf("Failed to create a peer: %+v", err)
+               }
+               err = peer.HandshakeTimeout(&NodeInfo{
+                       PubKey:  p.PrivKey.PubKey().Unwrap().(crypto.PubKeyEd25519),
+                       Moniker: "remote_peer",
+                       Network: "testing",
+                       Version: "123.123.123",
+               }, 1*time.Second)
+               if err != nil {
+                       golog.Fatalf("Failed to perform handshake: %+v", err)
+               }
+               select {
+               case <-p.quit:
+                       conn.Close()
+                       return
+               default:
+               }
+       }
+}
diff --git a/p2p/pex_reactor.go b/p2p/pex_reactor.go
new file mode 100644 (file)
index 0000000..269a8d0
--- /dev/null
@@ -0,0 +1,358 @@
+package p2p
+
+import (
+       "bytes"
+       "fmt"
+       "math/rand"
+       "reflect"
+       "time"
+
+       wire "github.com/tendermint/go-wire"
+       cmn "github.com/tendermint/tmlibs/common"
+)
+
+const (
+       // PexChannel is a channel for PEX messages
+       PexChannel = byte(0x00)
+
+       // period to ensure peers connected
+       defaultEnsurePeersPeriod = 30 * time.Second
+       minNumOutboundPeers      = 10
+       maxPexMessageSize        = 1048576 // 1MB
+
+       // maximum messages one peer can send to us during `msgCountByPeerFlushInterval`
+       defaultMaxMsgCountByPeer    = 1000
+       msgCountByPeerFlushInterval = 1 * time.Hour
+)
+
+// PEXReactor handles PEX (peer exchange) and ensures that an
+// adequate number of peers are connected to the switch.
+//
+// It uses `AddrBook` (address book) to store `NetAddress`es of the peers.
+//
+// ## Preventing abuse
+//
+// For now, it just limits the number of messages from one peer to
+// `defaultMaxMsgCountByPeer` messages per `msgCountByPeerFlushInterval` (1000
+// msg/hour).
+//
+// NOTE [2017-01-17]:
+//   Limiting is fine for now. Maybe down the road we want to keep track of the
+//   quality of peer messages so if peerA keeps telling us about peers we can't
+//   connect to then maybe we should care less about peerA. But I don't think
+//   that kind of complexity is priority right now.
+type PEXReactor struct {
+       BaseReactor
+
+       sw                *Switch
+       book              *AddrBook
+       ensurePeersPeriod time.Duration
+
+       // tracks message count by peer, so we can prevent abuse
+       msgCountByPeer    *cmn.CMap
+       maxMsgCountByPeer uint16
+}
+
+// NewPEXReactor creates new PEX reactor.
+func NewPEXReactor(b *AddrBook) *PEXReactor {
+       r := &PEXReactor{
+               book:              b,
+               ensurePeersPeriod: defaultEnsurePeersPeriod,
+               msgCountByPeer:    cmn.NewCMap(),
+               maxMsgCountByPeer: defaultMaxMsgCountByPeer,
+       }
+       r.BaseReactor = *NewBaseReactor("PEXReactor", r)
+       return r
+}
+
+// OnStart implements BaseService
+func (r *PEXReactor) OnStart() error {
+       r.BaseReactor.OnStart()
+       r.book.Start()
+       go r.ensurePeersRoutine()
+       go r.flushMsgCountByPeer()
+       return nil
+}
+
+// OnStop implements BaseService
+func (r *PEXReactor) OnStop() {
+       r.BaseReactor.OnStop()
+       r.book.Stop()
+}
+
+// GetChannels implements Reactor
+func (r *PEXReactor) GetChannels() []*ChannelDescriptor {
+       return []*ChannelDescriptor{
+               &ChannelDescriptor{
+                       ID:                PexChannel,
+                       Priority:          1,
+                       SendQueueCapacity: 10,
+               },
+       }
+}
+
+// AddPeer implements Reactor by adding peer to the address book (if inbound)
+// or by requesting more addresses (if outbound).
+func (r *PEXReactor) AddPeer(p *Peer) {
+       if p.IsOutbound() {
+               // For outbound peers, the address is already in the books.
+               // Either it was added in DialSeeds or when we
+               // received the peer's address in r.Receive
+               if r.book.NeedMoreAddrs() {
+                       r.RequestPEX(p)
+               }
+       } else { // For inbound connections, the peer is its own source
+               addr, err := NewNetAddressString(p.ListenAddr)
+               if err != nil {
+                       // this should never happen
+                       r.Logger.Error("Error in AddPeer: invalid peer address", "addr", p.ListenAddr, "error", err)
+                       return
+               }
+               r.book.AddAddress(addr, addr)
+       }
+}
+
+// RemovePeer implements Reactor.
+func (r *PEXReactor) RemovePeer(p *Peer, reason interface{}) {
+       // If we aren't keeping track of local temp data for each peer here, then we
+       // don't have to do anything.
+}
+
+// Receive implements Reactor by handling incoming PEX messages.
+func (r *PEXReactor) Receive(chID byte, src *Peer, msgBytes []byte) {
+       srcAddr := src.Connection().RemoteAddress
+       srcAddrStr := srcAddr.String()
+
+       r.IncrementMsgCountForPeer(srcAddrStr)
+       if r.ReachedMaxMsgCountForPeer(srcAddrStr) {
+               r.Logger.Error("Maximum number of messages reached for peer", "peer", srcAddrStr)
+               // TODO remove src from peers?
+               return
+       }
+
+       _, msg, err := DecodeMessage(msgBytes)
+       if err != nil {
+               r.Logger.Error("Error decoding message", "error", err)
+               return
+       }
+       r.Logger.Info("Received message", "msg", msg)
+
+       switch msg := msg.(type) {
+       case *pexRequestMessage:
+               // src requested some peers.
+               r.SendAddrs(src, r.book.GetSelection())
+       case *pexAddrsMessage:
+               // We received some peer addresses from src.
+               // (We don't want to get spammed with bad peers)
+               for _, addr := range msg.Addrs {
+                       if addr != nil {
+                               r.book.AddAddress(addr, srcAddr)
+                       }
+               }
+       default:
+               r.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
+       }
+}
+
+// RequestPEX asks peer for more addresses.
+func (r *PEXReactor) RequestPEX(p *Peer) {
+       p.Send(PexChannel, struct{ PexMessage }{&pexRequestMessage{}})
+}
+
+// SendAddrs sends addrs to the peer.
+func (r *PEXReactor) SendAddrs(p *Peer, addrs []*NetAddress) {
+       p.Send(PexChannel, struct{ PexMessage }{&pexAddrsMessage{Addrs: addrs}})
+}
+
+// SetEnsurePeersPeriod sets period to ensure peers connected.
+func (r *PEXReactor) SetEnsurePeersPeriod(d time.Duration) {
+       r.ensurePeersPeriod = d
+}
+
+// SetMaxMsgCountByPeer sets maximum messages one peer can send to us during 'msgCountByPeerFlushInterval'.
+func (r *PEXReactor) SetMaxMsgCountByPeer(v uint16) {
+       r.maxMsgCountByPeer = v
+}
+
+// ReachedMaxMsgCountForPeer returns true if we received too many
+// messages from peer with address `addr`.
+// NOTE: assumes the value in the CMap is non-nil
+func (r *PEXReactor) ReachedMaxMsgCountForPeer(addr string) bool {
+       return r.msgCountByPeer.Get(addr).(uint16) >= r.maxMsgCountByPeer
+}
+
+// Increment or initialize the msg count for the peer in the CMap
+func (r *PEXReactor) IncrementMsgCountForPeer(addr string) {
+       var count uint16
+       countI := r.msgCountByPeer.Get(addr)
+       if countI != nil {
+               count = countI.(uint16)
+       }
+       count++
+       r.msgCountByPeer.Set(addr, count)
+}
+
+// Ensures that sufficient peers are connected. (continuous)
+func (r *PEXReactor) ensurePeersRoutine() {
+       // Randomize when routine starts
+       ensurePeersPeriodMs := r.ensurePeersPeriod.Nanoseconds() / 1e6
+       time.Sleep(time.Duration(rand.Int63n(ensurePeersPeriodMs)) * time.Millisecond)
+
+       // fire once immediately.
+       r.ensurePeers()
+
+       // fire periodically
+       ticker := time.NewTicker(r.ensurePeersPeriod)
+
+       for {
+               select {
+               case <-ticker.C:
+                       r.ensurePeers()
+               case <-r.Quit:
+                       ticker.Stop()
+                       return
+               }
+       }
+}
+
+// ensurePeers ensures that sufficient peers are connected. (once)
+//
+// Old bucket / New bucket are arbitrary categories to denote whether an
+// address is vetted or not, and this needs to be determined over time via a
+// heuristic that we haven't perfected yet, or, perhaps is manually edited by
+// the node operator. It should not be used to compute what addresses are
+// already connected or not.
+//
+// TODO Basically, we need to work harder on our good-peer/bad-peer marking.
+// What we're currently doing in terms of marking good/bad peers is just a
+// placeholder. It should not be the case that an address becomes old/vetted
+// upon a single successful connection.
+func (r *PEXReactor) ensurePeers() {
+       numOutPeers, _, numDialing := r.Switch.NumPeers()
+       numToDial := minNumOutboundPeers - (numOutPeers + numDialing)
+       r.Logger.Info("Ensure peers", "numOutPeers", numOutPeers, "numDialing", numDialing, "numToDial", numToDial)
+       if numToDial <= 0 {
+               return
+       }
+
+       toDial := make(map[string]*NetAddress)
+
+       // Try to pick numToDial addresses to dial.
+       for i := 0; i < numToDial; i++ {
+               // The purpose of newBias is to first prioritize old (more vetted) peers
+               // when we have few connections, but to allow for new (less vetted) peers
+               // if we already have many connections. This algorithm isn't perfect, but
+               // it somewhat ensures that we prioritize connecting to more-vetted
+               // peers.
+               newBias := cmn.MinInt(numOutPeers, 8)*10 + 10
+               var picked *NetAddress
+               // Try to fetch a new peer 3 times.
+               // This caps the maximum number of tries to 3 * numToDial.
+               for j := 0; j < 3; j++ {
+                       try := r.book.PickAddress(newBias)
+                       if try == nil {
+                               break
+                       }
+                       _, alreadySelected := toDial[try.IP.String()]
+                       alreadyDialing := r.Switch.IsDialing(try)
+                       alreadyConnected := r.Switch.Peers().Has(try.IP.String())
+                       if alreadySelected || alreadyDialing || alreadyConnected {
+                               // r.Logger.Info("Cannot dial address", "addr", try,
+                               //      "alreadySelected", alreadySelected,
+                               //      "alreadyDialing", alreadyDialing,
+                               //  "alreadyConnected", alreadyConnected)
+                               continue
+                       } else {
+                               r.Logger.Info("Will dial address", "addr", try)
+                               picked = try
+                               break
+                       }
+               }
+               if picked == nil {
+                       continue
+               }
+               toDial[picked.IP.String()] = picked
+       }
+
+       // Dial picked addresses
+       for _, item := range toDial {
+               go func(picked *NetAddress) {
+                       _, err := r.Switch.DialPeerWithAddress(picked, false)
+                       if err != nil {
+                               r.book.MarkAttempt(picked)
+                       }
+               }(item)
+       }
+
+       // If we need more addresses, pick a random peer and ask for more.
+       if r.book.NeedMoreAddrs() {
+               if peers := r.Switch.Peers().List(); len(peers) > 0 {
+                       i := rand.Int() % len(peers)
+                       peer := peers[i]
+                       r.Logger.Info("No addresses to dial. Sending pexRequest to random peer", "peer", peer)
+                       r.RequestPEX(peer)
+               }
+       }
+}
+
+func (r *PEXReactor) flushMsgCountByPeer() {
+       ticker := time.NewTicker(msgCountByPeerFlushInterval)
+
+       for {
+               select {
+               case <-ticker.C:
+                       r.msgCountByPeer.Clear()
+               case <-r.Quit:
+                       ticker.Stop()
+                       return
+               }
+       }
+}
+
+//-----------------------------------------------------------------------------
+// Messages
+
+const (
+       msgTypeRequest = byte(0x01)
+       msgTypeAddrs   = byte(0x02)
+)
+
+// PexMessage is a primary type for PEX messages. Underneath, it could contain
+// either pexRequestMessage, or pexAddrsMessage messages.
+type PexMessage interface{}
+
+var _ = wire.RegisterInterface(
+       struct{ PexMessage }{},
+       wire.ConcreteType{&pexRequestMessage{}, msgTypeRequest},
+       wire.ConcreteType{&pexAddrsMessage{}, msgTypeAddrs},
+)
+
+// DecodeMessage implements interface registered above.
+func DecodeMessage(bz []byte) (msgType byte, msg PexMessage, err error) {
+       msgType = bz[0]
+       n := new(int)
+       r := bytes.NewReader(bz)
+       msg = wire.ReadBinary(struct{ PexMessage }{}, r, maxPexMessageSize, n, &err).(struct{ PexMessage }).PexMessage
+       return
+}
+
+/*
+A pexRequestMessage requests additional peer addresses.
+*/
+type pexRequestMessage struct {
+}
+
+func (m *pexRequestMessage) String() string {
+       return "[pexRequest]"
+}
+
+/*
+A message with announced peer addresses.
+*/
+type pexAddrsMessage struct {
+       Addrs []*NetAddress
+}
+
+func (m *pexAddrsMessage) String() string {
+       return fmt.Sprintf("[pexAddrs %v]", m.Addrs)
+}
diff --git a/p2p/pex_reactor_test.go b/p2p/pex_reactor_test.go
new file mode 100644 (file)
index 0000000..2ce131a
--- /dev/null
@@ -0,0 +1,178 @@
+package p2p
+
+import (
+       "io/ioutil"
+       "math/rand"
+       "os"
+       "testing"
+       "time"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+       wire "github.com/tendermint/go-wire"
+       cmn "github.com/tendermint/tmlibs/common"
+       "github.com/tendermint/tmlibs/log"
+)
+
+func TestPEXReactorBasic(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       dir, err := ioutil.TempDir("", "pex_reactor")
+       require.Nil(err)
+       defer os.RemoveAll(dir)
+       book := NewAddrBook(dir+"addrbook.json", true)
+       book.SetLogger(log.TestingLogger())
+
+       r := NewPEXReactor(book)
+       r.SetLogger(log.TestingLogger())
+
+       assert.NotNil(r)
+       assert.NotEmpty(r.GetChannels())
+}
+
+func TestPEXReactorAddRemovePeer(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       dir, err := ioutil.TempDir("", "pex_reactor")
+       require.Nil(err)
+       defer os.RemoveAll(dir)
+       book := NewAddrBook(dir+"addrbook.json", true)
+       book.SetLogger(log.TestingLogger())
+
+       r := NewPEXReactor(book)
+       r.SetLogger(log.TestingLogger())
+
+       size := book.Size()
+       peer := createRandomPeer(false)
+
+       r.AddPeer(peer)
+       assert.Equal(size+1, book.Size())
+
+       r.RemovePeer(peer, "peer not available")
+       assert.Equal(size+1, book.Size())
+
+       outboundPeer := createRandomPeer(true)
+
+       r.AddPeer(outboundPeer)
+       assert.Equal(size+1, book.Size(), "outbound peers should not be added to the address book")
+
+       r.RemovePeer(outboundPeer, "peer not available")
+       assert.Equal(size+1, book.Size())
+}
+
+func TestPEXReactorRunning(t *testing.T) {
+       require := require.New(t)
+
+       N := 3
+       switches := make([]*Switch, N)
+
+       dir, err := ioutil.TempDir("", "pex_reactor")
+       require.Nil(err)
+       defer os.RemoveAll(dir)
+       book := NewAddrBook(dir+"addrbook.json", false)
+       book.SetLogger(log.TestingLogger())
+
+       // create switches
+       for i := 0; i < N; i++ {
+               switches[i] = makeSwitch(config, i, "127.0.0.1", "123.123.123", func(i int, sw *Switch) *Switch {
+                       sw.SetLogger(log.TestingLogger().With("switch", i))
+
+                       r := NewPEXReactor(book)
+                       r.SetLogger(log.TestingLogger())
+                       r.SetEnsurePeersPeriod(250 * time.Millisecond)
+                       sw.AddReactor("pex", r)
+                       return sw
+               })
+       }
+
+       // fill the address book and add listeners
+       for _, s := range switches {
+               addr, _ := NewNetAddressString(s.NodeInfo().ListenAddr)
+               book.AddAddress(addr, addr)
+               s.AddListener(NewDefaultListener("tcp", s.NodeInfo().ListenAddr, true, log.TestingLogger()))
+       }
+
+       // start switches
+       for _, s := range switches {
+               _, err := s.Start() // start switch and reactors
+               require.Nil(err)
+       }
+
+       time.Sleep(1 * time.Second)
+
+       // check peers are connected after some time
+       for _, s := range switches {
+               outbound, inbound, _ := s.NumPeers()
+               if outbound+inbound == 0 {
+                       t.Errorf("%v expected to be connected to at least one peer", s.NodeInfo().ListenAddr)
+               }
+       }
+
+       // stop them
+       for _, s := range switches {
+               s.Stop()
+       }
+}
+
+func TestPEXReactorReceive(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       dir, err := ioutil.TempDir("", "pex_reactor")
+       require.Nil(err)
+       defer os.RemoveAll(dir)
+       book := NewAddrBook(dir+"addrbook.json", true)
+       book.SetLogger(log.TestingLogger())
+
+       r := NewPEXReactor(book)
+       r.SetLogger(log.TestingLogger())
+
+       peer := createRandomPeer(false)
+
+       size := book.Size()
+       netAddr, _ := NewNetAddressString(peer.ListenAddr)
+       addrs := []*NetAddress{netAddr}
+       msg := wire.BinaryBytes(struct{ PexMessage }{&pexAddrsMessage{Addrs: addrs}})
+       r.Receive(PexChannel, peer, msg)
+       assert.Equal(size+1, book.Size())
+
+       msg = wire.BinaryBytes(struct{ PexMessage }{&pexRequestMessage{}})
+       r.Receive(PexChannel, peer, msg)
+}
+
+func TestPEXReactorAbuseFromPeer(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       dir, err := ioutil.TempDir("", "pex_reactor")
+       require.Nil(err)
+       defer os.RemoveAll(dir)
+       book := NewAddrBook(dir+"addrbook.json", true)
+       book.SetLogger(log.TestingLogger())
+
+       r := NewPEXReactor(book)
+       r.SetLogger(log.TestingLogger())
+       r.SetMaxMsgCountByPeer(5)
+
+       peer := createRandomPeer(false)
+
+       msg := wire.BinaryBytes(struct{ PexMessage }{&pexRequestMessage{}})
+       for i := 0; i < 10; i++ {
+               r.Receive(PexChannel, peer, msg)
+       }
+
+       assert.True(r.ReachedMaxMsgCountForPeer(peer.ListenAddr))
+}
+
+func createRandomPeer(outbound bool) *Peer {
+       addr := cmn.Fmt("%v.%v.%v.%v:46656", rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256)
+       netAddr, _ := NewNetAddressString(addr)
+       p := &Peer{
+               Key: cmn.RandStr(12),
+               NodeInfo: &NodeInfo{
+                       ListenAddr: addr,
+               },
+               outbound: outbound,
+               mconn:    &MConnection{RemoteAddress: netAddr},
+       }
+       p.SetLogger(log.TestingLogger().With("peer", addr))
+       return p
+}
diff --git a/p2p/secret_connection.go b/p2p/secret_connection.go
new file mode 100644 (file)
index 0000000..24cae0f
--- /dev/null
@@ -0,0 +1,346 @@
+// Uses nacl's secret_box to encrypt a net.Conn.
+// It is (meant to be) an implementation of the STS protocol.
+// Note we do not (yet) assume that a remote peer's pubkey
+// is known ahead of time, and thus we are technically
+// still vulnerable to MITM. (TODO!)
+// See docs/sts-final.pdf for more info
+package p2p
+
+import (
+       "bytes"
+       crand "crypto/rand"
+       "crypto/sha256"
+       "encoding/binary"
+       "errors"
+       "io"
+       "net"
+       "time"
+
+       "golang.org/x/crypto/nacl/box"
+       "golang.org/x/crypto/nacl/secretbox"
+       "golang.org/x/crypto/ripemd160"
+
+       "github.com/tendermint/go-crypto"
+       "github.com/tendermint/go-wire"
+       cmn "github.com/tendermint/tmlibs/common"
+)
+
+// 2 + 1024 == 1026 total frame size
+const dataLenSize = 2 // uint16 to describe the length, is <= dataMaxSize
+const dataMaxSize = 1024
+const totalFrameSize = dataMaxSize + dataLenSize
+const sealedFrameSize = totalFrameSize + secretbox.Overhead
+const authSigMsgSize = (32 + 1) + (64 + 1) // fixed size (length prefixed) byte arrays
+
+// Implements net.Conn
+type SecretConnection struct {
+       conn       io.ReadWriteCloser
+       recvBuffer []byte
+       recvNonce  *[24]byte
+       sendNonce  *[24]byte
+       remPubKey  crypto.PubKeyEd25519
+       shrSecret  *[32]byte // shared secret
+}
+
+// Performs handshake and returns a new authenticated SecretConnection.
+// Returns nil if error in handshake.
+// Caller should call conn.Close()
+// See docs/sts-final.pdf for more information.
+func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKeyEd25519) (*SecretConnection, error) {
+
+       locPubKey := locPrivKey.PubKey().Unwrap().(crypto.PubKeyEd25519)
+
+       // Generate ephemeral keys for perfect forward secrecy.
+       locEphPub, locEphPriv := genEphKeys()
+
+       // Write local ephemeral pubkey and receive one too.
+       // NOTE: every 32-byte string is accepted as a Curve25519 public key
+       // (see DJB's Curve25519 paper: http://cr.yp.to/ecdh/curve25519-20060209.pdf)
+       remEphPub, err := shareEphPubKey(conn, locEphPub)
+       if err != nil {
+               return nil, err
+       }
+
+       // Compute common shared secret.
+       shrSecret := computeSharedSecret(remEphPub, locEphPriv)
+
+       // Sort by lexical order.
+       loEphPub, hiEphPub := sort32(locEphPub, remEphPub)
+
+       // Generate nonces to use for secretbox.
+       recvNonce, sendNonce := genNonces(loEphPub, hiEphPub, locEphPub == loEphPub)
+
+       // Generate common challenge to sign.
+       challenge := genChallenge(loEphPub, hiEphPub)
+
+       // Construct SecretConnection.
+       sc := &SecretConnection{
+               conn:       conn,
+               recvBuffer: nil,
+               recvNonce:  recvNonce,
+               sendNonce:  sendNonce,
+               shrSecret:  shrSecret,
+       }
+
+       // Sign the challenge bytes for authentication.
+       locSignature := signChallenge(challenge, locPrivKey)
+
+       // Share (in secret) each other's pubkey & challenge signature
+       authSigMsg, err := shareAuthSignature(sc, locPubKey, locSignature)
+       if err != nil {
+               return nil, err
+       }
+       remPubKey, remSignature := authSigMsg.Key, authSigMsg.Sig
+       if !remPubKey.VerifyBytes(challenge[:], remSignature) {
+               return nil, errors.New("Challenge verification failed")
+       }
+
+       // We've authorized.
+       sc.remPubKey = remPubKey.Unwrap().(crypto.PubKeyEd25519)
+       return sc, nil
+}
+
+// Returns authenticated remote pubkey
+func (sc *SecretConnection) RemotePubKey() crypto.PubKeyEd25519 {
+       return sc.remPubKey
+}
+
+// Writes encrypted frames of `sealedFrameSize`
+// CONTRACT: data smaller than dataMaxSize is read atomically.
+func (sc *SecretConnection) Write(data []byte) (n int, err error) {
+       for 0 < len(data) {
+               var frame []byte = make([]byte, totalFrameSize)
+               var chunk []byte
+               if dataMaxSize < len(data) {
+                       chunk = data[:dataMaxSize]
+                       data = data[dataMaxSize:]
+               } else {
+                       chunk = data
+                       data = nil
+               }
+               chunkLength := len(chunk)
+               binary.BigEndian.PutUint16(frame, uint16(chunkLength))
+               copy(frame[dataLenSize:], chunk)
+
+               // encrypt the frame
+               var sealedFrame = make([]byte, sealedFrameSize)
+               secretbox.Seal(sealedFrame[:0], frame, sc.sendNonce, sc.shrSecret)
+               // fmt.Printf("secretbox.Seal(sealed:%X,sendNonce:%X,shrSecret:%X\n", sealedFrame, sc.sendNonce, sc.shrSecret)
+               incr2Nonce(sc.sendNonce)
+               // end encryption
+
+               _, err := sc.conn.Write(sealedFrame)
+               if err != nil {
+                       return n, err
+               } else {
+                       n += len(chunk)
+               }
+       }
+       return
+}
+
+// CONTRACT: data smaller than dataMaxSize is read atomically.
+func (sc *SecretConnection) Read(data []byte) (n int, err error) {
+       if 0 < len(sc.recvBuffer) {
+               n_ := copy(data, sc.recvBuffer)
+               sc.recvBuffer = sc.recvBuffer[n_:]
+               return
+       }
+
+       sealedFrame := make([]byte, sealedFrameSize)
+       _, err = io.ReadFull(sc.conn, sealedFrame)
+       if err != nil {
+               return
+       }
+
+       // decrypt the frame
+       var frame = make([]byte, totalFrameSize)
+       // fmt.Printf("secretbox.Open(sealed:%X,recvNonce:%X,shrSecret:%X\n", sealedFrame, sc.recvNonce, sc.shrSecret)
+       _, ok := secretbox.Open(frame[:0], sealedFrame, sc.recvNonce, sc.shrSecret)
+       if !ok {
+               return n, errors.New("Failed to decrypt SecretConnection")
+       }
+       incr2Nonce(sc.recvNonce)
+       // end decryption
+
+       var chunkLength = binary.BigEndian.Uint16(frame) // read the first two bytes
+       if chunkLength > dataMaxSize {
+               return 0, errors.New("chunkLength is greater than dataMaxSize")
+       }
+       var chunk = frame[dataLenSize : dataLenSize+chunkLength]
+
+       n = copy(data, chunk)
+       sc.recvBuffer = chunk[n:]
+       return
+}
+
+// Implements net.Conn
+func (sc *SecretConnection) Close() error                  { return sc.conn.Close() }
+func (sc *SecretConnection) LocalAddr() net.Addr           { return sc.conn.(net.Conn).LocalAddr() }
+func (sc *SecretConnection) RemoteAddr() net.Addr          { return sc.conn.(net.Conn).RemoteAddr() }
+func (sc *SecretConnection) SetDeadline(t time.Time) error { return sc.conn.(net.Conn).SetDeadline(t) }
+func (sc *SecretConnection) SetReadDeadline(t time.Time) error {
+       return sc.conn.(net.Conn).SetReadDeadline(t)
+}
+func (sc *SecretConnection) SetWriteDeadline(t time.Time) error {
+       return sc.conn.(net.Conn).SetWriteDeadline(t)
+}
+
+func genEphKeys() (ephPub, ephPriv *[32]byte) {
+       var err error
+       ephPub, ephPriv, err = box.GenerateKey(crand.Reader)
+       if err != nil {
+               cmn.PanicCrisis("Could not generate ephemeral keypairs")
+       }
+       return
+}
+
+func shareEphPubKey(conn io.ReadWriteCloser, locEphPub *[32]byte) (remEphPub *[32]byte, err error) {
+       var err1, err2 error
+
+       cmn.Parallel(
+               func() {
+                       _, err1 = conn.Write(locEphPub[:])
+               },
+               func() {
+                       remEphPub = new([32]byte)
+                       _, err2 = io.ReadFull(conn, remEphPub[:])
+               },
+       )
+
+       if err1 != nil {
+               return nil, err1
+       }
+       if err2 != nil {
+               return nil, err2
+       }
+
+       return remEphPub, nil
+}
+
+func computeSharedSecret(remPubKey, locPrivKey *[32]byte) (shrSecret *[32]byte) {
+       shrSecret = new([32]byte)
+       box.Precompute(shrSecret, remPubKey, locPrivKey)
+       return
+}
+
+func sort32(foo, bar *[32]byte) (lo, hi *[32]byte) {
+       if bytes.Compare(foo[:], bar[:]) < 0 {
+               lo = foo
+               hi = bar
+       } else {
+               lo = bar
+               hi = foo
+       }
+       return
+}
+
+func genNonces(loPubKey, hiPubKey *[32]byte, locIsLo bool) (recvNonce, sendNonce *[24]byte) {
+       nonce1 := hash24(append(loPubKey[:], hiPubKey[:]...))
+       nonce2 := new([24]byte)
+       copy(nonce2[:], nonce1[:])
+       nonce2[len(nonce2)-1] ^= 0x01
+       if locIsLo {
+               recvNonce = nonce1
+               sendNonce = nonce2
+       } else {
+               recvNonce = nonce2
+               sendNonce = nonce1
+       }
+       return
+}
+
+func genChallenge(loPubKey, hiPubKey *[32]byte) (challenge *[32]byte) {
+       return hash32(append(loPubKey[:], hiPubKey[:]...))
+}
+
+func signChallenge(challenge *[32]byte, locPrivKey crypto.PrivKeyEd25519) (signature crypto.SignatureEd25519) {
+       signature = locPrivKey.Sign(challenge[:]).Unwrap().(crypto.SignatureEd25519)
+       return
+}
+
+type authSigMessage struct {
+       Key crypto.PubKey
+       Sig crypto.Signature
+}
+
+func shareAuthSignature(sc *SecretConnection, pubKey crypto.PubKeyEd25519, signature crypto.SignatureEd25519) (*authSigMessage, error) {
+       var recvMsg authSigMessage
+       var err1, err2 error
+
+       cmn.Parallel(
+               func() {
+                       msgBytes := wire.BinaryBytes(authSigMessage{pubKey.Wrap(), signature.Wrap()})
+                       _, err1 = sc.Write(msgBytes)
+               },
+               func() {
+                       readBuffer := make([]byte, authSigMsgSize)
+                       _, err2 = io.ReadFull(sc, readBuffer)
+                       if err2 != nil {
+                               return
+                       }
+                       n := int(0) // not used.
+                       recvMsg = wire.ReadBinary(authSigMessage{}, bytes.NewBuffer(readBuffer), authSigMsgSize, &n, &err2).(authSigMessage)
+               })
+
+       if err1 != nil {
+               return nil, err1
+       }
+       if err2 != nil {
+               return nil, err2
+       }
+
+       return &recvMsg, nil
+}
+
+func verifyChallengeSignature(challenge *[32]byte, remPubKey crypto.PubKeyEd25519, remSignature crypto.SignatureEd25519) bool {
+       return remPubKey.VerifyBytes(challenge[:], remSignature.Wrap())
+}
+
+//--------------------------------------------------------------------------------
+
+// sha256
+func hash32(input []byte) (res *[32]byte) {
+       hasher := sha256.New()
+       hasher.Write(input) // does not error
+       resSlice := hasher.Sum(nil)
+       res = new([32]byte)
+       copy(res[:], resSlice)
+       return
+}
+
+// We only fill in the first 20 bytes with ripemd160
+func hash24(input []byte) (res *[24]byte) {
+       hasher := ripemd160.New()
+       hasher.Write(input) // does not error
+       resSlice := hasher.Sum(nil)
+       res = new([24]byte)
+       copy(res[:], resSlice)
+       return
+}
+
+// ripemd160
+func hash20(input []byte) (res *[20]byte) {
+       hasher := ripemd160.New()
+       hasher.Write(input) // does not error
+       resSlice := hasher.Sum(nil)
+       res = new([20]byte)
+       copy(res[:], resSlice)
+       return
+}
+
+// increment nonce big-endian by 2 with wraparound.
+func incr2Nonce(nonce *[24]byte) {
+       incrNonce(nonce)
+       incrNonce(nonce)
+}
+
+// increment nonce big-endian by 1 with wraparound.
+func incrNonce(nonce *[24]byte) {
+       for i := 23; 0 <= i; i-- {
+               nonce[i] += 1
+               if nonce[i] != 0 {
+                       return
+               }
+       }
+}
diff --git a/p2p/secret_connection_test.go b/p2p/secret_connection_test.go
new file mode 100644 (file)
index 0000000..d0d0085
--- /dev/null
@@ -0,0 +1,202 @@
+package p2p
+
+import (
+       "bytes"
+       "io"
+       "testing"
+
+       "github.com/tendermint/go-crypto"
+       cmn "github.com/tendermint/tmlibs/common"
+)
+
+type dummyConn struct {
+       *io.PipeReader
+       *io.PipeWriter
+}
+
+func (drw dummyConn) Close() (err error) {
+       err2 := drw.PipeWriter.CloseWithError(io.EOF)
+       err1 := drw.PipeReader.Close()
+       if err2 != nil {
+               return err
+       }
+       return err1
+}
+
+// Each returned ReadWriteCloser is akin to a net.Connection
+func makeDummyConnPair() (fooConn, barConn dummyConn) {
+       barReader, fooWriter := io.Pipe()
+       fooReader, barWriter := io.Pipe()
+       return dummyConn{fooReader, fooWriter}, dummyConn{barReader, barWriter}
+}
+
+func makeSecretConnPair(tb testing.TB) (fooSecConn, barSecConn *SecretConnection) {
+       fooConn, barConn := makeDummyConnPair()
+       fooPrvKey := crypto.GenPrivKeyEd25519()
+       fooPubKey := fooPrvKey.PubKey().Unwrap().(crypto.PubKeyEd25519)
+       barPrvKey := crypto.GenPrivKeyEd25519()
+       barPubKey := barPrvKey.PubKey().Unwrap().(crypto.PubKeyEd25519)
+
+       cmn.Parallel(
+               func() {
+                       var err error
+                       fooSecConn, err = MakeSecretConnection(fooConn, fooPrvKey)
+                       if err != nil {
+                               tb.Errorf("Failed to establish SecretConnection for foo: %v", err)
+                               return
+                       }
+                       remotePubBytes := fooSecConn.RemotePubKey()
+                       if !bytes.Equal(remotePubBytes[:], barPubKey[:]) {
+                               tb.Errorf("Unexpected fooSecConn.RemotePubKey.  Expected %v, got %v",
+                                       barPubKey, fooSecConn.RemotePubKey())
+                       }
+               },
+               func() {
+                       var err error
+                       barSecConn, err = MakeSecretConnection(barConn, barPrvKey)
+                       if barSecConn == nil {
+                               tb.Errorf("Failed to establish SecretConnection for bar: %v", err)
+                               return
+                       }
+                       remotePubBytes := barSecConn.RemotePubKey()
+                       if !bytes.Equal(remotePubBytes[:], fooPubKey[:]) {
+                               tb.Errorf("Unexpected barSecConn.RemotePubKey.  Expected %v, got %v",
+                                       fooPubKey, barSecConn.RemotePubKey())
+                       }
+               })
+
+       return
+}
+
+func TestSecretConnectionHandshake(t *testing.T) {
+       fooSecConn, barSecConn := makeSecretConnPair(t)
+       fooSecConn.Close()
+       barSecConn.Close()
+}
+
+func TestSecretConnectionReadWrite(t *testing.T) {
+       fooConn, barConn := makeDummyConnPair()
+       fooWrites, barWrites := []string{}, []string{}
+       fooReads, barReads := []string{}, []string{}
+
+       // Pre-generate the things to write (for foo & bar)
+       for i := 0; i < 100; i++ {
+               fooWrites = append(fooWrites, cmn.RandStr((cmn.RandInt()%(dataMaxSize*5))+1))
+               barWrites = append(barWrites, cmn.RandStr((cmn.RandInt()%(dataMaxSize*5))+1))
+       }
+
+       // A helper that will run with (fooConn, fooWrites, fooReads) and vice versa
+       genNodeRunner := func(nodeConn dummyConn, nodeWrites []string, nodeReads *[]string) func() {
+               return func() {
+                       // Node handskae
+                       nodePrvKey := crypto.GenPrivKeyEd25519()
+                       nodeSecretConn, err := MakeSecretConnection(nodeConn, nodePrvKey)
+                       if err != nil {
+                               t.Errorf("Failed to establish SecretConnection for node: %v", err)
+                               return
+                       }
+                       // In parallel, handle reads and writes
+                       cmn.Parallel(
+                               func() {
+                                       // Node writes
+                                       for _, nodeWrite := range nodeWrites {
+                                               n, err := nodeSecretConn.Write([]byte(nodeWrite))
+                                               if err != nil {
+                                                       t.Errorf("Failed to write to nodeSecretConn: %v", err)
+                                                       return
+                                               }
+                                               if n != len(nodeWrite) {
+                                                       t.Errorf("Failed to write all bytes. Expected %v, wrote %v", len(nodeWrite), n)
+                                                       return
+                                               }
+                                       }
+                                       nodeConn.PipeWriter.Close()
+                               },
+                               func() {
+                                       // Node reads
+                                       readBuffer := make([]byte, dataMaxSize)
+                                       for {
+                                               n, err := nodeSecretConn.Read(readBuffer)
+                                               if err == io.EOF {
+                                                       return
+                                               } else if err != nil {
+                                                       t.Errorf("Failed to read from nodeSecretConn: %v", err)
+                                                       return
+                                               }
+                                               *nodeReads = append(*nodeReads, string(readBuffer[:n]))
+                                       }
+                                       nodeConn.PipeReader.Close()
+                               })
+               }
+       }
+
+       // Run foo & bar in parallel
+       cmn.Parallel(
+               genNodeRunner(fooConn, fooWrites, &fooReads),
+               genNodeRunner(barConn, barWrites, &barReads),
+       )
+
+       // A helper to ensure that the writes and reads match.
+       // Additionally, small writes (<= dataMaxSize) must be atomically read.
+       compareWritesReads := func(writes []string, reads []string) {
+               for {
+                       // Pop next write & corresponding reads
+                       var read, write string = "", writes[0]
+                       var readCount = 0
+                       for _, readChunk := range reads {
+                               read += readChunk
+                               readCount += 1
+                               if len(write) <= len(read) {
+                                       break
+                               }
+                               if len(write) <= dataMaxSize {
+                                       break // atomicity of small writes
+                               }
+                       }
+                       // Compare
+                       if write != read {
+                               t.Errorf("Expected to read %X, got %X", write, read)
+                       }
+                       // Iterate
+                       writes = writes[1:]
+                       reads = reads[readCount:]
+                       if len(writes) == 0 {
+                               break
+                       }
+               }
+       }
+
+       compareWritesReads(fooWrites, barReads)
+       compareWritesReads(barWrites, fooReads)
+
+}
+
+func BenchmarkSecretConnection(b *testing.B) {
+       b.StopTimer()
+       fooSecConn, barSecConn := makeSecretConnPair(b)
+       fooWriteText := cmn.RandStr(dataMaxSize)
+       // Consume reads from bar's reader
+       go func() {
+               readBuffer := make([]byte, dataMaxSize)
+               for {
+                       _, err := barSecConn.Read(readBuffer)
+                       if err == io.EOF {
+                               return
+                       } else if err != nil {
+                               b.Fatalf("Failed to read from barSecConn: %v", err)
+                       }
+               }
+       }()
+
+       b.StartTimer()
+       for i := 0; i < b.N; i++ {
+               _, err := fooSecConn.Write([]byte(fooWriteText))
+               if err != nil {
+                       b.Fatalf("Failed to write to fooSecConn: %v", err)
+               }
+       }
+       b.StopTimer()
+
+       fooSecConn.Close()
+       //barSecConn.Close() race condition
+}
diff --git a/p2p/switch.go b/p2p/switch.go
new file mode 100644 (file)
index 0000000..e4ea24a
--- /dev/null
@@ -0,0 +1,577 @@
+package p2p
+
+import (
+       "errors"
+       "fmt"
+       "math/rand"
+       "net"
+       "time"
+
+       crypto "github.com/tendermint/go-crypto"
+       cfg "github.com/blockchain/config"
+       cmn "github.com/tendermint/tmlibs/common"
+)
+
+const (
+       reconnectAttempts = 30
+       reconnectInterval = 3 * time.Second
+)
+
+type Reactor interface {
+       cmn.Service // Start, Stop
+
+       SetSwitch(*Switch)
+       GetChannels() []*ChannelDescriptor
+       AddPeer(peer *Peer)
+       RemovePeer(peer *Peer, reason interface{})
+       Receive(chID byte, peer *Peer, msgBytes []byte)
+}
+
+//--------------------------------------
+
+type BaseReactor struct {
+       cmn.BaseService // Provides Start, Stop, .Quit
+       Switch          *Switch
+}
+
+func NewBaseReactor(name string, impl Reactor) *BaseReactor {
+       return &BaseReactor{
+               BaseService: *cmn.NewBaseService(nil, name, impl),
+               Switch:      nil,
+       }
+}
+
+func (br *BaseReactor) SetSwitch(sw *Switch) {
+       br.Switch = sw
+}
+func (_ *BaseReactor) GetChannels() []*ChannelDescriptor              { return nil }
+func (_ *BaseReactor) AddPeer(peer *Peer)                             {}
+func (_ *BaseReactor) RemovePeer(peer *Peer, reason interface{})      {}
+func (_ *BaseReactor) Receive(chID byte, peer *Peer, msgBytes []byte) {}
+
+//-----------------------------------------------------------------------------
+
+/*
+The `Switch` handles peer connections and exposes an API to receive incoming messages
+on `Reactors`.  Each `Reactor` is responsible for handling incoming messages of one
+or more `Channels`.  So while sending outgoing messages is typically performed on the peer,
+incoming messages are received on the reactor.
+*/
+type Switch struct {
+       cmn.BaseService
+
+       config       *cfg.P2PConfig
+       peerConfig   *PeerConfig
+       listeners    []Listener
+       reactors     map[string]Reactor
+       chDescs      []*ChannelDescriptor
+       reactorsByCh map[byte]Reactor
+       peers        *PeerSet
+       dialing      *cmn.CMap
+       nodeInfo     *NodeInfo             // our node info
+       nodePrivKey  crypto.PrivKeyEd25519 // our node privkey
+
+       filterConnByAddr   func(net.Addr) error
+       filterConnByPubKey func(crypto.PubKeyEd25519) error
+}
+
+var (
+       ErrSwitchDuplicatePeer = errors.New("Duplicate peer")
+)
+
+func NewSwitch(config *cfg.P2PConfig) *Switch {
+       sw := &Switch{
+               config:       config,
+               peerConfig:   DefaultPeerConfig(),
+               reactors:     make(map[string]Reactor),
+               chDescs:      make([]*ChannelDescriptor, 0),
+               reactorsByCh: make(map[byte]Reactor),
+               peers:        NewPeerSet(),
+               dialing:      cmn.NewCMap(),
+               nodeInfo:     nil,
+       }
+       sw.BaseService = *cmn.NewBaseService(nil, "P2P Switch", sw)
+       return sw
+}
+
+// Not goroutine safe.
+func (sw *Switch) AddReactor(name string, reactor Reactor) Reactor {
+       // Validate the reactor.
+       // No two reactors can share the same channel.
+       reactorChannels := reactor.GetChannels()
+       for _, chDesc := range reactorChannels {
+               chID := chDesc.ID
+               if sw.reactorsByCh[chID] != nil {
+                       cmn.PanicSanity(fmt.Sprintf("Channel %X has multiple reactors %v & %v", chID, sw.reactorsByCh[chID], reactor))
+               }
+               sw.chDescs = append(sw.chDescs, chDesc)
+               sw.reactorsByCh[chID] = reactor
+       }
+       sw.reactors[name] = reactor
+       reactor.SetSwitch(sw)
+       return reactor
+}
+
+// Not goroutine safe.
+func (sw *Switch) Reactors() map[string]Reactor {
+       return sw.reactors
+}
+
+// Not goroutine safe.
+func (sw *Switch) Reactor(name string) Reactor {
+       return sw.reactors[name]
+}
+
+// Not goroutine safe.
+func (sw *Switch) AddListener(l Listener) {
+       sw.listeners = append(sw.listeners, l)
+}
+
+// Not goroutine safe.
+func (sw *Switch) Listeners() []Listener {
+       return sw.listeners
+}
+
+// Not goroutine safe.
+func (sw *Switch) IsListening() bool {
+       return len(sw.listeners) > 0
+}
+
+// Not goroutine safe.
+func (sw *Switch) SetNodeInfo(nodeInfo *NodeInfo) {
+       sw.nodeInfo = nodeInfo
+}
+
+// Not goroutine safe.
+func (sw *Switch) NodeInfo() *NodeInfo {
+       return sw.nodeInfo
+}
+
+// Not goroutine safe.
+// NOTE: Overwrites sw.nodeInfo.PubKey
+func (sw *Switch) SetNodePrivKey(nodePrivKey crypto.PrivKeyEd25519) {
+       sw.nodePrivKey = nodePrivKey
+       if sw.nodeInfo != nil {
+               sw.nodeInfo.PubKey = nodePrivKey.PubKey().Unwrap().(crypto.PubKeyEd25519)
+       }
+}
+
+// Switch.Start() starts all the reactors, peers, and listeners.
+func (sw *Switch) OnStart() error {
+       sw.BaseService.OnStart()
+       // Start reactors
+       for _, reactor := range sw.reactors {
+               _, err := reactor.Start()
+               if err != nil {
+                       return err
+               }
+       }
+       // Start peers
+       for _, peer := range sw.peers.List() {
+               sw.startInitPeer(peer)
+       }
+       // Start listeners
+       for _, listener := range sw.listeners {
+               go sw.listenerRoutine(listener)
+       }
+       return nil
+}
+
+func (sw *Switch) OnStop() {
+       sw.BaseService.OnStop()
+       // Stop listeners
+       for _, listener := range sw.listeners {
+               listener.Stop()
+       }
+       sw.listeners = nil
+       // Stop peers
+       for _, peer := range sw.peers.List() {
+               peer.Stop()
+               sw.peers.Remove(peer)
+       }
+       // Stop reactors
+       for _, reactor := range sw.reactors {
+               reactor.Stop()
+       }
+}
+
+// NOTE: This performs a blocking handshake before the peer is added.
+// CONTRACT: If error is returned, peer is nil, and conn is immediately closed.
+func (sw *Switch) AddPeer(peer *Peer) error {
+       if err := sw.FilterConnByAddr(peer.Addr()); err != nil {
+               return err
+       }
+
+       if err := sw.FilterConnByPubKey(peer.PubKey()); err != nil {
+               return err
+       }
+
+       if err := peer.HandshakeTimeout(sw.nodeInfo, time.Duration(sw.peerConfig.HandshakeTimeout*time.Second)); err != nil {
+               return err
+       }
+
+       // Avoid self
+       if sw.nodeInfo.PubKey.Equals(peer.PubKey().Wrap()) {
+               return errors.New("Ignoring connection from self")
+       }
+
+       // Check version, chain id
+       if err := sw.nodeInfo.CompatibleWith(peer.NodeInfo); err != nil {
+               return err
+       }
+
+       // Check for duplicate peer
+       if sw.peers.Has(peer.Key) {
+               return ErrSwitchDuplicatePeer
+
+       }
+
+       // Start peer
+       if sw.IsRunning() {
+               sw.startInitPeer(peer)
+       }
+
+       // Add the peer to .peers.
+       // We start it first so that a peer in the list is safe to Stop.
+       // It should not err since we already checked peers.Has()
+       if err := sw.peers.Add(peer); err != nil {
+               return err
+       }
+
+       sw.Logger.Info("Added peer", "peer", peer)
+       return nil
+}
+
+func (sw *Switch) FilterConnByAddr(addr net.Addr) error {
+       if sw.filterConnByAddr != nil {
+               return sw.filterConnByAddr(addr)
+       }
+       return nil
+}
+
+func (sw *Switch) FilterConnByPubKey(pubkey crypto.PubKeyEd25519) error {
+       if sw.filterConnByPubKey != nil {
+               return sw.filterConnByPubKey(pubkey)
+       }
+       return nil
+
+}
+
+func (sw *Switch) SetAddrFilter(f func(net.Addr) error) {
+       sw.filterConnByAddr = f
+}
+
+func (sw *Switch) SetPubKeyFilter(f func(crypto.PubKeyEd25519) error) {
+       sw.filterConnByPubKey = f
+}
+
+func (sw *Switch) startInitPeer(peer *Peer) {
+       peer.Start() // spawn send/recv routines
+       for _, reactor := range sw.reactors {
+               reactor.AddPeer(peer)
+       }
+}
+
+// Dial a list of seeds asynchronously in random order
+func (sw *Switch) DialSeeds(addrBook *AddrBook, seeds []string) error {
+
+       netAddrs, err := NewNetAddressStrings(seeds)
+       if err != nil {
+               return err
+       }
+
+       if addrBook != nil {
+               // add seeds to `addrBook`
+               ourAddrS := sw.nodeInfo.ListenAddr
+               ourAddr, _ := NewNetAddressString(ourAddrS)
+               for _, netAddr := range netAddrs {
+                       // do not add ourselves
+                       if netAddr.Equals(ourAddr) {
+                               continue
+                       }
+                       addrBook.AddAddress(netAddr, ourAddr)
+               }
+               addrBook.Save()
+       }
+
+       // permute the list, dial them in random order.
+       perm := rand.Perm(len(netAddrs))
+       for i := 0; i < len(perm); i++ {
+               go func(i int) {
+                       time.Sleep(time.Duration(rand.Int63n(3000)) * time.Millisecond)
+                       j := perm[i]
+                       sw.dialSeed(netAddrs[j])
+               }(i)
+       }
+       return nil
+}
+
+func (sw *Switch) dialSeed(addr *NetAddress) {
+       peer, err := sw.DialPeerWithAddress(addr, true)
+       if err != nil {
+               sw.Logger.Error("Error dialing seed", "error", err)
+       } else {
+               sw.Logger.Info("Connected to seed", "peer", peer)
+       }
+}
+
+func (sw *Switch) DialPeerWithAddress(addr *NetAddress, persistent bool) (*Peer, error) {
+       sw.dialing.Set(addr.IP.String(), addr)
+       defer sw.dialing.Delete(addr.IP.String())
+
+       sw.Logger.Info("Dialing peer", "address", addr)
+       peer, err := newOutboundPeerWithConfig(addr, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey, sw.peerConfig)
+       if err != nil {
+               sw.Logger.Error("Failed to dial peer", "address", addr, "error", err)
+               return nil, err
+       }
+       peer.SetLogger(sw.Logger.With("peer", addr))
+       if persistent {
+               peer.makePersistent()
+       }
+       err = sw.AddPeer(peer)
+       if err != nil {
+               sw.Logger.Error("Failed to add peer", "address", addr, "error", err)
+               peer.CloseConn()
+               return nil, err
+       }
+       sw.Logger.Info("Dialed and added peer", "address", addr, "peer", peer)
+       return peer, nil
+}
+
+func (sw *Switch) IsDialing(addr *NetAddress) bool {
+       return sw.dialing.Has(addr.IP.String())
+}
+
+// Broadcast runs a go routine for each attempted send, which will block
+// trying to send for defaultSendTimeoutSeconds. Returns a channel
+// which receives success values for each attempted send (false if times out)
+// NOTE: Broadcast uses goroutines, so order of broadcast may not be preserved.
+func (sw *Switch) Broadcast(chID byte, msg interface{}) chan bool {
+       successChan := make(chan bool, len(sw.peers.List()))
+       sw.Logger.Debug("Broadcast", "channel", chID, "msg", msg)
+       for _, peer := range sw.peers.List() {
+               go func(peer *Peer) {
+                       success := peer.Send(chID, msg)
+                       successChan <- success
+               }(peer)
+       }
+       return successChan
+}
+
+// Returns the count of outbound/inbound and outbound-dialing peers.
+func (sw *Switch) NumPeers() (outbound, inbound, dialing int) {
+       peers := sw.peers.List()
+       for _, peer := range peers {
+               if peer.outbound {
+                       outbound++
+               } else {
+                       inbound++
+               }
+       }
+       dialing = sw.dialing.Size()
+       return
+}
+
+func (sw *Switch) Peers() IPeerSet {
+       return sw.peers
+}
+
+// Disconnect from a peer due to external error, retry if it is a persistent peer.
+// TODO: make record depending on reason.
+func (sw *Switch) StopPeerForError(peer *Peer, reason interface{}) {
+       addr := NewNetAddress(peer.Addr())
+       sw.Logger.Info("Stopping peer for error", "peer", peer, "error", reason)
+       sw.stopAndRemovePeer(peer, reason)
+
+       if peer.IsPersistent() {
+               go func() {
+                       sw.Logger.Info("Reconnecting to peer", "peer", peer)
+                       for i := 1; i < reconnectAttempts; i++ {
+                               if !sw.IsRunning() {
+                                       return
+                               }
+
+                               peer, err := sw.DialPeerWithAddress(addr, true)
+                               if err != nil {
+                                       if i == reconnectAttempts {
+                                               sw.Logger.Info("Error reconnecting to peer. Giving up", "tries", i, "error", err)
+                                               return
+                                       }
+                                       sw.Logger.Info("Error reconnecting to peer. Trying again", "tries", i, "error", err)
+                                       time.Sleep(reconnectInterval)
+                                       continue
+                               }
+
+                               sw.Logger.Info("Reconnected to peer", "peer", peer)
+                               return
+                       }
+               }()
+       }
+}
+
+// Disconnect from a peer gracefully.
+// TODO: handle graceful disconnects.
+func (sw *Switch) StopPeerGracefully(peer *Peer) {
+       sw.Logger.Info("Stopping peer gracefully")
+       sw.stopAndRemovePeer(peer, nil)
+}
+
+func (sw *Switch) stopAndRemovePeer(peer *Peer, reason interface{}) {
+       sw.peers.Remove(peer)
+       peer.Stop()
+       for _, reactor := range sw.reactors {
+               reactor.RemovePeer(peer, reason)
+       }
+}
+
+func (sw *Switch) listenerRoutine(l Listener) {
+       for {
+               inConn, ok := <-l.Connections()
+               if !ok {
+                       break
+               }
+
+               // ignore connection if we already have enough
+               maxPeers := sw.config.MaxNumPeers
+               if maxPeers <= sw.peers.Size() {
+                       sw.Logger.Info("Ignoring inbound connection: already have enough peers", "address", inConn.RemoteAddr().String(), "numPeers", sw.peers.Size(), "max", maxPeers)
+                       continue
+               }
+
+               // New inbound connection!
+               err := sw.addPeerWithConnectionAndConfig(inConn, sw.peerConfig)
+               if err != nil {
+                       sw.Logger.Info("Ignoring inbound connection: error while adding peer", "address", inConn.RemoteAddr().String(), "error", err)
+                       continue
+               }
+
+               // NOTE: We don't yet have the listening port of the
+               // remote (if they have a listener at all).
+               // The peerHandshake will handle that
+       }
+
+       // cleanup
+}
+
+//-----------------------------------------------------------------------------
+
+type SwitchEventNewPeer struct {
+       Peer *Peer
+}
+
+type SwitchEventDonePeer struct {
+       Peer  *Peer
+       Error interface{}
+}
+
+//------------------------------------------------------------------
+// Switches connected via arbitrary net.Conn; useful for testing
+
+// Returns n switches, connected according to the connect func.
+// If connect==Connect2Switches, the switches will be fully connected.
+// initSwitch defines how the ith switch should be initialized (ie. with what reactors).
+// NOTE: panics if any switch fails to start.
+func MakeConnectedSwitches(cfg *cfg.P2PConfig, n int, initSwitch func(int, *Switch) *Switch, connect func([]*Switch, int, int)) []*Switch {
+       switches := make([]*Switch, n)
+       for i := 0; i < n; i++ {
+               switches[i] = makeSwitch(cfg, i, "testing", "123.123.123", initSwitch)
+       }
+
+       if err := StartSwitches(switches); err != nil {
+               panic(err)
+       }
+
+       for i := 0; i < n; i++ {
+               for j := i; j < n; j++ {
+                       connect(switches, i, j)
+               }
+       }
+
+       return switches
+}
+
+var PanicOnAddPeerErr = false
+
+// Will connect switches i and j via net.Pipe()
+// Blocks until a conection is established.
+// NOTE: caller ensures i and j are within bounds
+func Connect2Switches(switches []*Switch, i, j int) {
+       switchI := switches[i]
+       switchJ := switches[j]
+       c1, c2 := net.Pipe()
+       doneCh := make(chan struct{})
+       go func() {
+               err := switchI.addPeerWithConnection(c1)
+               if PanicOnAddPeerErr && err != nil {
+                       panic(err)
+               }
+               doneCh <- struct{}{}
+       }()
+       go func() {
+               err := switchJ.addPeerWithConnection(c2)
+               if PanicOnAddPeerErr && err != nil {
+                       panic(err)
+               }
+               doneCh <- struct{}{}
+       }()
+       <-doneCh
+       <-doneCh
+}
+
+func StartSwitches(switches []*Switch) error {
+       for _, s := range switches {
+               _, err := s.Start() // start switch and reactors
+               if err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
+func makeSwitch(cfg *cfg.P2PConfig, i int, network, version string, initSwitch func(int, *Switch) *Switch) *Switch {
+       privKey := crypto.GenPrivKeyEd25519()
+       // new switch, add reactors
+       // TODO: let the config be passed in?
+       s := initSwitch(i, NewSwitch(cfg))
+       s.SetNodeInfo(&NodeInfo{
+               PubKey:     privKey.PubKey().Unwrap().(crypto.PubKeyEd25519),
+               Moniker:    cmn.Fmt("switch%d", i),
+               Network:    network,
+               Version:    version,
+               RemoteAddr: cmn.Fmt("%v:%v", network, rand.Intn(64512)+1023),
+               ListenAddr: cmn.Fmt("%v:%v", network, rand.Intn(64512)+1023),
+       })
+       s.SetNodePrivKey(privKey)
+       return s
+}
+
+func (sw *Switch) addPeerWithConnection(conn net.Conn) error {
+       peer, err := newInboundPeer(conn, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey)
+       if err != nil {
+               conn.Close()
+               return err
+       }
+       peer.SetLogger(sw.Logger.With("peer", conn.RemoteAddr()))
+       if err = sw.AddPeer(peer); err != nil {
+               conn.Close()
+               return err
+       }
+
+       return nil
+}
+
+func (sw *Switch) addPeerWithConnectionAndConfig(conn net.Conn, config *PeerConfig) error {
+       peer, err := newInboundPeerWithConfig(conn, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey, config)
+       if err != nil {
+               conn.Close()
+               return err
+       }
+       peer.SetLogger(sw.Logger.With("peer", conn.RemoteAddr()))
+       if err = sw.AddPeer(peer); err != nil {
+               conn.Close()
+               return err
+       }
+
+       return nil
+}
diff --git a/p2p/switch_test.go b/p2p/switch_test.go
new file mode 100644 (file)
index 0000000..e4b13ac
--- /dev/null
@@ -0,0 +1,331 @@
+package p2p
+
+import (
+       "bytes"
+       "fmt"
+       "net"
+       "sync"
+       "testing"
+       "time"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+       crypto "github.com/tendermint/go-crypto"
+       wire "github.com/tendermint/go-wire"
+
+       cfg "github.com/blockchain/config"
+       "github.com/tendermint/tmlibs/log"
+)
+
+var (
+       config *cfg.P2PConfig
+)
+
+func init() {
+       config = cfg.DefaultP2PConfig()
+       config.PexReactor = true
+}
+
+type PeerMessage struct {
+       PeerKey string
+       Bytes   []byte
+       Counter int
+}
+
+type TestReactor struct {
+       BaseReactor
+
+       mtx          sync.Mutex
+       channels     []*ChannelDescriptor
+       peersAdded   []*Peer
+       peersRemoved []*Peer
+       logMessages  bool
+       msgsCounter  int
+       msgsReceived map[byte][]PeerMessage
+}
+
+func NewTestReactor(channels []*ChannelDescriptor, logMessages bool) *TestReactor {
+       tr := &TestReactor{
+               channels:     channels,
+               logMessages:  logMessages,
+               msgsReceived: make(map[byte][]PeerMessage),
+       }
+       tr.BaseReactor = *NewBaseReactor("TestReactor", tr)
+       tr.SetLogger(log.TestingLogger())
+       return tr
+}
+
+func (tr *TestReactor) GetChannels() []*ChannelDescriptor {
+       return tr.channels
+}
+
+func (tr *TestReactor) AddPeer(peer *Peer) {
+       tr.mtx.Lock()
+       defer tr.mtx.Unlock()
+       tr.peersAdded = append(tr.peersAdded, peer)
+}
+
+func (tr *TestReactor) RemovePeer(peer *Peer, reason interface{}) {
+       tr.mtx.Lock()
+       defer tr.mtx.Unlock()
+       tr.peersRemoved = append(tr.peersRemoved, peer)
+}
+
+func (tr *TestReactor) Receive(chID byte, peer *Peer, msgBytes []byte) {
+       if tr.logMessages {
+               tr.mtx.Lock()
+               defer tr.mtx.Unlock()
+               //fmt.Printf("Received: %X, %X\n", chID, msgBytes)
+               tr.msgsReceived[chID] = append(tr.msgsReceived[chID], PeerMessage{peer.Key, msgBytes, tr.msgsCounter})
+               tr.msgsCounter++
+       }
+}
+
+func (tr *TestReactor) getMsgs(chID byte) []PeerMessage {
+       tr.mtx.Lock()
+       defer tr.mtx.Unlock()
+       return tr.msgsReceived[chID]
+}
+
+//-----------------------------------------------------------------------------
+
+// convenience method for creating two switches connected to each other.
+// XXX: note this uses net.Pipe and not a proper TCP conn
+func makeSwitchPair(t testing.TB, initSwitch func(int, *Switch) *Switch) (*Switch, *Switch) {
+       // Create two switches that will be interconnected.
+       switches := MakeConnectedSwitches(config, 2, initSwitch, Connect2Switches)
+       return switches[0], switches[1]
+}
+
+func initSwitchFunc(i int, sw *Switch) *Switch {
+       // Make two reactors of two channels each
+       sw.AddReactor("foo", NewTestReactor([]*ChannelDescriptor{
+               &ChannelDescriptor{ID: byte(0x00), Priority: 10},
+               &ChannelDescriptor{ID: byte(0x01), Priority: 10},
+       }, true))
+       sw.AddReactor("bar", NewTestReactor([]*ChannelDescriptor{
+               &ChannelDescriptor{ID: byte(0x02), Priority: 10},
+               &ChannelDescriptor{ID: byte(0x03), Priority: 10},
+       }, true))
+       return sw
+}
+
+func TestSwitches(t *testing.T) {
+       s1, s2 := makeSwitchPair(t, initSwitchFunc)
+       defer s1.Stop()
+       defer s2.Stop()
+
+       if s1.Peers().Size() != 1 {
+               t.Errorf("Expected exactly 1 peer in s1, got %v", s1.Peers().Size())
+       }
+       if s2.Peers().Size() != 1 {
+               t.Errorf("Expected exactly 1 peer in s2, got %v", s2.Peers().Size())
+       }
+
+       // Lets send some messages
+       ch0Msg := "channel zero"
+       ch1Msg := "channel foo"
+       ch2Msg := "channel bar"
+
+       s1.Broadcast(byte(0x00), ch0Msg)
+       s1.Broadcast(byte(0x01), ch1Msg)
+       s1.Broadcast(byte(0x02), ch2Msg)
+
+       // Wait for things to settle...
+       time.Sleep(5000 * time.Millisecond)
+
+       // Check message on ch0
+       ch0Msgs := s2.Reactor("foo").(*TestReactor).getMsgs(byte(0x00))
+       if len(ch0Msgs) != 1 {
+               t.Errorf("Expected to have received 1 message in ch0")
+       }
+       if !bytes.Equal(ch0Msgs[0].Bytes, wire.BinaryBytes(ch0Msg)) {
+               t.Errorf("Unexpected message bytes. Wanted: %X, Got: %X", wire.BinaryBytes(ch0Msg), ch0Msgs[0].Bytes)
+       }
+
+       // Check message on ch1
+       ch1Msgs := s2.Reactor("foo").(*TestReactor).getMsgs(byte(0x01))
+       if len(ch1Msgs) != 1 {
+               t.Errorf("Expected to have received 1 message in ch1")
+       }
+       if !bytes.Equal(ch1Msgs[0].Bytes, wire.BinaryBytes(ch1Msg)) {
+               t.Errorf("Unexpected message bytes. Wanted: %X, Got: %X", wire.BinaryBytes(ch1Msg), ch1Msgs[0].Bytes)
+       }
+
+       // Check message on ch2
+       ch2Msgs := s2.Reactor("bar").(*TestReactor).getMsgs(byte(0x02))
+       if len(ch2Msgs) != 1 {
+               t.Errorf("Expected to have received 1 message in ch2")
+       }
+       if !bytes.Equal(ch2Msgs[0].Bytes, wire.BinaryBytes(ch2Msg)) {
+               t.Errorf("Unexpected message bytes. Wanted: %X, Got: %X", wire.BinaryBytes(ch2Msg), ch2Msgs[0].Bytes)
+       }
+
+}
+
+func TestConnAddrFilter(t *testing.T) {
+       s1 := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc)
+       s2 := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc)
+
+       c1, c2 := net.Pipe()
+
+       s1.SetAddrFilter(func(addr net.Addr) error {
+               if addr.String() == c1.RemoteAddr().String() {
+                       return fmt.Errorf("Error: pipe is blacklisted")
+               }
+               return nil
+       })
+
+       // connect to good peer
+       go func() {
+               s1.addPeerWithConnection(c1)
+       }()
+       go func() {
+               s2.addPeerWithConnection(c2)
+       }()
+
+       // Wait for things to happen, peers to get added...
+       time.Sleep(100 * time.Millisecond * time.Duration(4))
+
+       defer s1.Stop()
+       defer s2.Stop()
+       if s1.Peers().Size() != 0 {
+               t.Errorf("Expected s1 not to connect to peers, got %d", s1.Peers().Size())
+       }
+       if s2.Peers().Size() != 0 {
+               t.Errorf("Expected s2 not to connect to peers, got %d", s2.Peers().Size())
+       }
+}
+
+func TestConnPubKeyFilter(t *testing.T) {
+       s1 := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc)
+       s2 := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc)
+
+       c1, c2 := net.Pipe()
+
+       // set pubkey filter
+       s1.SetPubKeyFilter(func(pubkey crypto.PubKeyEd25519) error {
+               if bytes.Equal(pubkey.Bytes(), s2.nodeInfo.PubKey.Bytes()) {
+                       return fmt.Errorf("Error: pipe is blacklisted")
+               }
+               return nil
+       })
+
+       // connect to good peer
+       go func() {
+               s1.addPeerWithConnection(c1)
+       }()
+       go func() {
+               s2.addPeerWithConnection(c2)
+       }()
+
+       // Wait for things to happen, peers to get added...
+       time.Sleep(100 * time.Millisecond * time.Duration(4))
+
+       defer s1.Stop()
+       defer s2.Stop()
+       if s1.Peers().Size() != 0 {
+               t.Errorf("Expected s1 not to connect to peers, got %d", s1.Peers().Size())
+       }
+       if s2.Peers().Size() != 0 {
+               t.Errorf("Expected s2 not to connect to peers, got %d", s2.Peers().Size())
+       }
+}
+
+func TestSwitchStopsNonPersistentPeerOnError(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       sw := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc)
+       sw.Start()
+       defer sw.Stop()
+
+       // simulate remote peer
+       rp := &remotePeer{PrivKey: crypto.GenPrivKeyEd25519(), Config: DefaultPeerConfig()}
+       rp.Start()
+       defer rp.Stop()
+
+       peer, err := newOutboundPeer(rp.Addr(), sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey)
+       require.Nil(err)
+       err = sw.AddPeer(peer)
+       require.Nil(err)
+
+       // simulate failure by closing connection
+       peer.CloseConn()
+
+       time.Sleep(100 * time.Millisecond)
+
+       assert.Zero(sw.Peers().Size())
+       assert.False(peer.IsRunning())
+}
+
+func TestSwitchReconnectsToPersistentPeer(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       sw := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc)
+       sw.Start()
+       defer sw.Stop()
+
+       // simulate remote peer
+       rp := &remotePeer{PrivKey: crypto.GenPrivKeyEd25519(), Config: DefaultPeerConfig()}
+       rp.Start()
+       defer rp.Stop()
+
+       peer, err := newOutboundPeer(rp.Addr(), sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey)
+       peer.makePersistent()
+       require.Nil(err)
+       err = sw.AddPeer(peer)
+       require.Nil(err)
+
+       // simulate failure by closing connection
+       peer.CloseConn()
+
+       // TODO: actually detect the disconnection and wait for reconnect
+       time.Sleep(100 * time.Millisecond)
+
+       assert.NotZero(sw.Peers().Size())
+       assert.False(peer.IsRunning())
+}
+
+func BenchmarkSwitches(b *testing.B) {
+       b.StopTimer()
+
+       s1, s2 := makeSwitchPair(b, func(i int, sw *Switch) *Switch {
+               // Make bar reactors of bar channels each
+               sw.AddReactor("foo", NewTestReactor([]*ChannelDescriptor{
+                       &ChannelDescriptor{ID: byte(0x00), Priority: 10},
+                       &ChannelDescriptor{ID: byte(0x01), Priority: 10},
+               }, false))
+               sw.AddReactor("bar", NewTestReactor([]*ChannelDescriptor{
+                       &ChannelDescriptor{ID: byte(0x02), Priority: 10},
+                       &ChannelDescriptor{ID: byte(0x03), Priority: 10},
+               }, false))
+               return sw
+       })
+       defer s1.Stop()
+       defer s2.Stop()
+
+       // Allow time for goroutines to boot up
+       time.Sleep(1000 * time.Millisecond)
+       b.StartTimer()
+
+       numSuccess, numFailure := 0, 0
+
+       // Send random message from foo channel to another
+       for i := 0; i < b.N; i++ {
+               chID := byte(i % 4)
+               successChan := s1.Broadcast(chID, "test data")
+               for s := range successChan {
+                       if s {
+                               numSuccess++
+                       } else {
+                               numFailure++
+                       }
+               }
+       }
+
+       b.Logf("success: %v, failure: %v", numSuccess, numFailure)
+
+       // Allow everything to flush before stopping switches & closing connections.
+       b.StopTimer()
+       time.Sleep(1000 * time.Millisecond)
+}
diff --git a/p2p/types.go b/p2p/types.go
new file mode 100644 (file)
index 0000000..1d3770b
--- /dev/null
@@ -0,0 +1,81 @@
+package p2p
+
+import (
+       "fmt"
+       "net"
+       "strconv"
+       "strings"
+
+       crypto "github.com/tendermint/go-crypto"
+)
+
+const maxNodeInfoSize = 10240 // 10Kb
+
+type NodeInfo struct {
+       PubKey     crypto.PubKeyEd25519 `json:"pub_key"`
+       Moniker    string               `json:"moniker"`
+       Network    string               `json:"network"`
+       RemoteAddr string               `json:"remote_addr"`
+       ListenAddr string               `json:"listen_addr"`
+       Version    string               `json:"version"` // major.minor.revision
+       Other      []string             `json:"other"`   // other application specific data
+}
+
+// CONTRACT: two nodes are compatible if the major/minor versions match and network match
+func (info *NodeInfo) CompatibleWith(other *NodeInfo) error {
+       iMajor, iMinor, _, iErr := splitVersion(info.Version)
+       oMajor, oMinor, _, oErr := splitVersion(other.Version)
+
+       // if our own version number is not formatted right, we messed up
+       if iErr != nil {
+               return iErr
+       }
+
+       // version number must be formatted correctly ("x.x.x")
+       if oErr != nil {
+               return oErr
+       }
+
+       // major version must match
+       if iMajor != oMajor {
+               return fmt.Errorf("Peer is on a different major version. Got %v, expected %v", oMajor, iMajor)
+       }
+
+       // minor version must match
+       if iMinor != oMinor {
+               return fmt.Errorf("Peer is on a different minor version. Got %v, expected %v", oMinor, iMinor)
+       }
+
+       // nodes must be on the same network
+       if info.Network != other.Network {
+               return fmt.Errorf("Peer is on a different network. Got %v, expected %v", other.Network, info.Network)
+       }
+
+       return nil
+}
+
+func (info *NodeInfo) ListenHost() string {
+       host, _, _ := net.SplitHostPort(info.ListenAddr)
+       return host
+}
+
+func (info *NodeInfo) ListenPort() int {
+       _, port, _ := net.SplitHostPort(info.ListenAddr)
+       port_i, err := strconv.Atoi(port)
+       if err != nil {
+               return -1
+       }
+       return port_i
+}
+
+func (info NodeInfo) String() string {
+       return fmt.Sprintf("NodeInfo{pk: %v, moniker: %v, network: %v [remote %v, listen %v], version: %v (%v)}", info.PubKey, info.Moniker, info.Network, info.RemoteAddr, info.ListenAddr, info.Version, info.Other)
+}
+
+func splitVersion(version string) (string, string, string, error) {
+       spl := strings.Split(version, ".")
+       if len(spl) != 3 {
+               return "", "", "", fmt.Errorf("Invalid version format %v", version)
+       }
+       return spl[0], spl[1], spl[2], nil
+}
diff --git a/p2p/upnp/README.md b/p2p/upnp/README.md
new file mode 100644 (file)
index 0000000..557d05b
--- /dev/null
@@ -0,0 +1,5 @@
+# `tendermint/p2p/upnp`
+
+## Resources
+
+* http://www.upnp-hacks.org/upnp.html
diff --git a/p2p/upnp/probe.go b/p2p/upnp/probe.go
new file mode 100644 (file)
index 0000000..3537e1c
--- /dev/null
@@ -0,0 +1,112 @@
+package upnp
+
+import (
+       "errors"
+       "fmt"
+       "net"
+       "time"
+
+       cmn "github.com/tendermint/tmlibs/common"
+       "github.com/tendermint/tmlibs/log"
+)
+
+type UPNPCapabilities struct {
+       PortMapping bool
+       Hairpin     bool
+}
+
+func makeUPNPListener(intPort int, extPort int, logger log.Logger) (NAT, net.Listener, net.IP, error) {
+       nat, err := Discover()
+       if err != nil {
+               return nil, nil, nil, errors.New(fmt.Sprintf("NAT upnp could not be discovered: %v", err))
+       }
+       logger.Info(cmn.Fmt("ourIP: %v", nat.(*upnpNAT).ourIP))
+
+       ext, err := nat.GetExternalAddress()
+       if err != nil {
+               return nat, nil, nil, errors.New(fmt.Sprintf("External address error: %v", err))
+       }
+       logger.Info(cmn.Fmt("External address: %v", ext))
+
+       port, err := nat.AddPortMapping("tcp", extPort, intPort, "Tendermint UPnP Probe", 0)
+       if err != nil {
+               return nat, nil, ext, errors.New(fmt.Sprintf("Port mapping error: %v", err))
+       }
+       logger.Info(cmn.Fmt("Port mapping mapped: %v", port))
+
+       // also run the listener, open for all remote addresses.
+       listener, err := net.Listen("tcp", fmt.Sprintf(":%v", intPort))
+       if err != nil {
+               return nat, nil, ext, errors.New(fmt.Sprintf("Error establishing listener: %v", err))
+       }
+       return nat, listener, ext, nil
+}
+
+func testHairpin(listener net.Listener, extAddr string, logger log.Logger) (supportsHairpin bool) {
+       // Listener
+       go func() {
+               inConn, err := listener.Accept()
+               if err != nil {
+                       logger.Info(cmn.Fmt("Listener.Accept() error: %v", err))
+                       return
+               }
+               logger.Info(cmn.Fmt("Accepted incoming connection: %v -> %v", inConn.LocalAddr(), inConn.RemoteAddr()))
+               buf := make([]byte, 1024)
+               n, err := inConn.Read(buf)
+               if err != nil {
+                       logger.Info(cmn.Fmt("Incoming connection read error: %v", err))
+                       return
+               }
+               logger.Info(cmn.Fmt("Incoming connection read %v bytes: %X", n, buf))
+               if string(buf) == "test data" {
+                       supportsHairpin = true
+                       return
+               }
+       }()
+
+       // Establish outgoing
+       outConn, err := net.Dial("tcp", extAddr)
+       if err != nil {
+               logger.Info(cmn.Fmt("Outgoing connection dial error: %v", err))
+               return
+       }
+
+       n, err := outConn.Write([]byte("test data"))
+       if err != nil {
+               logger.Info(cmn.Fmt("Outgoing connection write error: %v", err))
+               return
+       }
+       logger.Info(cmn.Fmt("Outgoing connection wrote %v bytes", n))
+
+       // Wait for data receipt
+       time.Sleep(1 * time.Second)
+       return
+}
+
+func Probe(logger log.Logger) (caps UPNPCapabilities, err error) {
+       logger.Info("Probing for UPnP!")
+
+       intPort, extPort := 8001, 8001
+
+       nat, listener, ext, err := makeUPNPListener(intPort, extPort, logger)
+       if err != nil {
+               return
+       }
+       caps.PortMapping = true
+
+       // Deferred cleanup
+       defer func() {
+               err = nat.DeletePortMapping("tcp", intPort, extPort)
+               if err != nil {
+                       logger.Error(cmn.Fmt("Port mapping delete error: %v", err))
+               }
+               listener.Close()
+       }()
+
+       supportsHairpin := testHairpin(listener, fmt.Sprintf("%v:%v", ext, extPort), logger)
+       if supportsHairpin {
+               caps.Hairpin = true
+       }
+
+       return
+}
diff --git a/p2p/upnp/upnp.go b/p2p/upnp/upnp.go
new file mode 100644 (file)
index 0000000..3d6c550
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+Taken from taipei-torrent
+
+Just enough UPnP to be able to forward ports
+*/
+package upnp
+
+// BUG(jae): TODO: use syscalls to get actual ourIP. http://pastebin.com/9exZG4rh
+
+import (
+       "bytes"
+       "encoding/xml"
+       "errors"
+       "io/ioutil"
+       "net"
+       "net/http"
+       "strconv"
+       "strings"
+       "time"
+)
+
+type upnpNAT struct {
+       serviceURL string
+       ourIP      string
+       urnDomain  string
+}
+
+// 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)
+}
+
+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(3 * time.Second))
+       if err != nil {
+               return
+       }
+
+       st := "InternetGatewayDevice:1"
+
+       buf := bytes.NewBufferString(
+               "M-SEARCH * HTTP/1.1\r\n" +
+                       "HOST: 239.255.255.250:1900\r\n" +
+                       "ST: ssdp:all\r\n" +
+                       "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)
+               for {
+                       n, _, err = socket.ReadFromUDP(answerBytes)
+                       if err != nil {
+                               break
+                       }
+                       answer := string(answerBytes[0:n])
+                       if strings.Index(answer, 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 := strings.TrimSpace(loc[0:endIndex])
+                       var serviceURL, urnDomain string
+                       serviceURL, urnDomain, err = getServiceURL(locURL)
+                       if err != nil {
+                               return
+                       }
+                       var ourIP net.IP
+                       ourIP, err = localIPv4()
+                       if err != nil {
+                               return
+                       }
+                       nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP.String(), urnDomain: urnDomain}
+                       return
+               }
+       }
+       err = errors.New("UPnP port discovery failed.")
+       return
+}
+
+type Envelope struct {
+       XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
+       Soap    *SoapBody
+}
+type SoapBody struct {
+       XMLName    xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
+       ExternalIP *ExternalIPAddressResponse
+}
+
+type ExternalIPAddressResponse struct {
+       XMLName   xml.Name `xml:"GetExternalIPAddressResponse"`
+       IPAddress string   `xml:"NewExternalIPAddress"`
+}
+
+type ExternalIPAddress struct {
+       XMLName xml.Name `xml:"NewExternalIPAddress"`
+       IP      string
+}
+
+type UPNPService struct {
+       ServiceType string `xml:"serviceType"`
+       ControlURL  string `xml:"controlURL"`
+}
+
+type DeviceList struct {
+       Device []Device `xml:"device"`
+}
+
+type ServiceList struct {
+       Service []UPNPService `xml:"service"`
+}
+
+type Device struct {
+       XMLName     xml.Name    `xml:"device"`
+       DeviceType  string      `xml:"deviceType"`
+       DeviceList  DeviceList  `xml:"deviceList"`
+       ServiceList ServiceList `xml:"serviceList"`
+}
+
+type Root struct {
+       Device Device
+}
+
+func getChildDevice(d *Device, deviceType string) *Device {
+       dl := d.DeviceList.Device
+       for i := 0; i < len(dl); i++ {
+               if strings.Index(dl[i].DeviceType, deviceType) >= 0 {
+                       return &dl[i]
+               }
+       }
+       return nil
+}
+
+func getChildService(d *Device, serviceType string) *UPNPService {
+       sl := d.ServiceList.Service
+       for i := 0; i < len(sl); i++ {
+               if strings.Index(sl[i].ServiceType, serviceType) >= 0 {
+                       return &sl[i]
+               }
+       }
+       return nil
+}
+
+func localIPv4() (net.IP, error) {
+       tt, err := net.Interfaces()
+       if err != nil {
+               return nil, err
+       }
+       for _, t := range tt {
+               aa, err := t.Addrs()
+               if err != nil {
+                       return nil, err
+               }
+               for _, a := range aa {
+                       ipnet, ok := a.(*net.IPNet)
+                       if !ok {
+                               continue
+                       }
+                       v4 := ipnet.IP.To4()
+                       if v4 == nil || v4[0] == 127 { // loopback address
+                               continue
+                       }
+                       return v4, nil
+               }
+       }
+       return nil, errors.New("cannot find local IP address")
+}
+
+func getServiceURL(rootURL string) (url, urnDomain 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 strings.Index(a.DeviceType, "InternetGatewayDevice:1") < 0 {
+               err = errors.New("No InternetGatewayDevice")
+               return
+       }
+       b := getChildDevice(a, "WANDevice:1")
+       if b == nil {
+               err = errors.New("No WANDevice")
+               return
+       }
+       c := getChildDevice(b, "WANConnectionDevice:1")
+       if c == nil {
+               err = errors.New("No WANConnectionDevice")
+               return
+       }
+       d := getChildService(c, "WANIPConnection:1")
+       if d == nil {
+               // Some routers don't follow the UPnP spec, and put WanIPConnection under WanDevice,
+               // instead of under WanConnectionDevice
+               d = getChildService(b, "WANIPConnection:1")
+
+               if d == nil {
+                       err = errors.New("No WANIPConnection")
+                       return
+               }
+       }
+       // Extract the domain name, which isn't always 'schemas-upnp-org'
+       urnDomain = strings.Split(d.ServiceType, ":")[1]
+       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, domain 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:"+domain+":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)
+
+       r, err = http.DefaultClient.Do(req)
+       if err != nil {
+               return nil, err
+       }
+       /*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) getExternalIPAddress() (info statusInfo, err error) {
+
+       message := "<u:GetExternalIPAddress xmlns:u=\"urn:" + n.urnDomain + ":service:WANIPConnection:1\">\r\n" +
+               "</u:GetExternalIPAddress>"
+
+       var response *http.Response
+       response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)
+       if response != nil {
+               defer response.Body.Close()
+       }
+       if err != nil {
+               return
+       }
+       var envelope Envelope
+       data, err := ioutil.ReadAll(response.Body)
+       reader := bytes.NewReader(data)
+       xml.NewDecoder(reader).Decode(&envelope)
+
+       info = statusInfo{envelope.Soap.ExternalIP.IPAddress}
+
+       if err != nil {
+               return
+       }
+
+       return
+}
+
+func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
+       info, err := n.getExternalIPAddress()
+       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:" + n.urnDomain + ":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, n.urnDomain)
+       if response != nil {
+               defer response.Body.Close()
+       }
+       if err != nil {
+               return
+       }
+
+       // TODO: check response to see if the port was forwarded
+       // log.Println(message, response)
+       // JAE:
+       // body, err := ioutil.ReadAll(response.Body)
+       // fmt.Println(string(body), err)
+       mappedExternalPort = externalPort
+       _ = response
+       return
+}
+
+func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
+
+       message := "<u:DeletePortMapping xmlns:u=\"urn:" + n.urnDomain + ":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, n.urnDomain)
+       if response != nil {
+               defer response.Body.Close()
+       }
+       if err != nil {
+               return
+       }
+
+       // TODO: check response to see if the port was deleted
+       // log.Println(message, response)
+       _ = response
+       return
+}
diff --git a/p2p/util.go b/p2p/util.go
new file mode 100644 (file)
index 0000000..2be3202
--- /dev/null
@@ -0,0 +1,15 @@
+package p2p
+
+import (
+       "crypto/sha256"
+)
+
+// doubleSha256 calculates sha256(sha256(b)) and returns the resulting bytes.
+func doubleSha256(b []byte) []byte {
+       hasher := sha256.New()
+       hasher.Write(b)
+       sum := hasher.Sum(nil)
+       hasher.Reset()
+       hasher.Write(sum)
+       return hasher.Sum(nil)
+}
diff --git a/p2p/version.go b/p2p/version.go
new file mode 100644 (file)
index 0000000..9a4c7bb
--- /dev/null
@@ -0,0 +1,3 @@
+package p2p
+
+const Version = "0.5.0"
index 97496f6..876ad82 100644 (file)
@@ -5,8 +5,8 @@ import (
        "errors"
        "io"
 
-       "chain/crypto/sha3pool"
-       "chain/encoding/blockchain"
+       "github.com/blockchain/crypto/sha3pool"
+       "github.com/blockchain/encoding/blockchain"
 )
 
 // AssetID is the Hash256 of the asset definition.
index 80bb2f7..b402f57 100644 (file)
@@ -9,12 +9,12 @@ import (
 
        "golang.org/x/crypto/sha3"
 
-       "chain/crypto/ed25519/chainkd"
-       "chain/protocol/bc"
-       "chain/protocol/bc/legacy"
-       "chain/protocol/vm"
-       "chain/protocol/vm/vmutil"
-       "chain/testutil"
+       "github.com/blockchain/crypto/ed25519/chainkd"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/vm"
+       "github.com/blockchain/protocol/vm/vmutil"
+       "github.com/blockchain/testutil"
 )
 
 // NewIssuanceTx creates a new signed, issuance transaction issuing 100 units
index a7d809d..49287e7 100644 (file)
@@ -7,9 +7,9 @@ import (
 
        "github.com/golang/protobuf/proto"
 
-       "chain/crypto/sha3pool"
-       "chain/encoding/blockchain"
-       "chain/errors"
+       "github.com/blockchain/crypto/sha3pool"
+       "github.com/blockchain/encoding/blockchain"
+       "github.com/blockchain/errors"
 )
 
 // Entry is the interface implemented by each addressable unit in a
index a69875c..b69870f 100644 (file)
@@ -5,7 +5,7 @@ import (
        "io"
        "testing"
 
-       "chain/protocol/bc"
+       "github.com/blockchain/protocol/bc"
 )
 
 func serialize(t *testing.T, wt io.WriterTo) []byte {
index 57ce185..58bf088 100644 (file)
@@ -7,9 +7,9 @@ import (
        "fmt"
        "io"
 
-       "chain/encoding/blockchain"
-       "chain/encoding/bufpool"
-       "chain/errors"
+       "github.com/blockchain/encoding/blockchain"
+       "github.com/blockchain/encoding/bufpool"
+       "github.com/blockchain/errors"
 )
 
 const (
index 846b4bb..a28854c 100644 (file)
@@ -3,8 +3,8 @@ package legacy
 import (
        "io"
 
-       "chain/encoding/blockchain"
-       "chain/protocol/bc"
+       "github.com/blockchain/encoding/blockchain"
+       "github.com/blockchain/protocol/bc"
 )
 
 type BlockCommitment struct {
index 54c2c36..3aa16e3 100644 (file)
@@ -8,10 +8,10 @@ import (
        "io"
        "time"
 
-       "chain/encoding/blockchain"
-       "chain/encoding/bufpool"
-       "chain/errors"
-       "chain/protocol/bc"
+       "github.com/blockchain/encoding/blockchain"
+       "github.com/blockchain/encoding/bufpool"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/bc"
 )
 
 // BlockHeader describes necessary data of the block.
index b14a409..1e5c14d 100644 (file)
@@ -9,8 +9,8 @@ import (
 
        "github.com/davecgh/go-spew/spew"
 
-       "chain/protocol/bc"
-       "chain/testutil"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/testutil"
 )
 
 func TestMarshalBlock(t *testing.T) {
index 5ee6d30..41ab115 100644 (file)
@@ -3,7 +3,7 @@ package legacy
 import (
        "io"
 
-       "chain/encoding/blockchain"
+       "github.com/blockchain/encoding/blockchain"
 )
 
 type BlockWitness struct {
index 6a75573..c337562 100644 (file)
@@ -1,8 +1,8 @@
 package legacy
 
 import (
-       "chain/crypto/sha3pool"
-       "chain/protocol/bc"
+       "github.com/blockchain/crypto/sha3pool"
+       "github.com/blockchain/protocol/bc"
 )
 
 type IssuanceInput struct {
index a876e30..bcaf098 100644 (file)
@@ -1,6 +1,6 @@
 package legacy
 
-import "chain/protocol/bc"
+import "github.com/blockchain/protocol/bc"
 
 type IssuanceWitness struct {
        InitialBlock    bc.Hash
index c20e80a..d9bbb54 100644 (file)
@@ -1,10 +1,10 @@
 package legacy
 
 import (
-       "chain/crypto/sha3pool"
-       "chain/protocol/bc"
-       "chain/protocol/vm"
-       "chain/protocol/vm/vmutil"
+       "github.com/blockchain/crypto/sha3pool"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/vm"
+       "github.com/blockchain/protocol/vm/vmutil"
 )
 
 // MapTx converts a legacy TxData object into its entries-based
index 895ed31..c93895e 100644 (file)
@@ -6,7 +6,7 @@ import (
 
        "github.com/davecgh/go-spew/spew"
 
-       "chain/protocol/bc"
+       "github.com/blockchain/protocol/bc"
 )
 
 func TestMapTx(t *testing.T) {
index 8efe6e0..ca74075 100644 (file)
@@ -4,10 +4,10 @@ import (
        "fmt"
        "io"
 
-       "chain/crypto/sha3pool"
-       "chain/encoding/blockchain"
-       "chain/errors"
-       "chain/protocol/bc"
+       "github.com/blockchain/crypto/sha3pool"
+       "github.com/blockchain/encoding/blockchain"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/bc"
 )
 
 // OutputCommitment contains the commitment data for a transaction
index 00f3ec6..b3c1409 100644 (file)
@@ -4,10 +4,10 @@ import (
        "fmt"
        "io"
 
-       "chain/crypto/sha3pool"
-       "chain/encoding/blockchain"
-       "chain/errors"
-       "chain/protocol/bc"
+       "github.com/blockchain/crypto/sha3pool"
+       "github.com/blockchain/encoding/blockchain"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/bc"
 )
 
 // SpendInput satisfies the TypedInput interface and represents a spend transaction.
index 843ac49..276a8e5 100644 (file)
@@ -6,10 +6,10 @@ import (
        "fmt"
        "io"
 
-       "chain/crypto/sha3pool"
-       "chain/encoding/blockchain"
-       "chain/errors"
-       "chain/protocol/bc"
+       "github.com/blockchain/crypto/sha3pool"
+       "github.com/blockchain/encoding/blockchain"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/bc"
 )
 
 // CurrentTransactionVersion is the current latest
index 505d073..31228a9 100644 (file)
@@ -10,9 +10,9 @@ import (
 
        "github.com/davecgh/go-spew/spew"
 
-       "chain/errors"
-       "chain/protocol/bc"
-       "chain/testutil"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/testutil"
 )
 
 func TestTransactionTrailingGarbage(t *testing.T) {
index 957c0ae..b03ed31 100644 (file)
@@ -3,7 +3,7 @@ package legacy
 import (
        "testing"
 
-       "chain/protocol/bc"
+       "github.com/blockchain/protocol/bc"
 
        "github.com/davecgh/go-spew/spew"
 )
index 4b62125..0bd0b04 100644 (file)
@@ -4,9 +4,9 @@ import (
        "fmt"
        "io"
 
-       "chain/encoding/blockchain"
-       "chain/errors"
-       "chain/protocol/bc"
+       "github.com/blockchain/encoding/blockchain"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/bc"
 )
 
 type (
index 1b73074..8661eea 100644 (file)
@@ -3,9 +3,9 @@ package legacy
 import (
        "io"
 
-       "chain/encoding/blockchain"
-       "chain/errors"
-       "chain/protocol/bc"
+       "github.com/blockchain/encoding/blockchain"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/bc"
 )
 
 // TODO(bobg): Review serialization/deserialization logic for
index c646a30..a7d6ba1 100644 (file)
@@ -3,7 +3,7 @@ package bc
 import (
        "math"
 
-       "chain/crypto/sha3pool"
+       "github.com/blockchain/crypto/sha3pool"
 )
 
 var (
index 584abce..a7f36ee 100644 (file)
@@ -4,9 +4,9 @@ import (
        "testing"
        "time"
 
-       . "chain/protocol/bc"
-       "chain/protocol/bc/legacy"
-       "chain/protocol/vm"
+       . "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/vm"
 )
 
 func TestMerkleRoot(t *testing.T) {
index 27dbaa2..fc1a585 100644 (file)
@@ -1,8 +1,8 @@
 package bc
 
 import (
-       "chain/crypto/sha3pool"
-       "chain/errors"
+       "github.com/blockchain/crypto/sha3pool"
+       "github.com/blockchain/errors"
 )
 
 // Tx is a wrapper for the entries-based representation of a transaction.
index 6b9a0fc..f0655d0 100644 (file)
@@ -5,14 +5,14 @@ import (
        "fmt"
        "time"
 
-       "chain/crypto/ed25519"
-       "chain/errors"
-       "chain/log"
-       "chain/protocol/bc"
-       "chain/protocol/bc/legacy"
-       "chain/protocol/state"
-       "chain/protocol/validation"
-       "chain/protocol/vm/vmutil"
+       "github.com/blockchain/crypto/ed25519"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/log"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/state"
+       "github.com/blockchain/protocol/validation"
+       "github.com/blockchain/protocol/vm/vmutil"
 )
 
 // maxBlockTxs limits the number of transactions
index 05493cf..28dddf0 100644 (file)
@@ -6,11 +6,11 @@ import (
        "testing"
        "time"
 
-       "chain/protocol/bc"
-       "chain/protocol/bc/legacy"
-       "chain/protocol/prottest/memstore"
-       "chain/protocol/state"
-       "chain/testutil"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/prottest/memstore"
+       "github.com/blockchain/protocol/state"
+       "github.com/blockchain/testutil"
 )
 
 func TestGetBlock(t *testing.T) {
index b99297b..4b4e790 100644 (file)
@@ -22,9 +22,9 @@ package patricia
 import (
        "bytes"
 
-       "chain/crypto/sha3pool"
-       "chain/errors"
-       "chain/protocol/bc"
+       "github.com/blockchain/crypto/sha3pool"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/bc"
 )
 
 var (
index fc15b87..d8e3019 100644 (file)
@@ -11,8 +11,8 @@ import (
 
        "golang.org/x/crypto/sha3"
 
-       "chain/protocol/bc"
-       "chain/testutil"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/testutil"
 )
 
 func BenchmarkInserts(b *testing.B) {
index c03fa2c..82c28da 100644 (file)
@@ -47,11 +47,11 @@ import (
 
        "github.com/golang/groupcache/lru"
 
-       "chain/errors"
-       "chain/log"
-       "chain/protocol/bc"
-       "chain/protocol/bc/legacy"
-       "chain/protocol/state"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/log"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/state"
 )
 
 // maxCachedValidatedTxs is the max number of validated txs to cache.
index 0a5900e..86ca647 100644 (file)
@@ -6,13 +6,13 @@ import (
        "testing"
        "time"
 
-       "chain/crypto/ed25519"
-       "chain/protocol"
-       "chain/protocol/bc"
-       "chain/protocol/bc/legacy"
-       "chain/protocol/prottest/memstore"
-       "chain/protocol/state"
-       "chain/testutil"
+       "github.com/blockchain/crypto/ed25519"
+       "github.com/blockchain/protocol"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/prottest/memstore"
+       "github.com/blockchain/protocol/state"
+       "github.com/blockchain/testutil"
 )
 
 var (
index 6f91f92..0946192 100644 (file)
@@ -9,8 +9,8 @@ import (
        "fmt"
        "sync"
 
-       "chain/protocol/bc/legacy"
-       "chain/protocol/state"
+       "github.com/blockchain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/state"
 )
 
 // MemStore satisfies the Store interface.
index 6d9aec7..618b9fb 100644 (file)
@@ -4,9 +4,9 @@ import (
        "context"
        "fmt"
 
-       "chain/errors"
-       "chain/protocol/bc/legacy"
-       "chain/protocol/state"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/state"
 )
 
 // Recover performs crash recovery, restoring the blockchain
index 7e51166..a82ad3e 100644 (file)
@@ -6,11 +6,11 @@ import (
        "testing"
        "time"
 
-       "chain/protocol/bc"
-       "chain/protocol/bc/legacy"
-       "chain/protocol/prottest/memstore"
-       "chain/protocol/state"
-       "chain/testutil"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/prottest/memstore"
+       "github.com/blockchain/protocol/state"
+       "github.com/blockchain/testutil"
 )
 
 func TestRecoverSnapshotNoAdditionalBlocks(t *testing.T) {
index ed6f1df..73d70d3 100644 (file)
@@ -3,9 +3,9 @@ package state
 import (
        "fmt"
 
-       "chain/errors"
-       "chain/protocol/bc"
-       "chain/protocol/patricia"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/patricia"
 )
 
 // Snapshot encompasses a snapshot of entire blockchain state. It
index aba6740..81f0fdc 100644 (file)
@@ -5,9 +5,9 @@ import (
        "testing"
        "time"
 
-       "chain/protocol/bc"
-       "chain/protocol/bc/bctest"
-       "chain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/bc/bctest"
+       "github.com/blockchain/protocol/bc/legacy"
 )
 
 func TestApplyTxSpend(t *testing.T) {
index 8743a9f..a91e0a8 100644 (file)
@@ -5,9 +5,9 @@ import (
 
        "github.com/golang/groupcache/lru"
 
-       "chain/errors"
-       "chain/protocol/bc"
-       "chain/protocol/validation"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/validation"
 )
 
 // ErrBadTx is returned for transactions failing validation
index 4a60625..46743f6 100644 (file)
@@ -8,13 +8,13 @@ import (
 
        "golang.org/x/crypto/sha3"
 
-       "chain/crypto/ed25519"
-       "chain/protocol/bc"
-       "chain/protocol/bc/legacy"
-       "chain/protocol/state"
-       "chain/protocol/vm"
-       "chain/protocol/vm/vmutil"
-       "chain/testutil"
+       "github.com/blockchain/crypto/ed25519"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/state"
+       "github.com/blockchain/protocol/vm"
+       "github.com/blockchain/protocol/vm/vmutil"
+       "github.com/blockchain/testutil"
 )
 
 func TestBadMaxIssuanceWindow(t *testing.T) {
index 1cbfbb7..f287f43 100644 (file)
@@ -4,10 +4,10 @@ import (
        "testing"
        "time"
 
-       "chain/protocol/bc"
-       "chain/protocol/bc/legacy"
-       "chain/protocol/vm"
-       "chain/protocol/vm/vmutil"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/vm"
+       "github.com/blockchain/protocol/vm/vmutil"
 )
 
 func TestValidateBlock1(t *testing.T) {
index b489f8b..931c8ac 100644 (file)
@@ -3,8 +3,8 @@ package validation
 import (
        "testing"
 
-       "chain/protocol/bc"
-       "chain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/bc/legacy"
 )
 
 func TestFuzzAssetIdNilPointer(t *testing.T) {
index 4073b20..c5e1323 100644 (file)
@@ -3,10 +3,10 @@ package validation
 import (
        "fmt"
 
-       "chain/errors"
-       "chain/math/checked"
-       "chain/protocol/bc"
-       "chain/protocol/vm"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/math/checked"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/vm"
 )
 
 // validationState contains the context that must propagate through
index 0fa728f..a9d2c94 100644 (file)
@@ -6,13 +6,13 @@ import (
        "testing"
        "time"
 
-       "chain/crypto/sha3pool"
-       "chain/errors"
-       "chain/protocol/bc"
-       "chain/protocol/bc/bctest"
-       "chain/protocol/bc/legacy"
-       "chain/protocol/vm"
-       "chain/testutil"
+       "github.com/blockchain/crypto/sha3pool"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/bc/bctest"
+       "github.com/blockchain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/vm"
+       "github.com/blockchain/testutil"
 
        "github.com/davecgh/go-spew/spew"
        "github.com/golang/protobuf/proto"
index 8b36112..3654f7a 100644 (file)
@@ -3,10 +3,10 @@ package validation
 import (
        "bytes"
 
-       "chain/crypto/sha3pool"
-       "chain/errors"
-       "chain/protocol/bc"
-       "chain/protocol/vm"
+       "github.com/blockchain/crypto/sha3pool"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/vm"
 )
 
 func newBlockVMContext(block *bc.Block, prog []byte, args [][]byte) *vm.Context {
index f5f8389..999a8c8 100644 (file)
@@ -5,10 +5,10 @@ import (
        "fmt"
        "testing"
 
-       "chain/errors"
-       "chain/protocol/bc"
-       "chain/protocol/bc/legacy"
-       "chain/protocol/vm"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/bc"
+       "github.com/blockchain/protocol/bc/legacy"
+       "github.com/blockchain/protocol/vm"
 )
 
 func TestCheckOutput(t *testing.T) {
index 8684c05..7e1ba03 100644 (file)
@@ -10,7 +10,7 @@ import (
        "strings"
        "unicode"
 
-       "chain/errors"
+       "github.com/blockchain/errors"
 )
 
 // Assemble converts a string like "2 3 ADD 5 NUMEQUAL" into 0x525393559c.
index 8ca0864..f507998 100644 (file)
@@ -5,7 +5,7 @@ import (
        "encoding/hex"
        "testing"
 
-       "chain/errors"
+       "github.com/blockchain/errors"
 )
 
 func TestAssemble(t *testing.T) {
index 7516b54..5ebef9f 100644 (file)
@@ -3,7 +3,7 @@ package vm
 import (
        "testing"
 
-       "chain/testutil"
+       "github.com/blockchain/testutil"
 )
 
 func TestBitwiseOps(t *testing.T) {
index d6ef8ea..81fe931 100644 (file)
@@ -3,7 +3,7 @@ package vm
 import (
        "testing"
 
-       "chain/testutil"
+       "github.com/blockchain/testutil"
 )
 
 func TestControlOps(t *testing.T) {
index 1346dbc..9825a7c 100644 (file)
@@ -6,8 +6,8 @@ import (
 
        "golang.org/x/crypto/sha3"
 
-       "chain/crypto/ed25519"
-       "chain/math/checked"
+       "github.com/blockchain/crypto/ed25519"
+       "github.com/blockchain/math/checked"
 )
 
 func opSha256(vm *virtualMachine) error {
index c9efc78..cccdde9 100644 (file)
@@ -3,7 +3,7 @@ package vm
 import (
        "testing"
 
-       "chain/testutil"
+       "github.com/blockchain/testutil"
 )
 
 var emptyBlockVMContext = &Context{
index c7a86fd..b041905 100644 (file)
@@ -5,8 +5,8 @@ import (
 
        "github.com/davecgh/go-spew/spew"
 
-       "chain/errors"
-       "chain/testutil"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/testutil"
 )
 
 func TestNextProgram(t *testing.T) {
index a478b33..8a81d53 100644 (file)
@@ -3,7 +3,7 @@ package vm
 import (
        "math"
 
-       "chain/math/checked"
+       "github.com/blockchain/math/checked"
 )
 
 func op1Add(vm *virtualMachine) error {
index 04b2d85..3eec739 100644 (file)
@@ -5,7 +5,7 @@ import (
        "math"
        "testing"
 
-       "chain/testutil"
+       "github.com/blockchain/testutil"
 )
 
 func TestNumericOps(t *testing.T) {
index 13e9e04..a351a53 100644 (file)
@@ -5,8 +5,8 @@ import (
        "fmt"
        "math"
 
-       "chain/errors"
-       "chain/math/checked"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/math/checked"
 )
 
 type Op uint8
index 50f7827..0ff64cc 100644 (file)
@@ -4,9 +4,9 @@ import (
        "fmt"
        "testing"
 
-       "chain/errors"
-       "chain/math/checked"
-       "chain/testutil"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/math/checked"
+       "github.com/blockchain/testutil"
 )
 
 func TestParseOp(t *testing.T) {
index 1f5a034..9602301 100644 (file)
@@ -4,7 +4,7 @@ import (
        "bytes"
        "testing"
 
-       "chain/testutil"
+       "github.com/blockchain/testutil"
 )
 
 func TestPushdataOps(t *testing.T) {
index 976acdb..7b97532 100644 (file)
@@ -1,6 +1,6 @@
 package vm
 
-import "chain/math/checked"
+import "github.com/blockchain/math/checked"
 
 func opCat(vm *virtualMachine) error {
        err := vm.applyCost(4)
index 5758be1..05e9e47 100644 (file)
@@ -3,7 +3,7 @@ package vm
 import (
        "testing"
 
-       "chain/testutil"
+       "github.com/blockchain/testutil"
 )
 
 func TestSpliceOps(t *testing.T) {
index 47e2ba8..9a72d8b 100644 (file)
@@ -1,6 +1,6 @@
 package vm
 
-import "chain/math/checked"
+import "github.com/blockchain/math/checked"
 
 func opToAltStack(vm *virtualMachine) error {
        err := vm.applyCost(2)
index 148efb1..9fffe60 100644 (file)
@@ -7,7 +7,7 @@ import (
        "strings"
        "testing"
 
-       "chain/testutil"
+       "github.com/blockchain/testutil"
 )
 
 func TestStackOps(t *testing.T) {
index dc2d523..3c8d057 100644 (file)
@@ -6,7 +6,7 @@ import (
        "io"
        "strings"
 
-       "chain/errors"
+       "github.com/blockchain/errors"
 )
 
 const initialRunLimit = 10000
index afe2cb5..26de7c9 100644 (file)
@@ -8,8 +8,8 @@ import (
        "testing"
        "testing/quick"
 
-       "chain/errors"
-       "chain/testutil"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/testutil"
 )
 
 type tracebuf struct {
index d3d8fe8..c98789d 100644 (file)
@@ -3,8 +3,8 @@ package vmutil
 import (
        "encoding/binary"
 
-       "chain/errors"
-       "chain/protocol/vm"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/vm"
 )
 
 type Builder struct {
index f6be334..9c3bddb 100644 (file)
@@ -5,7 +5,7 @@ import (
        "encoding/hex"
        "testing"
 
-       "chain/protocol/vm"
+       "github.com/blockchain/protocol/vm"
 )
 
 func TestAddJump(t *testing.T) {
index 8f9b46f..5c1162b 100644 (file)
@@ -1,9 +1,9 @@
 package vmutil
 
 import (
-       "chain/crypto/ed25519"
-       "chain/errors"
-       "chain/protocol/vm"
+       "github.com/blockchain/crypto/ed25519"
+       "github.com/blockchain/errors"
+       "github.com/blockchain/protocol/vm"
 )
 
 var (
index 278f806..f49816e 100644 (file)
@@ -4,7 +4,7 @@ import (
        "bytes"
        "testing"
 
-       "chain/crypto/ed25519"
+       "github.com/blockchain/crypto/ed25519"
 )
 
 // TestIsUnspendable ensures the IsUnspendable function returns the expected
diff --git a/types/block.go b/types/block.go
new file mode 100644 (file)
index 0000000..b306d57
--- /dev/null
@@ -0,0 +1,423 @@
+package types
+
+import (
+       "bytes"
+       "errors"
+       "fmt"
+       "io"
+       "strings"
+       "time"
+
+       wire "github.com/tendermint/go-wire"
+       "github.com/tendermint/go-wire/data"
+       . "github.com/tendermint/tmlibs/common"
+       "github.com/tendermint/tmlibs/merkle"
+)
+
+const (
+       MaxBlockSize         = 22020096 // 21MB TODO make it configurable
+       DefaultBlockPartSize = 65536    // 64kB TODO: put part size in parts header?
+)
+
+type Block struct {
+       *Header    `json:"header"`
+       *Data      `json:"data"`
+       LastCommit *Commit `json:"last_commit"`
+}
+
+// TODO: version
+func MakeBlock(height int, chainID string, txs []Tx, commit *Commit,
+       prevBlockID BlockID, valHash, appHash []byte, partSize int) (*Block, *PartSet) {
+       block := &Block{
+               Header: &Header{
+                       ChainID:        chainID,
+                       Height:         height,
+                       Time:           time.Now(),
+                       NumTxs:         len(txs),
+                       LastBlockID:    prevBlockID,
+                       ValidatorsHash: valHash,
+                       AppHash:        appHash, // state merkle root of txs from the previous block.
+               },
+               LastCommit: commit,
+               Data: &Data{
+                       Txs: txs,
+               },
+       }
+       block.FillHeader()
+       return block, block.MakePartSet(partSize)
+}
+
+// Basic validation that doesn't involve state data.
+func (b *Block) ValidateBasic(chainID string, lastBlockHeight int, lastBlockID BlockID,
+       lastBlockTime time.Time, appHash []byte) error {
+       if b.ChainID != chainID {
+               return errors.New(Fmt("Wrong Block.Header.ChainID. Expected %v, got %v", chainID, b.ChainID))
+       }
+       if b.Height != lastBlockHeight+1 {
+               return errors.New(Fmt("Wrong Block.Header.Height. Expected %v, got %v", lastBlockHeight+1, b.Height))
+       }
+       /*      TODO: Determine bounds for Time
+               See blockchain/reactor "stopSyncingDurationMinutes"
+
+               if !b.Time.After(lastBlockTime) {
+                       return errors.New("Invalid Block.Header.Time")
+               }
+       */
+       if b.NumTxs != len(b.Data.Txs) {
+               return errors.New(Fmt("Wrong Block.Header.NumTxs. Expected %v, got %v", len(b.Data.Txs), b.NumTxs))
+       }
+       if !b.LastBlockID.Equals(lastBlockID) {
+               return errors.New(Fmt("Wrong Block.Header.LastBlockID.  Expected %v, got %v", lastBlockID, b.LastBlockID))
+       }
+       if !bytes.Equal(b.LastCommitHash, b.LastCommit.Hash()) {
+               return errors.New(Fmt("Wrong Block.Header.LastCommitHash.  Expected %v, got %v", b.LastCommitHash, b.LastCommit.Hash()))
+       }
+       if b.Header.Height != 1 {
+               if err := b.LastCommit.ValidateBasic(); err != nil {
+                       return err
+               }
+       }
+       if !bytes.Equal(b.DataHash, b.Data.Hash()) {
+               return errors.New(Fmt("Wrong Block.Header.DataHash.  Expected %v, got %v", b.DataHash, b.Data.Hash()))
+       }
+       if !bytes.Equal(b.AppHash, appHash) {
+               return errors.New(Fmt("Wrong Block.Header.AppHash.  Expected %X, got %v", appHash, b.AppHash))
+       }
+       // NOTE: the AppHash and ValidatorsHash are validated later.
+       return nil
+}
+
+func (b *Block) FillHeader() {
+       if b.LastCommitHash == nil {
+               b.LastCommitHash = b.LastCommit.Hash()
+       }
+       if b.DataHash == nil {
+               b.DataHash = b.Data.Hash()
+       }
+}
+
+// Computes and returns the block hash.
+// If the block is incomplete, block hash is nil for safety.
+func (b *Block) Hash() data.Bytes {
+       // fmt.Println(">>", b.Data)
+       if b == nil || b.Header == nil || b.Data == nil || b.LastCommit == nil {
+               return nil
+       }
+       b.FillHeader()
+       return b.Header.Hash()
+}
+
+func (b *Block) MakePartSet(partSize int) *PartSet {
+       return NewPartSetFromData(wire.BinaryBytes(b), partSize)
+}
+
+// Convenience.
+// A nil block never hashes to anything.
+// Nothing hashes to a nil hash.
+func (b *Block) HashesTo(hash []byte) bool {
+       if len(hash) == 0 {
+               return false
+       }
+       if b == nil {
+               return false
+       }
+       return bytes.Equal(b.Hash(), hash)
+}
+
+func (b *Block) String() string {
+       return b.StringIndented("")
+}
+
+func (b *Block) StringIndented(indent string) string {
+       if b == nil {
+               return "nil-Block"
+       }
+       return fmt.Sprintf(`Block{
+%s  %v
+%s  %v
+%s  %v
+%s}#%v`,
+               indent, b.Header.StringIndented(indent+"  "),
+               indent, b.Data.StringIndented(indent+"  "),
+               indent, b.LastCommit.StringIndented(indent+"  "),
+               indent, b.Hash())
+}
+
+func (b *Block) StringShort() string {
+       if b == nil {
+               return "nil-Block"
+       } else {
+               return fmt.Sprintf("Block#%v", b.Hash())
+       }
+}
+
+//-----------------------------------------------------------------------------
+
+type Header struct {
+       ChainID        string     `json:"chain_id"`
+       Height         int        `json:"height"`
+       Time           time.Time  `json:"time"`
+       NumTxs         int        `json:"num_txs"` // XXX: Can we get rid of this?
+       LastBlockID    BlockID    `json:"last_block_id"`
+       LastCommitHash data.Bytes `json:"last_commit_hash"` // commit from validators from the last block
+       DataHash       data.Bytes `json:"data_hash"`        // transactions
+       ValidatorsHash data.Bytes `json:"validators_hash"`  // validators for the current block
+       AppHash        data.Bytes `json:"app_hash"`         // state after txs from the previous block
+}
+
+// NOTE: hash is nil if required fields are missing.
+func (h *Header) Hash() data.Bytes {
+       if len(h.ValidatorsHash) == 0 {
+               return nil
+       }
+       return merkle.SimpleHashFromMap(map[string]interface{}{
+               "ChainID":     h.ChainID,
+               "Height":      h.Height,
+               "Time":        h.Time,
+               "NumTxs":      h.NumTxs,
+               "LastBlockID": h.LastBlockID,
+               "LastCommit":  h.LastCommitHash,
+               "Data":        h.DataHash,
+               "Validators":  h.ValidatorsHash,
+               "App":         h.AppHash,
+       })
+}
+
+func (h *Header) StringIndented(indent string) string {
+       if h == nil {
+               return "nil-Header"
+       }
+       return fmt.Sprintf(`Header{
+%s  ChainID:        %v
+%s  Height:         %v
+%s  Time:           %v
+%s  NumTxs:         %v
+%s  LastBlockID:    %v
+%s  LastCommit:     %v
+%s  Data:           %v
+%s  Validators:     %v
+%s  App:            %v
+%s}#%v`,
+               indent, h.ChainID,
+               indent, h.Height,
+               indent, h.Time,
+               indent, h.NumTxs,
+               indent, h.LastBlockID,
+               indent, h.LastCommitHash,
+               indent, h.DataHash,
+               indent, h.ValidatorsHash,
+               indent, h.AppHash,
+               indent, h.Hash())
+}
+
+//-------------------------------------
+
+// NOTE: Commit is empty for height 1, but never nil.
+type Commit struct {
+       // NOTE: The Precommits are in order of address to preserve the bonded ValidatorSet order.
+       // Any peer with a block can gossip precommits by index with a peer without recalculating the
+       // active ValidatorSet.
+       BlockID    BlockID `json:"blockID"`
+       Precommits []*Vote `json:"precommits"`
+
+       // Volatile
+       firstPrecommit *Vote
+       hash           data.Bytes
+       bitArray       *BitArray
+}
+
+func (commit *Commit) FirstPrecommit() *Vote {
+       if len(commit.Precommits) == 0 {
+               return nil
+       }
+       if commit.firstPrecommit != nil {
+               return commit.firstPrecommit
+       }
+       for _, precommit := range commit.Precommits {
+               if precommit != nil {
+                       commit.firstPrecommit = precommit
+                       return precommit
+               }
+       }
+       return nil
+}
+
+func (commit *Commit) Height() int {
+       if len(commit.Precommits) == 0 {
+               return 0
+       }
+       return commit.FirstPrecommit().Height
+}
+
+func (commit *Commit) Round() int {
+       if len(commit.Precommits) == 0 {
+               return 0
+       }
+       return commit.FirstPrecommit().Round
+}
+
+func (commit *Commit) Type() byte {
+       return VoteTypePrecommit
+}
+
+func (commit *Commit) Size() int {
+       if commit == nil {
+               return 0
+       }
+       return len(commit.Precommits)
+}
+
+func (commit *Commit) BitArray() *BitArray {
+       if commit.bitArray == nil {
+               commit.bitArray = NewBitArray(len(commit.Precommits))
+               for i, precommit := range commit.Precommits {
+                       commit.bitArray.SetIndex(i, precommit != nil)
+               }
+       }
+       return commit.bitArray
+}
+
+func (commit *Commit) GetByIndex(index int) *Vote {
+       return commit.Precommits[index]
+}
+
+func (commit *Commit) IsCommit() bool {
+       if len(commit.Precommits) == 0 {
+               return false
+       }
+       return true
+}
+
+func (commit *Commit) ValidateBasic() error {
+       if commit.BlockID.IsZero() {
+               return errors.New("Commit cannot be for nil block")
+       }
+       if len(commit.Precommits) == 0 {
+               return errors.New("No precommits in commit")
+       }
+       height, round := commit.Height(), commit.Round()
+
+       // validate the precommits
+       for _, precommit := range commit.Precommits {
+               // It's OK for precommits to be missing.
+               if precommit == nil {
+                       continue
+               }
+               // Ensure that all votes are precommits
+               if precommit.Type != VoteTypePrecommit {
+                       return fmt.Errorf("Invalid commit vote. Expected precommit, got %v",
+                               precommit.Type)
+               }
+               // Ensure that all heights are the same
+               if precommit.Height != height {
+                       return fmt.Errorf("Invalid commit precommit height. Expected %v, got %v",
+                               height, precommit.Height)
+               }
+               // Ensure that all rounds are the same
+               if precommit.Round != round {
+                       return fmt.Errorf("Invalid commit precommit round. Expected %v, got %v",
+                               round, precommit.Round)
+               }
+       }
+       return nil
+}
+
+func (commit *Commit) Hash() data.Bytes {
+       if commit.hash == nil {
+               bs := make([]interface{}, len(commit.Precommits))
+               for i, precommit := range commit.Precommits {
+                       bs[i] = precommit
+               }
+               commit.hash = merkle.SimpleHashFromBinaries(bs)
+       }
+       return commit.hash
+}
+
+func (commit *Commit) StringIndented(indent string) string {
+       if commit == nil {
+               return "nil-Commit"
+       }
+       precommitStrings := make([]string, len(commit.Precommits))
+       for i, precommit := range commit.Precommits {
+               precommitStrings[i] = precommit.String()
+       }
+       return fmt.Sprintf(`Commit{
+%s  BlockID:    %v
+%s  Precommits: %v
+%s}#%v`,
+               indent, commit.BlockID,
+               indent, strings.Join(precommitStrings, "\n"+indent+"  "),
+               indent, commit.hash)
+}
+
+//-----------------------------------------------------------------------------
+
+type Data struct {
+
+       // Txs that will be applied by state @ block.Height+1.
+       // NOTE: not all txs here are valid.  We're just agreeing on the order first.
+       // This means that block.AppHash does not include these txs.
+       Txs Txs `json:"txs"`
+
+       // Volatile
+       hash data.Bytes
+}
+
+func (data *Data) Hash() data.Bytes {
+       if data.hash == nil {
+               data.hash = data.Txs.Hash() // NOTE: leaves of merkle tree are TxIDs
+       }
+       return data.hash
+}
+
+func (data *Data) StringIndented(indent string) string {
+       if data == nil {
+               return "nil-Data"
+       }
+       txStrings := make([]string, MinInt(len(data.Txs), 21))
+       for i, tx := range data.Txs {
+               if i == 20 {
+                       txStrings[i] = fmt.Sprintf("... (%v total)", len(data.Txs))
+                       break
+               }
+               txStrings[i] = fmt.Sprintf("Tx:%v", tx)
+       }
+       return fmt.Sprintf(`Data{
+%s  %v
+%s}#%v`,
+               indent, strings.Join(txStrings, "\n"+indent+"  "),
+               indent, data.hash)
+}
+
+//--------------------------------------------------------------------------------
+
+type BlockID struct {
+       Hash        data.Bytes    `json:"hash"`
+       PartsHeader PartSetHeader `json:"parts"`
+}
+
+func (blockID BlockID) IsZero() bool {
+       return len(blockID.Hash) == 0 && blockID.PartsHeader.IsZero()
+}
+
+func (blockID BlockID) Equals(other BlockID) bool {
+       return bytes.Equal(blockID.Hash, other.Hash) &&
+               blockID.PartsHeader.Equals(other.PartsHeader)
+}
+
+func (blockID BlockID) Key() string {
+       return string(blockID.Hash) + string(wire.BinaryBytes(blockID.PartsHeader))
+}
+
+func (blockID BlockID) WriteSignBytes(w io.Writer, n *int, err *error) {
+       if blockID.IsZero() {
+               wire.WriteTo([]byte("null"), w, n, err)
+       } else {
+               wire.WriteJSON(CanonicalBlockID(blockID), w, n, err)
+       }
+
+}
+
+func (blockID BlockID) String() string {
+       return fmt.Sprintf(`%v:%v`, blockID.Hash, blockID.PartsHeader)
+}
diff --git a/types/block_meta.go b/types/block_meta.go
new file mode 100644 (file)
index 0000000..8e5bd43
--- /dev/null
@@ -0,0 +1,13 @@
+package types
+
+type BlockMeta struct {
+       BlockID BlockID `json:"block_id"` // the block hash and partsethash
+       Header  *Header `json:"header"`   // The block's Header
+}
+
+func NewBlockMeta(block *Block, blockParts *PartSet) *BlockMeta {
+       return &BlockMeta{
+               BlockID: BlockID{block.Hash(), blockParts.Header()},
+               Header:  block.Header,
+       }
+}
diff --git a/types/canonical_json.go b/types/canonical_json.go
new file mode 100644 (file)
index 0000000..2e8583a
--- /dev/null
@@ -0,0 +1,81 @@
+package types
+
+import (
+       "github.com/tendermint/go-wire/data"
+)
+
+// canonical json is go-wire's json for structs with fields in alphabetical order
+
+type CanonicalJSONBlockID struct {
+       Hash        data.Bytes                 `json:"hash,omitempty"`
+       PartsHeader CanonicalJSONPartSetHeader `json:"parts,omitempty"`
+}
+
+type CanonicalJSONPartSetHeader struct {
+       Hash  data.Bytes `json:"hash"`
+       Total int        `json:"total"`
+}
+
+type CanonicalJSONProposal struct {
+       BlockPartsHeader CanonicalJSONPartSetHeader `json:"block_parts_header"`
+       Height           int                        `json:"height"`
+       POLBlockID       CanonicalJSONBlockID       `json:"pol_block_id"`
+       POLRound         int                        `json:"pol_round"`
+       Round            int                        `json:"round"`
+}
+
+type CanonicalJSONVote struct {
+       BlockID CanonicalJSONBlockID `json:"block_id"`
+       Height  int                  `json:"height"`
+       Round   int                  `json:"round"`
+       Type    byte                 `json:"type"`
+}
+
+//------------------------------------
+// Messages including a "chain id" can only be applied to one chain, hence "Once"
+
+type CanonicalJSONOnceProposal struct {
+       ChainID  string                `json:"chain_id"`
+       Proposal CanonicalJSONProposal `json:"proposal"`
+}
+
+type CanonicalJSONOnceVote struct {
+       ChainID string            `json:"chain_id"`
+       Vote    CanonicalJSONVote `json:"vote"`
+}
+
+//-----------------------------------
+// Canonicalize the structs
+
+func CanonicalBlockID(blockID BlockID) CanonicalJSONBlockID {
+       return CanonicalJSONBlockID{
+               Hash:        blockID.Hash,
+               PartsHeader: CanonicalPartSetHeader(blockID.PartsHeader),
+       }
+}
+
+func CanonicalPartSetHeader(psh PartSetHeader) CanonicalJSONPartSetHeader {
+       return CanonicalJSONPartSetHeader{
+               psh.Hash,
+               psh.Total,
+       }
+}
+
+func CanonicalProposal(proposal *Proposal) CanonicalJSONProposal {
+       return CanonicalJSONProposal{
+               BlockPartsHeader: CanonicalPartSetHeader(proposal.BlockPartsHeader),
+               Height:           proposal.Height,
+               POLBlockID:       CanonicalBlockID(proposal.POLBlockID),
+               POLRound:         proposal.POLRound,
+               Round:            proposal.Round,
+       }
+}
+
+func CanonicalVote(vote *Vote) CanonicalJSONVote {
+       return CanonicalJSONVote{
+               CanonicalBlockID(vote.BlockID),
+               vote.Height,
+               vote.Round,
+               vote.Type,
+       }
+}
diff --git a/types/events.go b/types/events.go
new file mode 100644 (file)
index 0000000..8c29c44
--- /dev/null
@@ -0,0 +1,234 @@
+package types
+
+import (
+       // for registering TMEventData as events.EventData
+       abci "github.com/tendermint/abci/types"
+       "github.com/tendermint/go-wire/data"
+       cmn "github.com/tendermint/tmlibs/common"
+       "github.com/tendermint/tmlibs/events"
+)
+
+// Functions to generate eventId strings
+
+// Reserved
+func EventStringBond() string    { return "Bond" }
+func EventStringUnbond() string  { return "Unbond" }
+func EventStringRebond() string  { return "Rebond" }
+func EventStringDupeout() string { return "Dupeout" }
+func EventStringFork() string    { return "Fork" }
+func EventStringTx(tx Tx) string { return cmn.Fmt("Tx:%X", tx.Hash()) }
+
+func EventStringNewBlock() string         { return "NewBlock" }
+func EventStringNewBlockHeader() string   { return "NewBlockHeader" }
+func EventStringNewRound() string         { return "NewRound" }
+func EventStringNewRoundStep() string     { return "NewRoundStep" }
+func EventStringTimeoutPropose() string   { return "TimeoutPropose" }
+func EventStringCompleteProposal() string { return "CompleteProposal" }
+func EventStringPolka() string            { return "Polka" }
+func EventStringUnlock() string           { return "Unlock" }
+func EventStringLock() string             { return "Lock" }
+func EventStringRelock() string           { return "Relock" }
+func EventStringTimeoutWait() string      { return "TimeoutWait" }
+func EventStringVote() string             { return "Vote" }
+
+//----------------------------------------
+
+var (
+       EventDataNameNewBlock       = "new_block"
+       EventDataNameNewBlockHeader = "new_block_header"
+       EventDataNameTx             = "tx"
+       EventDataNameRoundState     = "round_state"
+       EventDataNameVote           = "vote"
+)
+
+//----------------------------------------
+
+// implements events.EventData
+type TMEventDataInner interface {
+       events.EventData
+}
+
+type TMEventData struct {
+       TMEventDataInner `json:"unwrap"`
+}
+
+func (tmr TMEventData) MarshalJSON() ([]byte, error) {
+       return tmEventDataMapper.ToJSON(tmr.TMEventDataInner)
+}
+
+func (tmr *TMEventData) UnmarshalJSON(data []byte) (err error) {
+       parsed, err := tmEventDataMapper.FromJSON(data)
+       if err == nil && parsed != nil {
+               tmr.TMEventDataInner = parsed.(TMEventDataInner)
+       }
+       return
+}
+
+func (tmr TMEventData) Unwrap() TMEventDataInner {
+       tmrI := tmr.TMEventDataInner
+       for wrap, ok := tmrI.(TMEventData); ok; wrap, ok = tmrI.(TMEventData) {
+               tmrI = wrap.TMEventDataInner
+       }
+       return tmrI
+}
+
+func (tmr TMEventData) Empty() bool {
+       return tmr.TMEventDataInner == nil
+}
+
+const (
+       EventDataTypeNewBlock       = byte(0x01)
+       EventDataTypeFork           = byte(0x02)
+       EventDataTypeTx             = byte(0x03)
+       EventDataTypeNewBlockHeader = byte(0x04)
+
+       EventDataTypeRoundState = byte(0x11)
+       EventDataTypeVote       = byte(0x12)
+)
+
+var tmEventDataMapper = data.NewMapper(TMEventData{}).
+       RegisterImplementation(EventDataNewBlock{}, EventDataNameNewBlock, EventDataTypeNewBlock).
+       RegisterImplementation(EventDataNewBlockHeader{}, EventDataNameNewBlockHeader, EventDataTypeNewBlockHeader).
+       RegisterImplementation(EventDataTx{}, EventDataNameTx, EventDataTypeTx).
+       RegisterImplementation(EventDataRoundState{}, EventDataNameRoundState, EventDataTypeRoundState).
+       RegisterImplementation(EventDataVote{}, EventDataNameVote, EventDataTypeVote)
+
+// Most event messages are basic types (a block, a transaction)
+// but some (an input to a call tx or a receive) are more exotic
+
+type EventDataNewBlock struct {
+       Block *Block `json:"block"`
+}
+
+// light weight event for benchmarking
+type EventDataNewBlockHeader struct {
+       Header *Header `json:"header"`
+}
+
+// All txs fire EventDataTx
+type EventDataTx struct {
+       Height int           `json:"height"`
+       Tx     Tx            `json:"tx"`
+       Data   data.Bytes    `json:"data"`
+       Log    string        `json:"log"`
+       Code   abci.CodeType `json:"code"`
+       Error  string        `json:"error"` // this is redundant information for now
+}
+
+// NOTE: This goes into the replay WAL
+type EventDataRoundState struct {
+       Height int    `json:"height"`
+       Round  int    `json:"round"`
+       Step   string `json:"step"`
+
+       // private, not exposed to websockets
+       RoundState interface{} `json:"-"`
+}
+
+type EventDataVote struct {
+       Vote *Vote
+}
+
+func (_ EventDataNewBlock) AssertIsTMEventData()       {}
+func (_ EventDataNewBlockHeader) AssertIsTMEventData() {}
+func (_ EventDataTx) AssertIsTMEventData()             {}
+func (_ EventDataRoundState) AssertIsTMEventData()     {}
+func (_ EventDataVote) AssertIsTMEventData()           {}
+
+//----------------------------------------
+// Wrappers for type safety
+
+type Fireable interface {
+       events.Fireable
+}
+
+type Eventable interface {
+       SetEventSwitch(EventSwitch)
+}
+
+type EventSwitch interface {
+       events.EventSwitch
+}
+
+type EventCache interface {
+       Fireable
+       Flush()
+}
+
+func NewEventSwitch() EventSwitch {
+       return events.NewEventSwitch()
+}
+
+func NewEventCache(evsw EventSwitch) EventCache {
+       return events.NewEventCache(evsw)
+}
+
+// All events should be based on this FireEvent to ensure they are TMEventData
+func fireEvent(fireable events.Fireable, event string, data TMEventData) {
+       if fireable != nil {
+               fireable.FireEvent(event, data)
+       }
+}
+
+func AddListenerForEvent(evsw EventSwitch, id, event string, cb func(data TMEventData)) {
+       evsw.AddListenerForEvent(id, event, func(data events.EventData) {
+               cb(data.(TMEventData))
+       })
+
+}
+
+//--- block, tx, and vote events
+
+func FireEventNewBlock(fireable events.Fireable, block EventDataNewBlock) {
+       fireEvent(fireable, EventStringNewBlock(), TMEventData{block})
+}
+
+func FireEventNewBlockHeader(fireable events.Fireable, header EventDataNewBlockHeader) {
+       fireEvent(fireable, EventStringNewBlockHeader(), TMEventData{header})
+}
+
+func FireEventVote(fireable events.Fireable, vote EventDataVote) {
+       fireEvent(fireable, EventStringVote(), TMEventData{vote})
+}
+
+func FireEventTx(fireable events.Fireable, tx EventDataTx) {
+       fireEvent(fireable, EventStringTx(tx.Tx), TMEventData{tx})
+}
+
+//--- EventDataRoundState events
+
+func FireEventNewRoundStep(fireable events.Fireable, rs EventDataRoundState) {
+       fireEvent(fireable, EventStringNewRoundStep(), TMEventData{rs})
+}
+
+func FireEventTimeoutPropose(fireable events.Fireable, rs EventDataRoundState) {
+       fireEvent(fireable, EventStringTimeoutPropose(), TMEventData{rs})
+}
+
+func FireEventTimeoutWait(fireable events.Fireable, rs EventDataRoundState) {
+       fireEvent(fireable, EventStringTimeoutWait(), TMEventData{rs})
+}
+
+func FireEventNewRound(fireable events.Fireable, rs EventDataRoundState) {
+       fireEvent(fireable, EventStringNewRound(), TMEventData{rs})
+}
+
+func FireEventCompleteProposal(fireable events.Fireable, rs EventDataRoundState) {
+       fireEvent(fireable, EventStringCompleteProposal(), TMEventData{rs})
+}
+
+func FireEventPolka(fireable events.Fireable, rs EventDataRoundState) {
+       fireEvent(fireable, EventStringPolka(), TMEventData{rs})
+}
+
+func FireEventUnlock(fireable events.Fireable, rs EventDataRoundState) {
+       fireEvent(fireable, EventStringUnlock(), TMEventData{rs})
+}
+
+func FireEventRelock(fireable events.Fireable, rs EventDataRoundState) {
+       fireEvent(fireable, EventStringRelock(), TMEventData{rs})
+}
+
+func FireEventLock(fireable events.Fireable, rs EventDataRoundState) {
+       fireEvent(fireable, EventStringLock(), TMEventData{rs})
+}
diff --git a/types/genesis.go b/types/genesis.go
new file mode 100644 (file)
index 0000000..75999f6
--- /dev/null
@@ -0,0 +1,49 @@
+package types
+
+import (
+       "encoding/json"
+       "time"
+
+       "github.com/tendermint/go-crypto"
+       "github.com/tendermint/go-wire/data"
+       cmn "github.com/tendermint/tmlibs/common"
+)
+
+//------------------------------------------------------------
+// we store the gendoc in the db
+
+var GenDocKey = []byte("GenDocKey")
+
+//------------------------------------------------------------
+// core types for a genesis definition
+
+type GenesisValidator struct {
+       PubKey crypto.PubKey `json:"pub_key"`
+       Amount int64         `json:"amount"`
+       Name   string        `json:"name"`
+}
+
+type GenesisDoc struct {
+       GenesisTime time.Time          `json:"genesis_time"`
+       ChainID     string             `json:"chain_id"`
+       Validators  []GenesisValidator `json:"validators"`
+       AppHash     data.Bytes         `json:"app_hash"`
+}
+
+// Utility method for saving GenensisDoc as JSON file.
+func (genDoc *GenesisDoc) SaveAs(file string) error {
+       genDocBytes, err := json.Marshal(genDoc)
+       if err != nil {
+               return err
+       }
+       return cmn.WriteFile(file, genDocBytes, 0644)
+}
+
+//------------------------------------------------------------
+// Make genesis state from file
+
+func GenesisDocFromJSON(jsonBlob []byte) (*GenesisDoc, error) {
+       genDoc := GenesisDoc{}
+       err := json.Unmarshal(jsonBlob, &genDoc)
+       return &genDoc, err
+}
diff --git a/types/keys.go b/types/keys.go
new file mode 100644 (file)
index 0000000..90591b9
--- /dev/null
@@ -0,0 +1,6 @@
+package types
+
+var (
+       PeerStateKey     = "ConsensusReactor.peerState"
+       PeerMempoolChKey = "MempoolReactor.peerMempoolCh"
+)
diff --git a/types/part_set.go b/types/part_set.go
new file mode 100644 (file)
index 0000000..e15d2ca
--- /dev/null
@@ -0,0 +1,276 @@
+package types
+
+import (
+       "bytes"
+       "errors"
+       "fmt"
+       "io"
+       "sync"
+
+       "golang.org/x/crypto/ripemd160"
+
+       "github.com/tendermint/go-wire"
+       "github.com/tendermint/go-wire/data"
+       cmn "github.com/tendermint/tmlibs/common"
+       "github.com/tendermint/tmlibs/merkle"
+)
+
+var (
+       ErrPartSetUnexpectedIndex = errors.New("Error part set unexpected index")
+       ErrPartSetInvalidProof    = errors.New("Error part set invalid proof")
+)
+
+type Part struct {
+       Index int                `json:"index"`
+       Bytes data.Bytes         `json:"bytes"`
+       Proof merkle.SimpleProof `json:"proof"`
+
+       // Cache
+       hash []byte
+}
+
+func (part *Part) Hash() []byte {
+       if part.hash != nil {
+               return part.hash
+       } else {
+               hasher := ripemd160.New()
+               hasher.Write(part.Bytes) // doesn't err
+               part.hash = hasher.Sum(nil)
+               return part.hash
+       }
+}
+
+func (part *Part) String() string {
+       return part.StringIndented("")
+}
+
+func (part *Part) StringIndented(indent string) string {
+       return fmt.Sprintf(`Part{#%v
+%s  Bytes: %X...
+%s  Proof: %v
+%s}`,
+               part.Index,
+               indent, cmn.Fingerprint(part.Bytes),
+               indent, part.Proof.StringIndented(indent+"  "),
+               indent)
+}
+
+//-------------------------------------
+
+type PartSetHeader struct {
+       Total int        `json:"total"`
+       Hash  data.Bytes `json:"hash"`
+}
+
+func (psh PartSetHeader) String() string {
+       return fmt.Sprintf("%v:%X", psh.Total, cmn.Fingerprint(psh.Hash))
+}
+
+func (psh PartSetHeader) IsZero() bool {
+       return psh.Total == 0
+}
+
+func (psh PartSetHeader) Equals(other PartSetHeader) bool {
+       return psh.Total == other.Total && bytes.Equal(psh.Hash, other.Hash)
+}
+
+func (psh PartSetHeader) WriteSignBytes(w io.Writer, n *int, err *error) {
+       wire.WriteJSON(CanonicalPartSetHeader(psh), w, n, err)
+}
+
+//-------------------------------------
+
+type PartSet struct {
+       total int
+       hash  []byte
+
+       mtx           sync.Mutex
+       parts         []*Part
+       partsBitArray *cmn.BitArray
+       count         int
+}
+
+// Returns an immutable, full PartSet from the data bytes.
+// The data bytes are split into "partSize" chunks, and merkle tree computed.
+func NewPartSetFromData(data []byte, partSize int) *PartSet {
+       // divide data into 4kb parts.
+       total := (len(data) + partSize - 1) / partSize
+       parts := make([]*Part, total)
+       parts_ := make([]merkle.Hashable, total)
+       partsBitArray := cmn.NewBitArray(total)
+       for i := 0; i < total; i++ {
+               part := &Part{
+                       Index: i,
+                       Bytes: data[i*partSize : cmn.MinInt(len(data), (i+1)*partSize)],
+               }
+               parts[i] = part
+               parts_[i] = part
+               partsBitArray.SetIndex(i, true)
+       }
+       // Compute merkle proofs
+       root, proofs := merkle.SimpleProofsFromHashables(parts_)
+       for i := 0; i < total; i++ {
+               parts[i].Proof = *proofs[i]
+       }
+       return &PartSet{
+               total:         total,
+               hash:          root,
+               parts:         parts,
+               partsBitArray: partsBitArray,
+               count:         total,
+       }
+}
+
+// Returns an empty PartSet ready to be populated.
+func NewPartSetFromHeader(header PartSetHeader) *PartSet {
+       return &PartSet{
+               total:         header.Total,
+               hash:          header.Hash,
+               parts:         make([]*Part, header.Total),
+               partsBitArray: cmn.NewBitArray(header.Total),
+               count:         0,
+       }
+}
+
+func (ps *PartSet) Header() PartSetHeader {
+       if ps == nil {
+               return PartSetHeader{}
+       } else {
+               return PartSetHeader{
+                       Total: ps.total,
+                       Hash:  ps.hash,
+               }
+       }
+}
+
+func (ps *PartSet) HasHeader(header PartSetHeader) bool {
+       if ps == nil {
+               return false
+       } else {
+               return ps.Header().Equals(header)
+       }
+}
+
+func (ps *PartSet) BitArray() *cmn.BitArray {
+       ps.mtx.Lock()
+       defer ps.mtx.Unlock()
+       return ps.partsBitArray.Copy()
+}
+
+func (ps *PartSet) Hash() []byte {
+       if ps == nil {
+               return nil
+       }
+       return ps.hash
+}
+
+func (ps *PartSet) HashesTo(hash []byte) bool {
+       if ps == nil {
+               return false
+       }
+       return bytes.Equal(ps.hash, hash)
+}
+
+func (ps *PartSet) Count() int {
+       if ps == nil {
+               return 0
+       }
+       return ps.count
+}
+
+func (ps *PartSet) Total() int {
+       if ps == nil {
+               return 0
+       }
+       return ps.total
+}
+
+func (ps *PartSet) AddPart(part *Part, verify bool) (bool, error) {
+       ps.mtx.Lock()
+       defer ps.mtx.Unlock()
+
+       // Invalid part index
+       if part.Index >= ps.total {
+               return false, ErrPartSetUnexpectedIndex
+       }
+
+       // If part already exists, return false.
+       if ps.parts[part.Index] != nil {
+               return false, nil
+       }
+
+       // Check hash proof
+       if verify {
+               if !part.Proof.Verify(part.Index, ps.total, part.Hash(), ps.Hash()) {
+                       return false, ErrPartSetInvalidProof
+               }
+       }
+
+       // Add part
+       ps.parts[part.Index] = part
+       ps.partsBitArray.SetIndex(part.Index, true)
+       ps.count++
+       return true, nil
+}
+
+func (ps *PartSet) GetPart(index int) *Part {
+       ps.mtx.Lock()
+       defer ps.mtx.Unlock()
+       return ps.parts[index]
+}
+
+func (ps *PartSet) IsComplete() bool {
+       return ps.count == ps.total
+}
+
+func (ps *PartSet) GetReader() io.Reader {
+       if !ps.IsComplete() {
+               cmn.PanicSanity("Cannot GetReader() on incomplete PartSet")
+       }
+       return NewPartSetReader(ps.parts)
+}
+
+type PartSetReader struct {
+       i      int
+       parts  []*Part
+       reader *bytes.Reader
+}
+
+func NewPartSetReader(parts []*Part) *PartSetReader {
+       return &PartSetReader{
+               i:      0,
+               parts:  parts,
+               reader: bytes.NewReader(parts[0].Bytes),
+       }
+}
+
+func (psr *PartSetReader) Read(p []byte) (n int, err error) {
+       readerLen := psr.reader.Len()
+       if readerLen >= len(p) {
+               return psr.reader.Read(p)
+       } else if readerLen > 0 {
+               n1, err := psr.Read(p[:readerLen])
+               if err != nil {
+                       return n1, err
+               }
+               n2, err := psr.Read(p[readerLen:])
+               return n1 + n2, err
+       }
+
+       psr.i += 1
+       if psr.i >= len(psr.parts) {
+               return 0, io.EOF
+       }
+       psr.reader = bytes.NewReader(psr.parts[psr.i].Bytes)
+       return psr.Read(p)
+}
+
+func (ps *PartSet) StringShort() string {
+       if ps == nil {
+               return "nil-PartSet"
+       } else {
+               ps.mtx.Lock()
+               defer ps.mtx.Unlock()
+               return fmt.Sprintf("(%v of %v)", ps.Count(), ps.Total())
+       }
+}
diff --git a/types/part_set_test.go b/types/part_set_test.go
new file mode 100644 (file)
index 0000000..7088ef3
--- /dev/null
@@ -0,0 +1,90 @@
+package types
+
+import (
+       "bytes"
+       "io/ioutil"
+       "testing"
+
+       . "github.com/tendermint/tmlibs/common"
+)
+
+const (
+       testPartSize = 65536 // 64KB ...  4096 // 4KB
+)
+
+func TestBasicPartSet(t *testing.T) {
+
+       // Construct random data of size partSize * 100
+       data := RandBytes(testPartSize * 100)
+
+       partSet := NewPartSetFromData(data, testPartSize)
+       if len(partSet.Hash()) == 0 {
+               t.Error("Expected to get hash")
+       }
+       if partSet.Total() != 100 {
+               t.Errorf("Expected to get 100 parts, but got %v", partSet.Total())
+       }
+       if !partSet.IsComplete() {
+               t.Errorf("PartSet should be complete")
+       }
+
+       // Test adding parts to a new partSet.
+       partSet2 := NewPartSetFromHeader(partSet.Header())
+
+       for i := 0; i < partSet.Total(); i++ {
+               part := partSet.GetPart(i)
+               //t.Logf("\n%v", part)
+               added, err := partSet2.AddPart(part, true)
+               if !added || err != nil {
+                       t.Errorf("Failed to add part %v, error: %v", i, err)
+               }
+       }
+
+       if !bytes.Equal(partSet.Hash(), partSet2.Hash()) {
+               t.Error("Expected to get same hash")
+       }
+       if partSet2.Total() != 100 {
+               t.Errorf("Expected to get 100 parts, but got %v", partSet2.Total())
+       }
+       if !partSet2.IsComplete() {
+               t.Errorf("Reconstructed PartSet should be complete")
+       }
+
+       // Reconstruct data, assert that they are equal.
+       data2Reader := partSet2.GetReader()
+       data2, err := ioutil.ReadAll(data2Reader)
+       if err != nil {
+               t.Errorf("Error reading data2Reader: %v", err)
+       }
+       if !bytes.Equal(data, data2) {
+               t.Errorf("Got wrong data.")
+       }
+
+}
+
+func TestWrongProof(t *testing.T) {
+
+       // Construct random data of size partSize * 100
+       data := RandBytes(testPartSize * 100)
+       partSet := NewPartSetFromData(data, testPartSize)
+
+       // Test adding a part with wrong data.
+       partSet2 := NewPartSetFromHeader(partSet.Header())
+
+       // Test adding a part with wrong trail.
+       part := partSet.GetPart(0)
+       part.Proof.Aunts[0][0] += byte(0x01)
+       added, err := partSet2.AddPart(part, true)
+       if added || err == nil {
+               t.Errorf("Expected to fail adding a part with bad trail.")
+       }
+
+       // Test adding a part with wrong bytes.
+       part = partSet.GetPart(1)
+       part.Bytes[0] += byte(0x01)
+       added, err = partSet2.AddPart(part, true)
+       if added || err == nil {
+               t.Errorf("Expected to fail adding a part with bad bytes.")
+       }
+
+}
diff --git a/types/priv_validator.go b/types/priv_validator.go
new file mode 100644 (file)
index 0000000..8c9a011
--- /dev/null
@@ -0,0 +1,275 @@
+package types
+
+import (
+       "bytes"
+       "encoding/json"
+       "errors"
+       "fmt"
+       "io/ioutil"
+       "os"
+       "sync"
+
+       crypto "github.com/tendermint/go-crypto"
+       data "github.com/tendermint/go-wire/data"
+       . "github.com/tendermint/tmlibs/common"
+       "github.com/tendermint/tmlibs/log"
+)
+
+const (
+       stepNone      = 0 // Used to distinguish the initial state
+       stepPropose   = 1
+       stepPrevote   = 2
+       stepPrecommit = 3
+)
+
+func voteToStep(vote *Vote) int8 {
+       switch vote.Type {
+       case VoteTypePrevote:
+               return stepPrevote
+       case VoteTypePrecommit:
+               return stepPrecommit
+       default:
+               PanicSanity("Unknown vote type")
+               return 0
+       }
+}
+
+type PrivValidator struct {
+       Address       data.Bytes       `json:"address"`
+       PubKey        crypto.PubKey    `json:"pub_key"`
+       LastHeight    int              `json:"last_height"`
+       LastRound     int              `json:"last_round"`
+       LastStep      int8             `json:"last_step"`
+       LastSignature crypto.Signature `json:"last_signature,omitempty"` // so we dont lose signatures
+       LastSignBytes data.Bytes       `json:"last_signbytes,omitempty"` // so we dont lose signatures
+
+       // PrivKey should be empty if a Signer other than the default is being used.
+       PrivKey crypto.PrivKey `json:"priv_key"`
+       Signer  `json:"-"`
+
+       // For persistence.
+       // Overloaded for testing.
+       filePath string
+       mtx      sync.Mutex
+}
+
+// This is used to sign votes.
+// It is the caller's duty to verify the msg before calling Sign,
+// eg. to avoid double signing.
+// Currently, the only callers are SignVote and SignProposal
+type Signer interface {
+       PubKey() crypto.PubKey
+       Sign(msg []byte) crypto.Signature
+}
+
+// Implements Signer
+type DefaultSigner struct {
+       priv crypto.PrivKey
+}
+
+func NewDefaultSigner(priv crypto.PrivKey) *DefaultSigner {
+       return &DefaultSigner{priv: priv}
+}
+
+// Implements Signer
+func (ds *DefaultSigner) Sign(msg []byte) crypto.Signature {
+       return ds.priv.Sign(msg)
+}
+
+// Implements Signer
+func (ds *DefaultSigner) PubKey() crypto.PubKey {
+       return ds.priv.PubKey()
+}
+
+func (privVal *PrivValidator) SetSigner(s Signer) {
+       privVal.Signer = s
+       privVal.setPubKeyAndAddress()
+}
+
+// Overwrite address and pubkey for convenience
+func (privVal *PrivValidator) setPubKeyAndAddress() {
+       privVal.PubKey = privVal.Signer.PubKey()
+       privVal.Address = privVal.PubKey.Address()
+}
+
+// Generates a new validator with private key.
+func GenPrivValidator() *PrivValidator {
+       privKey := crypto.GenPrivKeyEd25519().Wrap()
+       pubKey := privKey.PubKey()
+       return &PrivValidator{
+               Address:  pubKey.Address(),
+               PubKey:   pubKey,
+               PrivKey:  privKey,
+               LastStep: stepNone,
+               filePath: "",
+               Signer:   NewDefaultSigner(privKey),
+       }
+}
+
+func LoadPrivValidator(filePath string) *PrivValidator {
+       privValJSONBytes, err := ioutil.ReadFile(filePath)
+       if err != nil {
+               Exit(err.Error())
+       }
+       privVal := PrivValidator{}
+       err = json.Unmarshal(privValJSONBytes, &privVal)
+       if err != nil {
+               Exit(Fmt("Error reading PrivValidator from %v: %v\n", filePath, err))
+       }
+
+       privVal.filePath = filePath
+       privVal.Signer = NewDefaultSigner(privVal.PrivKey)
+       privVal.setPubKeyAndAddress()
+       return &privVal
+}
+
+func LoadOrGenPrivValidator(filePath string, logger log.Logger) *PrivValidator {
+       var privValidator *PrivValidator
+       if _, err := os.Stat(filePath); err == nil {
+               privValidator = LoadPrivValidator(filePath)
+               logger.Info("Loaded PrivValidator",
+                       "file", filePath, "privValidator", privValidator)
+       } else {
+               privValidator = GenPrivValidator()
+               privValidator.SetFile(filePath)
+               privValidator.Save()
+               logger.Info("Generated PrivValidator", "file", filePath)
+       }
+       return privValidator
+}
+
+func (privVal *PrivValidator) SetFile(filePath string) {
+       privVal.mtx.Lock()
+       defer privVal.mtx.Unlock()
+       privVal.filePath = filePath
+}
+
+func (privVal *PrivValidator) Save() {
+       privVal.mtx.Lock()
+       defer privVal.mtx.Unlock()
+       privVal.save()
+}
+
+func (privVal *PrivValidator) save() {
+       if privVal.filePath == "" {
+               PanicSanity("Cannot save PrivValidator: filePath not set")
+       }
+       jsonBytes, err := json.Marshal(privVal)
+       if err != nil {
+               // `@; BOOM!!!
+               PanicCrisis(err)
+       }
+       err = WriteFileAtomic(privVal.filePath, jsonBytes, 0600)
+       if err != nil {
+               // `@; BOOM!!!
+               PanicCrisis(err)
+       }
+}
+
+// NOTE: Unsafe!
+func (privVal *PrivValidator) Reset() {
+       privVal.LastHeight = 0
+       privVal.LastRound = 0
+       privVal.LastStep = 0
+       privVal.LastSignature = crypto.Signature{}
+       privVal.LastSignBytes = nil
+       privVal.Save()
+}
+
+func (privVal *PrivValidator) GetAddress() []byte {
+       return privVal.Address
+}
+
+func (privVal *PrivValidator) SignVote(chainID string, vote *Vote) error {
+       privVal.mtx.Lock()
+       defer privVal.mtx.Unlock()
+       signature, err := privVal.signBytesHRS(vote.Height, vote.Round, voteToStep(vote), SignBytes(chainID, vote))
+       if err != nil {
+               return errors.New(Fmt("Error signing vote: %v", err))
+       }
+       vote.Signature = signature
+       return nil
+}
+
+func (privVal *PrivValidator) SignProposal(chainID string, proposal *Proposal) error {
+       privVal.mtx.Lock()
+       defer privVal.mtx.Unlock()
+       signature, err := privVal.signBytesHRS(proposal.Height, proposal.Round, stepPropose, SignBytes(chainID, proposal))
+       if err != nil {
+               return fmt.Errorf("Error signing proposal: %v", err)
+       }
+       proposal.Signature = signature
+       return nil
+}
+
+// check if there's a regression. Else sign and write the hrs+signature to disk
+func (privVal *PrivValidator) signBytesHRS(height, round int, step int8, signBytes []byte) (crypto.Signature, error) {
+       sig := crypto.Signature{}
+       // If height regression, err
+       if privVal.LastHeight > height {
+               return sig, errors.New("Height regression")
+       }
+       // More cases for when the height matches
+       if privVal.LastHeight == height {
+               // If round regression, err
+               if privVal.LastRound > round {
+                       return sig, errors.New("Round regression")
+               }
+               // If step regression, err
+               if privVal.LastRound == round {
+                       if privVal.LastStep > step {
+                               return sig, errors.New("Step regression")
+                       } else if privVal.LastStep == step {
+                               if privVal.LastSignBytes != nil {
+                                       if privVal.LastSignature.Empty() {
+                                               PanicSanity("privVal: LastSignature is nil but LastSignBytes is not!")
+                                       }
+                                       // so we dont sign a conflicting vote or proposal
+                                       // NOTE: proposals are non-deterministic (include time),
+                                       // so we can actually lose them, but will still never sign conflicting ones
+                                       if bytes.Equal(privVal.LastSignBytes, signBytes) {
+                                               // log.Notice("Using privVal.LastSignature", "sig", privVal.LastSignature)
+                                               return privVal.LastSignature, nil
+                                       }
+                               }
+                               return sig, errors.New("Step regression")
+                       }
+               }
+       }
+
+       // Sign
+       sig = privVal.Sign(signBytes)
+
+       // Persist height/round/step
+       privVal.LastHeight = height
+       privVal.LastRound = round
+       privVal.LastStep = step
+       privVal.LastSignature = sig
+       privVal.LastSignBytes = signBytes
+       privVal.save()
+
+       return sig, nil
+
+}
+
+func (privVal *PrivValidator) String() string {
+       return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", privVal.Address, privVal.LastHeight, privVal.LastRound, privVal.LastStep)
+}
+
+//-------------------------------------
+
+type PrivValidatorsByAddress []*PrivValidator
+
+func (pvs PrivValidatorsByAddress) Len() int {
+       return len(pvs)
+}
+
+func (pvs PrivValidatorsByAddress) Less(i, j int) bool {
+       return bytes.Compare(pvs[i].Address, pvs[j].Address) == -1
+}
+
+func (pvs PrivValidatorsByAddress) Swap(i, j int) {
+       it := pvs[i]
+       pvs[i] = pvs[j]
+       pvs[j] = it
+}
diff --git a/types/priv_validator_test.go b/types/priv_validator_test.go
new file mode 100644 (file)
index 0000000..1eb0b57
--- /dev/null
@@ -0,0 +1,60 @@
+package types
+
+import (
+       "encoding/hex"
+       "encoding/json"
+       "fmt"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+       crypto "github.com/tendermint/go-crypto"
+)
+
+func TestLoadValidator(t *testing.T) {
+       assert, require := assert.New(t), require.New(t)
+
+       // create some fixed values
+       addrStr := "D028C9981F7A87F3093672BF0D5B0E2A1B3ED456"
+       pubStr := "3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
+       privStr := "27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
+       addrBytes, _ := hex.DecodeString(addrStr)
+       pubBytes, _ := hex.DecodeString(pubStr)
+       privBytes, _ := hex.DecodeString(privStr)
+
+       // prepend type byte
+       pubKey, err := crypto.PubKeyFromBytes(append([]byte{1}, pubBytes...))
+       require.Nil(err, "%+v", err)
+       privKey, err := crypto.PrivKeyFromBytes(append([]byte{1}, privBytes...))
+       require.Nil(err, "%+v", err)
+
+       serialized := fmt.Sprintf(`{
+  "address": "%s",
+  "pub_key": {
+    "type": "ed25519",
+    "data": "%s"
+  },
+  "priv_key": {
+    "type": "ed25519",
+    "data": "%s"
+  },
+  "last_height": 0,
+  "last_round": 0,
+  "last_step": 0,
+  "last_signature": null
+}`, addrStr, pubStr, privStr)
+
+       val := PrivValidator{}
+       err = json.Unmarshal([]byte(serialized), &val)
+       require.Nil(err, "%+v", err)
+
+       // make sure the values match
+       assert.EqualValues(addrBytes, val.Address)
+       assert.EqualValues(pubKey, val.PubKey)
+       assert.EqualValues(privKey, val.PrivKey)
+
+       // export it and make sure it is the same
+       out, err := json.Marshal(val)
+       require.Nil(err, "%+v", err)
+       assert.JSONEq(serialized, string(out))
+}
diff --git a/types/proposal.go b/types/proposal.go
new file mode 100644 (file)
index 0000000..8406403
--- /dev/null
@@ -0,0 +1,48 @@
+package types
+
+import (
+       "errors"
+       "fmt"
+       "io"
+
+       //. "github.com/tendermint/tmlibs/common"
+       "github.com/tendermint/go-crypto"
+       "github.com/tendermint/go-wire"
+)
+
+var (
+       ErrInvalidBlockPartSignature = errors.New("Error invalid block part signature")
+       ErrInvalidBlockPartHash      = errors.New("Error invalid block part hash")
+)
+
+type Proposal struct {
+       Height           int              `json:"height"`
+       Round            int              `json:"round"`
+       BlockPartsHeader PartSetHeader    `json:"block_parts_header"`
+       POLRound         int              `json:"pol_round"`    // -1 if null.
+       POLBlockID       BlockID          `json:"pol_block_id"` // zero if null.
+       Signature        crypto.Signature `json:"signature"`
+}
+
+// polRound: -1 if no polRound.
+func NewProposal(height int, round int, blockPartsHeader PartSetHeader, polRound int, polBlockID BlockID) *Proposal {
+       return &Proposal{
+               Height:           height,
+               Round:            round,
+               BlockPartsHeader: blockPartsHeader,
+               POLRound:         polRound,
+               POLBlockID:       polBlockID,
+       }
+}
+
+func (p *Proposal) String() string {
+       return fmt.Sprintf("Proposal{%v/%v %v (%v,%v) %v}", p.Height, p.Round,
+               p.BlockPartsHeader, p.POLRound, p.POLBlockID, p.Signature)
+}
+
+func (p *Proposal) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
+       wire.WriteJSON(CanonicalJSONOnceProposal{
+               ChainID:  chainID,
+               Proposal: CanonicalProposal(p),
+       }, w, n, err)
+}
diff --git a/types/proposal_test.go b/types/proposal_test.go
new file mode 100644 (file)
index 0000000..622236b
--- /dev/null
@@ -0,0 +1,46 @@
+package types
+
+import (
+       "testing"
+)
+
+var testProposal = &Proposal{
+       Height:           12345,
+       Round:            23456,
+       BlockPartsHeader: PartSetHeader{111, []byte("blockparts")},
+       POLRound:         -1,
+}
+
+func TestProposalSignable(t *testing.T) {
+       signBytes := SignBytes("test_chain_id", testProposal)
+       signStr := string(signBytes)
+
+       expected := `{"chain_id":"test_chain_id","proposal":{"block_parts_header":{"hash":"626C6F636B7061727473","total":111},"height":12345,"pol_block_id":{},"pol_round":-1,"round":23456}}`
+       if signStr != expected {
+               t.Errorf("Got unexpected sign string for Proposal. Expected:\n%v\nGot:\n%v", expected, signStr)
+       }
+}
+
+func BenchmarkProposalWriteSignBytes(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               SignBytes("test_chain_id", testProposal)
+       }
+}
+
+func BenchmarkProposalSign(b *testing.B) {
+       privVal := GenPrivValidator()
+       for i := 0; i < b.N; i++ {
+               privVal.Sign(SignBytes("test_chain_id", testProposal))
+       }
+}
+
+func BenchmarkProposalVerifySignature(b *testing.B) {
+       signBytes := SignBytes("test_chain_id", testProposal)
+       privVal := GenPrivValidator()
+       signature := privVal.Sign(signBytes)
+       pubKey := privVal.PubKey
+
+       for i := 0; i < b.N; i++ {
+               pubKey.VerifyBytes(SignBytes("test_chain_id", testProposal), signature)
+       }
+}
diff --git a/types/protobuf.go b/types/protobuf.go
new file mode 100644 (file)
index 0000000..59994fe
--- /dev/null
@@ -0,0 +1,52 @@
+package types
+
+import (
+       "github.com/tendermint/abci/types"
+)
+
+// Convert tendermint types to protobuf types
+var TM2PB = tm2pb{}
+
+type tm2pb struct{}
+
+func (tm2pb) Header(header *Header) *types.Header {
+       return &types.Header{
+               ChainId:        header.ChainID,
+               Height:         uint64(header.Height),
+               Time:           uint64(header.Time.Unix()),
+               NumTxs:         uint64(header.NumTxs),
+               LastBlockId:    TM2PB.BlockID(header.LastBlockID),
+               LastCommitHash: header.LastCommitHash,
+               DataHash:       header.DataHash,
+               AppHash:        header.AppHash,
+       }
+}
+
+func (tm2pb) BlockID(blockID BlockID) *types.BlockID {
+       return &types.BlockID{
+               Hash:  blockID.Hash,
+               Parts: TM2PB.PartSetHeader(blockID.PartsHeader),
+       }
+}
+
+func (tm2pb) PartSetHeader(partSetHeader PartSetHeader) *types.PartSetHeader {
+       return &types.PartSetHeader{
+               Total: uint64(partSetHeader.Total),
+               Hash:  partSetHeader.Hash,
+       }
+}
+
+func (tm2pb) Validator(val *Validator) *types.Validator {
+       return &types.Validator{
+               PubKey: val.PubKey.Bytes(),
+               Power:  uint64(val.VotingPower),
+       }
+}
+
+func (tm2pb) Validators(vals *ValidatorSet) []*types.Validator {
+       validators := make([]*types.Validator, len(vals.Validators))
+       for i, val := range vals.Validators {
+               validators[i] = TM2PB.Validator(val)
+       }
+       return validators
+}
diff --git a/types/services.go b/types/services.go
new file mode 100644 (file)
index 0000000..ee20487
--- /dev/null
@@ -0,0 +1,56 @@
+package types
+
+import (
+       abci "github.com/tendermint/abci/types"
+)
+
+//------------------------------------------------------
+// blockchain services types
+// NOTE: Interfaces used by RPC must be thread safe!
+//------------------------------------------------------
+
+//------------------------------------------------------
+// mempool
+
+// Updates to the mempool need to be synchronized with committing a block
+// so apps can reset their transient state on Commit
+type Mempool interface {
+       Lock()
+       Unlock()
+
+       Size() int
+       CheckTx(Tx, func(*abci.Response)) error
+       Reap(int) Txs
+       Update(height int, txs Txs)
+       Flush()
+}
+
+type MockMempool struct {
+}
+
+func (m MockMempool) Lock()                                        {}
+func (m MockMempool) Unlock()                                      {}
+func (m MockMempool) Size() int                                    { return 0 }
+func (m MockMempool) CheckTx(tx Tx, cb func(*abci.Response)) error { return nil }
+func (m MockMempool) Reap(n int) Txs                               { return Txs{} }
+func (m MockMempool) Update(height int, txs Txs)                   {}
+func (m MockMempool) Flush()                                       {}
+
+//------------------------------------------------------
+// blockstore
+
+type BlockStoreRPC interface {
+       Height() int
+
+       LoadBlockMeta(height int) *BlockMeta
+       LoadBlock(height int) *Block
+       LoadBlockPart(height int, index int) *Part
+
+       LoadBlockCommit(height int) *Commit
+       LoadSeenCommit(height int) *Commit
+}
+
+type BlockStore interface {
+       BlockStoreRPC
+       SaveBlock(block *Block, blockParts *PartSet, seenCommit *Commit)
+}
diff --git a/types/signable.go b/types/signable.go
new file mode 100644 (file)
index 0000000..13389fe
--- /dev/null
@@ -0,0 +1,30 @@
+package types
+
+import (
+       "bytes"
+       "io"
+
+       . "github.com/tendermint/tmlibs/common"
+       "github.com/tendermint/tmlibs/merkle"
+)
+
+// Signable is an interface for all signable things.
+// It typically removes signatures before serializing.
+type Signable interface {
+       WriteSignBytes(chainID string, w io.Writer, n *int, err *error)
+}
+
+// SignBytes is a convenience method for getting the bytes to sign of a Signable.
+func SignBytes(chainID string, o Signable) []byte {
+       buf, n, err := new(bytes.Buffer), new(int), new(error)
+       o.WriteSignBytes(chainID, buf, n, err)
+       if *err != nil {
+               PanicCrisis(err)
+       }
+       return buf.Bytes()
+}
+
+// HashSignBytes is a convenience method for getting the hash of the bytes of a signable
+func HashSignBytes(chainID string, o Signable) []byte {
+       return merkle.SimpleHashFromBinary(SignBytes(chainID, o))
+}
diff --git a/types/tx.go b/types/tx.go
new file mode 100644 (file)
index 0000000..0334452
--- /dev/null
@@ -0,0 +1,119 @@
+package types
+
+import (
+       "bytes"
+       "errors"
+       "fmt"
+
+       abci "github.com/tendermint/abci/types"
+       "github.com/tendermint/go-wire/data"
+       "github.com/tendermint/tmlibs/merkle"
+)
+
+type Tx []byte
+
+// NOTE: this is the hash of the go-wire encoded Tx.
+// Tx has no types at this level, so just length-prefixed.
+// Alternatively, it may make sense to add types here and let
+// []byte be type 0x1 so we can have versioned txs if need be in the future.
+func (tx Tx) Hash() []byte {
+       return merkle.SimpleHashFromBinary(tx)
+}
+
+func (tx Tx) String() string {
+       return fmt.Sprintf("Tx{%X}", []byte(tx))
+}
+
+type Txs []Tx
+
+func (txs Txs) Hash() []byte {
+       // Recursive impl.
+       // Copied from tmlibs/merkle to avoid allocations
+       switch len(txs) {
+       case 0:
+               return nil
+       case 1:
+               return txs[0].Hash()
+       default:
+               left := Txs(txs[:(len(txs)+1)/2]).Hash()
+               right := Txs(txs[(len(txs)+1)/2:]).Hash()
+               return merkle.SimpleHashFromTwoHashes(left, right)
+       }
+}
+
+// Index returns the index of this transaction in the list, or -1 if not found
+func (txs Txs) Index(tx Tx) int {
+       for i := range txs {
+               if bytes.Equal(txs[i], tx) {
+                       return i
+               }
+       }
+       return -1
+}
+
+// Index returns the index of this transaction hash in the list, or -1 if not found
+func (txs Txs) IndexByHash(hash []byte) int {
+       for i := range txs {
+               if bytes.Equal(txs[i].Hash(), hash) {
+                       return i
+               }
+       }
+       return -1
+}
+
+// Proof returns a simple merkle proof for this node.
+//
+// Panics if i < 0 or i >= len(txs)
+//
+// TODO: optimize this!
+func (txs Txs) Proof(i int) TxProof {
+       l := len(txs)
+       hashables := make([]merkle.Hashable, l)
+       for i := 0; i < l; i++ {
+               hashables[i] = txs[i]
+       }
+       root, proofs := merkle.SimpleProofsFromHashables(hashables)
+
+       return TxProof{
+               Index:    i,
+               Total:    l,
+               RootHash: root,
+               Data:     txs[i],
+               Proof:    *proofs[i],
+       }
+}
+
+type TxProof struct {
+       Index, Total int
+       RootHash     data.Bytes
+       Data         Tx
+       Proof        merkle.SimpleProof
+}
+
+func (tp TxProof) LeafHash() []byte {
+       return tp.Data.Hash()
+}
+
+// Validate returns nil if it matches the dataHash, and is internally consistent
+// otherwise, returns a sensible error
+func (tp TxProof) Validate(dataHash []byte) error {
+       if !bytes.Equal(dataHash, tp.RootHash) {
+               return errors.New("Proof matches different data hash")
+       }
+
+       valid := tp.Proof.Verify(tp.Index, tp.Total, tp.LeafHash(), tp.RootHash)
+       if !valid {
+               return errors.New("Proof is not internally consistent")
+       }
+       return nil
+}
+
+// TxResult contains results of executing the transaction.
+//
+// One usage is indexing transaction results.
+type TxResult struct {
+       Height uint64                 `json:"height"`
+       Index  uint32                 `json:"index"`
+       Tx     Tx                     `json:"tx"`
+       Result abci.ResponseDeliverTx `json:"result"`
+}
diff --git a/types/tx_test.go b/types/tx_test.go
new file mode 100644 (file)
index 0000000..91cddec
--- /dev/null
@@ -0,0 +1,122 @@
+package types
+
+import (
+       "bytes"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       wire "github.com/tendermint/go-wire"
+       cmn "github.com/tendermint/tmlibs/common"
+       ctest "github.com/tendermint/tmlibs/test"
+)
+
+func makeTxs(cnt, size int) Txs {
+       txs := make(Txs, cnt)
+       for i := 0; i < cnt; i++ {
+               txs[i] = cmn.RandBytes(size)
+       }
+       return txs
+}
+
+func randInt(low, high int) int {
+       off := cmn.RandInt() % (high - low)
+       return low + off
+}
+
+func TestTxIndex(t *testing.T) {
+       assert := assert.New(t)
+       for i := 0; i < 20; i++ {
+               txs := makeTxs(15, 60)
+               for j := 0; j < len(txs); j++ {
+                       tx := txs[j]
+                       idx := txs.Index(tx)
+                       assert.Equal(j, idx)
+               }
+               assert.Equal(-1, txs.Index(nil))
+               assert.Equal(-1, txs.Index(Tx("foodnwkf")))
+       }
+}
+
+func TestValidTxProof(t *testing.T) {
+       assert := assert.New(t)
+       cases := []struct {
+               txs Txs
+       }{
+               {Txs{{1, 4, 34, 87, 163, 1}}},
+               {Txs{{5, 56, 165, 2}, {4, 77}}},
+               {Txs{Tx("foo"), Tx("bar"), Tx("baz")}},
+               {makeTxs(20, 5)},
+               {makeTxs(7, 81)},
+               {makeTxs(61, 15)},
+       }
+
+       for h, tc := range cases {
+               txs := tc.txs
+               root := txs.Hash()
+               // make sure valid proof for every tx
+               for i := range txs {
+                       leaf := txs[i]
+                       leafHash := leaf.Hash()
+                       proof := txs.Proof(i)
+                       assert.Equal(i, proof.Index, "%d: %d", h, i)
+                       assert.Equal(len(txs), proof.Total, "%d: %d", h, i)
+                       assert.EqualValues(root, proof.RootHash, "%d: %d", h, i)
+                       assert.EqualValues(leaf, proof.Data, "%d: %d", h, i)
+                       assert.EqualValues(leafHash, proof.LeafHash(), "%d: %d", h, i)
+                       assert.Nil(proof.Validate(root), "%d: %d", h, i)
+                       assert.NotNil(proof.Validate([]byte("foobar")), "%d: %d", h, i)
+
+                       // read-write must also work
+                       var p2 TxProof
+                       bin := wire.BinaryBytes(proof)
+                       err := wire.ReadBinaryBytes(bin, &p2)
+                       if assert.Nil(err, "%d: %d: %+v", h, i, err) {
+                               assert.Nil(p2.Validate(root), "%d: %d", h, i)
+                       }
+               }
+       }
+}
+
+func TestTxProofUnchangable(t *testing.T) {
+       // run the other test a bunch...
+       for i := 0; i < 40; i++ {
+               testTxProofUnchangable(t)
+       }
+}
+
+func testTxProofUnchangable(t *testing.T) {
+       assert := assert.New(t)
+
+       // make some proof
+       txs := makeTxs(randInt(2, 100), randInt(16, 128))
+       root := txs.Hash()
+       i := randInt(0, len(txs)-1)
+       proof := txs.Proof(i)
+
+       // make sure it is valid to start with
+       assert.Nil(proof.Validate(root))
+       bin := wire.BinaryBytes(proof)
+
+       // try mutating the data and make sure nothing breaks
+       for j := 0; j < 500; j++ {
+               bad := ctest.MutateByteSlice(bin)
+               if !bytes.Equal(bad, bin) {
+                       assertBadProof(t, root, bad, proof)
+               }
+       }
+}
+
+// this make sure the proof doesn't deserialize into something valid
+func assertBadProof(t *testing.T, root []byte, bad []byte, good TxProof) {
+       var proof TxProof
+       err := wire.ReadBinaryBytes(bad, &proof)
+       if err == nil {
+               err = proof.Validate(root)
+               if err == nil {
+                       // okay, this can happen if we have a slightly different total
+                       // (where the path ends up the same), if it is something else, we have
+                       // a real problem
+                       assert.NotEqual(t, proof.Total, good.Total, "bad: %#v\ngood: %#v", proof, good)
+               }
+       }
+}
diff --git a/types/validator.go b/types/validator.go
new file mode 100644 (file)
index 0000000..24f8974
--- /dev/null
@@ -0,0 +1,119 @@
+package types
+
+import (
+       "bytes"
+       "fmt"
+       "io"
+
+       "github.com/tendermint/go-crypto"
+       "github.com/tendermint/go-wire"
+       "github.com/tendermint/go-wire/data"
+       cmn "github.com/tendermint/tmlibs/common"
+)
+
+// Volatile state for each Validator
+// NOTE: The Accum is not included in Validator.Hash();
+// make sure to update that method if changes are made here
+type Validator struct {
+       Address     data.Bytes    `json:"address"`
+       PubKey      crypto.PubKey `json:"pub_key"`
+       VotingPower int64         `json:"voting_power"`
+
+       Accum int64 `json:"accum"`
+}
+
+func NewValidator(pubKey crypto.PubKey, votingPower int64) *Validator {
+       return &Validator{
+               Address:     pubKey.Address(),
+               PubKey:      pubKey,
+               VotingPower: votingPower,
+               Accum:       0,
+       }
+}
+
+// Creates a new copy of the validator so we can mutate accum.
+// Panics if the validator is nil.
+func (v *Validator) Copy() *Validator {
+       vCopy := *v
+       return &vCopy
+}
+
+// Returns the one with higher Accum.
+func (v *Validator) CompareAccum(other *Validator) *Validator {
+       if v == nil {
+               return other
+       }
+       if v.Accum > other.Accum {
+               return v
+       } else if v.Accum < other.Accum {
+               return other
+       } else {
+               if bytes.Compare(v.Address, other.Address) < 0 {
+                       return v
+               } else if bytes.Compare(v.Address, other.Address) > 0 {
+                       return other
+               } else {
+                       cmn.PanicSanity("Cannot compare identical validators")
+                       return nil
+               }
+       }
+}
+
+func (v *Validator) String() string {
+       if v == nil {
+               return "nil-Validator"
+       }
+       return fmt.Sprintf("Validator{%v %v VP:%v A:%v}",
+               v.Address,
+               v.PubKey,
+               v.VotingPower,
+               v.Accum)
+}
+
+// Hash computes the unique ID of a validator with a given voting power.
+// It exludes the Accum value, which changes with every round.
+func (v *Validator) Hash() []byte {
+       return wire.BinaryRipemd160(struct {
+               Address     data.Bytes
+               PubKey      crypto.PubKey
+               VotingPower int64
+       }{
+               v.Address,
+               v.PubKey,
+               v.VotingPower,
+       })
+}
+
+//-------------------------------------
+
+var ValidatorCodec = validatorCodec{}
+
+type validatorCodec struct{}
+
+func (vc validatorCodec) Encode(o interface{}, w io.Writer, n *int, err *error) {
+       wire.WriteBinary(o.(*Validator), w, n, err)
+}
+
+func (vc validatorCodec) Decode(r io.Reader, n *int, err *error) interface{} {
+       return wire.ReadBinary(&Validator{}, r, 0, n, err)
+}
+
+func (vc validatorCodec) Compare(o1 interface{}, o2 interface{}) int {
+       cmn.PanicSanity("ValidatorCodec.Compare not implemented")
+       return 0
+}
+
+//--------------------------------------------------------------------------------
+// For testing...
+
+func RandValidator(randPower bool, minPower int64) (*Validator, *PrivValidator) {
+       privVal := GenPrivValidator()
+       _, tempFilePath := cmn.Tempfile("priv_validator_")
+       privVal.SetFile(tempFilePath)
+       votePower := minPower
+       if randPower {
+               votePower += int64(cmn.RandUint32())
+       }
+       val := NewValidator(privVal.PubKey, votePower)
+       return val, privVal
+}
diff --git a/types/validator_set.go b/types/validator_set.go
new file mode 100644 (file)
index 0000000..b374df5
--- /dev/null
@@ -0,0 +1,383 @@
+package types
+
+import (
+       "bytes"
+       "fmt"
+       "sort"
+       "strings"
+
+       "github.com/tendermint/go-wire"
+       cmn "github.com/tendermint/tmlibs/common"
+       "github.com/tendermint/tmlibs/merkle"
+)
+
+// ValidatorSet represent a set of *Validator at a given height.
+// The validators can be fetched by address or index.
+// The index is in order of .Address, so the indices are fixed
+// for all rounds of a given blockchain height.
+// On the other hand, the .AccumPower of each validator and
+// the designated .GetProposer() of a set changes every round,
+// upon calling .IncrementAccum().
+// NOTE: Not goroutine-safe.
+// NOTE: All get/set to validators should copy the value for safety.
+// TODO: consider validator Accum overflow
+type ValidatorSet struct {
+       // NOTE: persisted via reflect, must be exported.
+       Validators []*Validator `json:"validators"`
+       Proposer   *Validator   `json:"proposer"`
+
+       // cached (unexported)
+       totalVotingPower int64
+}
+
+func NewValidatorSet(vals []*Validator) *ValidatorSet {
+       validators := make([]*Validator, len(vals))
+       for i, val := range vals {
+               validators[i] = val.Copy()
+       }
+       sort.Sort(ValidatorsByAddress(validators))
+       vs := &ValidatorSet{
+               Validators: validators,
+       }
+
+       if vals != nil {
+               vs.IncrementAccum(1)
+       }
+
+       return vs
+}
+
+// incrementAccum and update the proposer
+// TODO: mind the overflow when times and votingPower shares too large.
+func (valSet *ValidatorSet) IncrementAccum(times int) {
+       // Add VotingPower * times to each validator and order into heap.
+       validatorsHeap := cmn.NewHeap()
+       for _, val := range valSet.Validators {
+               val.Accum += int64(val.VotingPower) * int64(times) // TODO: mind overflow
+               validatorsHeap.Push(val, accumComparable{val})
+       }
+
+       // Decrement the validator with most accum times times
+       for i := 0; i < times; i++ {
+               mostest := validatorsHeap.Peek().(*Validator)
+               if i == times-1 {
+                       valSet.Proposer = mostest
+               }
+               mostest.Accum -= int64(valSet.TotalVotingPower())
+               validatorsHeap.Update(mostest, accumComparable{mostest})
+       }
+}
+
+func (valSet *ValidatorSet) Copy() *ValidatorSet {
+       validators := make([]*Validator, len(valSet.Validators))
+       for i, val := range valSet.Validators {
+               // NOTE: must copy, since IncrementAccum updates in place.
+               validators[i] = val.Copy()
+       }
+       return &ValidatorSet{
+               Validators:       validators,
+               Proposer:         valSet.Proposer,
+               totalVotingPower: valSet.totalVotingPower,
+       }
+}
+
+func (valSet *ValidatorSet) HasAddress(address []byte) bool {
+       idx := sort.Search(len(valSet.Validators), func(i int) bool {
+               return bytes.Compare(address, valSet.Validators[i].Address) <= 0
+       })
+       return idx != len(valSet.Validators) && bytes.Compare(valSet.Validators[idx].Address, address) == 0
+}
+
+func (valSet *ValidatorSet) GetByAddress(address []byte) (index int, val *Validator) {
+       idx := sort.Search(len(valSet.Validators), func(i int) bool {
+               return bytes.Compare(address, valSet.Validators[i].Address) <= 0
+       })
+       if idx != len(valSet.Validators) && bytes.Compare(valSet.Validators[idx].Address, address) == 0 {
+               return idx, valSet.Validators[idx].Copy()
+       } else {
+               return 0, nil
+       }
+}
+
+func (valSet *ValidatorSet) GetByIndex(index int) (address []byte, val *Validator) {
+       val = valSet.Validators[index]
+       return val.Address, val.Copy()
+}
+
+func (valSet *ValidatorSet) Size() int {
+       return len(valSet.Validators)
+}
+
+func (valSet *ValidatorSet) TotalVotingPower() int64 {
+       if valSet.totalVotingPower == 0 {
+               for _, val := range valSet.Validators {
+                       valSet.totalVotingPower += val.VotingPower
+               }
+       }
+       return valSet.totalVotingPower
+}
+
+func (valSet *ValidatorSet) GetProposer() (proposer *Validator) {
+       if len(valSet.Validators) == 0 {
+               return nil
+       }
+       if valSet.Proposer == nil {
+               valSet.Proposer = valSet.findProposer()
+       }
+       return valSet.Proposer.Copy()
+}
+
+func (valSet *ValidatorSet) findProposer() *Validator {
+       var proposer *Validator
+       for _, val := range valSet.Validators {
+               if proposer == nil || !bytes.Equal(val.Address, proposer.Address) {
+                       proposer = proposer.CompareAccum(val)
+               }
+       }
+       return proposer
+}
+
+func (valSet *ValidatorSet) Hash() []byte {
+       if len(valSet.Validators) == 0 {
+               return nil
+       }
+       hashables := make([]merkle.Hashable, len(valSet.Validators))
+       for i, val := range valSet.Validators {
+               hashables[i] = val
+       }
+       return merkle.SimpleHashFromHashables(hashables)
+}
+
+func (valSet *ValidatorSet) Add(val *Validator) (added bool) {
+       val = val.Copy()
+       idx := sort.Search(len(valSet.Validators), func(i int) bool {
+               return bytes.Compare(val.Address, valSet.Validators[i].Address) <= 0
+       })
+       if idx == len(valSet.Validators) {
+               valSet.Validators = append(valSet.Validators, val)
+               // Invalidate cache
+               valSet.Proposer = nil
+               valSet.totalVotingPower = 0
+               return true
+       } else if bytes.Compare(valSet.Validators[idx].Address, val.Address) == 0 {
+               return false
+       } else {
+               newValidators := make([]*Validator, len(valSet.Validators)+1)
+               copy(newValidators[:idx], valSet.Validators[:idx])
+               newValidators[idx] = val
+               copy(newValidators[idx+1:], valSet.Validators[idx:])
+               valSet.Validators = newValidators
+               // Invalidate cache
+               valSet.Proposer = nil
+               valSet.totalVotingPower = 0
+               return true
+       }
+}
+
+func (valSet *ValidatorSet) Update(val *Validator) (updated bool) {
+       index, sameVal := valSet.GetByAddress(val.Address)
+       if sameVal == nil {
+               return false
+       } else {
+               valSet.Validators[index] = val.Copy()
+               // Invalidate cache
+               valSet.Proposer = nil
+               valSet.totalVotingPower = 0
+               return true
+       }
+}
+
+func (valSet *ValidatorSet) Remove(address []byte) (val *Validator, removed bool) {
+       idx := sort.Search(len(valSet.Validators), func(i int) bool {
+               return bytes.Compare(address, valSet.Validators[i].Address) <= 0
+       })
+       if idx == len(valSet.Validators) || bytes.Compare(valSet.Validators[idx].Address, address) != 0 {
+               return nil, false
+       } else {
+               removedVal := valSet.Validators[idx]
+               newValidators := valSet.Validators[:idx]
+               if idx+1 < len(valSet.Validators) {
+                       newValidators = append(newValidators, valSet.Validators[idx+1:]...)
+               }
+               valSet.Validators = newValidators
+               // Invalidate cache
+               valSet.Proposer = nil
+               valSet.totalVotingPower = 0
+               return removedVal, true
+       }
+}
+
+func (valSet *ValidatorSet) Iterate(fn func(index int, val *Validator) bool) {
+       for i, val := range valSet.Validators {
+               stop := fn(i, val.Copy())
+               if stop {
+                       break
+               }
+       }
+}
+
+// Verify that +2/3 of the set had signed the given signBytes
+func (valSet *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height int, commit *Commit) error {
+       if valSet.Size() != len(commit.Precommits) {
+               return fmt.Errorf("Invalid commit -- wrong set size: %v vs %v", valSet.Size(), len(commit.Precommits))
+       }
+       if height != commit.Height() {
+               return fmt.Errorf("Invalid commit -- wrong height: %v vs %v", height, commit.Height())
+       }
+
+       talliedVotingPower := int64(0)
+       round := commit.Round()
+
+       for idx, precommit := range commit.Precommits {
+               // may be nil if validator skipped.
+               if precommit == nil {
+                       continue
+               }
+               if precommit.Height != height {
+                       return fmt.Errorf("Invalid commit -- wrong height: %v vs %v", height, precommit.Height)
+               }
+               if precommit.Round != round {
+                       return fmt.Errorf("Invalid commit -- wrong round: %v vs %v", round, precommit.Round)
+               }
+               if precommit.Type != VoteTypePrecommit {
+                       return fmt.Errorf("Invalid commit -- not precommit @ index %v", idx)
+               }
+               _, val := valSet.GetByIndex(idx)
+               // Validate signature
+               precommitSignBytes := SignBytes(chainID, precommit)
+               if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) {
+                       return fmt.Errorf("Invalid commit -- invalid signature: %v", precommit)
+               }
+               if !blockID.Equals(precommit.BlockID) {
+                       continue // Not an error, but doesn't count
+               }
+               // Good precommit!
+               talliedVotingPower += val.VotingPower
+       }
+
+       if talliedVotingPower > valSet.TotalVotingPower()*2/3 {
+               return nil
+       } else {
+               return fmt.Errorf("Invalid commit -- insufficient voting power: got %v, needed %v",
+                       talliedVotingPower, (valSet.TotalVotingPower()*2/3 + 1))
+       }
+}
+
+// Verify that +2/3 of this set had signed the given signBytes.
+// Unlike VerifyCommit(), this function can verify commits with differeent sets.
+func (valSet *ValidatorSet) VerifyCommitAny(chainID string, blockID BlockID, height int, commit *Commit) error {
+       panic("Not yet implemented")
+       /*
+                       Start like:
+
+               FOR_LOOP:
+                       for _, val := range vals {
+                               if len(precommits) == 0 {
+                                       break FOR_LOOP
+                               }
+                               next := precommits[0]
+                               switch bytes.Compare(val.Address(), next.ValidatorAddress) {
+                               case -1:
+                                       continue FOR_LOOP
+                               case 0:
+                                       signBytes := tm.SignBytes(next)
+                                       ...
+                               case 1:
+                                       ... // error?
+                               }
+                       }
+       */
+}
+
+func (valSet *ValidatorSet) ToBytes() []byte {
+       buf, n, err := new(bytes.Buffer), new(int), new(error)
+       wire.WriteBinary(valSet, buf, n, err)
+       if *err != nil {
+               cmn.PanicCrisis(*err)
+       }
+       return buf.Bytes()
+}
+
+func (valSet *ValidatorSet) FromBytes(b []byte) {
+       r, n, err := bytes.NewReader(b), new(int), new(error)
+       wire.ReadBinary(valSet, r, 0, n, err)
+       if *err != nil {
+               // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED
+               cmn.PanicCrisis(*err)
+       }
+}
+
+func (valSet *ValidatorSet) String() string {
+       return valSet.StringIndented("")
+}
+
+func (valSet *ValidatorSet) StringIndented(indent string) string {
+       if valSet == nil {
+               return "nil-ValidatorSet"
+       }
+       valStrings := []string{}
+       valSet.Iterate(func(index int, val *Validator) bool {
+               valStrings = append(valStrings, val.String())
+               return false
+       })
+       return fmt.Sprintf(`ValidatorSet{
+%s  Proposer: %v
+%s  Validators:
+%s    %v
+%s}`,
+               indent, valSet.GetProposer().String(),
+               indent,
+               indent, strings.Join(valStrings, "\n"+indent+"    "),
+               indent)
+
+}
+
+//-------------------------------------
+// Implements sort for sorting validators by address.
+
+type ValidatorsByAddress []*Validator
+
+func (vs ValidatorsByAddress) Len() int {
+       return len(vs)
+}
+
+func (vs ValidatorsByAddress) Less(i, j int) bool {
+       return bytes.Compare(vs[i].Address, vs[j].Address) == -1
+}
+
+func (vs ValidatorsByAddress) Swap(i, j int) {
+       it := vs[i]
+       vs[i] = vs[j]
+       vs[j] = it
+}
+
+//-------------------------------------
+// Use with Heap for sorting validators by accum
+
+type accumComparable struct {
+       *Validator
+}
+
+// We want to find the validator with the greatest accum.
+func (ac accumComparable) Less(o interface{}) bool {
+       other := o.(accumComparable).Validator
+       larger := ac.CompareAccum(other)
+       return bytes.Equal(larger.Address, ac.Address)
+}
+
+//----------------------------------------
+// For testing
+
+// NOTE: PrivValidator are in order.
+func RandValidatorSet(numValidators int, votingPower int64) (*ValidatorSet, []*PrivValidator) {
+       vals := make([]*Validator, numValidators)
+       privValidators := make([]*PrivValidator, numValidators)
+       for i := 0; i < numValidators; i++ {
+               val, privValidator := RandValidator(false, votingPower)
+               vals[i] = val
+               privValidators[i] = privValidator
+       }
+       valSet := NewValidatorSet(vals)
+       sort.Sort(PrivValidatorsByAddress(privValidators))
+       return valSet, privValidators
+}
diff --git a/types/validator_set_test.go b/types/validator_set_test.go
new file mode 100644 (file)
index 0000000..71a1993
--- /dev/null
@@ -0,0 +1,208 @@
+package types
+
+import (
+       "bytes"
+       "strings"
+       "testing"
+
+       cmn "github.com/tendermint/tmlibs/common"
+       "github.com/tendermint/go-crypto"
+)
+
+func randPubKey() crypto.PubKey {
+       var pubKey [32]byte
+       copy(pubKey[:], cmn.RandBytes(32))
+       return crypto.PubKeyEd25519(pubKey).Wrap()
+}
+
+func randValidator_() *Validator {
+       val := NewValidator(randPubKey(), cmn.RandInt64())
+       val.Accum = cmn.RandInt64()
+       return val
+}
+
+func randValidatorSet(numValidators int) *ValidatorSet {
+       validators := make([]*Validator, numValidators)
+       for i := 0; i < numValidators; i++ {
+               validators[i] = randValidator_()
+       }
+       return NewValidatorSet(validators)
+}
+
+func TestCopy(t *testing.T) {
+       vset := randValidatorSet(10)
+       vsetHash := vset.Hash()
+       if len(vsetHash) == 0 {
+               t.Fatalf("ValidatorSet had unexpected zero hash")
+       }
+
+       vsetCopy := vset.Copy()
+       vsetCopyHash := vsetCopy.Hash()
+
+       if !bytes.Equal(vsetHash, vsetCopyHash) {
+               t.Fatalf("ValidatorSet copy had wrong hash. Orig: %X, Copy: %X", vsetHash, vsetCopyHash)
+       }
+}
+
+func TestProposerSelection1(t *testing.T) {
+       vset := NewValidatorSet([]*Validator{
+               newValidator([]byte("foo"), 1000),
+               newValidator([]byte("bar"), 300),
+               newValidator([]byte("baz"), 330),
+       })
+       proposers := []string{}
+       for i := 0; i < 99; i++ {
+               val := vset.GetProposer()
+               proposers = append(proposers, string(val.Address))
+               vset.IncrementAccum(1)
+       }
+       expected := `foo baz foo bar foo foo baz foo bar foo foo baz foo foo bar foo baz foo foo bar foo foo baz foo bar foo foo baz foo bar foo foo baz foo foo bar foo baz foo foo bar foo baz foo foo bar foo baz foo foo bar foo baz foo foo foo baz bar foo foo foo baz foo bar foo foo baz foo bar foo foo baz foo bar foo foo baz foo bar foo foo baz foo foo bar foo baz foo foo bar foo baz foo foo bar foo baz foo foo`
+       if expected != strings.Join(proposers, " ") {
+               t.Errorf("Expected sequence of proposers was\n%v\nbut got \n%v", expected, strings.Join(proposers, " "))
+       }
+}
+
+func newValidator(address []byte, power int64) *Validator {
+       return &Validator{Address: address, VotingPower: power}
+}
+
+func TestProposerSelection2(t *testing.T) {
+       addr0 := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+       addr1 := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
+       addr2 := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}
+
+       // when all voting power is same, we go in order of addresses
+       val0, val1, val2 := newValidator(addr0, 100), newValidator(addr1, 100), newValidator(addr2, 100)
+       valList := []*Validator{val0, val1, val2}
+       vals := NewValidatorSet(valList)
+       for i := 0; i < len(valList)*5; i++ {
+               ii := (i) % len(valList)
+               prop := vals.GetProposer()
+               if !bytes.Equal(prop.Address, valList[ii].Address) {
+                       t.Fatalf("(%d): Expected %X. Got %X", i, valList[ii].Address, prop.Address)
+               }
+               vals.IncrementAccum(1)
+       }
+
+       // One validator has more than the others, but not enough to propose twice in a row
+       *val2 = *newValidator(addr2, 400)
+       vals = NewValidatorSet(valList)
+       // vals.IncrementAccum(1)
+       prop := vals.GetProposer()
+       if !bytes.Equal(prop.Address, addr2) {
+               t.Fatalf("Expected address with highest voting power to be first proposer. Got %X", prop.Address)
+       }
+       vals.IncrementAccum(1)
+       prop = vals.GetProposer()
+       if !bytes.Equal(prop.Address, addr0) {
+               t.Fatalf("Expected smallest address to be validator. Got %X", prop.Address)
+       }
+
+       // One validator has more than the others, and enough to be proposer twice in a row
+       *val2 = *newValidator(addr2, 401)
+       vals = NewValidatorSet(valList)
+       prop = vals.GetProposer()
+       if !bytes.Equal(prop.Address, addr2) {
+               t.Fatalf("Expected address with highest voting power to be first proposer. Got %X", prop.Address)
+       }
+       vals.IncrementAccum(1)
+       prop = vals.GetProposer()
+       if !bytes.Equal(prop.Address, addr2) {
+               t.Fatalf("Expected address with highest voting power to be second proposer. Got %X", prop.Address)
+       }
+       vals.IncrementAccum(1)
+       prop = vals.GetProposer()
+       if !bytes.Equal(prop.Address, addr0) {
+               t.Fatalf("Expected smallest address to be validator. Got %X", prop.Address)
+       }
+
+       // each validator should be the proposer a proportional number of times
+       val0, val1, val2 = newValidator(addr0, 4), newValidator(addr1, 5), newValidator(addr2, 3)
+       valList = []*Validator{val0, val1, val2}
+       propCount := make([]int, 3)
+       vals = NewValidatorSet(valList)
+       N := 1
+       for i := 0; i < 120*N; i++ {
+               prop := vals.GetProposer()
+               ii := prop.Address[19]
+               propCount[ii] += 1
+               vals.IncrementAccum(1)
+       }
+
+       if propCount[0] != 40*N {
+               t.Fatalf("Expected prop count for validator with 4/12 of voting power to be %d/%d. Got %d/%d", 40*N, 120*N, propCount[0], 120*N)
+       }
+       if propCount[1] != 50*N {
+               t.Fatalf("Expected prop count for validator with 5/12 of voting power to be %d/%d. Got %d/%d", 50*N, 120*N, propCount[1], 120*N)
+       }
+       if propCount[2] != 30*N {
+               t.Fatalf("Expected prop count for validator with 3/12 of voting power to be %d/%d. Got %d/%d", 30*N, 120*N, propCount[2], 120*N)
+       }
+}
+
+func TestProposerSelection3(t *testing.T) {
+       vset := NewValidatorSet([]*Validator{
+               newValidator([]byte("a"), 1),
+               newValidator([]byte("b"), 1),
+               newValidator([]byte("c"), 1),
+               newValidator([]byte("d"), 1),
+       })
+
+       proposerOrder := make([]*Validator, 4)
+       for i := 0; i < 4; i++ {
+               proposerOrder[i] = vset.GetProposer()
+               vset.IncrementAccum(1)
+       }
+
+       // i for the loop
+       // j for the times
+       // we should go in order for ever, despite some IncrementAccums with times > 1
+       var i, j int
+       for ; i < 10000; i++ {
+               got := vset.GetProposer().Address
+               expected := proposerOrder[j%4].Address
+               if !bytes.Equal(got, expected) {
+                       t.Fatalf(cmn.Fmt("vset.Proposer (%X) does not match expected proposer (%X) for (%d, %d)", got, expected, i, j))
+               }
+
+               // serialize, deserialize, check proposer
+               b := vset.ToBytes()
+               vset.FromBytes(b)
+
+               computed := vset.GetProposer() // findGetProposer()
+               if i != 0 {
+                       if !bytes.Equal(got, computed.Address) {
+                               t.Fatalf(cmn.Fmt("vset.Proposer (%X) does not match computed proposer (%X) for (%d, %d)", got, computed.Address, i, j))
+                       }
+               }
+
+               // times is usually 1
+               times := 1
+               mod := (cmn.RandInt() % 5) + 1
+               if cmn.RandInt()%mod > 0 {
+                       // sometimes its up to 5
+                       times = cmn.RandInt() % 5
+               }
+               vset.IncrementAccum(times)
+
+               j += times
+       }
+}
+
+func BenchmarkValidatorSetCopy(b *testing.B) {
+       b.StopTimer()
+       vset := NewValidatorSet([]*Validator{})
+       for i := 0; i < 1000; i++ {
+               privKey := crypto.GenPrivKeyEd25519()
+               pubKey := privKey.PubKey()
+               val := NewValidator(pubKey, 0)
+               if !vset.Add(val) {
+                       panic("Failed to add validator")
+               }
+       }
+       b.StartTimer()
+
+       for i := 0; i < b.N; i++ {
+               vset.Copy()
+       }
+}
diff --git a/types/vote.go b/types/vote.go
new file mode 100644 (file)
index 0000000..164293c
--- /dev/null
@@ -0,0 +1,90 @@
+package types
+
+import (
+       "errors"
+       "fmt"
+       "io"
+
+       "github.com/tendermint/go-crypto"
+       "github.com/tendermint/go-wire"
+       "github.com/tendermint/go-wire/data"
+       cmn "github.com/tendermint/tmlibs/common"
+)
+
+var (
+       ErrVoteUnexpectedStep          = errors.New("Unexpected step")
+       ErrVoteInvalidValidatorIndex   = errors.New("Invalid round vote validator index")
+       ErrVoteInvalidValidatorAddress = errors.New("Invalid round vote validator address")
+       ErrVoteInvalidSignature        = errors.New("Invalid round vote signature")
+       ErrVoteInvalidBlockHash        = errors.New("Invalid block hash")
+)
+
+type ErrVoteConflictingVotes struct {
+       VoteA *Vote
+       VoteB *Vote
+}
+
+func (err *ErrVoteConflictingVotes) Error() string {
+       return "Conflicting votes"
+}
+
+// Types of votes
+// TODO Make a new type "VoteType"
+const (
+       VoteTypePrevote   = byte(0x01)
+       VoteTypePrecommit = byte(0x02)
+)
+
+func IsVoteTypeValid(type_ byte) bool {
+       switch type_ {
+       case VoteTypePrevote:
+               return true
+       case VoteTypePrecommit:
+               return true
+       default:
+               return false
+       }
+}
+
+// Represents a prevote, precommit, or commit vote from validators for consensus.
+type Vote struct {
+       ValidatorAddress data.Bytes       `json:"validator_address"`
+       ValidatorIndex   int              `json:"validator_index"`
+       Height           int              `json:"height"`
+       Round            int              `json:"round"`
+       Type             byte             `json:"type"`
+       BlockID          BlockID          `json:"block_id"` // zero if vote is nil.
+       Signature        crypto.Signature `json:"signature"`
+}
+
+func (vote *Vote) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
+       wire.WriteJSON(CanonicalJSONOnceVote{
+               chainID,
+               CanonicalVote(vote),
+       }, w, n, err)
+}
+
+func (vote *Vote) Copy() *Vote {
+       voteCopy := *vote
+       return &voteCopy
+}
+
+func (vote *Vote) String() string {
+       if vote == nil {
+               return "nil-Vote"
+       }
+       var typeString string
+       switch vote.Type {
+       case VoteTypePrevote:
+               typeString = "Prevote"
+       case VoteTypePrecommit:
+               typeString = "Precommit"
+       default:
+               cmn.PanicSanity("Unknown vote type")
+       }
+
+       return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %v}",
+               vote.ValidatorIndex, cmn.Fingerprint(vote.ValidatorAddress),
+               vote.Height, vote.Round, vote.Type, typeString,
+               cmn.Fingerprint(vote.BlockID.Hash), vote.Signature)
+}
diff --git a/types/vote_set.go b/types/vote_set.go
new file mode 100644 (file)
index 0000000..938dbcb
--- /dev/null
@@ -0,0 +1,527 @@
+package types
+
+import (
+       "bytes"
+       "fmt"
+       "strings"
+       "sync"
+
+       . "github.com/tendermint/tmlibs/common"
+)
+
+/*
+       VoteSet helps collect signatures from validators at each height+round for a
+       predefined vote type.
+
+       We need VoteSet to be able to keep track of conflicting votes when validators
+       double-sign.  Yet, we can't keep track of *all* the votes seen, as that could
+       be a DoS attack vector.
+
+       There are two storage areas for votes.
+       1. voteSet.votes
+       2. voteSet.votesByBlock
+
+       `.votes` is the "canonical" list of votes.  It always has at least one vote,
+       if a vote from a validator had been seen at all.  Usually it keeps track of
+       the first vote seen, but when a 2/3 majority is found, votes for that get
+       priority and are copied over from `.votesByBlock`.
+
+       `.votesByBlock` keeps track of a list of votes for a particular block.  There
+       are two ways a &blockVotes{} gets created in `.votesByBlock`.
+       1. the first vote seen by a validator was for the particular block.
+       2. a peer claims to have seen 2/3 majority for the particular block.
+
+       Since the first vote from a validator will always get added in `.votesByBlock`
+       , all votes in `.votes` will have a corresponding entry in `.votesByBlock`.
+
+       When a &blockVotes{} in `.votesByBlock` reaches a 2/3 majority quorum, its
+       votes are copied into `.votes`.
+
+       All this is memory bounded because conflicting votes only get added if a peer
+       told us to track that block, each peer only gets to tell us 1 such block, and,
+       there's only a limited number of peers.
+
+       NOTE: Assumes that the sum total of voting power does not exceed MaxUInt64.
+*/
+type VoteSet struct {
+       chainID string
+       height  int
+       round   int
+       type_   byte
+
+       mtx           sync.Mutex
+       valSet        *ValidatorSet
+       votesBitArray *BitArray
+       votes         []*Vote                // Primary votes to share
+       sum           int64                  // Sum of voting power for seen votes, discounting conflicts
+       maj23         *BlockID               // First 2/3 majority seen
+       votesByBlock  map[string]*blockVotes // string(blockHash|blockParts) -> blockVotes
+       peerMaj23s    map[string]BlockID     // Maj23 for each peer
+}
+
+// Constructs a new VoteSet struct used to accumulate votes for given height/round.
+func NewVoteSet(chainID string, height int, round int, type_ byte, valSet *ValidatorSet) *VoteSet {
+       if height == 0 {
+               PanicSanity("Cannot make VoteSet for height == 0, doesn't make sense.")
+       }
+       return &VoteSet{
+               chainID:       chainID,
+               height:        height,
+               round:         round,
+               type_:         type_,
+               valSet:        valSet,
+               votesBitArray: NewBitArray(valSet.Size()),
+               votes:         make([]*Vote, valSet.Size()),
+               sum:           0,
+               maj23:         nil,
+               votesByBlock:  make(map[string]*blockVotes, valSet.Size()),
+               peerMaj23s:    make(map[string]BlockID),
+       }
+}
+
+func (voteSet *VoteSet) ChainID() string {
+       return voteSet.chainID
+}
+
+func (voteSet *VoteSet) Height() int {
+       if voteSet == nil {
+               return 0
+       } else {
+               return voteSet.height
+       }
+}
+
+func (voteSet *VoteSet) Round() int {
+       if voteSet == nil {
+               return -1
+       } else {
+               return voteSet.round
+       }
+}
+
+func (voteSet *VoteSet) Type() byte {
+       if voteSet == nil {
+               return 0x00
+       } else {
+               return voteSet.type_
+       }
+}
+
+func (voteSet *VoteSet) Size() int {
+       if voteSet == nil {
+               return 0
+       } else {
+               return voteSet.valSet.Size()
+       }
+}
+
+// Returns added=true if vote is valid and new.
+// Otherwise returns err=ErrVote[
+//             UnexpectedStep | InvalidIndex | InvalidAddress |
+//             InvalidSignature | InvalidBlockHash | ConflictingVotes ]
+// Duplicate votes return added=false, err=nil.
+// Conflicting votes return added=*, err=ErrVoteConflictingVotes.
+// NOTE: vote should not be mutated after adding.
+// NOTE: VoteSet must not be nil
+func (voteSet *VoteSet) AddVote(vote *Vote) (added bool, err error) {
+       if voteSet == nil {
+               PanicSanity("AddVote() on nil VoteSet")
+       }
+       voteSet.mtx.Lock()
+       defer voteSet.mtx.Unlock()
+
+       return voteSet.addVote(vote)
+}
+
+// NOTE: Validates as much as possible before attempting to verify the signature.
+func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
+       valIndex := vote.ValidatorIndex
+       valAddr := vote.ValidatorAddress
+       blockKey := vote.BlockID.Key()
+
+       // Ensure that validator index was set
+       if valIndex < 0 || len(valAddr) == 0 {
+               panic("Validator index or address was not set in vote.")
+       }
+
+       // Make sure the step matches.
+       if (vote.Height != voteSet.height) ||
+               (vote.Round != voteSet.round) ||
+               (vote.Type != voteSet.type_) {
+               return false, ErrVoteUnexpectedStep
+       }
+
+       // Ensure that signer is a validator.
+       lookupAddr, val := voteSet.valSet.GetByIndex(valIndex)
+       if val == nil {
+               return false, ErrVoteInvalidValidatorIndex
+       }
+
+       // Ensure that the signer has the right address
+       if !bytes.Equal(valAddr, lookupAddr) {
+               return false, ErrVoteInvalidValidatorAddress
+       }
+
+       // If we already know of this vote, return false.
+       if existing, ok := voteSet.getVote(valIndex, blockKey); ok {
+               if existing.Signature.Equals(vote.Signature) {
+                       return false, nil // duplicate
+               } else {
+                       return false, ErrVoteInvalidSignature // NOTE: assumes deterministic signatures
+               }
+       }
+
+       // Check signature.
+       if !val.PubKey.VerifyBytes(SignBytes(voteSet.chainID, vote), vote.Signature) {
+               // Bad signature.
+               return false, ErrVoteInvalidSignature
+       }
+
+       // Add vote and get conflicting vote if any
+       added, conflicting := voteSet.addVerifiedVote(vote, blockKey, val.VotingPower)
+       if conflicting != nil {
+               return added, &ErrVoteConflictingVotes{
+                       VoteA: conflicting,
+                       VoteB: vote,
+               }
+       } else {
+               if !added {
+                       PanicSanity("Expected to add non-conflicting vote")
+               }
+               return added, nil
+       }
+
+}
+
+// Returns (vote, true) if vote exists for valIndex and blockKey
+func (voteSet *VoteSet) getVote(valIndex int, blockKey string) (vote *Vote, ok bool) {
+       if existing := voteSet.votes[valIndex]; existing != nil && existing.BlockID.Key() == blockKey {
+               return existing, true
+       }
+       if existing := voteSet.votesByBlock[blockKey].getByIndex(valIndex); existing != nil {
+               return existing, true
+       }
+       return nil, false
+}
+
+// Assumes signature is valid.
+// If conflicting vote exists, returns it.
+func (voteSet *VoteSet) addVerifiedVote(vote *Vote, blockKey string, votingPower int64) (added bool, conflicting *Vote) {
+       valIndex := vote.ValidatorIndex
+
+       // Already exists in voteSet.votes?
+       if existing := voteSet.votes[valIndex]; existing != nil {
+               if existing.BlockID.Equals(vote.BlockID) {
+                       PanicSanity("addVerifiedVote does not expect duplicate votes")
+               } else {
+                       conflicting = existing
+               }
+               // Replace vote if blockKey matches voteSet.maj23.
+               if voteSet.maj23 != nil && voteSet.maj23.Key() == blockKey {
+                       voteSet.votes[valIndex] = vote
+                       voteSet.votesBitArray.SetIndex(valIndex, true)
+               }
+               // Otherwise don't add it to voteSet.votes
+       } else {
+               // Add to voteSet.votes and incr .sum
+               voteSet.votes[valIndex] = vote
+               voteSet.votesBitArray.SetIndex(valIndex, true)
+               voteSet.sum += votingPower
+       }
+
+       votesByBlock, ok := voteSet.votesByBlock[blockKey]
+       if ok {
+               if conflicting != nil && !votesByBlock.peerMaj23 {
+                       // There's a conflict and no peer claims that this block is special.
+                       return false, conflicting
+               }
+               // We'll add the vote in a bit.
+       } else {
+               // .votesByBlock doesn't exist...
+               if conflicting != nil {
+                       // ... and there's a conflicting vote.
+                       // We're not even tracking this blockKey, so just forget it.
+                       return false, conflicting
+               } else {
+                       // ... and there's no conflicting vote.
+                       // Start tracking this blockKey
+                       votesByBlock = newBlockVotes(false, voteSet.valSet.Size())
+                       voteSet.votesByBlock[blockKey] = votesByBlock
+                       // We'll add the vote in a bit.
+               }
+       }
+
+       // Before adding to votesByBlock, see if we'll exceed quorum
+       origSum := votesByBlock.sum
+       quorum := voteSet.valSet.TotalVotingPower()*2/3 + 1
+
+       // Add vote to votesByBlock
+       votesByBlock.addVerifiedVote(vote, votingPower)
+
+       // If we just crossed the quorum threshold and have 2/3 majority...
+       if origSum < quorum && quorum <= votesByBlock.sum {
+               // Only consider the first quorum reached
+               if voteSet.maj23 == nil {
+                       maj23BlockID := vote.BlockID
+                       voteSet.maj23 = &maj23BlockID
+                       // And also copy votes over to voteSet.votes
+                       for i, vote := range votesByBlock.votes {
+                               if vote != nil {
+                                       voteSet.votes[i] = vote
+                               }
+                       }
+               }
+       }
+
+       return true, conflicting
+}
+
+// If a peer claims that it has 2/3 majority for given blockKey, call this.
+// NOTE: if there are too many peers, or too much peer churn,
+// this can cause memory issues.
+// TODO: implement ability to remove peers too
+// NOTE: VoteSet must not be nil
+func (voteSet *VoteSet) SetPeerMaj23(peerID string, blockID BlockID) {
+       if voteSet == nil {
+               PanicSanity("SetPeerMaj23() on nil VoteSet")
+       }
+       voteSet.mtx.Lock()
+       defer voteSet.mtx.Unlock()
+
+       blockKey := blockID.Key()
+
+       // Make sure peer hasn't already told us something.
+       if existing, ok := voteSet.peerMaj23s[peerID]; ok {
+               if existing.Equals(blockID) {
+                       return // Nothing to do
+               } else {
+                       return // TODO bad peer!
+               }
+       }
+       voteSet.peerMaj23s[peerID] = blockID
+
+       // Create .votesByBlock entry if needed.
+       votesByBlock, ok := voteSet.votesByBlock[blockKey]
+       if ok {
+               if votesByBlock.peerMaj23 {
+                       return // Nothing to do
+               } else {
+                       votesByBlock.peerMaj23 = true
+                       // No need to copy votes, already there.
+               }
+       } else {
+               votesByBlock = newBlockVotes(true, voteSet.valSet.Size())
+               voteSet.votesByBlock[blockKey] = votesByBlock
+               // No need to copy votes, no votes to copy over.
+       }
+}
+
+func (voteSet *VoteSet) BitArray() *BitArray {
+       if voteSet == nil {
+               return nil
+       }
+       voteSet.mtx.Lock()
+       defer voteSet.mtx.Unlock()
+       return voteSet.votesBitArray.Copy()
+}
+
+func (voteSet *VoteSet) BitArrayByBlockID(blockID BlockID) *BitArray {
+       if voteSet == nil {
+               return nil
+       }
+       voteSet.mtx.Lock()
+       defer voteSet.mtx.Unlock()
+       votesByBlock, ok := voteSet.votesByBlock[blockID.Key()]
+       if ok {
+               return votesByBlock.bitArray.Copy()
+       }
+       return nil
+}
+
+// NOTE: if validator has conflicting votes, returns "canonical" vote
+func (voteSet *VoteSet) GetByIndex(valIndex int) *Vote {
+       if voteSet == nil {
+               return nil
+       }
+       voteSet.mtx.Lock()
+       defer voteSet.mtx.Unlock()
+       return voteSet.votes[valIndex]
+}
+
+func (voteSet *VoteSet) GetByAddress(address []byte) *Vote {
+       if voteSet == nil {
+               return nil
+       }
+       voteSet.mtx.Lock()
+       defer voteSet.mtx.Unlock()
+       valIndex, val := voteSet.valSet.GetByAddress(address)
+       if val == nil {
+               PanicSanity("GetByAddress(address) returned nil")
+       }
+       return voteSet.votes[valIndex]
+}
+
+func (voteSet *VoteSet) HasTwoThirdsMajority() bool {
+       if voteSet == nil {
+               return false
+       }
+       voteSet.mtx.Lock()
+       defer voteSet.mtx.Unlock()
+       return voteSet.maj23 != nil
+}
+
+func (voteSet *VoteSet) IsCommit() bool {
+       if voteSet == nil {
+               return false
+       }
+       if voteSet.type_ != VoteTypePrecommit {
+               return false
+       }
+       voteSet.mtx.Lock()
+       defer voteSet.mtx.Unlock()
+       return voteSet.maj23 != nil
+}
+
+func (voteSet *VoteSet) HasTwoThirdsAny() bool {
+       if voteSet == nil {
+               return false
+       }
+       voteSet.mtx.Lock()
+       defer voteSet.mtx.Unlock()
+       return voteSet.sum > voteSet.valSet.TotalVotingPower()*2/3
+}
+
+func (voteSet *VoteSet) HasAll() bool {
+       return voteSet.sum == voteSet.valSet.TotalVotingPower()
+}
+
+// Returns either a blockhash (or nil) that received +2/3 majority.
+// If there exists no such majority, returns (nil, PartSetHeader{}, false).
+func (voteSet *VoteSet) TwoThirdsMajority() (blockID BlockID, ok bool) {
+       if voteSet == nil {
+               return BlockID{}, false
+       }
+       voteSet.mtx.Lock()
+       defer voteSet.mtx.Unlock()
+       if voteSet.maj23 != nil {
+               return *voteSet.maj23, true
+       } else {
+               return BlockID{}, false
+       }
+}
+
+func (voteSet *VoteSet) String() string {
+       if voteSet == nil {
+               return "nil-VoteSet"
+       }
+       return voteSet.StringIndented("")
+}
+
+func (voteSet *VoteSet) StringIndented(indent string) string {
+       voteStrings := make([]string, len(voteSet.votes))
+       for i, vote := range voteSet.votes {
+               if vote == nil {
+                       voteStrings[i] = "nil-Vote"
+               } else {
+                       voteStrings[i] = vote.String()
+               }
+       }
+       return fmt.Sprintf(`VoteSet{
+%s  H:%v R:%v T:%v
+%s  %v
+%s  %v
+%s  %v
+%s}`,
+               indent, voteSet.height, voteSet.round, voteSet.type_,
+               indent, strings.Join(voteStrings, "\n"+indent+"  "),
+               indent, voteSet.votesBitArray,
+               indent, voteSet.peerMaj23s,
+               indent)
+}
+
+func (voteSet *VoteSet) StringShort() string {
+       if voteSet == nil {
+               return "nil-VoteSet"
+       }
+       voteSet.mtx.Lock()
+       defer voteSet.mtx.Unlock()
+       return fmt.Sprintf(`VoteSet{H:%v R:%v T:%v +2/3:%v %v %v}`,
+               voteSet.height, voteSet.round, voteSet.type_, voteSet.maj23, voteSet.votesBitArray, voteSet.peerMaj23s)
+}
+
+//--------------------------------------------------------------------------------
+// Commit
+
+func (voteSet *VoteSet) MakeCommit() *Commit {
+       if voteSet.type_ != VoteTypePrecommit {
+               PanicSanity("Cannot MakeCommit() unless VoteSet.Type is VoteTypePrecommit")
+       }
+       voteSet.mtx.Lock()
+       defer voteSet.mtx.Unlock()
+
+       // Make sure we have a 2/3 majority
+       if voteSet.maj23 == nil {
+               PanicSanity("Cannot MakeCommit() unless a blockhash has +2/3")
+       }
+
+       // For every validator, get the precommit
+       votesCopy := make([]*Vote, len(voteSet.votes))
+       copy(votesCopy, voteSet.votes)
+       return &Commit{
+               BlockID:    *voteSet.maj23,
+               Precommits: votesCopy,
+       }
+}
+
+//--------------------------------------------------------------------------------
+
+/*
+       Votes for a particular block
+       There are two ways a *blockVotes gets created for a blockKey.
+       1. first (non-conflicting) vote of a validator w/ blockKey (peerMaj23=false)
+       2. A peer claims to have a 2/3 majority w/ blockKey (peerMaj23=true)
+*/
+type blockVotes struct {
+       peerMaj23 bool      // peer claims to have maj23
+       bitArray  *BitArray // valIndex -> hasVote?
+       votes     []*Vote   // valIndex -> *Vote
+       sum       int64     // vote sum
+}
+
+func newBlockVotes(peerMaj23 bool, numValidators int) *blockVotes {
+       return &blockVotes{
+               peerMaj23: peerMaj23,
+               bitArray:  NewBitArray(numValidators),
+               votes:     make([]*Vote, numValidators),
+               sum:       0,
+       }
+}
+
+func (vs *blockVotes) addVerifiedVote(vote *Vote, votingPower int64) {
+       valIndex := vote.ValidatorIndex
+       if existing := vs.votes[valIndex]; existing == nil {
+               vs.bitArray.SetIndex(valIndex, true)
+               vs.votes[valIndex] = vote
+               vs.sum += votingPower
+       }
+}
+
+func (vs *blockVotes) getByIndex(index int) *Vote {
+       if vs == nil {
+               return nil
+       }
+       return vs.votes[index]
+}
+
+//--------------------------------------------------------------------------------
+
+// Common interface between *consensus.VoteSet and types.Commit
+type VoteSetReader interface {
+       Height() int
+       Round() int
+       Type() byte
+       Size() int
+       BitArray() *BitArray
+       GetByIndex(int) *Vote
+       IsCommit() bool
+}
diff --git a/types/vote_set_test.go b/types/vote_set_test.go
new file mode 100644 (file)
index 0000000..84e13ac
--- /dev/null
@@ -0,0 +1,471 @@
+package types
+
+import (
+       "bytes"
+
+       "github.com/tendermint/go-crypto"
+       . "github.com/tendermint/tmlibs/common"
+       . "github.com/tendermint/tmlibs/test"
+
+       "testing"
+)
+
+// NOTE: privValidators are in order
+func randVoteSet(height int, round int, type_ byte, numValidators int, votingPower int64) (*VoteSet, *ValidatorSet, []*PrivValidator) {
+       valSet, privValidators := RandValidatorSet(numValidators, votingPower)
+       return NewVoteSet("test_chain_id", height, round, type_, valSet), valSet, privValidators
+}
+
+// Convenience: Return new vote with different validator address/index
+func withValidator(vote *Vote, addr []byte, idx int) *Vote {
+       vote = vote.Copy()
+       vote.ValidatorAddress = addr
+       vote.ValidatorIndex = idx
+       return vote
+}
+
+// Convenience: Return new vote with different height
+func withHeight(vote *Vote, height int) *Vote {
+       vote = vote.Copy()
+       vote.Height = height
+       return vote
+}
+
+// Convenience: Return new vote with different round
+func withRound(vote *Vote, round int) *Vote {
+       vote = vote.Copy()
+       vote.Round = round
+       return vote
+}
+
+// Convenience: Return new vote with different type
+func withType(vote *Vote, type_ byte) *Vote {
+       vote = vote.Copy()
+       vote.Type = type_
+       return vote
+}
+
+// Convenience: Return new vote with different blockHash
+func withBlockHash(vote *Vote, blockHash []byte) *Vote {
+       vote = vote.Copy()
+       vote.BlockID.Hash = blockHash
+       return vote
+}
+
+// Convenience: Return new vote with different blockParts
+func withBlockPartsHeader(vote *Vote, blockPartsHeader PartSetHeader) *Vote {
+       vote = vote.Copy()
+       vote.BlockID.PartsHeader = blockPartsHeader
+       return vote
+}
+
+func signAddVote(privVal *PrivValidator, vote *Vote, voteSet *VoteSet) (bool, error) {
+       vote.Signature = privVal.Sign(SignBytes(voteSet.ChainID(), vote))
+       added, err := voteSet.AddVote(vote)
+       return added, err
+}
+
+func TestAddVote(t *testing.T) {
+       height, round := 1, 0
+       voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1)
+       val0 := privValidators[0]
+
+       // t.Logf(">> %v", voteSet)
+
+       if voteSet.GetByAddress(val0.Address) != nil {
+               t.Errorf("Expected GetByAddress(val0.Address) to be nil")
+       }
+       if voteSet.BitArray().GetIndex(0) {
+               t.Errorf("Expected BitArray.GetIndex(0) to be false")
+       }
+       blockID, ok := voteSet.TwoThirdsMajority()
+       if ok || !blockID.IsZero() {
+               t.Errorf("There should be no 2/3 majority")
+       }
+
+       vote := &Vote{
+               ValidatorAddress: val0.Address,
+               ValidatorIndex:   0, // since privValidators are in order
+               Height:           height,
+               Round:            round,
+               Type:             VoteTypePrevote,
+               BlockID:          BlockID{nil, PartSetHeader{}},
+       }
+       _, err := signAddVote(val0, vote, voteSet)
+       if err != nil {
+               t.Error(err)
+       }
+
+       if voteSet.GetByAddress(val0.Address) == nil {
+               t.Errorf("Expected GetByAddress(val0.Address) to be present")
+       }
+       if !voteSet.BitArray().GetIndex(0) {
+               t.Errorf("Expected BitArray.GetIndex(0) to be true")
+       }
+       blockID, ok = voteSet.TwoThirdsMajority()
+       if ok || !blockID.IsZero() {
+               t.Errorf("There should be no 2/3 majority")
+       }
+}
+
+func Test2_3Majority(t *testing.T) {
+       height, round := 1, 0
+       voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1)
+
+       voteProto := &Vote{
+               ValidatorAddress: nil, // NOTE: must fill in
+               ValidatorIndex:   -1,  // NOTE: must fill in
+               Height:           height,
+               Round:            round,
+               Type:             VoteTypePrevote,
+               BlockID:          BlockID{nil, PartSetHeader{}},
+       }
+       // 6 out of 10 voted for nil.
+       for i := 0; i < 6; i++ {
+               vote := withValidator(voteProto, privValidators[i].Address, i)
+               signAddVote(privValidators[i], vote, voteSet)
+       }
+       blockID, ok := voteSet.TwoThirdsMajority()
+       if ok || !blockID.IsZero() {
+               t.Errorf("There should be no 2/3 majority")
+       }
+
+       // 7th validator voted for some blockhash
+       {
+               vote := withValidator(voteProto, privValidators[6].Address, 6)
+               signAddVote(privValidators[6], withBlockHash(vote, RandBytes(32)), voteSet)
+               blockID, ok = voteSet.TwoThirdsMajority()
+               if ok || !blockID.IsZero() {
+                       t.Errorf("There should be no 2/3 majority")
+               }
+       }
+
+       // 8th validator voted for nil.
+       {
+               vote := withValidator(voteProto, privValidators[7].Address, 7)
+               signAddVote(privValidators[7], vote, voteSet)
+               blockID, ok = voteSet.TwoThirdsMajority()
+               if !ok || !blockID.IsZero() {
+                       t.Errorf("There should be 2/3 majority for nil")
+               }
+       }
+}
+
+func Test2_3MajorityRedux(t *testing.T) {
+       height, round := 1, 0
+       voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 100, 1)
+
+       blockHash := crypto.CRandBytes(32)
+       blockPartsTotal := 123
+       blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
+
+       voteProto := &Vote{
+               ValidatorAddress: nil, // NOTE: must fill in
+               ValidatorIndex:   -1,  // NOTE: must fill in
+               Height:           height,
+               Round:            round,
+               Type:             VoteTypePrevote,
+               BlockID:          BlockID{blockHash, blockPartsHeader},
+       }
+
+       // 66 out of 100 voted for nil.
+       for i := 0; i < 66; i++ {
+               vote := withValidator(voteProto, privValidators[i].Address, i)
+               signAddVote(privValidators[i], vote, voteSet)
+       }
+       blockID, ok := voteSet.TwoThirdsMajority()
+       if ok || !blockID.IsZero() {
+               t.Errorf("There should be no 2/3 majority")
+       }
+
+       // 67th validator voted for nil
+       {
+               vote := withValidator(voteProto, privValidators[66].Address, 66)
+               signAddVote(privValidators[66], withBlockHash(vote, nil), voteSet)
+               blockID, ok = voteSet.TwoThirdsMajority()
+               if ok || !blockID.IsZero() {
+                       t.Errorf("There should be no 2/3 majority: last vote added was nil")
+               }
+       }
+
+       // 68th validator voted for a different BlockParts PartSetHeader
+       {
+               vote := withValidator(voteProto, privValidators[67].Address, 67)
+               blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
+               signAddVote(privValidators[67], withBlockPartsHeader(vote, blockPartsHeader), voteSet)
+               blockID, ok = voteSet.TwoThirdsMajority()
+               if ok || !blockID.IsZero() {
+                       t.Errorf("There should be no 2/3 majority: last vote added had different PartSetHeader Hash")
+               }
+       }
+
+       // 69th validator voted for different BlockParts Total
+       {
+               vote := withValidator(voteProto, privValidators[68].Address, 68)
+               blockPartsHeader := PartSetHeader{blockPartsTotal + 1, blockPartsHeader.Hash}
+               signAddVote(privValidators[68], withBlockPartsHeader(vote, blockPartsHeader), voteSet)
+               blockID, ok = voteSet.TwoThirdsMajority()
+               if ok || !blockID.IsZero() {
+                       t.Errorf("There should be no 2/3 majority: last vote added had different PartSetHeader Total")
+               }
+       }
+
+       // 70th validator voted for different BlockHash
+       {
+               vote := withValidator(voteProto, privValidators[69].Address, 69)
+               signAddVote(privValidators[69], withBlockHash(vote, RandBytes(32)), voteSet)
+               blockID, ok = voteSet.TwoThirdsMajority()
+               if ok || !blockID.IsZero() {
+                       t.Errorf("There should be no 2/3 majority: last vote added had different BlockHash")
+               }
+       }
+
+       // 71st validator voted for the right BlockHash & BlockPartsHeader
+       {
+               vote := withValidator(voteProto, privValidators[70].Address, 70)
+               signAddVote(privValidators[70], vote, voteSet)
+               blockID, ok = voteSet.TwoThirdsMajority()
+               if !ok || !blockID.Equals(BlockID{blockHash, blockPartsHeader}) {
+                       t.Errorf("There should be 2/3 majority")
+               }
+       }
+}
+
+func TestBadVotes(t *testing.T) {
+       height, round := 1, 0
+       voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1)
+
+       voteProto := &Vote{
+               ValidatorAddress: nil,
+               ValidatorIndex:   -1,
+               Height:           height,
+               Round:            round,
+               Type:             VoteTypePrevote,
+               BlockID:          BlockID{nil, PartSetHeader{}},
+       }
+
+       // val0 votes for nil.
+       {
+               vote := withValidator(voteProto, privValidators[0].Address, 0)
+               added, err := signAddVote(privValidators[0], vote, voteSet)
+               if !added || err != nil {
+                       t.Errorf("Expected VoteSet.Add to succeed")
+               }
+       }
+
+       // val0 votes again for some block.
+       {
+               vote := withValidator(voteProto, privValidators[0].Address, 0)
+               added, err := signAddVote(privValidators[0], withBlockHash(vote, RandBytes(32)), voteSet)
+               if added || err == nil {
+                       t.Errorf("Expected VoteSet.Add to fail, conflicting vote.")
+               }
+       }
+
+       // val1 votes on another height
+       {
+               vote := withValidator(voteProto, privValidators[1].Address, 1)
+               added, err := signAddVote(privValidators[1], withHeight(vote, height+1), voteSet)
+               if added || err == nil {
+                       t.Errorf("Expected VoteSet.Add to fail, wrong height")
+               }
+       }
+
+       // val2 votes on another round
+       {
+               vote := withValidator(voteProto, privValidators[2].Address, 2)
+               added, err := signAddVote(privValidators[2], withRound(vote, round+1), voteSet)
+               if added || err == nil {
+                       t.Errorf("Expected VoteSet.Add to fail, wrong round")
+               }
+       }
+
+       // val3 votes of another type.
+       {
+               vote := withValidator(voteProto, privValidators[3].Address, 3)
+               added, err := signAddVote(privValidators[3], withType(vote, VoteTypePrecommit), voteSet)
+               if added || err == nil {
+                       t.Errorf("Expected VoteSet.Add to fail, wrong type")
+               }
+       }
+}
+
+func TestConflicts(t *testing.T) {
+       height, round := 1, 0
+       voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 4, 1)
+       blockHash1 := RandBytes(32)
+       blockHash2 := RandBytes(32)
+
+       voteProto := &Vote{
+               ValidatorAddress: nil,
+               ValidatorIndex:   -1,
+               Height:           height,
+               Round:            round,
+               Type:             VoteTypePrevote,
+               BlockID:          BlockID{nil, PartSetHeader{}},
+       }
+
+       // val0 votes for nil.
+       {
+               vote := withValidator(voteProto, privValidators[0].Address, 0)
+               added, err := signAddVote(privValidators[0], vote, voteSet)
+               if !added || err != nil {
+                       t.Errorf("Expected VoteSet.Add to succeed")
+               }
+       }
+
+       // val0 votes again for blockHash1.
+       {
+               vote := withValidator(voteProto, privValidators[0].Address, 0)
+               added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash1), voteSet)
+               if added {
+                       t.Errorf("Expected VoteSet.Add to fail, conflicting vote.")
+               }
+               if err == nil {
+                       t.Errorf("Expected VoteSet.Add to return error, conflicting vote.")
+               }
+       }
+
+       // start tracking blockHash1
+       voteSet.SetPeerMaj23("peerA", BlockID{blockHash1, PartSetHeader{}})
+
+       // val0 votes again for blockHash1.
+       {
+               vote := withValidator(voteProto, privValidators[0].Address, 0)
+               added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash1), voteSet)
+               if !added {
+                       t.Errorf("Expected VoteSet.Add to succeed, called SetPeerMaj23().")
+               }
+               if err == nil {
+                       t.Errorf("Expected VoteSet.Add to return error, conflicting vote.")
+               }
+       }
+
+       // attempt tracking blockHash2, should fail because already set for peerA.
+       voteSet.SetPeerMaj23("peerA", BlockID{blockHash2, PartSetHeader{}})
+
+       // val0 votes again for blockHash1.
+       {
+               vote := withValidator(voteProto, privValidators[0].Address, 0)
+               added, err := signAddVote(privValidators[0], withBlockHash(vote, blockHash2), voteSet)
+               if added {
+                       t.Errorf("Expected VoteSet.Add to fail, duplicate SetPeerMaj23() from peerA")
+               }
+               if err == nil {
+                       t.Errorf("Expected VoteSet.Add to return error, conflicting vote.")
+               }
+       }
+
+       // val1 votes for blockHash1.
+       {
+               vote := withValidator(voteProto, privValidators[1].Address, 1)
+               added, err := signAddVote(privValidators[1], withBlockHash(vote, blockHash1), voteSet)
+               if !added || err != nil {
+                       t.Errorf("Expected VoteSet.Add to succeed")
+               }
+       }
+
+       // check
+       if voteSet.HasTwoThirdsMajority() {
+               t.Errorf("We shouldn't have 2/3 majority yet")
+       }
+       if voteSet.HasTwoThirdsAny() {
+               t.Errorf("We shouldn't have 2/3 if any votes yet")
+       }
+
+       // val2 votes for blockHash2.
+       {
+               vote := withValidator(voteProto, privValidators[2].Address, 2)
+               added, err := signAddVote(privValidators[2], withBlockHash(vote, blockHash2), voteSet)
+               if !added || err != nil {
+                       t.Errorf("Expected VoteSet.Add to succeed")
+               }
+       }
+
+       // check
+       if voteSet.HasTwoThirdsMajority() {
+               t.Errorf("We shouldn't have 2/3 majority yet")
+       }
+       if !voteSet.HasTwoThirdsAny() {
+               t.Errorf("We should have 2/3 if any votes")
+       }
+
+       // now attempt tracking blockHash1
+       voteSet.SetPeerMaj23("peerB", BlockID{blockHash1, PartSetHeader{}})
+
+       // val2 votes for blockHash1.
+       {
+               vote := withValidator(voteProto, privValidators[2].Address, 2)
+               added, err := signAddVote(privValidators[2], withBlockHash(vote, blockHash1), voteSet)
+               if !added {
+                       t.Errorf("Expected VoteSet.Add to succeed")
+               }
+               if err == nil {
+                       t.Errorf("Expected VoteSet.Add to return error, conflicting vote")
+               }
+       }
+
+       // check
+       if !voteSet.HasTwoThirdsMajority() {
+               t.Errorf("We should have 2/3 majority for blockHash1")
+       }
+       blockIDMaj23, _ := voteSet.TwoThirdsMajority()
+       if !bytes.Equal(blockIDMaj23.Hash, blockHash1) {
+               t.Errorf("Got the wrong 2/3 majority blockhash")
+       }
+       if !voteSet.HasTwoThirdsAny() {
+               t.Errorf("We should have 2/3 if any votes")
+       }
+
+}
+
+func TestMakeCommit(t *testing.T) {
+       height, round := 1, 0
+       voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrecommit, 10, 1)
+       blockHash, blockPartsHeader := crypto.CRandBytes(32), PartSetHeader{123, crypto.CRandBytes(32)}
+
+       voteProto := &Vote{
+               ValidatorAddress: nil,
+               ValidatorIndex:   -1,
+               Height:           height,
+               Round:            round,
+               Type:             VoteTypePrecommit,
+               BlockID:          BlockID{blockHash, blockPartsHeader},
+       }
+
+       // 6 out of 10 voted for some block.
+       for i := 0; i < 6; i++ {
+               vote := withValidator(voteProto, privValidators[i].Address, i)
+               signAddVote(privValidators[i], vote, voteSet)
+       }
+
+       // MakeCommit should fail.
+       AssertPanics(t, "Doesn't have +2/3 majority", func() { voteSet.MakeCommit() })
+
+       // 7th voted for some other block.
+       {
+               vote := withValidator(voteProto, privValidators[6].Address, 6)
+               vote = withBlockHash(vote, RandBytes(32))
+               vote = withBlockPartsHeader(vote, PartSetHeader{123, RandBytes(32)})
+               signAddVote(privValidators[6], vote, voteSet)
+       }
+
+       // The 8th voted like everyone else.
+       {
+               vote := withValidator(voteProto, privValidators[7].Address, 7)
+               signAddVote(privValidators[7], vote, voteSet)
+       }
+
+       commit := voteSet.MakeCommit()
+
+       // Commit should have 10 elements
+       if len(commit.Precommits) != 10 {
+               t.Errorf("Commit Precommits should have the same number of precommits as validators")
+       }
+
+       // Ensure that Commit precommits are ordered.
+       if err := commit.ValidateBasic(); err != nil {
+               t.Errorf("Error in Commit.ValidateBasic(): %v", err)
+       }
+
+}
diff --git a/types/vote_test.go b/types/vote_test.go
new file mode 100644 (file)
index 0000000..353acfa
--- /dev/null
@@ -0,0 +1,30 @@
+package types
+
+import (
+       "testing"
+)
+
+func TestVoteSignable(t *testing.T) {
+       vote := &Vote{
+               ValidatorAddress: []byte("addr"),
+               ValidatorIndex:   56789,
+               Height:           12345,
+               Round:            23456,
+               Type:             byte(2),
+               BlockID: BlockID{
+                       Hash: []byte("hash"),
+                       PartsHeader: PartSetHeader{
+                               Total: 1000000,
+                               Hash:  []byte("parts_hash"),
+                       },
+               },
+       }
+       signBytes := SignBytes("test_chain_id", vote)
+       signStr := string(signBytes)
+
+       expected := `{"chain_id":"test_chain_id","vote":{"block_id":{"hash":"68617368","parts":{"hash":"70617274735F68617368","total":1000000}},"height":12345,"round":23456,"type":2}}`
+       if signStr != expected {
+               // NOTE: when this fails, you probably want to fix up consensus/replay_test too
+               t.Errorf("Got unexpected sign string for Vote. Expected:\n%v\nGot:\n%v", expected, signStr)
+       }
+}