From cb7a4f857db2ac9c930f07a6085b8b980ff5a5db Mon Sep 17 00:00:00 2001 From: arnaucube Date: Sun, 7 Apr 2019 00:41:41 +0200 Subject: [PATCH] blockchain data structure and initial functions --- core/blockchain.go | 80 ++++++++++++++++++++++++++++++++++ core/blockchain_test.go | 96 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 core/blockchain.go create mode 100644 core/blockchain_test.go diff --git a/core/blockchain.go b/core/blockchain.go new file mode 100644 index 0000000..aa7891d --- /dev/null +++ b/core/blockchain.go @@ -0,0 +1,80 @@ +package core + +import ( + "errors" + "slothdb/db" + "time" +) + +type Blockchain struct { + Id []byte // Id allows to have multiple blockchains + Genesis Hash + LastBlock *Block + db *db.Db +} + +func NewBlockchain(database *db.Db) *Blockchain { + blockchain := &Blockchain{ + Id: []byte{}, + Genesis: Hash{}, + LastBlock: &Block{}, + db: database, + } + return blockchain +} + +func (bc *Blockchain) NewBlock(txs []Tx) *Block { + block := &Block{ + Height: bc.GetHeight() + 1, + PrevHash: bc.LastBlock.Hash, + Txs: txs, + Miner: Address{}, // TODO put the node address + Timestamp: time.Now(), + Nonce: 0, + Hash: Hash{}, + Signature: []byte{}, + } + return block +} + +func (bc *Blockchain) GetHeight() uint64 { + return bc.LastBlock.Height +} + +func (bc *Blockchain) GetLastBlock() *Block { + return bc.LastBlock +} + +func (bc *Blockchain) AddBlock(block *Block) error { + bc.LastBlock = block + err := bc.db.Put(block.Hash[:], block.Bytes()) + return err +} + +func (bc *Blockchain) GetBlock(hash Hash) (*Block, error) { + v, err := bc.db.Get(hash[:]) + if err != nil { + return nil, err + } + block, err := BlockFromBytes(v) + if err != nil { + return nil, err + } + return block, nil +} + +func (bc *Blockchain) GetPrevBlock(hash Hash) (*Block, error) { + currentBlock, err := bc.GetBlock(hash) + if err != nil { + return nil, err + } + if currentBlock.PrevHash.IsZero() { + return nil, errors.New("This was the oldest block") + } + prevBlock, err := bc.GetBlock(currentBlock.PrevHash) + if err != nil { + return nil, err + } + + return prevBlock, nil +} diff --git a/core/blockchain_test.go b/core/blockchain_test.go new file mode 100644 index 0000000..8ec7396 --- /dev/null +++ b/core/blockchain_test.go @@ -0,0 +1,96 @@ +package core + +import ( + "io/ioutil" + "slothdb/db" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBlockchainDataStructure(t *testing.T) { + dir, err := ioutil.TempDir("", "db") + assert.Nil(t, err) + db, err := db.New(dir) + assert.Nil(t, err) + + bc := NewBlockchain(db) + block := bc.NewBlock([]Tx{}) + + block2, err := BlockFromBytes(block.Bytes()) + assert.Nil(t, err) + assert.Equal(t, block2.Bytes(), block.Bytes()) +} + +func TestGetBlock(t *testing.T) { + dir, err := ioutil.TempDir("", "db") + assert.Nil(t, err) + db, err := db.New(dir) + assert.Nil(t, err) + + bc := NewBlockchain(db) + + block := bc.NewBlock([]Tx{}) + assert.Equal(t, block.Height, uint64(1)) + + err = bc.AddBlock(block) + assert.Nil(t, err) + + block2, err := bc.GetBlock(block.Hash) + assert.Nil(t, err) + assert.Equal(t, block.Bytes(), block2.Bytes()) +} + +func TestGetPrevBlock(t *testing.T) { + dir, err := ioutil.TempDir("", "db") + assert.Nil(t, err) + db, err := db.New(dir) + assert.Nil(t, err) + + difficulty := 1 + bc := NewBlockchain(db) + + for i := 0; i < 10; i++ { + block := bc.NewBlock([]Tx{}) + block.CalculatePoW(difficulty) + assert.Equal(t, block.Height, uint64(i+1)) + + err = bc.AddBlock(block) + assert.Nil(t, err) + assert.Equal(t, bc.LastBlock.Height, block.Height) + } + block9, err := bc.GetPrevBlock(bc.LastBlock.Hash) + assert.Nil(t, err) + assert.Equal(t, block9.Height, uint64(9)) + + block8, err := bc.GetPrevBlock(block9.Hash) + assert.Nil(t, err) + assert.Equal(t, block8.Height, uint64(8)) + + currentBlock := bc.LastBlock + for err == nil { + currentBlock, err = bc.GetPrevBlock(currentBlock.Hash) + } + assert.Equal(t, err.Error(), "This was the oldest block") +} + +func TestAddBlockWithTx(t *testing.T) { + addr0 := Address(HashBytes([]byte("addr0"))) + addr1 := Address(HashBytes([]byte("addr1"))) + + dir, err := ioutil.TempDir("", "db") + assert.Nil(t, err) + db, err := db.New(dir) + assert.Nil(t, err) + + bc := NewBlockchain(db) + + var txs []Tx + tx := NewTx(addr0, addr1, []Input{}, []Output{}) + txs = append(txs, *tx) + block := bc.NewBlock(txs) + + block2, err := BlockFromBytes(block.Bytes()) + assert.Nil(t, err) + assert.Equal(t, block2.Bytes(), block.Bytes()) +}