package core
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ecdsa"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/arnaucube/slowlorisdb/db"
|
|
)
|
|
|
|
type PoA struct {
|
|
AuthMiners []*ecdsa.PublicKey
|
|
}
|
|
|
|
type Blockchain struct {
|
|
Id []byte // Id allows to have multiple blockchains
|
|
Difficulty uint64
|
|
Genesis Hash
|
|
LastBlock *Block
|
|
blockdb *db.Db
|
|
txdb *db.Db
|
|
PoA PoA
|
|
}
|
|
|
|
func NewBlockchain(database *db.Db, dif uint64) *Blockchain {
|
|
blockchain := &Blockchain{
|
|
Id: []byte{},
|
|
Difficulty: dif,
|
|
Genesis: HashBytes([]byte("genesis")),
|
|
LastBlock: &Block{},
|
|
blockdb: database,
|
|
PoA: PoA{},
|
|
}
|
|
return blockchain
|
|
}
|
|
|
|
func NewPoABlockchain(database *db.Db, authNodes []*ecdsa.PublicKey) *Blockchain {
|
|
poa := PoA{
|
|
AuthMiners: authNodes,
|
|
}
|
|
blockchain := &Blockchain{
|
|
Id: []byte{},
|
|
Difficulty: uint64(0),
|
|
Genesis: HashBytes([]byte("genesis")), // tmp
|
|
LastBlock: &Block{},
|
|
blockdb: database,
|
|
PoA: poa,
|
|
}
|
|
return blockchain
|
|
}
|
|
|
|
func (bc *Blockchain) GetHeight() uint64 {
|
|
return bc.LastBlock.Height
|
|
}
|
|
|
|
func (bc *Blockchain) GetLastBlock() *Block {
|
|
return bc.LastBlock
|
|
}
|
|
|
|
func (bc *Blockchain) AddBlock(block *Block) error {
|
|
bc.LastBlock = block
|
|
err := bc.blockdb.Put(block.Hash[:], block.Bytes())
|
|
return err
|
|
}
|
|
|
|
func (bc *Blockchain) GetBlock(hash Hash) (*Block, error) {
|
|
v, err := bc.blockdb.Get(hash[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
block, err := BlockFromBytes(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return block, nil
|
|
}
|
|
|
|
func (bc *Blockchain) GetPrevBlock(hash Hash) (*Block, error) {
|
|
currentBlock, err := bc.GetBlock(hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if currentBlock.PrevHash.IsZero() {
|
|
return nil, errors.New("This was the oldest block")
|
|
}
|
|
prevBlock, err := bc.GetBlock(currentBlock.PrevHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return prevBlock, nil
|
|
}
|
|
|
|
func (bc *Blockchain) verifyBlockSignature(block *Block) bool {
|
|
// check if the signer is one of the blockchain.AuthMiners
|
|
signerIsMiner := false
|
|
for _, pubK := range bc.PoA.AuthMiners {
|
|
if bytes.Equal(PackPubK(pubK), block.Miner[:]) {
|
|
signerIsMiner = true
|
|
}
|
|
}
|
|
if !signerIsMiner && len(bc.PoA.AuthMiners) > 0 {
|
|
return false
|
|
}
|
|
|
|
// get the signature
|
|
sig, err := SignatureFromBytes(block.Signature)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
// check if the signature is by the miner
|
|
return VerifySignature(block.MinerPubK, block.Hash[:], *sig)
|
|
}
|
|
|
|
func (bc *Blockchain) VerifyBlock(block *Block) bool {
|
|
// verify block signature
|
|
if !bc.verifyBlockSignature(block) {
|
|
return false
|
|
}
|
|
|
|
// verify timestamp
|
|
|
|
// verify prev hash
|
|
// check that the block.PrevHash is the blockchain current last block
|
|
fmt.Println(block.PrevHash)
|
|
fmt.Println(bc.LastBlock.Hash)
|
|
if !bytes.Equal(block.PrevHash[:], bc.LastBlock.Hash[:]) {
|
|
return false
|
|
}
|
|
|
|
// verify block height
|
|
// check that the block height is the last block + 1
|
|
if block.Height != bc.LastBlock.Height+1 {
|
|
return false
|
|
}
|
|
|
|
// verify block transactions
|
|
|
|
return true
|
|
}
|
|
|
|
// func (bc *Blockchain) Mint(toAddr Address, amount uint64) error {
|
|
// fromAddr := Address(HashBytes([]byte("mint")))
|
|
// out :=
|
|
// tx := NewTx(fromAddr, toAddr, []Input, )
|
|
// }
|