diff --git a/blockchain/block.go b/blockchain/block.go new file mode 100644 index 0000000..11eb130 --- /dev/null +++ b/blockchain/block.go @@ -0,0 +1,57 @@ +package blockchain + +// BlockHeader contains metadata about a block +import ( + "crypto/sha256" + "encoding/binary" + "encoding/gob" + "io" +) + +// BlockHeader contains metadata about a block +type BlockHeader struct { + blockNumber uint32 + lastBlock Hash + miner Wallet +} + +// 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()...) + return buf +} + +// Block represents a block in the blockchain. Contains transactions and header metadata. +type Block struct { + BlockHeader + transactions []*Transaction +} + +// Marshal converts a Block to a byte slice +func (b *Block) Marshal() []byte { + 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) +} + +// Hash computes and returns the SHA256 hash of the block +func (b *Block) Hash() Hash { + return sha256.Sum256(b.Marshal()) +} diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go new file mode 100644 index 0000000..4ce12c7 --- /dev/null +++ b/blockchain/blockchain.go @@ -0,0 +1,45 @@ +package blockchain + +import ( + "encoding/gob" + "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 +} + +// 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) +} + +// ValidTransaction checks whether a transaction is valid, assuming the blockchain is valid. +func (bc *BlockChain) ValidTransaction(t *Transaction) bool { + // Find the transaction input (I) in the chain (by hash) + // Check that output to sender in I is equal to outputs in T + // Verify signature of T + return false +} + +// ValidBlock checks whether a block is valid +func (bc *BlockChain) ValidBlock(b *Block) bool { + for _, t := range b.transactions { + if !bc.ValidTransaction(t) { + return false + } + } + // Check that block number is one greater than last block + // Check that hash of last block is correct + return false +} diff --git a/blockchain/transaction.go b/blockchain/transaction.go new file mode 100644 index 0000000..74a406c --- /dev/null +++ b/blockchain/transaction.go @@ -0,0 +1,71 @@ +package blockchain + +import ( + "crypto/sha256" + "encoding/binary" +) + +// TxHashPointer is a reference to a transaction on the blockchain. +type TxHashPointer struct { + blockNumber uint32 + hash Hash +} + +// Marshal converts a TxHashPointer to a byte slice +func (thp TxHashPointer) Marshal() []byte { + buf := []byte{} + binary.LittleEndian.PutUint32(buf, thp.blockNumber) + for _, b := range thp.hash { + buf = append(buf, b) + } + return buf +} + +// TxOutput defines an output to a transaction +type TxOutput struct { + amount uint64 + recipient Wallet +} + +// Marshal converts a TxOutput to a byte slice +func (to TxOutput) Marshal() []byte { + buf := []byte{} + binary.LittleEndian.PutUint64(buf, to.amount) + buf = append(buf, to.recipient.Marshal()...) + return buf +} + +// TxBody contains all relevant information about a transaction +type TxBody struct { + sender Wallet + input TxHashPointer + outputs []TxOutput +} + +// Marshal converts a TxBody to a byte slice +func (tb TxBody) Marshal() []byte { + buf := tb.sender.Marshal() + buf = append(buf, tb.input.Marshal()...) + for _, out := range tb.outputs { + buf = append(buf, out.Marshal()...) + } + return buf +} + +// Transaction contains a TxBody and a signature verifying it +type Transaction struct { + TxBody + sig Signature +} + +// Marshal converts a Transaction to a byte slice +func (t *Transaction) Marshal() []byte { + buf := t.TxBody.Marshal() + buf = append(buf, t.sig.Marshal()...) + return buf +} + +// Hash returns the SHA256 hash of a transaction +func (t *Transaction) Hash() Hash { + return sha256.Sum256(t.Marshal()) +} diff --git a/blockchain/wallet.go b/blockchain/wallet.go new file mode 100644 index 0000000..b99bfca --- /dev/null +++ b/blockchain/wallet.go @@ -0,0 +1,51 @@ +package blockchain + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "fmt" + "math/big" +) + +// The curve we use for our ECC crypto. +var curve = elliptic.P256() + +// Wallet represents a Cumulus wallet address in the blockchain. +type Wallet ecdsa.PublicKey + +// Signature represents a signature of a transaction. +type Signature struct { + X big.Int + Y big.Int +} + +// Marshal converts a signature to a byte slice +func (s *Signature) Marshal() []byte { + return append(s.X.Bytes(), s.Y.Bytes()...) +} + +// New creates a new Wallet backed by a ECC key pair. Uses system entropy. +func newWallet() (*Wallet, error) { + k, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + return nil, err + } + pk := Wallet(k.PublicKey) + return &pk, nil +} + +// String returns a human-readable string representation of a wallet +func (w *Wallet) String() string { + return fmt.Sprintf("%x-%x", w.X, w.Y) +} + +// Marshal converts the Wallet to a byte slice +func (w *Wallet) Marshal() []byte { + return elliptic.Marshal(curve, w.X, w.Y) +} + +// Equals checks whether two wallets are the same. +func (w *Wallet) Equals(other *Wallet) bool { + return w.X.Cmp(other.X) == 0 && w.Y.Cmp(other.Y) == 0 +}