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// }