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