arnaucube
arnaucube/slowlorisdbPublic Self-hosted repository
slowlorisdb/core/blockchain.go
core/blockchain.goRaw
  1package core
  2
  3import (
  4	"bytes"
  5	"crypto/ecdsa"
  6	"errors"
  7	"fmt"
  8
  9	"github.com/arnaucube/slowlorisdb/db"
 10)
 11
 12type PoA struct {
 13	AuthMiners []*ecdsa.PublicKey
 14}
 15
 16type Blockchain struct {
 17	Id         []byte // Id allows to have multiple blockchains
 18	Difficulty uint64
 19	Genesis    Hash
 20	LastBlock  *Block
 21	blockdb    *db.Db
 22	txdb       *db.Db
 23	addressdb  *db.Db
 24	walletsdb  *db.Db
 25	PoA        PoA
 26}
 27
 28func NewBlockchain(database *db.Db, dif uint64) *Blockchain {
 29	blockchain := &Blockchain{
 30		Id:         []byte{},
 31		Difficulty: dif,
 32		Genesis:    HashBytes([]byte("genesis")),
 33		LastBlock:  &Block{},
 34		blockdb:    database,
 35		PoA:        PoA{},
 36	}
 37	return blockchain
 38}
 39
 40func NewPoABlockchain(database *db.Db, authNodes []*ecdsa.PublicKey) *Blockchain {
 41	blockDb := database.WithPrefix([]byte("blockDb"))
 42	txDb := database.WithPrefix([]byte("txDb"))
 43	addressDb := database.WithPrefix([]byte("addressDb"))
 44
 45	poa := PoA{
 46		AuthMiners: authNodes,
 47	}
 48	blockchain := &Blockchain{
 49		Id:         []byte{},
 50		Difficulty: uint64(0),
 51		Genesis:    HashBytes([]byte("genesis")), // tmp
 52		LastBlock:  &Block{},
 53		blockdb:    blockDb,
 54		txdb:       txDb,
 55		addressdb:  addressDb,
 56		PoA:        poa,
 57	}
 58	return blockchain
 59}
 60
 61func (bc *Blockchain) GetHeight() uint64 {
 62	return bc.LastBlock.Height
 63}
 64
 65func (bc *Blockchain) GetLastBlock() *Block {
 66	return bc.LastBlock
 67}
 68
 69func (bc *Blockchain) AddBlock(block *Block) error {
 70	if !bc.VerifyBlock(block) {
 71		return errors.New("Block could not be verified")
 72	}
 73	bc.LastBlock = block.Copy()
 74	err := bc.blockdb.Put(block.Hash[:], block.Bytes())
 75	if err != nil {
 76		return err
 77	}
 78
 79	// add each tx to txDb & update addressDb balances
 80	for _, tx := range block.Txs {
 81		err = bc.txdb.Put(tx.TxId[:], tx.Bytes())
 82		if err != nil {
 83			return err
 84		}
 85		bc.UpdateWalletsWithNewTx(&tx)
 86	}
 87	return nil
 88}
 89
 90func (bc *Blockchain) UpdateWalletsWithNewTx(tx *Tx) error {
 91	for _, in := range tx.Inputs {
 92		balanceBytes, err := bc.addressdb.Get(PackPubK(tx.From))
 93		if err != nil {
 94			return err
 95		}
 96		balance := Uint64FromBytes(balanceBytes)
 97		balance = balance - in.Value
 98		err = bc.addressdb.Put(PackPubK(tx.From), Uint64ToBytes(balance))
 99		if err != nil {
100			return err
101		}
102	}
103	for _, out := range tx.Outputs {
104		balanceBytes, err := bc.addressdb.Get(PackPubK(tx.To))
105		if err != nil {
106			return err
107		}
108		balance := Uint64FromBytes(balanceBytes)
109		balance = balance + out.Value
110		err = bc.addressdb.Put(PackPubK(tx.To), Uint64ToBytes(balance))
111		if err != nil {
112			return err
113		}
114	}
115	return nil
116
117}
118
119func (bc *Blockchain) GetBlock(hash Hash) (*Block, error) {
120	v, err := bc.blockdb.Get(hash[:])
121	if err != nil {
122		return nil, err
123	}
124	block, err := BlockFromBytes(v)
125	if err != nil {
126		return nil, err
127	}
128	return block, nil
129}
130
131func (bc *Blockchain) GetPrevBlock(hash Hash) (*Block, error) {
132	currentBlock, err := bc.GetBlock(hash)
133	if err != nil {
134		return nil, err
135	}
136	if currentBlock.PrevHash.IsZero() {
137		return nil, errors.New("This was the oldest block")
138	}
139	prevBlock, err := bc.GetBlock(currentBlock.PrevHash)
140	if err != nil {
141		return nil, err
142	}
143
144	return prevBlock, nil
145}
146
147func (bc *Blockchain) verifyBlockSignature(block *Block) bool {
148	// check if the signer is one of the blockchain.AuthMiners
149	signerIsMiner := false
150	for _, pubK := range bc.PoA.AuthMiners {
151		if bytes.Equal(PackPubK(pubK), PackPubK(block.MinerPubK)) {
152			signerIsMiner = true
153		}
154	}
155	if !signerIsMiner && len(bc.PoA.AuthMiners) > 0 {
156		fmt.Println("signer is not miner")
157		return false
158	}
159
160	// get the signature
161	sig, err := SignatureFromBytes(block.Signature)
162	if err != nil {
163		fmt.Println("error parsing signature")
164		return false
165	}
166
167	// check if the signature is by the miner
168	return VerifySignature(block.MinerPubK, block.Hash[:], *sig)
169}
170
171func (bc *Blockchain) VerifyBlock(block *Block) bool {
172	// verify block signature
173	// TODO for the moment just covered the case of PoA blockchain
174	if !bc.verifyBlockSignature(block) {
175		fmt.Println("signature verification error")
176		return false
177	}
178
179	// verify timestamp
180
181	// verify prev hash
182	// check that the block.PrevHash is the blockchain current last block
183	if !bytes.Equal(block.PrevHash[:], bc.LastBlock.Hash[:]) {
184		fmt.Println("block.PrevHash not equal to last block hash")
185		return false
186	}
187
188	// verify block height
189	// check that the block height is the last block + 1
190	if block.Height != bc.LastBlock.Height+1 {
191		fmt.Println("block.Height error")
192		return false
193	}
194
195	// verify block transactions (not if the block is the genesis block)
196	if !bytes.Equal(block.Txs[0].TxId[:], GenesisHashTxInput[:]) {
197		for _, tx := range block.Txs {
198			txVerified := CheckTx(&tx)
199			if !txVerified {
200				fmt.Println("tx could not be verified")
201				return false
202			}
203		}
204	}
205
206	// TODO in --> out0
207	//          -> out1
208	//          -> ...
209
210	return true
211}
212
213// func (bc *Blockchain) Mint(toAddr Address, amount uint64) error {
214//         fromAddr := Address(HashBytes([]byte("mint")))
215//         out :=
216//         tx := NewTx(fromAddr, toAddr, []Input, )
217// }