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() }