Skip to content

Commit

Permalink
Documentation tweaks. (ethereum#80)
Browse files Browse the repository at this point in the history
Documentation updates and gofmt raft.
  • Loading branch information
joelburget authored and jpmsam committed Mar 23, 2017
1 parent b628632 commit 82a140a
Show file tree
Hide file tree
Showing 10 changed files with 45 additions and 43 deletions.
2 changes: 1 addition & 1 deletion raft/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
12 changes: 7 additions & 5 deletions raft/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,25 @@ 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 }
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,
},
}
}
Expand Down
8 changes: 4 additions & 4 deletions raft/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
18 changes: 9 additions & 9 deletions raft/doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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:
Expand All @@ -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.
Expand Down Expand Up @@ -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.

Expand All @@ -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
Expand Down Expand Up @@ -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. 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.
21 changes: 11 additions & 10 deletions raft/handler.go
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down Expand Up @@ -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
//
Expand Down
3 changes: 1 addition & 2 deletions raft/minter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion raft/persistence.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down
8 changes: 4 additions & 4 deletions raft/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ 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 {
if err := pm.snapshotter.SaveSnap(snap); err != nil {
return err
}

walSnap := walpb.Snapshot {
walSnap := walpb.Snapshot{
Index: snap.Metadata.Index,
Term: snap.Metadata.Term,
}
Expand All @@ -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
}

Expand Down Expand Up @@ -79,4 +79,4 @@ func (pm *ProtocolManager) applySnapshot(snap raftpb.Snapshot) {
pm.confState = snapMeta.ConfState
pm.snapshotIndex = snapMeta.Index
pm.advanceAppliedIndex(snapMeta.Index)
}
}
10 changes: 5 additions & 5 deletions raft/speculative_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}
}

Expand Down Expand Up @@ -172,4 +172,4 @@ func (chain *speculativeChain) withoutProposedTxes(addrTxes AddressTxes) Address
}

return newMap
}
}
4 changes: 2 additions & 2 deletions raft/wal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 82a140a

Please sign in to comment.