139 lines
3.7 KiB
Go
139 lines
3.7 KiB
Go
package common
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha512"
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"github.com/matchsystems/werr"
|
|
"io"
|
|
"time"
|
|
)
|
|
|
|
// BlockSecsDiff is the time in second that passes
|
|
// between blocks rotation
|
|
const BlockSecsDiff = 100
|
|
|
|
func GetBlockId(t time.Time) int64 {
|
|
// because number is int -- it will just cut off the floating point part
|
|
return (t.UTC().Unix() / BlockSecsDiff) * BlockSecsDiff
|
|
}
|
|
|
|
type BlockJson struct {
|
|
Id int64 `json:"id"`
|
|
PrevHash string `json:"prev_hash"`
|
|
Hash string `json:"hash"`
|
|
TXs []json.RawMessage `json:"txs"`
|
|
IsFinished bool `json:"is_finished"`
|
|
ByteEncoding string `json:"byte_encoding"`
|
|
}
|
|
|
|
type Block struct {
|
|
Id int64
|
|
PrevHash []byte
|
|
Hash []byte
|
|
TXs []*Tx
|
|
IsFinished bool
|
|
}
|
|
|
|
func (block *Block) CalcHash() ([]byte, error) {
|
|
hash := sha512.New()
|
|
if err := binary.Write(hash, binary.BigEndian, block.Id); err != nil {
|
|
return nil, werr.Wrapf(err, "failed to write block id into hash buffer for block %d", block.Id)
|
|
}
|
|
hash.Write(block.PrevHash)
|
|
for _, tx := range block.TXs {
|
|
hash.Write(tx.Hash)
|
|
}
|
|
return hash.Sum(nil), nil
|
|
}
|
|
|
|
// ValidateHash calculates new block hash and compares it to current block.Hash
|
|
// returning is block hash is valid
|
|
func (block *Block) ValidateHash() bool {
|
|
hash, err := block.CalcHash()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return bytes.Equal(block.Hash, hash)
|
|
}
|
|
|
|
// ValidateTXs returns the number of invalid transactions
|
|
// and map of hashes : reasons why txs were invalid.
|
|
// If invalid count > 0 -- the whole block isn't legit
|
|
func (block *Block) ValidateTXs() (invalidCount int, invalidTXs map[string]string) {
|
|
invalidTXs = make(map[string]string)
|
|
for _, tx := range block.TXs {
|
|
if err := tx.Validate(); err != nil {
|
|
invalidTXs[tx.HashString()] = err.Error()
|
|
invalidCount++
|
|
}
|
|
}
|
|
return invalidCount, invalidTXs
|
|
}
|
|
|
|
func (block *Block) ToJSON(byteEncodingStr string) ([]byte, error) {
|
|
encoding, err := GetEncoding(byteEncodingStr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var txData json.RawMessage
|
|
txsData := make([]json.RawMessage, len(block.TXs))
|
|
for i, tx := range block.TXs {
|
|
if txData, err = tx.ToJSON(byteEncodingStr); err != nil {
|
|
return nil, werr.Wrapf(err, "error while converting transaction to JSON %d", i)
|
|
}
|
|
txsData[i] = txData
|
|
}
|
|
data := &BlockJson{
|
|
Id: block.Id,
|
|
PrevHash: encoding.EncodeToString(block.PrevHash),
|
|
Hash: encoding.EncodeToString(block.Hash),
|
|
TXs: txsData,
|
|
IsFinished: block.IsFinished,
|
|
}
|
|
jsonBytes, err := json.Marshal(data)
|
|
if err != nil {
|
|
return nil, werr.Wrapf(err, "error while marshaling")
|
|
}
|
|
return jsonBytes, err
|
|
}
|
|
|
|
func (block *BlockJson) Process() (*Block, error) {
|
|
var err error
|
|
txs := make([]*Tx, len(block.TXs))
|
|
for i, jsonTx := range block.TXs {
|
|
if txs[i], err = TxFromJSON(jsonTx); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
out := &Block{
|
|
Id: block.Id,
|
|
TXs: txs,
|
|
IsFinished: block.IsFinished,
|
|
}
|
|
encoding, err := GetEncoding(block.ByteEncoding)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if out.Hash, err = encoding.DecodeString(block.Hash); err != nil {
|
|
return nil, werr.Wrapf(err, "error while decoding hash")
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func BlockFromJSON(marshaledData []byte) (opts *Block, err error) {
|
|
data := &BlockJson{}
|
|
if err := json.Unmarshal(marshaledData, data); err != nil {
|
|
return nil, werr.Wrapf(err, "error while unmarshaling")
|
|
}
|
|
return data.Process()
|
|
}
|
|
func BlockFromJsonReader(r io.Reader) (opts *Block, err error) {
|
|
data := &BlockJson{}
|
|
if err := json.NewDecoder(r).Decode(&data); err != nil {
|
|
return nil, werr.Wrapf(err, "error while unmarshaling")
|
|
}
|
|
return data.Process()
|
|
}
|