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