Archived
2
0
This repository has been archived on 2023-03-02. You can view files and clone it, but cannot push or open issues or pull requests.
kerma/models/block.go
2023-03-02 15:28:43 +01:00

232 lines
4.6 KiB
Go

// Package models provides the models for the KermaGo application
package models
import (
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"errors"
"math"
"time"
"github.com/docker/go/canonical/json"
"golang.org/x/exp/slices"
"golang.org/x/exp/utf8string"
)
/*
A Block is a struct that represents a block as per the Kerma specification
*/
type Block struct {
Type string `json:"type" binding:"required"`
Txids []string `json:"txids" binding:"required"`
Nonce string `json:"nonce" binding:"required"`
Previd *string `json:"previd"`
Created int64 `json:"created" binding:"required"`
Target string `json:"T" binding:"required"`
Miner string `json:"miner,omitempty"`
Note string `json:"note,omitempty"`
Height uint64 `json:"-"`
UTXOSet map[string]uint64 `json:"-"`
}
/*
Hash returns the sha256 hash of the canonical JSON of the block
*/
func (b *Block) Hash() (string, error) {
block, err := b.MarshalJson()
if err != nil {
return "", errors.New("could not parse block as json")
}
hashSum := sha256.Sum256(block)
return hex.EncodeToString(hashSum[:]), nil
}
/*
GetID returns the block id
*/
func (b *Block) GetID() string {
id, err := b.Hash()
if err != nil {
return ""
}
return id
}
/*
GetType returns the type of the block
*/
func (b *Block) GetType() string {
return b.Type
}
/*
GetEntity returns the block
*/
func (b *Block) GetEntity() interface{} {
return b
}
/*
Validate is responsible for validating the block; returns an error if unsuccessful
*/
func (b *Block) Validate() error {
if b.Type != "block" {
return errors.New("object not a block")
}
if b.IsGenesisBlock() {
return nil
}
if b.Previd == nil && !b.IsGenesisBlock() {
return errors.New("illegal genesis block detected")
}
if b.Target != GetGenesisBlock().Target {
return errors.New("incorrect target supplied")
}
if b.Created < GetGenesisBlock().Created {
return errors.New("timestamp before genesis block")
}
// TODO: check if this should be millis
if b.Created > time.Now().UnixMilli() {
return errors.New("block created in the future")
}
if b.Miner != "" {
if !utf8string.NewString(b.Miner).IsASCII() {
return errors.New("miner is not an ascii printable string")
}
if len(b.Miner) > 128 {
return errors.New("miner length incorrect")
}
}
if b.Note != "" {
if !utf8string.NewString(b.Note).IsASCII() {
return errors.New("note is not an ascii printable string")
}
if len(b.Note) > 128 {
return errors.New("note length incorrect")
}
}
_, err := hex.DecodeString(b.Nonce)
if err != nil {
return err
}
_, err = hex.DecodeString(*b.Previd)
if err != nil {
return err
}
bid, err := b.Hash()
if err != nil {
return err
}
bidBytes, _ := hex.DecodeString(bid)
bint := binary.BigEndian.Uint64(bidBytes)
if err != nil {
return err
}
tidBytes, _ := hex.DecodeString(b.Target)
tid := binary.BigEndian.Uint64(tidBytes)
if err != nil {
return err
}
if bint >= tid {
return errors.New("proof-of-work equation not satisfied")
}
return nil
}
/*
String returns the canonical JSON of the block as a string
*/
func (b *Block) String() string {
res, _ := b.MarshalJson()
return string(res)
}
/*
ContainsTransaction checks if the block contains the specified transaction
*/
func (b *Block) ContainsTransaction(txid string) bool {
return slices.Contains(b.Txids, txid)
}
/*
MarshalJson returns the canonical JSON of the block
*/
func (b *Block) MarshalJson() ([]byte, error) {
return json.MarshalCanonical(b)
}
/*
UnmarshalJSON creates a block from the input JSON byte array
*/
func (b *Block) UnmarshalJSON(data []byte) error {
if len(data) == 0 {
return nil
}
type tmp Block
err := json.Unmarshal(data, (*tmp)(b))
if err != nil {
return err
}
err = b.Validate()
if err != nil {
return err
}
return nil
}
/*
IsGenesisBlock checks if the block is the genesis block
*/
func (b *Block) IsGenesisBlock() bool {
return b.String() == GetGenesisBlock().String()
}
/*
GetGenesisBlock returns the genesis block
*/
func GetGenesisBlock() *Block {
genesis := Block{
Type: "block",
Target: "00000002af000000000000000000000000000000000000000000000000000000",
Created: 1624219079,
Miner: "dionyziz",
Nonce: "0000000000000000000000000000000000000000000000000000002634878840",
Note: "The Economist 2021-06-20: Crypto-miners are probably to blame for the graphics-chip shortage",
Previd: nil,
Txids: []string{},
Height: 0,
UTXOSet: map[string]uint64{},
}
return &genesis
}
/*
GetBlockReward returns the block reward
*/
func GetBlockReward() uint64 {
// 5 * (10^13)
return 5 * uint64(math.Pow(10, 13))
}