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