Skip to content

Commit

Permalink
Merge pull request #39 from ubclaunchpad/30-msg-layer
Browse files Browse the repository at this point in the history
Message layer
  • Loading branch information
bfbachmann committed May 22, 2017
2 parents 156dd85 + 3c898b2 commit 5b2f674
Show file tree
Hide file tree
Showing 19 changed files with 450 additions and 197 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ go:
- 1.7
before_install:
- go get github.com/mattn/goveralls
install:
- make install-glide
- make deps
script:
- $HOME/gopath/bin/goveralls -service=travis-ci
18 changes: 18 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
PACKAGES = `go list ./... | grep -v vendor/`

all: cumulus

cumulus:
go build

test:
go test $(PACKAGES)

deps:
glide install

clean:
rm cumulus

install-glide:
sh scripts/install_glide.sh
16 changes: 6 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,21 @@ Install dependencies. We need to manually use version 0.11.1 of Glide temporaril

Install Glide.
```sh
go get github.com/Masterminds/glide
cd $GOPATH/src/github.com/Masterminds/glide
git checkout tags/v0.11.1
go install
make install-glide
```

Verify you have the correct version installed.
Get dependencies.
```sh
glide --version
make deps
```

Get dependencies.
Build.
```sh
cd $GOPATH/src/github.com/ubclaunchpad/cumulus
glide install
make
```

## Testing

```
go test ./...
make test
```
61 changes: 38 additions & 23 deletions blockchain/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,71 @@ package blockchain

// BlockHeader contains metadata about a block
import (
"crypto/sha256"
"encoding/binary"
"encoding/gob"
"fmt"
"io"
)

const (
// BlockSize is the maximum size of a block in bytes when marshaled (about 250K).
BlockSize = 1 << 18
// BlockHeaderLen is the length in bytes of a block header.
BlockHeaderLen = 32/8 + HashLen + AddrLen
)

// BlockHeader contains metadata about a block
type BlockHeader struct {
blockNumber uint32
lastBlock Hash
miner Wallet
BlockNumber uint32
LastBlock Hash
Miner Address
}

// Marshal converts a BlockHeader to a byte slice
func (bh *BlockHeader) Marshal() []byte {
buf := []byte{}
binary.LittleEndian.PutUint32(buf, bh.blockNumber)
for _, b := range bh.lastBlock {
buf = append(buf, b)
}
buf = append(buf, bh.miner.Marshal()...)
buf := make([]byte, 4, BlockHeaderLen)
binary.LittleEndian.PutUint32(buf, bh.BlockNumber)
buf = append(buf, bh.LastBlock.Marshal()...)
buf = append(buf, bh.Miner.Marshal()...)
return buf
}

// Block represents a block in the blockchain. Contains transactions and header metadata.
type Block struct {
BlockHeader
transactions []*Transaction
Transactions []*Transaction
}

// Marshal converts a Block to a byte slice
// Len returns the length in bytes of the Block.
func (b *Block) Len() int {
l := BlockHeaderLen
for _, t := range b.Transactions {
l += t.Len()
}
return l
}

// Marshal converts a Block to a byte slice.
func (b *Block) Marshal() []byte {
buf := b.BlockHeader.Marshal()
for _, t := range b.transactions {
buf := make([]byte, 0, b.Len())
buf = append(buf, b.BlockHeader.Marshal()...)
for _, t := range b.Transactions {
buf = append(buf, t.Marshal()...)
}
return buf
}

// Encode writes the marshalled block to the given io.Writer
func (b *Block) Encode(w io.Writer) {
gob.NewEncoder(w).Encode(b)
}

// Decode reads the marshalled block from the given io.Reader
func (b *Block) Decode(r io.Reader) {
gob.NewDecoder(r).Decode(b)
err := gob.NewEncoder(w).Encode(b)
if err != nil {
fmt.Println(err.Error())
}
}

// Hash computes and returns the SHA256 hash of the block
func (b *Block) Hash() Hash {
return sha256.Sum256(b.Marshal())
// DecodeBlock reads the marshalled block from the given io.Reader and populates b
func DecodeBlock(r io.Reader) *Block {
var b Block
gob.NewDecoder(r).Decode(&b)
return &b
}
19 changes: 19 additions & 0 deletions blockchain/block_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package blockchain

import (
"bytes"
"testing"
)

func TestEncodeDecodeBlock(t *testing.T) {
b1 := newBlock()

buf := bytes.NewBuffer(make([]byte, 0, b1.Len()))

b1.Encode(buf)
b2 := DecodeBlock(buf)

if HashSum(b1) != HashSum(b2) {
t.Fail()
}
}
35 changes: 26 additions & 9 deletions blockchain/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,40 @@ import (
"io"
)

// Hash represents a 256-bit hash of a block or transaction
type Hash [32]byte

// BlockChain represents a linked list of blocks
type BlockChain struct {
blocks []*Block
head Hash
Blocks []*Block
Head Hash
}

// Len returns the length of the BlockChain when marshalled
func (bc *BlockChain) Len() int {
l := 0
for _, b := range bc.Blocks {
l += b.Len()
}
return l + HashLen
}

// Marshal converts the BlockChain to a byte slice.
func (bc *BlockChain) Marshal() []byte {
buf := make([]byte, 0, bc.Len())
for _, b := range bc.Blocks {
buf = append(buf, b.Marshal()...)
}
return append(buf, bc.Head.Marshal()...)
}

// Encode writes the marshalled blockchain to the given io.Writer
func (bc *BlockChain) Encode(w io.Writer) {
gob.NewEncoder(w).Encode(bc)
}

// Decode reads the marshalled blockchain from the given io.Reader
func (bc *BlockChain) Decode(r io.Reader) {
gob.NewDecoder(r).Decode(bc)
// DecodeBlockChain reads the marshalled blockchain from the given io.Reader
func DecodeBlockChain(r io.Reader) *BlockChain {
var bc BlockChain
gob.NewDecoder(r).Decode(&bc)
return &bc
}

// ValidTransaction checks whether a transaction is valid, assuming the blockchain is valid.
Expand All @@ -34,7 +51,7 @@ func (bc *BlockChain) ValidTransaction(t *Transaction) bool {

// ValidBlock checks whether a block is valid
func (bc *BlockChain) ValidBlock(b *Block) bool {
for _, t := range b.transactions {
for _, t := range b.Transactions {
if !bc.ValidTransaction(t) {
return false
}
Expand Down
19 changes: 19 additions & 0 deletions blockchain/blockchain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package blockchain

import (
"bytes"
"testing"
)

func TestEncodeDecodeBlockChain(t *testing.T) {
b1 := newBlockChain()

buf := bytes.NewBuffer(make([]byte, 0, b1.Len()))

b1.Encode(buf)
b2 := DecodeBlockChain(buf)

if HashSum(b1) != HashSum(b2) {
t.Fail()
}
}
28 changes: 28 additions & 0 deletions blockchain/hash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package blockchain

import "crypto/sha256"

// HashLen is the length in bytes of a hash.
const HashLen = 32

// Hash represents a 256-bit hash of a block or transaction
type Hash [HashLen]byte

// Marshal converts a Hash to a slice.
func (h Hash) Marshal() []byte {
buf := make([]byte, HashLen)
for i, b := range h {
buf[i] = b
}
return buf
}

// Marshaller is any type that can convert itself to a byte slice
type Marshaller interface {
Marshal() []byte
}

// HashSum computes the SHA256 hash of a Marshaller.
func HashSum(m Marshaller) Hash {
return sha256.Sum256(m.Marshal())
}
90 changes: 90 additions & 0 deletions blockchain/test_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package blockchain

import (
"crypto/ecdsa"
crand "crypto/rand"
"crypto/sha256"
mrand "math/rand"
)

func newHash() Hash {
message := make([]byte, 256)
crand.Read(message)
return sha256.Sum256(message)
}

func newWallet() Wallet {
priv, _ := ecdsa.GenerateKey(curve, crand.Reader)
return (*wallet)(priv)
}

func newTxHashPointer() TxHashPointer {
return TxHashPointer{
BlockNumber: mrand.Uint32(),
Hash: newHash(),
}
}

func newTxOutput() TxOutput {
return TxOutput{
Amount: uint64(mrand.Int63()),
Recipient: newWallet().Public(),
}
}

func newTxBody() TxBody {
// Uniform distribution on [1, 4]
nOutputs := mrand.Intn(4) + 1
body := TxBody{
Sender: newWallet().Public(),
Input: newTxHashPointer(),
Outputs: make([]TxOutput, nOutputs),
}
for i := 0; i < nOutputs; i++ {
body.Outputs[i] = newTxOutput()
}
return body
}

func newTransaction() *Transaction {
sender := newWallet()
tbody := newTxBody()
digest := HashSum(tbody)
sig, _ := sender.Sign(digest, crand.Reader)
return &Transaction{
TxBody: tbody,
Sig: sig,
}
}

func newBlockHeader() BlockHeader {
return BlockHeader{
BlockNumber: mrand.Uint32(),
LastBlock: newHash(),
Miner: newWallet().Public(),
}
}

func newBlock() *Block {
// Uniform distribution on [500, 999]
nTransactions := mrand.Intn(500) + 500
b := Block{
BlockHeader: newBlockHeader(),
Transactions: make([]*Transaction, nTransactions),
}
for i := 0; i < nTransactions; i++ {
b.Transactions[i] = newTransaction()
}
return &b
}

func newBlockChain() *BlockChain {
// Uniform distribution on [10, 50]
nBlocks := mrand.Intn(40) + 10
bc := BlockChain{Blocks: make([]*Block, nBlocks)}
for i := 0; i < nBlocks; i++ {
bc.Blocks[i] = newBlock()
}
bc.Head = HashSum(bc.Blocks[nBlocks-1])
return &bc
}

0 comments on commit 5b2f674

Please sign in to comment.