Skip to content

Commit

Permalink
add initial transaction support
Browse files Browse the repository at this point in the history
  • Loading branch information
fzerorubigd committed Nov 15, 2019
1 parent 2948f9d commit 79d1032
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 21 deletions.
8 changes: 4 additions & 4 deletions blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type BlockChain struct {
}

// Add a new data to the end of the block chain by creating a new block
func (bc *BlockChain) Add(data string) (*Block, error) {
func (bc *BlockChain) Add(data []*Transaction) (*Block, error) {
hash, err := bc.store.LastHash()
if err != nil {
return nil, fmt.Errorf("Getting the last block failed: %w", err)
Expand Down Expand Up @@ -66,7 +66,7 @@ func (bc *BlockChain) Validate() error {

// NewBlockChain creates a new block chain with a difficulty, difficulty in this
// block chain is the number of zeros in the begining of the generated hash
func NewBlockChain(genesis string, difficulty int, store Store) (*BlockChain, error) {
func NewBlockChain(genesis []byte, difficulty int, store Store) (*BlockChain, error) {
mask := GenerateMask(difficulty)
bc := BlockChain{
Difficulty: difficulty,
Expand All @@ -78,8 +78,8 @@ func NewBlockChain(genesis string, difficulty int, store Store) (*BlockChain, er
if !errors.Is(err, ErrNotInitialized) {
return nil, fmt.Errorf("store already initialized")
}

gb := NewBlock(genesis, bc.Mask, []byte{})
gbTxn := NewCoinBaseTxn(genesis, nil)
gb := NewBlock([]*Transaction{gbTxn}, bc.Mask, []byte{})
if err := store.Append(gb); err != nil {
return nil, fmt.Errorf("Add Genesis block to store failed: %w", err)
}
Expand Down
20 changes: 10 additions & 10 deletions blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
// Block is the core data for the block chain. it can contain anything,
// a block is like a record in a table in database
type Block struct {
Timestamp time.Time
Data []byte
Timestamp time.Time
Transactions []*Transaction

Nonce int32
PrevHash []byte
Expand All @@ -19,15 +19,15 @@ type Block struct {

func (b *Block) String() string {
return fmt.Sprintf(
"Time: %s\nData: %s\nHash: %x\nPrevHash: %x\nNonce: %d\n----\n",
b.Timestamp, b.Data, b.Hash, b.PrevHash, b.Nonce,
"Time: %s\nTxn Count: %d\nHash: %x\nPrevHash: %x\nNonce: %d\n----\n",
b.Timestamp, len(b.Transactions), b.Hash, b.PrevHash, b.Nonce,
)
}

// Validate try to validate the current block, it needs a difficulty mask
// for validating the hash difficulty
func (b *Block) Validate(mask []byte) error {
h := EasyHash(b.Timestamp.UnixNano(), b.Data, b.PrevHash, b.Nonce)
h := EasyHash(b.Timestamp.UnixNano(), calculateTxnsHash(b.Transactions...), b.PrevHash, b.Nonce)
if !bytes.Equal(h, b.Hash) {
return fmt.Errorf("the hash is invalid it should be %x is %x", h, b.Hash)
}
Expand All @@ -41,13 +41,13 @@ func (b *Block) Validate(mask []byte) error {

// NewBlock creates a new block in the system, it needs deficulty mask for
// create a good hash, and also the previous block hash
func NewBlock(data string, mask, prevHash []byte) *Block {
func NewBlock(txns []*Transaction, mask, prevHash []byte) *Block {
b := Block{
Timestamp: time.Now(),
Data: []byte(data),
PrevHash: prevHash,
Timestamp: time.Now(),
Transactions: txns,
PrevHash: prevHash,
}
b.Hash, b.Nonce = DifficultHash(mask, b.Timestamp.UnixNano(), b.Data, b.PrevHash)
b.Hash, b.Nonce = DifficultHash(mask, b.Timestamp.UnixNano(), calculateTxnsHash(b.Transactions...), b.PrevHash)

return &b
}
11 changes: 6 additions & 5 deletions blocks_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package bitacoin

import (
"encoding/hex"
"strings"
"testing"
)

func TestBlockCreation(t *testing.T) {
const data = "Block Data"
data := []*Transaction{NewCoinBaseTxn([]byte("bita"), nil)}
mask := GenerateMask(2)
prev := EasyHash("Prev hash")

Expand All @@ -16,8 +17,8 @@ func TestBlockCreation(t *testing.T) {
return
}

if !strings.Contains(b.String(), data) {
t.Errorf("The data is not in string")
if !strings.Contains(b.String(), hex.EncodeToString(prev)) {
t.Errorf("The prev hash is not in string")
}

mask2 := GenerateMask(8)
Expand All @@ -32,11 +33,11 @@ func TestBlockCreation(t *testing.T) {
}
b.Nonce--

b.Data = []byte("Something else")
b.Transactions = []*Transaction{NewCoinBaseTxn([]byte("forud"), nil)}
if err := b.Validate(mask); err == nil {
t.Errorf("Block should be invalid, but is not")
}
b.Data = []byte(data)
b.Transactions = data

b.PrevHash = EasyHash("Something else")
if err := b.Validate(mask); err == nil {
Expand Down
4 changes: 2 additions & 2 deletions cmd/bc/cli_initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ func initialize(store bitacoin.Store, args ...string) error {
var (
genesis string
)
fs.StringVar(&genesis, "genesis", "Genesis block", "Genesis data to use")
fs.StringVar(&genesis, "owner", "bita", "Genesis block owner")

fs.Parse(args[1:])

_, err := bitacoin.NewBlockChain(genesis, difficulty, store)
_, err := bitacoin.NewBlockChain([]byte(genesis), difficulty, store)
if err != nil {
return fmt.Errorf("create failed: %w", err)
}
Expand Down
61 changes: 61 additions & 0 deletions transaction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package bitacoin

import "time"

const (
coinBaseReward = 100000
)

type Transaction struct {
ID []byte

VOut []TXOutput
VIn []TXInput
}

type TXOutput struct {
Value int
PubKey []byte
}

type TXInput struct {
TXID []byte
VOut int
Sig []byte
}

func calculateTxnID(txn *Transaction) []byte {
return EasyHash(txn.VOut, txn.VIn)
}

func calculateTxnsHash(txns ...*Transaction) []byte {
data := make([]interface{}, len(txns))
for i := range txns {
data[i] = txns[i].ID
}

return EasyHash(data...)
}

func NewCoinBaseTxn(to, data []byte) *Transaction {
if len(data) == 0 {
data = EasyHash(to, time.Now())
}

txi := TXInput{
TXID: []byte{},
VOut: -1,
Sig: data,
}

txo := TXOutput{
Value: coinBaseReward,
PubKey: to,
}
txn := &Transaction{
VOut: []TXOutput{txo},
VIn: []TXInput{txi},
}
txn.ID = calculateTxnID(txn)
return txn
}

0 comments on commit 79d1032

Please sign in to comment.