mirror of
https://github.com/fluencelabs/tendermint
synced 2025-05-29 14:11:21 +00:00
Merge pull request #588 from tendermint/comments_cleanup
Comments and cleanup
This commit is contained in:
commit
695ad5fe2d
@ -50,6 +50,9 @@ TODO: Better handle abci client errors. (make it automatically handle connection
|
|||||||
|
|
||||||
const cacheSize = 100000
|
const cacheSize = 100000
|
||||||
|
|
||||||
|
// Mempool is an ordered in-memory pool for transactions before they are proposed in a consensus round.
|
||||||
|
// Transaction validity is checked using the CheckTx abci message before the transaction is added to the pool.
|
||||||
|
// The Mempool uses a concurrent list structure for storing transactions that can be efficiently accessed by multiple concurrent readers.
|
||||||
type Mempool struct {
|
type Mempool struct {
|
||||||
config *cfg.MempoolConfig
|
config *cfg.MempoolConfig
|
||||||
|
|
||||||
@ -72,6 +75,7 @@ type Mempool struct {
|
|||||||
logger log.Logger
|
logger log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewMempool returns a new Mempool with the given configuration and connection to an application.
|
||||||
func NewMempool(config *cfg.MempoolConfig, proxyAppConn proxy.AppConnMempool) *Mempool {
|
func NewMempool(config *cfg.MempoolConfig, proxyAppConn proxy.AppConnMempool) *Mempool {
|
||||||
mempool := &Mempool{
|
mempool := &Mempool{
|
||||||
config: config,
|
config: config,
|
||||||
@ -90,7 +94,7 @@ func NewMempool(config *cfg.MempoolConfig, proxyAppConn proxy.AppConnMempool) *M
|
|||||||
return mempool
|
return mempool
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLogger allows you to set your own Logger.
|
// SetLogger sets the Logger.
|
||||||
func (mem *Mempool) SetLogger(l log.Logger) {
|
func (mem *Mempool) SetLogger(l log.Logger) {
|
||||||
mem.logger = l
|
mem.logger = l
|
||||||
}
|
}
|
||||||
@ -110,21 +114,22 @@ func (mem *Mempool) initWAL() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// consensus must be able to hold lock to safely update
|
// Lock locks the mempool. The consensus must be able to hold lock to safely update.
|
||||||
func (mem *Mempool) Lock() {
|
func (mem *Mempool) Lock() {
|
||||||
mem.proxyMtx.Lock()
|
mem.proxyMtx.Lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unlock unlocks the mempool.
|
||||||
func (mem *Mempool) Unlock() {
|
func (mem *Mempool) Unlock() {
|
||||||
mem.proxyMtx.Unlock()
|
mem.proxyMtx.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number of transactions in the mempool clist
|
// Size returns the number of transactions in the mempool.
|
||||||
func (mem *Mempool) Size() int {
|
func (mem *Mempool) Size() int {
|
||||||
return mem.txs.Len()
|
return mem.txs.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all transactions from mempool and cache
|
// Flush removes all transactions from the mempool and cache
|
||||||
func (mem *Mempool) Flush() {
|
func (mem *Mempool) Flush() {
|
||||||
mem.proxyMtx.Lock()
|
mem.proxyMtx.Lock()
|
||||||
defer mem.proxyMtx.Unlock()
|
defer mem.proxyMtx.Unlock()
|
||||||
@ -137,14 +142,15 @@ func (mem *Mempool) Flush() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the first element of mem.txs for peer goroutines to call .NextWait() on.
|
// TxsFrontWait returns the first transaction in the ordered list for peer goroutines to call .NextWait() on.
|
||||||
// Blocks until txs has elements.
|
// It blocks until the mempool is not empty (ie. until the internal `mem.txs` has at least one element)
|
||||||
func (mem *Mempool) TxsFrontWait() *clist.CElement {
|
func (mem *Mempool) TxsFrontWait() *clist.CElement {
|
||||||
return mem.txs.FrontWait()
|
return mem.txs.FrontWait()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try a new transaction in the mempool.
|
// CheckTx executes a new transaction against the application to determine its validity
|
||||||
// Potentially blocking if we're blocking on Update() or Reap().
|
// and whether it should be added to the mempool.
|
||||||
|
// It blocks if we're waiting on Update() or Reap().
|
||||||
// cb: A callback from the CheckTx command.
|
// cb: A callback from the CheckTx command.
|
||||||
// It gets called from another goroutine.
|
// It gets called from another goroutine.
|
||||||
// CONTRACT: Either cb will get called, or err returned.
|
// CONTRACT: Either cb will get called, or err returned.
|
||||||
@ -256,8 +262,8 @@ func (mem *Mempool) resCbRecheck(req *abci.Request, res *abci.Response) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the valid transactions remaining
|
// Reap returns a list of transactions currently in the mempool.
|
||||||
// If maxTxs is -1, there is no cap on returned transactions.
|
// If maxTxs is -1, there is no cap on the number of returned transactions.
|
||||||
func (mem *Mempool) Reap(maxTxs int) types.Txs {
|
func (mem *Mempool) Reap(maxTxs int) types.Txs {
|
||||||
mem.proxyMtx.Lock()
|
mem.proxyMtx.Lock()
|
||||||
defer mem.proxyMtx.Unlock()
|
defer mem.proxyMtx.Unlock()
|
||||||
@ -286,8 +292,7 @@ func (mem *Mempool) collectTxs(maxTxs int) types.Txs {
|
|||||||
return txs
|
return txs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell mempool that these txs were committed.
|
// Update informs the mempool that the given txs were committed and can be discarded.
|
||||||
// Mempool will discard these txs.
|
|
||||||
// NOTE: this should be called *after* block is committed by consensus.
|
// NOTE: this should be called *after* block is committed by consensus.
|
||||||
// NOTE: unsafe; Lock/Unlock must be managed by caller
|
// NOTE: unsafe; Lock/Unlock must be managed by caller
|
||||||
func (mem *Mempool) Update(height int, txs types.Txs) {
|
func (mem *Mempool) Update(height int, txs types.Txs) {
|
||||||
@ -354,19 +359,21 @@ func (mem *Mempool) recheckTxs(goodTxs []types.Tx) {
|
|||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
// A transaction that successfully ran
|
// mempoolTx is a transaction that successfully ran
|
||||||
type mempoolTx struct {
|
type mempoolTx struct {
|
||||||
counter int64 // a simple incrementing counter
|
counter int64 // a simple incrementing counter
|
||||||
height int64 // height that this tx had been validated in
|
height int64 // height that this tx had been validated in
|
||||||
tx types.Tx //
|
tx types.Tx //
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Height returns the height for this transaction
|
||||||
func (memTx *mempoolTx) Height() int {
|
func (memTx *mempoolTx) Height() int {
|
||||||
return int(atomic.LoadInt64(&memTx.height))
|
return int(atomic.LoadInt64(&memTx.height))
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// txCache maintains a cache of transactions.
|
||||||
type txCache struct {
|
type txCache struct {
|
||||||
mtx sync.Mutex
|
mtx sync.Mutex
|
||||||
size int
|
size int
|
||||||
@ -374,6 +381,7 @@ type txCache struct {
|
|||||||
list *list.List // to remove oldest tx when cache gets too big
|
list *list.List // to remove oldest tx when cache gets too big
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newTxCache returns a new txCache.
|
||||||
func newTxCache(cacheSize int) *txCache {
|
func newTxCache(cacheSize int) *txCache {
|
||||||
return &txCache{
|
return &txCache{
|
||||||
size: cacheSize,
|
size: cacheSize,
|
||||||
@ -382,6 +390,7 @@ func newTxCache(cacheSize int) *txCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset resets the txCache to empty.
|
||||||
func (cache *txCache) Reset() {
|
func (cache *txCache) Reset() {
|
||||||
cache.mtx.Lock()
|
cache.mtx.Lock()
|
||||||
cache.map_ = make(map[string]struct{}, cacheSize)
|
cache.map_ = make(map[string]struct{}, cacheSize)
|
||||||
@ -389,6 +398,7 @@ func (cache *txCache) Reset() {
|
|||||||
cache.mtx.Unlock()
|
cache.mtx.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exists returns true if the given tx is cached.
|
||||||
func (cache *txCache) Exists(tx types.Tx) bool {
|
func (cache *txCache) Exists(tx types.Tx) bool {
|
||||||
cache.mtx.Lock()
|
cache.mtx.Lock()
|
||||||
_, exists := cache.map_[string(tx)]
|
_, exists := cache.map_[string(tx)]
|
||||||
@ -396,7 +406,7 @@ func (cache *txCache) Exists(tx types.Tx) bool {
|
|||||||
return exists
|
return exists
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns false if tx is in cache.
|
// Push adds the given tx to the txCache. It returns false if tx is already in the cache.
|
||||||
func (cache *txCache) Push(tx types.Tx) bool {
|
func (cache *txCache) Push(tx types.Tx) bool {
|
||||||
cache.mtx.Lock()
|
cache.mtx.Lock()
|
||||||
defer cache.mtx.Unlock()
|
defer cache.mtx.Unlock()
|
||||||
@ -418,6 +428,7 @@ func (cache *txCache) Push(tx types.Tx) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove removes the given tx from the cache.
|
||||||
func (cache *txCache) Remove(tx types.Tx) {
|
func (cache *txCache) Remove(tx types.Tx) {
|
||||||
cache.mtx.Lock()
|
cache.mtx.Lock()
|
||||||
delete(cache.map_, string(tx))
|
delete(cache.map_, string(tx))
|
||||||
|
@ -30,6 +30,7 @@ type MempoolReactor struct {
|
|||||||
evsw types.EventSwitch
|
evsw types.EventSwitch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewMempoolReactor returns a new MempoolReactor with the given config and mempool.
|
||||||
func NewMempoolReactor(config *cfg.MempoolConfig, mempool *Mempool) *MempoolReactor {
|
func NewMempoolReactor(config *cfg.MempoolConfig, mempool *Mempool) *MempoolReactor {
|
||||||
memR := &MempoolReactor{
|
memR := &MempoolReactor{
|
||||||
config: config,
|
config: config,
|
||||||
@ -39,7 +40,8 @@ func NewMempoolReactor(config *cfg.MempoolConfig, mempool *Mempool) *MempoolReac
|
|||||||
return memR
|
return memR
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements Reactor
|
// GetChannels implements Reactor.
|
||||||
|
// It returns the list of channels for this reactor.
|
||||||
func (memR *MempoolReactor) GetChannels() []*p2p.ChannelDescriptor {
|
func (memR *MempoolReactor) GetChannels() []*p2p.ChannelDescriptor {
|
||||||
return []*p2p.ChannelDescriptor{
|
return []*p2p.ChannelDescriptor{
|
||||||
&p2p.ChannelDescriptor{
|
&p2p.ChannelDescriptor{
|
||||||
@ -49,17 +51,19 @@ func (memR *MempoolReactor) GetChannels() []*p2p.ChannelDescriptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements Reactor
|
// AddPeer implements Reactor.
|
||||||
|
// It starts a broadcast routine ensuring all txs are forwarded to the given peer.
|
||||||
func (memR *MempoolReactor) AddPeer(peer *p2p.Peer) {
|
func (memR *MempoolReactor) AddPeer(peer *p2p.Peer) {
|
||||||
go memR.broadcastTxRoutine(peer)
|
go memR.broadcastTxRoutine(peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements Reactor
|
// RemovePeer implements Reactor.
|
||||||
func (memR *MempoolReactor) RemovePeer(peer *p2p.Peer, reason interface{}) {
|
func (memR *MempoolReactor) RemovePeer(peer *p2p.Peer, reason interface{}) {
|
||||||
// broadcast routine checks if peer is gone and returns
|
// broadcast routine checks if peer is gone and returns
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements Reactor
|
// Receive implements Reactor.
|
||||||
|
// It adds any received transactions to the mempool.
|
||||||
func (memR *MempoolReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) {
|
func (memR *MempoolReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) {
|
||||||
_, msg, err := DecodeMessage(msgBytes)
|
_, msg, err := DecodeMessage(msgBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -84,15 +88,17 @@ func (memR *MempoolReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just an alias for CheckTx since broadcasting happens in peer routines
|
// BroadcastTx is an alias for Mempool.CheckTx. Broadcasting itself happens in peer routines.
|
||||||
func (memR *MempoolReactor) BroadcastTx(tx types.Tx, cb func(*abci.Response)) error {
|
func (memR *MempoolReactor) BroadcastTx(tx types.Tx, cb func(*abci.Response)) error {
|
||||||
return memR.Mempool.CheckTx(tx, cb)
|
return memR.Mempool.CheckTx(tx, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PeerState describes the state of a peer.
|
||||||
type PeerState interface {
|
type PeerState interface {
|
||||||
GetHeight() int
|
GetHeight() int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Peer describes a peer.
|
||||||
type Peer interface {
|
type Peer interface {
|
||||||
IsRunning() bool
|
IsRunning() bool
|
||||||
Send(byte, interface{}) bool
|
Send(byte, interface{}) bool
|
||||||
@ -141,7 +147,7 @@ func (memR *MempoolReactor) broadcastTxRoutine(peer Peer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// implements events.Eventable
|
// SetEventSwitch implements events.Eventable.
|
||||||
func (memR *MempoolReactor) SetEventSwitch(evsw types.EventSwitch) {
|
func (memR *MempoolReactor) SetEventSwitch(evsw types.EventSwitch) {
|
||||||
memR.evsw = evsw
|
memR.evsw = evsw
|
||||||
}
|
}
|
||||||
@ -153,6 +159,7 @@ const (
|
|||||||
msgTypeTx = byte(0x01)
|
msgTypeTx = byte(0x01)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MempoolMessage is a message sent or received by the MempoolReactor.
|
||||||
type MempoolMessage interface{}
|
type MempoolMessage interface{}
|
||||||
|
|
||||||
var _ = wire.RegisterInterface(
|
var _ = wire.RegisterInterface(
|
||||||
@ -160,6 +167,7 @@ var _ = wire.RegisterInterface(
|
|||||||
wire.ConcreteType{&TxMessage{}, msgTypeTx},
|
wire.ConcreteType{&TxMessage{}, msgTypeTx},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DecodeMessage decodes a byte-array into a MempoolMessage.
|
||||||
func DecodeMessage(bz []byte) (msgType byte, msg MempoolMessage, err error) {
|
func DecodeMessage(bz []byte) (msgType byte, msg MempoolMessage, err error) {
|
||||||
msgType = bz[0]
|
msgType = bz[0]
|
||||||
n := new(int)
|
n := new(int)
|
||||||
@ -170,10 +178,12 @@ func DecodeMessage(bz []byte) (msgType byte, msg MempoolMessage, err error) {
|
|||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
|
// TxMessage is a MempoolMessage containing a transaction.
|
||||||
type TxMessage struct {
|
type TxMessage struct {
|
||||||
Tx types.Tx
|
Tx types.Tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the TxMessage.
|
||||||
func (m *TxMessage) String() string {
|
func (m *TxMessage) String() string {
|
||||||
return fmt.Sprintf("[TxMessage %v]", m.Tx)
|
return fmt.Sprintf("[TxMessage %v]", m.Tx)
|
||||||
}
|
}
|
||||||
|
33
rpc/grpc/grpc_test.go
Normal file
33
rpc/grpc/grpc_test.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package core_grpc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/tendermint/abci/example/dummy"
|
||||||
|
"github.com/tendermint/tendermint/rpc/grpc"
|
||||||
|
"github.com/tendermint/tendermint/rpc/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
// start a tendermint node (and merkleeyes) in the background to test against
|
||||||
|
app := dummy.NewDummyApplication()
|
||||||
|
node := rpctest.StartTendermint(app)
|
||||||
|
code := m.Run()
|
||||||
|
|
||||||
|
// and shut down proper at the end
|
||||||
|
node.Stop()
|
||||||
|
node.Wait()
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBroadcastTx(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
res, err := rpctest.GetGRPCClient().BroadcastTx(context.Background(), &core_grpc.RequestBroadcastTx{[]byte("this is a tx")})
|
||||||
|
require.Nil(err, "%+v", err)
|
||||||
|
require.EqualValues(0, res.CheckTx.Code)
|
||||||
|
require.EqualValues(0, res.DeliverTx.Code)
|
||||||
|
}
|
@ -1,18 +0,0 @@
|
|||||||
package rpctest
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
core_grpc "github.com/tendermint/tendermint/rpc/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBroadcastTx(t *testing.T) {
|
|
||||||
require := require.New(t)
|
|
||||||
res, err := GetGRPCClient().BroadcastTx(context.Background(), &core_grpc.RequestBroadcastTx{[]byte("this is a tx")})
|
|
||||||
require.Nil(err, "%+v", err)
|
|
||||||
require.EqualValues(0, res.CheckTx.Code)
|
|
||||||
require.EqualValues(0, res.DeliverTx.Code)
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
package tests contain integration tests and helper functions for testing
|
|
||||||
the RPC interface
|
|
||||||
|
|
||||||
In particular, it allows us to spin up a tendermint node in process, with
|
|
||||||
a live RPC server, which we can use to verify our rpc calls. It provides
|
|
||||||
all data structures, enabling us to do more complex tests (like node_test.go)
|
|
||||||
that introspect the blocks themselves to validate signatures and the like.
|
|
||||||
|
|
||||||
It currently only spins up one node, it would be interesting to expand it
|
|
||||||
to multiple nodes to see the real effects of validating partially signed
|
|
||||||
blocks.
|
|
||||||
*/
|
|
||||||
package rpctest
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/tendermint/abci/example/dummy"
|
|
||||||
nm "github.com/tendermint/tendermint/node"
|
|
||||||
)
|
|
||||||
|
|
||||||
var node *nm.Node
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
// start a tendermint node (and merkleeyes) in the background to test against
|
|
||||||
app := dummy.NewDummyApplication()
|
|
||||||
node = StartTendermint(app)
|
|
||||||
code := m.Run()
|
|
||||||
|
|
||||||
// and shut down proper at the end
|
|
||||||
node.Stop()
|
|
||||||
node.Wait()
|
|
||||||
os.Exit(code)
|
|
||||||
}
|
|
@ -10,10 +10,10 @@ PORT=$2
|
|||||||
|
|
||||||
for i in `seq 1 $N`; do
|
for i in `seq 1 $N`; do
|
||||||
# store key value pair
|
# store key value pair
|
||||||
KEY="abcd$i"
|
KEY=$(head -c 10 /dev/urandom)
|
||||||
VALUE="dcba$i"
|
VALUE="$i"
|
||||||
echo "$KEY:$VALUE"
|
echo $(toHex $KEY=$VALUE)
|
||||||
curl 127.0.0.1:$PORT/broadcast_tx_sync?tx=\"$(toHex $KEY=$VALUE)\"
|
curl 127.0.0.1:$PORT/broadcast_tx_sync?tx=0x$(toHex $KEY=$VALUE)
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
wire "github.com/tendermint/go-wire"
|
||||||
"github.com/tendermint/go-wire/data"
|
"github.com/tendermint/go-wire/data"
|
||||||
. "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
"github.com/tendermint/tmlibs/merkle"
|
"github.com/tendermint/tmlibs/merkle"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,12 +19,14 @@ const (
|
|||||||
DefaultBlockPartSize = 65536 // 64kB TODO: put part size in parts header?
|
DefaultBlockPartSize = 65536 // 64kB TODO: put part size in parts header?
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Block defines the atomic unit of a Tendermint blockchain
|
||||||
type Block struct {
|
type Block struct {
|
||||||
*Header `json:"header"`
|
*Header `json:"header"`
|
||||||
*Data `json:"data"`
|
*Data `json:"data"`
|
||||||
LastCommit *Commit `json:"last_commit"`
|
LastCommit *Commit `json:"last_commit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeBlock returns a new block and corresponding part set from the given information
|
||||||
// TODO: version
|
// TODO: version
|
||||||
func MakeBlock(height int, chainID string, txs []Tx, commit *Commit,
|
func MakeBlock(height int, chainID string, txs []Tx, commit *Commit,
|
||||||
prevBlockID BlockID, valHash, appHash []byte, partSize int) (*Block, *PartSet) {
|
prevBlockID BlockID, valHash, appHash []byte, partSize int) (*Block, *PartSet) {
|
||||||
@ -47,14 +49,14 @@ func MakeBlock(height int, chainID string, txs []Tx, commit *Commit,
|
|||||||
return block, block.MakePartSet(partSize)
|
return block, block.MakePartSet(partSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Basic validation that doesn't involve state data.
|
// ValidateBasic performs basic validation that doesn't involve state data.
|
||||||
func (b *Block) ValidateBasic(chainID string, lastBlockHeight int, lastBlockID BlockID,
|
func (b *Block) ValidateBasic(chainID string, lastBlockHeight int, lastBlockID BlockID,
|
||||||
lastBlockTime time.Time, appHash []byte) error {
|
lastBlockTime time.Time, appHash []byte) error {
|
||||||
if b.ChainID != chainID {
|
if b.ChainID != chainID {
|
||||||
return errors.New(Fmt("Wrong Block.Header.ChainID. Expected %v, got %v", chainID, b.ChainID))
|
return errors.New(cmn.Fmt("Wrong Block.Header.ChainID. Expected %v, got %v", chainID, b.ChainID))
|
||||||
}
|
}
|
||||||
if b.Height != lastBlockHeight+1 {
|
if b.Height != lastBlockHeight+1 {
|
||||||
return errors.New(Fmt("Wrong Block.Header.Height. Expected %v, got %v", lastBlockHeight+1, b.Height))
|
return errors.New(cmn.Fmt("Wrong Block.Header.Height. Expected %v, got %v", lastBlockHeight+1, b.Height))
|
||||||
}
|
}
|
||||||
/* TODO: Determine bounds for Time
|
/* TODO: Determine bounds for Time
|
||||||
See blockchain/reactor "stopSyncingDurationMinutes"
|
See blockchain/reactor "stopSyncingDurationMinutes"
|
||||||
@ -64,13 +66,13 @@ func (b *Block) ValidateBasic(chainID string, lastBlockHeight int, lastBlockID B
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
if b.NumTxs != len(b.Data.Txs) {
|
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))
|
return errors.New(cmn.Fmt("Wrong Block.Header.NumTxs. Expected %v, got %v", len(b.Data.Txs), b.NumTxs))
|
||||||
}
|
}
|
||||||
if !b.LastBlockID.Equals(lastBlockID) {
|
if !b.LastBlockID.Equals(lastBlockID) {
|
||||||
return errors.New(Fmt("Wrong Block.Header.LastBlockID. Expected %v, got %v", lastBlockID, b.LastBlockID))
|
return errors.New(cmn.Fmt("Wrong Block.Header.LastBlockID. Expected %v, got %v", lastBlockID, b.LastBlockID))
|
||||||
}
|
}
|
||||||
if !bytes.Equal(b.LastCommitHash, b.LastCommit.Hash()) {
|
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()))
|
return errors.New(cmn.Fmt("Wrong Block.Header.LastCommitHash. Expected %v, got %v", b.LastCommitHash, b.LastCommit.Hash()))
|
||||||
}
|
}
|
||||||
if b.Header.Height != 1 {
|
if b.Header.Height != 1 {
|
||||||
if err := b.LastCommit.ValidateBasic(); err != nil {
|
if err := b.LastCommit.ValidateBasic(); err != nil {
|
||||||
@ -78,15 +80,16 @@ func (b *Block) ValidateBasic(chainID string, lastBlockHeight int, lastBlockID B
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !bytes.Equal(b.DataHash, b.Data.Hash()) {
|
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()))
|
return errors.New(cmn.Fmt("Wrong Block.Header.DataHash. Expected %v, got %v", b.DataHash, b.Data.Hash()))
|
||||||
}
|
}
|
||||||
if !bytes.Equal(b.AppHash, appHash) {
|
if !bytes.Equal(b.AppHash, appHash) {
|
||||||
return errors.New(Fmt("Wrong Block.Header.AppHash. Expected %X, got %v", appHash, b.AppHash))
|
return errors.New(cmn.Fmt("Wrong Block.Header.AppHash. Expected %X, got %v", appHash, b.AppHash))
|
||||||
}
|
}
|
||||||
// NOTE: the AppHash and ValidatorsHash are validated later.
|
// NOTE: the AppHash and ValidatorsHash are validated later.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FillHeader fills in any remaining header fields that are a function of the block data
|
||||||
func (b *Block) FillHeader() {
|
func (b *Block) FillHeader() {
|
||||||
if b.LastCommitHash == nil {
|
if b.LastCommitHash == nil {
|
||||||
b.LastCommitHash = b.LastCommit.Hash()
|
b.LastCommitHash = b.LastCommit.Hash()
|
||||||
@ -96,7 +99,7 @@ func (b *Block) FillHeader() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes and returns the block hash.
|
// Hash computes and returns the block hash.
|
||||||
// If the block is incomplete, block hash is nil for safety.
|
// If the block is incomplete, block hash is nil for safety.
|
||||||
func (b *Block) Hash() data.Bytes {
|
func (b *Block) Hash() data.Bytes {
|
||||||
// fmt.Println(">>", b.Data)
|
// fmt.Println(">>", b.Data)
|
||||||
@ -107,13 +110,14 @@ func (b *Block) Hash() data.Bytes {
|
|||||||
return b.Header.Hash()
|
return b.Header.Hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakePartSet returns a PartSet containing parts of a serialized block.
|
||||||
|
// This is the form in which the block is gossipped to peers.
|
||||||
func (b *Block) MakePartSet(partSize int) *PartSet {
|
func (b *Block) MakePartSet(partSize int) *PartSet {
|
||||||
return NewPartSetFromData(wire.BinaryBytes(b), partSize)
|
return NewPartSetFromData(wire.BinaryBytes(b), partSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience.
|
// HashesTo is a convenience function that checks if a block hashes to the given argument.
|
||||||
// A nil block never hashes to anything.
|
// A nil block never hashes to anything, and nothing hashes to a nil hash.
|
||||||
// Nothing hashes to a nil hash.
|
|
||||||
func (b *Block) HashesTo(hash []byte) bool {
|
func (b *Block) HashesTo(hash []byte) bool {
|
||||||
if len(hash) == 0 {
|
if len(hash) == 0 {
|
||||||
return false
|
return false
|
||||||
@ -124,10 +128,12 @@ func (b *Block) HashesTo(hash []byte) bool {
|
|||||||
return bytes.Equal(b.Hash(), hash)
|
return bytes.Equal(b.Hash(), hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the block
|
||||||
func (b *Block) String() string {
|
func (b *Block) String() string {
|
||||||
return b.StringIndented("")
|
return b.StringIndented("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringIndented returns a string representation of the block
|
||||||
func (b *Block) StringIndented(indent string) string {
|
func (b *Block) StringIndented(indent string) string {
|
||||||
if b == nil {
|
if b == nil {
|
||||||
return "nil-Block"
|
return "nil-Block"
|
||||||
@ -143,6 +149,7 @@ func (b *Block) StringIndented(indent string) string {
|
|||||||
indent, b.Hash())
|
indent, b.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringShort returns a shortened string representation of the block
|
||||||
func (b *Block) StringShort() string {
|
func (b *Block) StringShort() string {
|
||||||
if b == nil {
|
if b == nil {
|
||||||
return "nil-Block"
|
return "nil-Block"
|
||||||
@ -153,6 +160,7 @@ func (b *Block) StringShort() string {
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Header defines the structure of a Tendermint block header
|
||||||
type Header struct {
|
type Header struct {
|
||||||
ChainID string `json:"chain_id"`
|
ChainID string `json:"chain_id"`
|
||||||
Height int `json:"height"`
|
Height int `json:"height"`
|
||||||
@ -165,6 +173,7 @@ type Header struct {
|
|||||||
AppHash data.Bytes `json:"app_hash"` // state after txs from the previous block
|
AppHash data.Bytes `json:"app_hash"` // state after txs from the previous block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hash returns the hash of the header.
|
||||||
// NOTE: hash is nil if required fields are missing.
|
// NOTE: hash is nil if required fields are missing.
|
||||||
func (h *Header) Hash() data.Bytes {
|
func (h *Header) Hash() data.Bytes {
|
||||||
if len(h.ValidatorsHash) == 0 {
|
if len(h.ValidatorsHash) == 0 {
|
||||||
@ -183,6 +192,7 @@ func (h *Header) Hash() data.Bytes {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringIndented returns a string representation of the header
|
||||||
func (h *Header) StringIndented(indent string) string {
|
func (h *Header) StringIndented(indent string) string {
|
||||||
if h == nil {
|
if h == nil {
|
||||||
return "nil-Header"
|
return "nil-Header"
|
||||||
@ -212,6 +222,7 @@ func (h *Header) StringIndented(indent string) string {
|
|||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
|
// Commit contains the evidence that a block was committed by a set of validators.
|
||||||
// NOTE: Commit is empty for height 1, but never nil.
|
// NOTE: Commit is empty for height 1, but never nil.
|
||||||
type Commit struct {
|
type Commit struct {
|
||||||
// NOTE: The Precommits are in order of address to preserve the bonded ValidatorSet order.
|
// NOTE: The Precommits are in order of address to preserve the bonded ValidatorSet order.
|
||||||
@ -223,9 +234,10 @@ type Commit struct {
|
|||||||
// Volatile
|
// Volatile
|
||||||
firstPrecommit *Vote
|
firstPrecommit *Vote
|
||||||
hash data.Bytes
|
hash data.Bytes
|
||||||
bitArray *BitArray
|
bitArray *cmn.BitArray
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FirstPrecommit returns the first non-nil precommit in the commit
|
||||||
func (commit *Commit) FirstPrecommit() *Vote {
|
func (commit *Commit) FirstPrecommit() *Vote {
|
||||||
if len(commit.Precommits) == 0 {
|
if len(commit.Precommits) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@ -242,6 +254,7 @@ func (commit *Commit) FirstPrecommit() *Vote {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Height returns the height of the commit
|
||||||
func (commit *Commit) Height() int {
|
func (commit *Commit) Height() int {
|
||||||
if len(commit.Precommits) == 0 {
|
if len(commit.Precommits) == 0 {
|
||||||
return 0
|
return 0
|
||||||
@ -249,6 +262,7 @@ func (commit *Commit) Height() int {
|
|||||||
return commit.FirstPrecommit().Height
|
return commit.FirstPrecommit().Height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Round returns the round of the commit
|
||||||
func (commit *Commit) Round() int {
|
func (commit *Commit) Round() int {
|
||||||
if len(commit.Precommits) == 0 {
|
if len(commit.Precommits) == 0 {
|
||||||
return 0
|
return 0
|
||||||
@ -256,10 +270,12 @@ func (commit *Commit) Round() int {
|
|||||||
return commit.FirstPrecommit().Round
|
return commit.FirstPrecommit().Round
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type returns the vote type of the commit, which is always VoteTypePrecommit
|
||||||
func (commit *Commit) Type() byte {
|
func (commit *Commit) Type() byte {
|
||||||
return VoteTypePrecommit
|
return VoteTypePrecommit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size returns the number of votes in the commit
|
||||||
func (commit *Commit) Size() int {
|
func (commit *Commit) Size() int {
|
||||||
if commit == nil {
|
if commit == nil {
|
||||||
return 0
|
return 0
|
||||||
@ -267,24 +283,30 @@ func (commit *Commit) Size() int {
|
|||||||
return len(commit.Precommits)
|
return len(commit.Precommits)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (commit *Commit) BitArray() *BitArray {
|
// BitArray returns a BitArray of which validators voted in this commit
|
||||||
|
func (commit *Commit) BitArray() *cmn.BitArray {
|
||||||
if commit.bitArray == nil {
|
if commit.bitArray == nil {
|
||||||
commit.bitArray = NewBitArray(len(commit.Precommits))
|
commit.bitArray = cmn.NewBitArray(len(commit.Precommits))
|
||||||
for i, precommit := range commit.Precommits {
|
for i, precommit := range commit.Precommits {
|
||||||
|
// TODO: need to check the BlockID otherwise we could be counting conflicts,
|
||||||
|
// not just the one with +2/3 !
|
||||||
commit.bitArray.SetIndex(i, precommit != nil)
|
commit.bitArray.SetIndex(i, precommit != nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return commit.bitArray
|
return commit.bitArray
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetByIndex returns the vote corresponding to a given validator index
|
||||||
func (commit *Commit) GetByIndex(index int) *Vote {
|
func (commit *Commit) GetByIndex(index int) *Vote {
|
||||||
return commit.Precommits[index]
|
return commit.Precommits[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsCommit returns true if there is at least one vote
|
||||||
func (commit *Commit) IsCommit() bool {
|
func (commit *Commit) IsCommit() bool {
|
||||||
return len(commit.Precommits) != 0
|
return len(commit.Precommits) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateBasic performs basic validation that doesn't involve state data.
|
||||||
func (commit *Commit) ValidateBasic() error {
|
func (commit *Commit) ValidateBasic() error {
|
||||||
if commit.BlockID.IsZero() {
|
if commit.BlockID.IsZero() {
|
||||||
return errors.New("Commit cannot be for nil block")
|
return errors.New("Commit cannot be for nil block")
|
||||||
@ -319,6 +341,7 @@ func (commit *Commit) ValidateBasic() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hash returns the hash of the commit
|
||||||
func (commit *Commit) Hash() data.Bytes {
|
func (commit *Commit) Hash() data.Bytes {
|
||||||
if commit.hash == nil {
|
if commit.hash == nil {
|
||||||
bs := make([]interface{}, len(commit.Precommits))
|
bs := make([]interface{}, len(commit.Precommits))
|
||||||
@ -330,6 +353,7 @@ func (commit *Commit) Hash() data.Bytes {
|
|||||||
return commit.hash
|
return commit.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringIndented returns a string representation of the commit
|
||||||
func (commit *Commit) StringIndented(indent string) string {
|
func (commit *Commit) StringIndented(indent string) string {
|
||||||
if commit == nil {
|
if commit == nil {
|
||||||
return "nil-Commit"
|
return "nil-Commit"
|
||||||
@ -349,6 +373,7 @@ func (commit *Commit) StringIndented(indent string) string {
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Data contains the set of transactions included in the block
|
||||||
type Data struct {
|
type Data struct {
|
||||||
|
|
||||||
// Txs that will be applied by state @ block.Height+1.
|
// Txs that will be applied by state @ block.Height+1.
|
||||||
@ -360,6 +385,7 @@ type Data struct {
|
|||||||
hash data.Bytes
|
hash data.Bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hash returns the hash of the data
|
||||||
func (data *Data) Hash() data.Bytes {
|
func (data *Data) Hash() data.Bytes {
|
||||||
if data.hash == nil {
|
if data.hash == nil {
|
||||||
data.hash = data.Txs.Hash() // NOTE: leaves of merkle tree are TxIDs
|
data.hash = data.Txs.Hash() // NOTE: leaves of merkle tree are TxIDs
|
||||||
@ -367,11 +393,12 @@ func (data *Data) Hash() data.Bytes {
|
|||||||
return data.hash
|
return data.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringIndented returns a string representation of the transactions
|
||||||
func (data *Data) StringIndented(indent string) string {
|
func (data *Data) StringIndented(indent string) string {
|
||||||
if data == nil {
|
if data == nil {
|
||||||
return "nil-Data"
|
return "nil-Data"
|
||||||
}
|
}
|
||||||
txStrings := make([]string, MinInt(len(data.Txs), 21))
|
txStrings := make([]string, cmn.MinInt(len(data.Txs), 21))
|
||||||
for i, tx := range data.Txs {
|
for i, tx := range data.Txs {
|
||||||
if i == 20 {
|
if i == 20 {
|
||||||
txStrings[i] = fmt.Sprintf("... (%v total)", len(data.Txs))
|
txStrings[i] = fmt.Sprintf("... (%v total)", len(data.Txs))
|
||||||
@ -388,24 +415,29 @@ func (data *Data) StringIndented(indent string) string {
|
|||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// BlockID defines the unique ID of a block as its Hash and its PartSetHeader
|
||||||
type BlockID struct {
|
type BlockID struct {
|
||||||
Hash data.Bytes `json:"hash"`
|
Hash data.Bytes `json:"hash"`
|
||||||
PartsHeader PartSetHeader `json:"parts"`
|
PartsHeader PartSetHeader `json:"parts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsZero returns true if this is the BlockID for a nil-block
|
||||||
func (blockID BlockID) IsZero() bool {
|
func (blockID BlockID) IsZero() bool {
|
||||||
return len(blockID.Hash) == 0 && blockID.PartsHeader.IsZero()
|
return len(blockID.Hash) == 0 && blockID.PartsHeader.IsZero()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Equals returns true if the BlockID matches the given BlockID
|
||||||
func (blockID BlockID) Equals(other BlockID) bool {
|
func (blockID BlockID) Equals(other BlockID) bool {
|
||||||
return bytes.Equal(blockID.Hash, other.Hash) &&
|
return bytes.Equal(blockID.Hash, other.Hash) &&
|
||||||
blockID.PartsHeader.Equals(other.PartsHeader)
|
blockID.PartsHeader.Equals(other.PartsHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Key returns a machine-readable string representation of the BlockID
|
||||||
func (blockID BlockID) Key() string {
|
func (blockID BlockID) Key() string {
|
||||||
return string(blockID.Hash) + string(wire.BinaryBytes(blockID.PartsHeader))
|
return string(blockID.Hash) + string(wire.BinaryBytes(blockID.PartsHeader))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteSignBytes writes the canonical bytes of the BlockID to the given writer for digital signing
|
||||||
func (blockID BlockID) WriteSignBytes(w io.Writer, n *int, err *error) {
|
func (blockID BlockID) WriteSignBytes(w io.Writer, n *int, err *error) {
|
||||||
if blockID.IsZero() {
|
if blockID.IsZero() {
|
||||||
wire.WriteTo([]byte("null"), w, n, err)
|
wire.WriteTo([]byte("null"), w, n, err)
|
||||||
@ -415,6 +447,7 @@ func (blockID BlockID) WriteSignBytes(w io.Writer, n *int, err *error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String returns a human readable string representation of the BlockID
|
||||||
func (blockID BlockID) String() string {
|
func (blockID BlockID) String() string {
|
||||||
return fmt.Sprintf(`%v:%v`, blockID.Hash, blockID.PartsHeader)
|
return fmt.Sprintf(`%v:%v`, blockID.Hash, blockID.PartsHeader)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user