@ -1,12 +1,13 @@ | |||
TenderMint - proof of concept | |||
* **[peer](https://github.com/tendermint/tendermint/blob/master/peer):** P2P networking stack. Designed to be extensible. | |||
* **[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. | |||
* **[crypto](https://github.com/tendermint/tendermint/blob/master/crypto):** Includes cgo bindings of ed25519. | |||
### Status | |||
* Node & testnet *now* | |||
* Block manager *now* | |||
* Node & testnet *complete* | |||
* PEX peer exchange *complete* | |||
* p2p/* *complete* | |||
* Ed25519 bindings *complete* | |||
@ -0,0 +1,163 @@ | |||
package blocks | |||
import ( | |||
"github.com/tendermint/tendermint/p2p" | |||
) | |||
const ( | |||
BlocksCh = "block" | |||
msgTypeUnknown = Byte(0x00) | |||
msgTypeState = Byte(0x01) | |||
msgTypeRequest = Byte(0x02) | |||
msgTypeData = Byte(0x03) | |||
dataTypeAll = byte(0x00) | |||
dataTypeValidation = byte(0x01) | |||
dataTypeTxs = byte(0x02) | |||
// dataTypeCheckpoint = byte(0x04) | |||
) | |||
/* | |||
*/ | |||
type BlockManager struct { | |||
quit chan struct{} | |||
started uint32 | |||
stopped uint32 | |||
} | |||
func NewBlockManager() *BlockManager { | |||
bm := &BlockManager{ | |||
sw: sw, | |||
quit: make(chan struct{}), | |||
} | |||
return bm | |||
} | |||
func (bm *BlockManager) Start() { | |||
if atomic.CompareAndSwapUint32(&bm.started, 0, 1) { | |||
log.Info("Starting BlockManager") | |||
go bm.XXX() | |||
} | |||
} | |||
func (bm *BlockManager) Stop() { | |||
if atomic.CompareAndSwapUint32(&bm.stopped, 0, 1) { | |||
log.Info("Stopping BlockManager") | |||
close(bm.quit) | |||
} | |||
} | |||
func (bm *BlockManager) XXX() { | |||
} | |||
//----------------------------------------------------------------------------- | |||
/* Messages */ | |||
// TODO: check for unnecessary extra bytes at the end. | |||
func decodeMessage(bz ByteSlice) (msg Message) { | |||
// log.Debug("decoding msg bytes: %X", bz) | |||
switch Byte(bz[0]) { | |||
case msgTypeState: | |||
return &StateMessage{} | |||
case msgTypeRequest: | |||
return readRequestMessage(bytes.NewReader(bz[1:])) | |||
case msgTypeData: | |||
return readDataMessage(bytes.NewReader(bz[1:])) | |||
default: | |||
return nil | |||
} | |||
} | |||
/* | |||
A StateMessage declares what (contiguous) blocks & headers are known. | |||
LastValidationHeight >= LastBlockHeight. | |||
*/ | |||
type StateMessage struct { | |||
LastBlockHeight UInt64 | |||
LastValidationHeight UInt64 | |||
} | |||
func readStateMessage(r io.Reader) *StateMessage { | |||
lastBlockHeight := ReadUInt64(r) | |||
lastValidationHeight := ReadUInt64(r) | |||
return &StateMessage{ | |||
LastBlockHeight: lastBlockHeight, | |||
LastValidationHeight: lastValidationHeight, | |||
} | |||
} | |||
func (m *StateMessage) WriteTo(w io.Writer) (n int64, err error) { | |||
n, err = WriteTo(msgTypeState, w, n, err) | |||
n, err = WriteTo(m.LastBlockHeight, w, n, err) | |||
n, err = WriteTo(m.LastValidationHeight, w, n, err) | |||
return | |||
} | |||
func (m *StateMessage) String() string { | |||
return fmt.Sprintf("[State %v/%v]", | |||
m.LastBlockHeight, m.LastValidationHeight) | |||
} | |||
/* | |||
A RequestMessage requests a block and/or header at a given height. | |||
*/ | |||
type RequestMessage struct { | |||
Type Byte | |||
Height UInt64 | |||
} | |||
func readRequestMessage(r io.Reader) *RequestMessage { | |||
requestType := ReadByte(r) | |||
height := ReadUInt64(r) | |||
return &RequestMessage{ | |||
Type: requestType, | |||
Height: height, | |||
} | |||
} | |||
func (m *RequestMessage) WriteTo(w io.Writer) (n int64, err error) { | |||
n, err = WriteTo(msgTypeRequest, w, n, err) | |||
n, err = WriteTo(m.Type, w, n, err) | |||
n, err = WriteTo(m.Height, w, n, err) | |||
return | |||
} | |||
func (m *RequestMessage) String() string { | |||
return fmt.Sprintf("[Request %X@%v]", m.Type, m.Height) | |||
} | |||
/* | |||
A DataMessage contains block data, maybe requested. | |||
The data can be a Validation, Txs, or whole Block object. | |||
*/ | |||
type DataMessage struct { | |||
Type Byte | |||
Height UInt64 | |||
Bytes ByteSlice | |||
} | |||
func readDataMessage(r io.Reader) *DataMessage { | |||
dataType := ReadByte(r) | |||
height := ReadUInt64(r) | |||
bytes := ReadByteSlice(r) | |||
return &DataMessage{ | |||
Type: dataType, | |||
Height: height, | |||
Bytes: bytes, | |||
} | |||
} | |||
func (m *DataMessage) WriteTo(w io.Writer) (n int64, err error) { | |||
n, err = WriteTo(msgTypeData, w, n, err) | |||
n, err = WriteTo(m.Type, w, n, err) | |||
n, err = WriteTo(m.Height, w, n, err) | |||
n, err = WriteTo(m.Bytes, w, n, err) | |||
return | |||
} | |||
func (m *DataMessage) String() string { | |||
return fmt.Sprintf("[Data %X@%v]", m.Type, m.Height) | |||
} |
@ -1,9 +1,94 @@ | |||
package p2p | |||
import ( | |||
"bytes" | |||
"fmt" | |||
"io" | |||
. "github.com/tendermint/tendermint/binary" | |||
) | |||
/* | |||
A Message is anything that can be serialized. | |||
The resulting serialized bytes of Message don't contain type information, | |||
so messages are typically wrapped in a TypedMessage before put in the wire. | |||
*/ | |||
type Message interface { | |||
Binary | |||
} | |||
/* | |||
A TypedMessage extends a Message with a single byte of type information. | |||
When deserializing a message from the wire, a switch statement is needed | |||
to dispatch to the correct constructor, typically named "ReadXXXMessage". | |||
*/ | |||
type TypedMessage struct { | |||
Type Byte | |||
Message Message | |||
} | |||
func (tm TypedMessage) WriteTo(w io.Writer) (n int64, err error) { | |||
n, err = WriteTo(tm.Type, w, n, err) | |||
n, err = WriteTo(tm.Message, w, n, err) | |||
return | |||
} | |||
func (tm TypedMessage) String() string { | |||
return fmt.Sprintf("0x%X⋺%v", tm.Type, tm.Message) | |||
} | |||
//----------------------------------------------------------------------------- | |||
/* | |||
Packet encapsulates a ByteSlice on a Channel. | |||
Typically the Bytes are the serialized form of a TypedMessage. | |||
*/ | |||
type Packet struct { | |||
Channel String | |||
Bytes ByteSlice | |||
// Hash | |||
} | |||
func NewPacket(chName String, msg Binary) Packet { | |||
msgBytes := BinaryBytes(msg) | |||
return Packet{ | |||
Channel: chName, | |||
Bytes: msgBytes, | |||
} | |||
} | |||
func (p Packet) WriteTo(w io.Writer) (n int64, err error) { | |||
n, err = WriteTo(p.Channel, w, n, err) | |||
n, err = WriteTo(p.Bytes, w, n, err) | |||
return | |||
} | |||
func (p Packet) Reader() io.Reader { | |||
return bytes.NewReader(p.Bytes) | |||
} | |||
func (p Packet) String() string { | |||
return fmt.Sprintf("%v:%X", p.Channel, p.Bytes) | |||
} | |||
func ReadPacketSafe(r io.Reader) (pkt Packet, err error) { | |||
chName, err := ReadStringSafe(r) | |||
if err != nil { | |||
return | |||
} | |||
// TODO: packet length sanity check. | |||
bytes, err := ReadByteSliceSafe(r) | |||
if err != nil { | |||
return | |||
} | |||
return Packet{Channel: chName, Bytes: bytes}, nil | |||
} | |||
/* | |||
InboundPacket extends Packet with fields relevant to inbound packets. | |||
*/ | |||
type InboundPacket struct { | |||
Peer *Peer | |||
Time Time | |||
Packet | |||
} |