From 03d1af711a9ce9e47ddbf2268ba145acd5cb1037 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Thu, 11 Sep 2014 01:11:41 -0700 Subject: [PATCH] consensus & account txs are all in Block.Data.Txs --- blocks/block.go | 99 +++++++++++++++++++++++--------------------- blocks/block_test.go | 9 ++-- blocks/codec_test.go | 12 +++--- blocks/tx.go | 6 +-- mempool/mempool.go | 83 ++++++++++++++++++++++++++++++------- 5 files changed, 132 insertions(+), 77 deletions(-) diff --git a/blocks/block.go b/blocks/block.go index ce88b5c34..c16e7f059 100644 --- a/blocks/block.go +++ b/blocks/block.go @@ -17,7 +17,7 @@ const ( type Block struct { Header Validation - Txs + Data // Volatile hash []byte @@ -27,14 +27,14 @@ func ReadBlock(r io.Reader, n *int64, err *error) *Block { return &Block{ Header: ReadHeader(r, n, err), Validation: ReadValidation(r, n, err), - Txs: ReadTxs(r, n, err), + Data: ReadData(r, n, err), } } func (b *Block) WriteTo(w io.Writer) (n int64, err error) { WriteBinary(w, &b.Header, &n, &err) WriteBinary(w, &b.Validation, &n, &err) - WriteBinary(w, &b.Txs, &n, &err) + WriteBinary(w, &b.Data, &n, &err) return } @@ -50,7 +50,7 @@ func (b *Block) Hash() []byte { hashes := [][]byte{ b.Header.Hash(), b.Validation.Hash(), - b.Txs.Hash(), + b.Data.Hash(), } // Merkle hash from sub-hashes. return merkle.HashFromByteSlices(hashes) @@ -79,6 +79,21 @@ func (b *Block) ToBlockPartSet() *BlockPartSet { return NewBlockPartSet(b.Height, parts) } +// Makes an empty next block. +func (b *Block) MakeNextBlock() *Block { + return &Block{ + Header: Header{ + Name: b.Header.Name, + Height: b.Header.Height + 1, + //Fees: uint64(0), + Time: time.Now(), + PrevHash: b.Hash(), + //ValidationStateHash: nil, + //AccountStateHash: nil, + }, + } +} + //----------------------------------------------------------------------------- /* @@ -133,15 +148,14 @@ func (bp *BlockPart) Hash() []byte { //----------------------------------------------------------------------------- -/* Header is part of a Block */ type Header struct { - Name string - Height uint32 - Fees uint64 - Time time.Time - PrevHash []byte - ValidationHash []byte - TxsHash []byte + Name string + Height uint32 + Fees uint64 + Time time.Time + PrevHash []byte + ValidationStateHash []byte + AccountStateHash []byte // Volatile hash []byte @@ -152,13 +166,13 @@ func ReadHeader(r io.Reader, n *int64, err *error) (h Header) { return Header{} } return Header{ - Name: ReadString(r, n, err), - Height: ReadUInt32(r, n, err), - Fees: ReadUInt64(r, n, err), - Time: ReadTime(r, n, err), - PrevHash: ReadByteSlice(r, n, err), - ValidationHash: ReadByteSlice(r, n, err), - TxsHash: ReadByteSlice(r, n, err), + Name: ReadString(r, n, err), + Height: ReadUInt32(r, n, err), + Fees: ReadUInt64(r, n, err), + Time: ReadTime(r, n, err), + PrevHash: ReadByteSlice(r, n, err), + ValidationStateHash: ReadByteSlice(r, n, err), + AccountStateHash: ReadByteSlice(r, n, err), } } @@ -168,8 +182,8 @@ func (h *Header) WriteTo(w io.Writer) (n int64, err error) { WriteUInt64(w, h.Fees, &n, &err) WriteTime(w, h.Time, &n, &err) WriteByteSlice(w, h.PrevHash, &n, &err) - WriteByteSlice(w, h.ValidationHash, &n, &err) - WriteByteSlice(w, h.TxsHash, &n, &err) + WriteByteSlice(w, h.ValidationStateHash, &n, &err) + WriteByteSlice(w, h.AccountStateHash, &n, &err) return } @@ -187,10 +201,10 @@ func (h *Header) Hash() []byte { } } -/* Validation is part of a block */ +//----------------------------------------------------------------------------- + type Validation struct { Signatures []Signature - Txs []Tx // Volatile hash []byte @@ -198,30 +212,20 @@ type Validation struct { func ReadValidation(r io.Reader, n *int64, err *error) Validation { numSigs := ReadUInt32(r, n, err) - numAdjs := ReadUInt32(r, n, err) sigs := make([]Signature, 0, numSigs) for i := uint32(0); i < numSigs; i++ { sigs = append(sigs, ReadSignature(r, n, err)) } - tx := make([]Tx, 0, numAdjs) - for i := uint32(0); i < numAdjs; i++ { - tx = append(tx, ReadTx(r, n, err)) - } return Validation{ 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.Txs)), &n, &err) for _, sig := range v.Signatures { WriteBinary(w, sig, &n, &err) } - for _, tx := range v.Txs { - WriteBinary(w, tx, &n, &err) - } return } @@ -239,40 +243,41 @@ func (v *Validation) Hash() []byte { } } -/* Txs is part of a block */ -type Txs struct { +//----------------------------------------------------------------------------- + +type Data struct { Txs []Tx // Volatile hash []byte } -func ReadTxs(r io.Reader, n *int64, err *error) Txs { +func ReadData(r io.Reader, n *int64, err *error) Data { numTxs := ReadUInt32(r, n, err) txs := make([]Tx, 0, numTxs) for i := uint32(0); i < numTxs; i++ { txs = append(txs, ReadTx(r, n, err)) } - return Txs{Txs: txs} + return Data{Txs: txs} } -func (txs *Txs) WriteTo(w io.Writer) (n int64, err error) { - WriteUInt32(w, uint32(len(txs.Txs)), &n, &err) - for _, tx := range txs.Txs { +func (data *Data) WriteTo(w io.Writer) (n int64, err error) { + WriteUInt32(w, uint32(len(data.Txs)), &n, &err) + for _, tx := range data.Txs { WriteBinary(w, tx, &n, &err) } return } -func (txs *Txs) Hash() []byte { - if txs.hash != nil { - return txs.hash +func (data *Data) Hash() []byte { + if data.hash != nil { + return data.hash } else { - bs := make([]Binary, 0, len(txs.Txs)) - for i, tx := range txs.Txs { + bs := make([]Binary, 0, len(data.Txs)) + for i, tx := range data.Txs { bs[i] = Binary(tx) } - txs.hash = merkle.HashFromBinarySlice(bs) - return txs.hash + data.hash = merkle.HashFromBinarySlice(bs) + return data.hash } } diff --git a/blocks/block_test.go b/blocks/block_test.go index fb0344312..c46631055 100644 --- a/blocks/block_test.go +++ b/blocks/block_test.go @@ -63,7 +63,7 @@ func TestBlock(t *testing.T) { PubKey: randBytes(32), } - // Consensus Txs + // Validation Txs bond := &Bond{ Signature: randSig(), @@ -106,15 +106,14 @@ func TestBlock(t *testing.T) { Time: randTime(), PrevHash: randBytes(32), ValidationHash: randBytes(32), - TxsHash: randBytes(32), + DataHash: randBytes(32), }, Validation: Validation{ Signatures: []Signature{randSig(), randSig()}, Txs: []Txs{bond, unbond, timeout, dupeout}, }, - Txs: Txs{ - Txs: []Tx{sendTx, nameTx}, - hash: nil, + Data: Data{ + Txs: []Tx{sendTx, nameTx}, }, } diff --git a/blocks/codec_test.go b/blocks/codec_test.go index 8c1499062..340d4b391 100644 --- a/blocks/codec_test.go +++ b/blocks/codec_test.go @@ -21,7 +21,7 @@ func BenchmarkTestCustom(b *testing.B) { Time: time.Unix(123, 0), PrevHash: []byte("prevhash"), ValidationHash: []byte("validationhash"), - TxsHash: []byte("txshash"), + DataHash: []byte("datahash"), } buf := bytes.NewBuffer(nil) @@ -46,7 +46,7 @@ type HHeader struct { Time uint64 `json:"T"` PrevHash []byte `json:"PH"` ValidationHash []byte `json:"VH"` - TxsHash []byte `json:"DH"` + DataHash []byte `json:"DH"` } func BenchmarkTestJSON(b *testing.B) { @@ -59,7 +59,7 @@ func BenchmarkTestJSON(b *testing.B) { Time: 123, PrevHash: []byte("prevhash"), ValidationHash: []byte("validationhash"), - TxsHash: []byte("txshash"), + DataHash: []byte("datahash"), } h2 := &HHeader{} @@ -88,7 +88,7 @@ func BenchmarkTestGob(b *testing.B) { Time: time.Unix(123, 0), PrevHash: []byte("prevhash"), ValidationHash: []byte("validationhash"), - TxsHash: []byte("txshash"), + DataHash: []byte("datahash"), } h2 := &Header{} @@ -117,7 +117,7 @@ func BenchmarkTestMsgPack(b *testing.B) { Time: time.Unix(123, 0), PrevHash: []byte("prevhash"), ValidationHash: []byte("validationhash"), - TxsHash: []byte("txshash"), + DataHash: []byte("datahash"), } h2 := &Header{} @@ -146,7 +146,7 @@ func BenchmarkTestMsgPack2(b *testing.B) { Time: time.Unix(123, 0), PrevHash: []byte("prevhash"), ValidationHash: []byte("validationhash"), - TxsHash: []byte("txshash"), + DataHash: []byte("datahash"), } h2 := &Header{} var mh codec.MsgpackHandle diff --git a/blocks/tx.go b/blocks/tx.go index 11893a26c..bd7d3adc0 100644 --- a/blocks/tx.go +++ b/blocks/tx.go @@ -22,7 +22,7 @@ Account Txs: 1. Send Send coins to account 2. Name Associate account with a name -Consensus Txs: +Validation Txs: 3. Bond New validator posts a bond 4. Unbond Validator leaves 5. Timeout Validator times out @@ -35,7 +35,7 @@ type Tx interface { Type() byte GetSequence() uint64 GetSignature() *Signature - //IsConsensus() bool + //IsValidation() bool Binary } @@ -44,7 +44,7 @@ const ( TX_TYPE_SEND = byte(0x01) TX_TYPE_NAME = byte(0x02) - // Consensus transactions + // Validation transactions TX_TYPE_BOND = byte(0x11) TX_TYPE_UNBOND = byte(0x12) TX_TYPE_TIMEOUT = byte(0x13) diff --git a/mempool/mempool.go b/mempool/mempool.go index 52903a67e..209fcd2de 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -1,12 +1,3 @@ -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. @@ -20,24 +11,33 @@ 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. */ -//----------------------------------------------------------------------------- +package mempool + +import ( + "sync" + + . "github.com/tendermint/tendermint/blocks" + . "github.com/tendermint/tendermint/state" +) type Mempool struct { - mtx sync.Mutex - state *State - txs []Tx + mtx sync.Mutex + lastBlock *Block + state *State + txs []Tx } -func NewMempool(state *State) *Mempool { +func NewMempool(lastBlock *Block, state *State) *Mempool { return &Mempool{ - state: state, + lastBlock: lastBlock, + state: state, } } +// Apply tx to the state and remember it. func (mem *Mempool) AddTx(tx Tx) (err error) { mem.mtx.Lock() defer mem.mtx.Unlock() - // Add the tx to the state. err = mem.state.CommitTx(tx) if err != nil { return err @@ -46,3 +46,54 @@ func (mem *Mempool) AddTx(tx Tx) (err error) { return nil } } + +// Returns a new block from the current state and associated transactions. +// The block's Validation is empty, and some parts of the header too. +func (mem *Mempool) MakeProposal() (*Block, *State) { + mem.mtx.Lock() + defer mem.mtx.Unlock() + nextBlock := mem.lastBlock.MakeNextBlock() + nextBlock.Data.Txs = mem.txs + return nextBlock, mem.state +} + +// Txs that are present in block are discarded from mempool. +// Txs that have become invalid in the new state are also discarded. +func (mem *Mempool) ResetForBlockandState(block *Block, state *State) { + mem.mtx.Lock() + defer mem.mtx.Unlock() + mem.lastBlock = block + mem.state = state.Copy() + + // First, create a lookup map of txns in new block. + blockTxsMap := make(map[string]struct{}) + for _, tx := range block.Data.Txs { + txHash := BinaryHash(tx) + blockTxsMap[string(txHash)] = struct{}{} + } + + // Next, filter all txs from mem.txs that are in blockTxsMap + txs := []Tx{} + for _, tx := range mem.txs { + txHash := BinaryHash(tx) + if _, ok := blockTxsMap[string(txHash)]; ok { + continue + } else { + txs = append(txs, tx) + } + } + + // Next, filter all txs that aren't valid given new state. + validTxs := []Tx{} + for _, tx := range txs { + err := mem.state.CommitTx(tx) + if err != nil { + validTxs = append(validTxs, tx) + } else { + // tx is no longer valid. + } + } + + // We're done! + mem.txs = validTxs +}