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