Browse Source

updated readme, implementing mempool.

pull/9/head
Jae Kwon 10 years ago
parent
commit
4c961bd565
7 changed files with 229 additions and 271 deletions
  1. +6
    -1
      README.md
  2. +0
    -166
      blocks/adjustment.go
  3. +9
    -9
      blocks/block.go
  4. +4
    -4
      blocks/block_test.go
  5. +149
    -2
      blocks/tx.go
  6. +59
    -0
      mempool/mempool.go
  7. +2
    -89
      p2p/README.md

+ 6
- 1
README.md View File

@ -2,11 +2,16 @@ TenderMint - proof of concept
* **[p2p](https://github.com/tendermint/tendermint/blob/master/p2p):** P2P networking stack. Designed to be extensible.
* **[merkle](https://github.com/tendermint/tendermint/blob/master/merkle):** Immutable Persistent Merkle-ized AVL+ Tree, used primarily for keeping track of mutable state like account balances.
* **[blocks](https://github.com/tendermint/tendermint/blob/master/blocks):** The blockchain, storage of blocks, and all the associated structures.
* **[state](https://github.com/tendermint/tendermint/blob/master/state):** The application state, which is mutated by blocks in the blockchain.
* **[consensus](https://github.com/tendermint/tendermint/blob/master/consensus):** The core consensus algorithm logic.
* **[mempool](https://github.com/tendermint/tendermint/blob/master/mempool):** Handles the broadcasting of uncommitted transactions.
* **[crypto](https://github.com/tendermint/tendermint/blob/master/crypto):** Includes cgo bindings of ed25519.
### Status
* Consensus *now*
* Mempool *now*
* Consensus *complete*
* Block propagation *sidelined*
* Node & testnet *complete*
* PEX peer exchange *complete*


+ 0
- 166
blocks/adjustment.go View File

@ -1,166 +0,0 @@
package blocks
import (
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
"io"
)
/* Adjustment
1. Bond New validator posts a bond
2. Unbond Validator leaves
3. Timeout Validator times out
4. Dupeout Validator dupes out (signs twice)
TODO: signing a bad checkpoint (block)
*/
type Adjustment interface {
Type() byte
Binary
}
const (
ADJ_TYPE_BOND = byte(0x01)
ADJ_TYPE_UNBOND = byte(0x02)
ADJ_TYPE_TIMEOUT = byte(0x03)
ADJ_TYPE_DUPEOUT = byte(0x04)
)
func ReadAdjustment(r io.Reader, n *int64, err *error) Adjustment {
switch t := ReadByte(r, n, err); t {
case ADJ_TYPE_BOND:
return &Bond{
Fee: ReadUInt64(r, n, err),
UnbondTo: ReadUInt64(r, n, err),
Amount: ReadUInt64(r, n, err),
Signature: ReadSignature(r, n, err),
}
case ADJ_TYPE_UNBOND:
return &Unbond{
Fee: ReadUInt64(r, n, err),
Amount: ReadUInt64(r, n, err),
Signature: ReadSignature(r, n, err),
}
case ADJ_TYPE_TIMEOUT:
return &Timeout{
AccountId: ReadUInt64(r, n, err),
Penalty: ReadUInt64(r, n, err),
}
case ADJ_TYPE_DUPEOUT:
return &Dupeout{
VoteA: ReadBlockVote(r, n, err),
VoteB: ReadBlockVote(r, n, err),
}
default:
Panicf("Unknown Adjustment type %x", t)
return nil
}
}
//-----------------------------------------------------------------------------
/* Bond < Adjustment */
type Bond struct {
Fee uint64
UnbondTo uint64
Amount uint64
Signature
}
func (self *Bond) Type() byte {
return ADJ_TYPE_BOND
}
func (self *Bond) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, self.Type(), &n, &err)
WriteUInt64(w, self.Fee, &n, &err)
WriteUInt64(w, self.UnbondTo, &n, &err)
WriteUInt64(w, self.Amount, &n, &err)
WriteBinary(w, self.Signature, &n, &err)
return
}
//-----------------------------------------------------------------------------
/* Unbond < Adjustment */
type Unbond struct {
Fee uint64
Amount uint64
Signature
}
func (self *Unbond) Type() byte {
return ADJ_TYPE_UNBOND
}
func (self *Unbond) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, self.Type(), &n, &err)
WriteUInt64(w, self.Fee, &n, &err)
WriteUInt64(w, self.Amount, &n, &err)
WriteBinary(w, self.Signature, &n, &err)
return
}
//-----------------------------------------------------------------------------
/* Timeout < Adjustment */
type Timeout struct {
AccountId uint64
Penalty uint64
}
func (self *Timeout) Type() byte {
return ADJ_TYPE_TIMEOUT
}
func (self *Timeout) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, self.Type(), &n, &err)
WriteUInt64(w, self.AccountId, &n, &err)
WriteUInt64(w, self.Penalty, &n, &err)
return
}
//-----------------------------------------------------------------------------
/*
The full vote structure is only needed when presented as evidence.
Typically only the signature is passed around, as the hash & height are implied.
*/
type BlockVote struct {
Height uint64
BlockHash []byte
Signature
}
func ReadBlockVote(r io.Reader, n *int64, err *error) BlockVote {
return BlockVote{
Height: ReadUInt64(r, n, err),
BlockHash: ReadByteSlice(r, n, err),
Signature: ReadSignature(r, n, err),
}
}
func (self BlockVote) WriteTo(w io.Writer) (n int64, err error) {
WriteUInt64(w, self.Height, &n, &err)
WriteByteSlice(w, self.BlockHash, &n, &err)
WriteBinary(w, self.Signature, &n, &err)
return
}
/* Dupeout < Adjustment */
type Dupeout struct {
VoteA BlockVote
VoteB BlockVote
}
func (self *Dupeout) Type() byte {
return ADJ_TYPE_DUPEOUT
}
func (self *Dupeout) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, self.Type(), &n, &err)
WriteBinary(w, self.VoteA, &n, &err)
WriteBinary(w, self.VoteB, &n, &err)
return
}

+ 9
- 9
blocks/block.go View File

@ -189,8 +189,8 @@ func (h *Header) Hash() []byte {
/* Validation is part of a block */
type Validation struct {
Signatures []Signature
Adjustments []Adjustment
Signatures []Signature
Txs []Tx
// Volatile
hash []byte
@ -203,24 +203,24 @@ func ReadValidation(r io.Reader, n *int64, err *error) Validation {
for i := uint32(0); i < numSigs; i++ {
sigs = append(sigs, ReadSignature(r, n, err))
}
adjs := make([]Adjustment, 0, numAdjs)
tx := make([]Tx, 0, numAdjs)
for i := uint32(0); i < numAdjs; i++ {
adjs = append(adjs, ReadAdjustment(r, n, err))
tx = append(tx, ReadTx(r, n, err))
}
return Validation{
Signatures: sigs,
Adjustments: adjs,
Signatures: sigs,
Txs: tx,
}
}
func (v *Validation) WriteTo(w io.Writer) (n int64, err error) {
WriteUInt32(w, uint32(len(v.Signatures)), &n, &err)
WriteUInt32(w, uint32(len(v.Adjustments)), &n, &err)
WriteUInt32(w, uint32(len(v.Txs)), &n, &err)
for _, sig := range v.Signatures {
WriteBinary(w, sig, &n, &err)
}
for _, adj := range v.Adjustments {
WriteBinary(w, adj, &n, &err)
for _, tx := range v.Txs {
WriteBinary(w, tx, &n, &err)
}
return
}


+ 4
- 4
blocks/block_test.go View File

@ -47,7 +47,7 @@ func randSig() Signature {
func TestBlock(t *testing.T) {
// Txs
// Account Txs
sendTx := &SendTx{
Signature: randSig(),
@ -63,7 +63,7 @@ func TestBlock(t *testing.T) {
PubKey: randBytes(32),
}
// Adjs
// Consensus Txs
bond := &Bond{
Signature: randSig(),
@ -109,8 +109,8 @@ func TestBlock(t *testing.T) {
TxsHash: randBytes(32),
},
Validation: Validation{
Signatures: []Signature{randSig(), randSig()},
Adjustments: []Adjustment{bond, unbond, timeout, dupeout},
Signatures: []Signature{randSig(), randSig()},
Txs: []Txs{bond, unbond, timeout, dupeout},
},
Txs: Txs{
Txs: []Tx{sendTx, nameTx},


+ 149
- 2
blocks/tx.go View File

@ -18,16 +18,35 @@ Tx wire format:
A account number, varint encoded (1+ bytes)
S signature of all prior bytes (32 bytes)
Account Txs:
1. Send Send coins to account
2. Name Associate account with a name
Consensus Txs:
3. Bond New validator posts a bond
4. Unbond Validator leaves
5. Timeout Validator times out
6. Dupeout Validator dupes out (signs twice)
*/
type Tx interface {
Type() byte
IsConsensus() bool
Binary
}
const (
// Account transactions
TX_TYPE_SEND = byte(0x01)
TX_TYPE_NAME = byte(0x02)
// Consensus transactions
TX_TYPE_BOND = byte(0x11)
TX_TYPE_UNBOND = byte(0x12)
TX_TYPE_TIMEOUT = byte(0x13)
TX_TYPE_DUPEOUT = byte(0x14)
)
func ReadTx(r io.Reader, n *int64, err *error) Tx {
@ -46,13 +65,36 @@ func ReadTx(r io.Reader, n *int64, err *error) Tx {
PubKey: ReadByteSlice(r, n, err),
Signature: ReadSignature(r, n, err),
}
case TX_TYPE_BOND:
return &BondTx{
Fee: ReadUInt64(r, n, err),
UnbondTo: ReadUInt64(r, n, err),
Amount: ReadUInt64(r, n, err),
Signature: ReadSignature(r, n, err),
}
case TX_TYPE_UNBOND:
return &UnbondTx{
Fee: ReadUInt64(r, n, err),
Amount: ReadUInt64(r, n, err),
Signature: ReadSignature(r, n, err),
}
case TX_TYPE_TIMEOUT:
return &TimeoutTx{
AccountId: ReadUInt64(r, n, err),
Penalty: ReadUInt64(r, n, err),
}
case TX_TYPE_DUPEOUT:
return &DupeoutTx{
VoteA: ReadBlockVote(r, n, err),
VoteB: ReadBlockVote(r, n, err),
}
default:
Panicf("Unknown Tx type %x", t)
return nil
}
}
/* SendTx < Tx */
//-----------------------------------------------------------------------------
type SendTx struct {
Fee uint64
@ -74,7 +116,7 @@ func (self *SendTx) WriteTo(w io.Writer) (n int64, err error) {
return
}
/* NameTx < Tx */
//-----------------------------------------------------------------------------
type NameTx struct {
Fee uint64
@ -95,3 +137,108 @@ func (self *NameTx) WriteTo(w io.Writer) (n int64, err error) {
WriteBinary(w, self.Signature, &n, &err)
return
}
//-----------------------------------------------------------------------------
type BondTx struct {
Fee uint64
UnbondTo uint64
Amount uint64
Signature
}
func (self *BondTx) Type() byte {
return TX_TYPE_BOND
}
func (self *BondTx) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, self.Type(), &n, &err)
WriteUInt64(w, self.Fee, &n, &err)
WriteUInt64(w, self.UnbondTo, &n, &err)
WriteUInt64(w, self.Amount, &n, &err)
WriteBinary(w, self.Signature, &n, &err)
return
}
//-----------------------------------------------------------------------------
type UnbondTx struct {
Fee uint64
Amount uint64
Signature
}
func (self *UnbondTx) Type() byte {
return TX_TYPE_UNBOND
}
func (self *UnbondTx) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, self.Type(), &n, &err)
WriteUInt64(w, self.Fee, &n, &err)
WriteUInt64(w, self.Amount, &n, &err)
WriteBinary(w, self.Signature, &n, &err)
return
}
//-----------------------------------------------------------------------------
type TimeoutTx struct {
AccountId uint64
Penalty uint64
}
func (self *TimeoutTx) Type() byte {
return TX_TYPE_TIMEOUT
}
func (self *TimeoutTx) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, self.Type(), &n, &err)
WriteUInt64(w, self.AccountId, &n, &err)
WriteUInt64(w, self.Penalty, &n, &err)
return
}
//-----------------------------------------------------------------------------
/*
The full vote structure is only needed when presented as evidence.
Typically only the signature is passed around, as the hash & height are implied.
*/
type BlockVote struct {
Height uint64
BlockHash []byte
Signature
}
func ReadBlockVote(r io.Reader, n *int64, err *error) BlockVote {
return BlockVote{
Height: ReadUInt64(r, n, err),
BlockHash: ReadByteSlice(r, n, err),
Signature: ReadSignature(r, n, err),
}
}
func (self BlockVote) WriteTo(w io.Writer) (n int64, err error) {
WriteUInt64(w, self.Height, &n, &err)
WriteByteSlice(w, self.BlockHash, &n, &err)
WriteBinary(w, self.Signature, &n, &err)
return
}
//-----------------------------------------------------------------------------
type DupeoutTx struct {
VoteA BlockVote
VoteB BlockVote
}
func (self *DupeoutTx) Type() byte {
return TX_TYPE_DUPEOUT
}
func (self *DupeoutTx) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, self.Type(), &n, &err)
WriteBinary(w, self.VoteA, &n, &err)
WriteBinary(w, self.VoteB, &n, &err)
return
}

+ 59
- 0
mempool/mempool.go View File

@ -0,0 +1,59 @@
package mempool
import (
"sync"
. "github.com/tendermint/tendermint/blocks"
. "github.com/tendermint/tendermint/state"
)
/*
Mempool receives new transactions and applies them to the latest committed state.
If the transaction is acceptable, then it broadcasts a fingerprint to peers.
The transaction fingerprint is a short sequence of bytes (shorter than a full hash).
Each peer connection uses a different algorithm for turning the tx hash into a
fingerprint in order to prevent transaction blocking attacks. Upon inspecting a
tx fingerprint, the receiver may query the source for the full tx bytes.
When this node happens to be the next proposer, it simply takes the recently
modified state (and the associated transactions) and use that as the proposal.
There are two types of transactions -- consensus txs (e.g. bonding / unbonding /
timeout / dupeout txs) and everything else. They are stored separately to allow
nodes to only request the kind they need.
TODO: make use of this potential feature when the time comes.
For simplicity we evaluate the consensus transactions after everything else.
*/
//-----------------------------------------------------------------------------
type Mempool struct {
mtx sync.Mutex
state *State
txs []Tx // Regular transactions
ctxs []Tx // Validator related transactions
}
func NewMempool(state *State) *Mempool {
return &Mempool{
state: state,
}
}
func (mem *Mempool) AddTx(tx Tx) bool {
mem.mtx.Lock()
defer mem.mtx.Unlock()
if tx.IsConsensus() {
// Remember consensus tx for later staging.
// We only keep 1 tx for each validator. TODO what? what about bonding?
// TODO talk about prioritization.
mem.ctxs = append(mem.ctxs, tx)
} else {
mem.txs = append(mem.txs, tx)
}
}
func (mem *Mempool) CollectForState() {
}

+ 2
- 89
p2p/README.md View File

@ -1,11 +1,8 @@
## Channels
Each peer connection is multiplexed into channels.
<hr />
### PEX channel
The PEX channel is used to exchange peer addresses.
The p2p module comes with a channel implementation used for peer
discovery (called PEX, short for "peer exchange").
<table>
<tr>
@ -24,90 +21,6 @@ The PEX channel is used to exchange peer addresses.
</table>
<hr />
### Block channel
The block channel is used to propagate block or header information to new peers or peers catching up with the blockchain.
<table>
<tr>
<td><b>Channel</b></td>
<td>"block"</td>
</tr>
<tr>
<td><b>Messages</b></td>
<td>
<ul>
<li>RequestMsg</li>
<li>BlockMsg</li>
<li>HeaderMsg</li>
</ul>
</td>
</tr>
<tr>
<td><b>Notes</b></td>
<td>
Nodes should only advertise having a header or block at height 'h' if it also has all the headers or blocks less than 'h'. Thus for each peer we need only keep track of two integers -- one for the most recent header height 'h_h' and one for the most recent block height 'h_b', where 'h_b' &lt;= 'h_h'.
</td>
</tr>
</table>
<hr />
### Mempool channel
The mempool channel is used for broadcasting new transactions that haven't yet entered the blockchain. It uses a lossy bloom filter on either end, but with sufficient fanout and filter nonce updates every new block, all transactions will eventually reach every node.
<table>
<tr>
<td><b>Channel</b></td>
<td>"mempool"</td>
</tr>
<tr>
<td><b>Messages</b></td>
<td>
<ul>
<li>MempoolTxMsg</li>
</ul>
</td>
</tr>
<tr>
<td><b>Notes</b></td>
<td>
Instead of keeping a perfect inventory of what peers have, we use a lossy filter.<br/>
Bloom filter (n:10k, p:0.02 -> k:6, m:10KB)<br/>
Each peer's filter has a random nonce that scrambles the message hashes.<br/>
The filter & nonce refreshes every new block.<br/>
</td>
</tr>
</table>
<hr />
### Consensus channel
The consensus channel broadcasts all information used in the rounds of the Tendermint consensus mechanism.
<table>
<tr>
<td><b>Channel</b></td>
<td>"consensus"</td>
</tr>
<tr>
<td><b>Messages</b></td>
<td>
<ul>
<li>ProposalMsg</li>
<li>VoteMsg</li>
<li>NewBlockMsg</li>
</ul>
</td>
</tr>
<tr>
<td><b>Notes</b></td>
<td>
How do optimize/balance propagation speed & bandwidth utilization?
</td>
</tr>
</table>
## Resources
* http://www.upnp-hacks.org/upnp.html

Loading…
Cancel
Save