diff --git a/raft/api.go b/raft/api.go index 87e65da13ddd..c7dc98b5820a 100644 --- a/raft/api.go +++ b/raft/api.go @@ -10,7 +10,7 @@ func NewPublicRaftAPI(raftService *RaftService) *PublicRaftAPI { func (s *PublicRaftAPI) Role() string { role := s.raftService.raftProtocolManager.role - if (role == minterRole) { + if role == minterRole { return "minter" } else { return "verifier" diff --git a/raft/backend.go b/raft/backend.go index 243351771bd7..8fb927d8047b 100644 --- a/raft/backend.go +++ b/raft/backend.go @@ -60,7 +60,8 @@ func New(ctx *node.ServiceContext, chainConfig *core.ChainConfig, id int, blockT return service, nil } -// Backend interface methods +// Backend interface methods: + func (service *RaftService) AccountManager() *accounts.Manager { return service.accountManager } func (service *RaftService) BlockChain() *core.BlockChain { return service.blockchain } func (service *RaftService) ChainDb() ethdb.Database { return service.chainDb } @@ -68,15 +69,16 @@ func (service *RaftService) DappDb() ethdb.Database { return nil } func (service *RaftService) EventMux() *event.TypeMux { return service.eventMux } func (service *RaftService) TxPool() *core.TxPool { return service.txPool } -// node.Service interface methods +// node.Service interface methods: + func (service *RaftService) Protocols() []p2p.Protocol { return []p2p.Protocol{} } func (service *RaftService) APIs() []rpc.API { return []rpc.API{ { Namespace: "raft", - Version: "1.0", - Service: NewPublicRaftAPI(service), - Public: true, + Version: "1.0", + Service: NewPublicRaftAPI(service), + Public: true, }, } } diff --git a/raft/constants.go b/raft/constants.go index eb865383f0e3..28de7f7362e6 100644 --- a/raft/constants.go +++ b/raft/constants.go @@ -28,10 +28,10 @@ const ( peerUrlKeyPrefix = "peerUrl-" // checkpoints - txCreated = "TX-CREATED" - txAccepted = "TX-ACCEPTED" - becameMinter = "BECAME-MINTER" - becameVerifier = "BECAME-VERIFIER" + txCreated = "TX-CREATED" + txAccepted = "TX-ACCEPTED" + becameMinter = "BECAME-MINTER" + becameVerifier = "BECAME-VERIFIER" ) var ( diff --git a/raft/doc.md b/raft/doc.md index 323ef117b15c..3ad83f901e04 100644 --- a/raft/doc.md +++ b/raft/doc.md @@ -33,7 +33,7 @@ verifier | follower When the minter creates a block, unlike in vanilla Ethereum where the block is written to the database and immediately considered the new head of the chain, we write the block as "detached" from the chain. We only set the new head of the chain once the block has flown through Raft. All nodes will extend the chain together in lock-step as they "apply" their Raft log. -From the point of view of Ethereum, Raft is integrated via an implementation of the `Service` interface in node/service.go: "an individual protocol that can be registered into a node". Other examples of services are `Ethereum`, `ReleaseService`, and `Whisper`. +From the point of view of Ethereum, Raft is integrated via an implementation of the [`Service`](https://godoc.org/github.com/jpmorganchase/quorum/node#Service) interface in [node/service.go](https://github.com/jpmorganchase/quorum/blob/master/node/service.go): "an individual protocol that can be registered into a node". Other examples of services are [`Ethereum`](https://godoc.org/github.com/jpmorganchase/quorum/eth#Ethereum), [`ReleaseService`](https://godoc.org/github.com/jpmorganchase/quorum/contracts/release#ReleaseService), and [`Whisper`](https://godoc.org/github.com/jpmorganchase/quorum/whisper/whisperv5#Whisper). ## The lifecycle of a transaction @@ -42,12 +42,12 @@ Let's follow the lifecycle of a typical transaction: #### on any node (whether minter or verifier): 1. The transaction is submitted via an RPC call to geth. -2. Using the existing (p2p) transaction propagation mechanism in Ethereum, the transaction is announced to all peers and, because our cluster is currently configured to use "static nodes," every transaction is sent to all peers in the cluster. +2. Using the existing (p2p) transaction propagation mechanism in Ethereum, the transaction is announced to all peers and, because our cluster is currently configured to use "static nodes," every transaction is sent to all peers in the cluster. #### on the minter: 3. It reaches the minter, where it's included in the next block (see `mintNewBlock`) via the transaction pool. -4. Block creation triggers a `NewMinedBlockEvent`, which the Raft protocol manager receives via its subscription `minedBlockSub`. The `minedBroadcastLoop` (in raft/handler.go) puts this new block to the `ProtocolManager.proposeC` channel. +4. Block creation triggers a [`NewMinedBlockEvent`](https://godoc.org/github.com/jpmorganchase/quorum/core#NewMinedBlockEvent), which the Raft protocol manager receives via its subscription `minedBlockSub`. The `minedBroadcastLoop` (in raft/handler.go) puts this new block to the `ProtocolManager.proposeC` channel. 5. `serveInternal` is waiting at the other end of the channel. Its job is to RLP-encode blocks and propose them to Raft. Once it flows through Raft, this block will likely become the new head of the blockchain (on all nodes.) #### on every node: @@ -56,9 +56,9 @@ Let's follow the lifecycle of a typical transaction: 7. Having crossed the network through Raft, the block reaches the `eventLoop` (which processes new Raft log entries.) It has arrived from the leader through `pm.transport`, an instance of [`rafthttp.Transport`](https://godoc.org/github.com/coreos/etcd/rafthttp#Transport). -8. The block is now handled by `applyNewChainHead`. This method checks whether the block extends the chain (i.e. it's parent is the current head of the chain; see below). If it does not extend the chain, it is simply ignored as a no-op. If it does extend chain, the block is validated and then written as the new head of the chain by `SetNewHeadBlock` (in blockchain.go). +8. The block is now handled by `applyNewChainHead`. This method checks whether the block extends the chain (i.e. it's parent is the current head of the chain; see below). If it does not extend the chain, it is simply ignored as a no-op. If it does extend chain, the block is validated and then written as the new head of the chain by [`SetNewHeadBlock`](https://godoc.org/github.com/jpmorganchase/quorum/core#BlockChain.SetNewHeadBlock) (in core/blockchain.go). -9. A `ChainHeadEvent` is posted to notify listeners that a new block has been accepted. This is relevant to us because: +9. A [`ChainHeadEvent`](https://godoc.org/github.com/jpmorganchase/quorum/core#ChainHeadEvent) is posted to notify listeners that a new block has been accepted. This is relevant to us because: * It removes the relevant transaction from the transaction pool. * It removes the relevant transaction from `speculativeChain`'s `proposedTxes` (see below). * It triggers `requestMinting` in (minter.go), telling the node to schedule the minting of a new block if any more transactions are pending. @@ -126,9 +126,9 @@ In speculative minting we allow the creation of a new block (and its proposal to Since this can happen repeatedly, these blocks (which each have a reference to their parent block) can form a sort of chain. We call this a "speculative chain." -During the course of operation that a speculative chain forms, we keep track of the subset of transactions in the pool that we have already put into blocks (in the speculative chain) that have not yet made it into the blockchain (and whereupon a `core.ChainHeadEvent` occurs.) These are called "proposed transactions" (see speculative_chain.go). +During the course of operation that a speculative chain forms, we keep track of the subset of transactions in the pool that we have already put into blocks (in the speculative chain) that have not yet made it into the blockchain (and whereupon a [`core.ChainHeadEvent`](https://godoc.org/github.com/jpmorganchase/quorum/core#ChainHeadEvent) occurs.) These are called "proposed transactions" (see speculative_chain.go). -Per the presence of "races" (as we detail above), it is possible that a block somewhere in the middle of a speculative chain ends up not making into the chain. In this scenario an `InvalidRaftOrdering` event will occur, and we clean up the state of the speculative chain accordingly. +Per the presence of "races" (as we detail above), it is possible that a block somewhere in the middle of a speculative chain ends up not making into the chain. In this scenario an [`InvalidRaftOrdering`](https://godoc.org/github.com/jpmorganchase/quorum/raft#InvalidRaftOrdering) event will occur, and we clean up the state of the speculative chain accordingly. There is currently no limit to the length of these speculative chains, but we plan to add support for this in the future. As a consequence, a minter can currently create arbitrarily many blocks back-to-back in a scenario where Raft stops making progress. @@ -139,7 +139,7 @@ There is currently no limit to the length of these speculative chains, but we pl * `unappliedBlocks`: A queue of blocks which have been proposed to Raft but not yet committed to the blockchain. - When minting a new block, we enqueue it at the end of this queue - `accept` is called to remove the oldest speculative block when it's accepted into the blockchain. - - When an `InvalidRaftOrdering` occurs, we unwind the queue by popping the most recent blocks from the "new end" of the queue until we find the invalid block. We must repeatedly remove these "newer" speculative blocks because they are all dependent on a block that we know has not been included in the blockchain. + - When an [`InvalidRaftOrdering`](https://godoc.org/github.com/jpmorganchase/quorum/raft#InvalidRaftOrdering) occurs, we unwind the queue by popping the most recent blocks from the "new end" of the queue until we find the invalid block. We must repeatedly remove these "newer" speculative blocks because they are all dependent on a block that we know has not been included in the blockchain. * `expectedInvalidBlockHashes`: The set of blocks which build on an invalid block, but haven't passsed through Raft yet. We remove these as we get them back. When these non-extending blocks come back through Raft we remove them from the speculative chain. We use this set as a "guard" against trying to trim the speculative chain when we shouldn't. ## The Raft transport layer @@ -167,4 +167,4 @@ Additionally there could even be multiple minters running at the same time, but ### Can transactions be reversed? Since raft log entries can be disregarded as "no-ops", does this imply transaction reversal? -No. When a Raft log entry containing a new block is disregarded as a "no-op", its transactions will remain in the transaction pool, and so they will be included in a future block in the chain. \ No newline at end of file +No. When a Raft log entry containing a new block is disregarded as a "no-op", its transactions will remain in the transaction pool, and so they will be included in a future block in the chain. diff --git a/raft/handler.go b/raft/handler.go index 80ac879b2d08..79795a02cc3a 100644 --- a/raft/handler.go +++ b/raft/handler.go @@ -1,13 +1,3 @@ -// Overview of the channels used in this module: -// -// Node. -// * quitSync: *Every* channel operation can be unblocked by closing this -// channel. -// -// ProtocolManager. -// * proposeC, for proposals flowing from ethereum to raft -// * confChangeC, currently unused; in the future for adding new, non-initial, raft peers -// * roleC, coming from raft notifies us when our role changes package raft import ( @@ -43,6 +33,17 @@ import ( "github.com/syndtr/goleveldb/leveldb" ) +// Overview of the channels used in this module: +// +// Node. +// * quitSync: *Every* channel operation can be unblocked by closing this +// channel. +// +// ProtocolManager. +// * proposeC, for proposals flowing from ethereum to raft +// * confChangeC, currently unused; in the future for adding new, non-initial, raft peers +// * roleC, coming from raft notifies us when our role changes + type ProtocolManager struct { // peers note -- each node tracks the peers acknowledged by raft // diff --git a/raft/minter.go b/raft/minter.go index 9c2daca15735..4947c17c8721 100644 --- a/raft/minter.go +++ b/raft/minter.go @@ -52,13 +52,12 @@ type minter struct { chain *core.BlockChain chainDb ethdb.Database coinbase common.Address - minting int32 // Atomic status counter + minting int32 // Atomic status counter shouldMine *channels.RingChannel blockTime time.Duration speculativeChain *speculativeChain } - func newMinter(config *core.ChainConfig, eth core.Backend, blockTime time.Duration) *minter { minter := &minter{ config: config, diff --git a/raft/persistence.go b/raft/persistence.go index bcbad77591bb..eebaa57b6f3d 100644 --- a/raft/persistence.go +++ b/raft/persistence.go @@ -6,8 +6,8 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" - "github.com/syndtr/goleveldb/leveldb/errors" "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/errors" "github.com/syndtr/goleveldb/leveldb/opt" ) diff --git a/raft/snapshot.go b/raft/snapshot.go index 8e4570268135..0c8ee41925a8 100644 --- a/raft/snapshot.go +++ b/raft/snapshot.go @@ -2,10 +2,10 @@ package raft import ( "github.com/coreos/etcd/raft/raftpb" + "github.com/coreos/etcd/snap" "github.com/coreos/etcd/wal/walpb" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" - "github.com/coreos/etcd/snap" ) func (pm *ProtocolManager) saveSnapshot(snap raftpb.Snapshot) error { @@ -13,7 +13,7 @@ func (pm *ProtocolManager) saveSnapshot(snap raftpb.Snapshot) error { return err } - walSnap := walpb.Snapshot { + walSnap := walpb.Snapshot{ Index: snap.Metadata.Index, Term: snap.Metadata.Term, } @@ -26,7 +26,7 @@ func (pm *ProtocolManager) saveSnapshot(snap raftpb.Snapshot) error { } func (pm *ProtocolManager) maybeTriggerSnapshot() { - if pm.appliedIndex - pm.snapshotIndex < snapshotPeriod { + if pm.appliedIndex-pm.snapshotIndex < snapshotPeriod { return } @@ -79,4 +79,4 @@ func (pm *ProtocolManager) applySnapshot(snap raftpb.Snapshot) { pm.confState = snapMeta.ConfState pm.snapshotIndex = snapMeta.Index pm.advanceAppliedIndex(snapMeta.Index) -} \ No newline at end of file +} diff --git a/raft/speculative_chain.go b/raft/speculative_chain.go index 0aa9351ca6e3..92f84f009640 100644 --- a/raft/speculative_chain.go +++ b/raft/speculative_chain.go @@ -27,11 +27,11 @@ type speculativeChain struct { } func newSpeculativeChain() *speculativeChain { - return &speculativeChain { - head: nil, - unappliedBlocks: lane.NewDeque(), + return &speculativeChain{ + head: nil, + unappliedBlocks: lane.NewDeque(), expectedInvalidBlockHashes: set.New(), - proposedTxes: set.New(), + proposedTxes: set.New(), } } @@ -172,4 +172,4 @@ func (chain *speculativeChain) withoutProposedTxes(addrTxes AddressTxes) Address } return newMap -} \ No newline at end of file +} diff --git a/raft/wal.go b/raft/wal.go index 16e3186960a7..2576b4274f7c 100644 --- a/raft/wal.go +++ b/raft/wal.go @@ -3,11 +3,11 @@ package raft import ( "os" + "github.com/coreos/etcd/raft/raftpb" "github.com/coreos/etcd/wal" "github.com/coreos/etcd/wal/walpb" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/coreos/etcd/raft/raftpb" "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" ) func (pm *ProtocolManager) openWAL(maybeSnapshot *raftpb.Snapshot) *wal.WAL {