ec7e17ffa6
Simplifies a lot of code and removes some duplication. Unfortunately I had to move test_util random functions in same commit to avoid cycle dependencies. One of these random functions was also used in core/transaction testing, to simplify things I've just dropped it there and used a static string (which is nice to have for a test anyway). There is still sha256 left in wallet (but it needs to pass Hash structure into the signing function).
160 lines
4.5 KiB
Go
160 lines
4.5 KiB
Go
package core
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
|
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
|
"github.com/CityOfZion/neo-go/pkg/util"
|
|
)
|
|
|
|
// BlockBase holds the base info of a block
|
|
type BlockBase struct {
|
|
// Version of the block.
|
|
Version uint32 `json:"version"`
|
|
|
|
// hash of the previous block.
|
|
PrevHash util.Uint256 `json:"previousblockhash"`
|
|
|
|
// Root hash of a transaction list.
|
|
MerkleRoot util.Uint256 `json:"merkleroot"`
|
|
|
|
// The time stamp of each block must be later than previous block's time stamp.
|
|
// Generally the difference of two block's time stamp is about 15 seconds and imprecision is allowed.
|
|
// The height of the block must be exactly equal to the height of the previous block plus 1.
|
|
Timestamp uint32 `json:"time"`
|
|
|
|
// index/height of the block
|
|
Index uint32 `json:"height"`
|
|
|
|
// Random number also called nonce
|
|
ConsensusData uint64 `json:"nonce"`
|
|
|
|
// Contract address of the next miner
|
|
NextConsensus util.Uint160 `json:"next_consensus"`
|
|
|
|
// Padding that is fixed to 1
|
|
_ uint8
|
|
|
|
// Script used to validate the block
|
|
Script *transaction.Witness `json:"script"`
|
|
|
|
// hash of this block, created when binary encoded.
|
|
hash util.Uint256
|
|
}
|
|
|
|
// Verify verifies the integrity of the BlockBase.
|
|
func (b *BlockBase) Verify() bool {
|
|
// TODO: Need a persisted blockchain for this.
|
|
return true
|
|
}
|
|
|
|
// Hash return the hash of the block.
|
|
func (b *BlockBase) Hash() util.Uint256 {
|
|
if b.hash.Equals(util.Uint256{}) {
|
|
b.createHash()
|
|
}
|
|
return b.hash
|
|
}
|
|
|
|
// DecodeBinary implements the payload interface.
|
|
func (b *BlockBase) DecodeBinary(r io.Reader) error {
|
|
if err := b.decodeHashableFields(r); err != nil {
|
|
return err
|
|
}
|
|
|
|
var padding uint8
|
|
if err := binary.Read(r, binary.LittleEndian, &padding); err != nil {
|
|
return err
|
|
}
|
|
if padding != 1 {
|
|
return fmt.Errorf("format error: padding must equal 1 got %d", padding)
|
|
}
|
|
|
|
b.Script = &transaction.Witness{}
|
|
return b.Script.DecodeBinary(r)
|
|
}
|
|
|
|
// EncodeBinary implements the Payload interface
|
|
func (b *BlockBase) EncodeBinary(w io.Writer) error {
|
|
if err := b.encodeHashableFields(w); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Write(w, binary.LittleEndian, uint8(1)); err != nil {
|
|
return err
|
|
}
|
|
return b.Script.EncodeBinary(w)
|
|
}
|
|
|
|
// createHash creates the hash of the block.
|
|
// When calculating the hash value of the block, instead of calculating the entire block,
|
|
// only first seven fields in the block head will be calculated, which are
|
|
// version, PrevBlock, MerkleRoot, timestamp, and height, the nonce, NextMiner.
|
|
// Since MerkleRoot already contains the hash value of all transactions,
|
|
// the modification of transaction will influence the hash value of the block.
|
|
func (b *BlockBase) createHash() error {
|
|
buf := new(bytes.Buffer)
|
|
if err := b.encodeHashableFields(buf); err != nil {
|
|
return err
|
|
}
|
|
b.hash = hash.DoubleSha256(buf.Bytes())
|
|
|
|
return nil
|
|
}
|
|
|
|
// encodeHashableFields will only encode the fields used for hashing.
|
|
// see Hash() for more information about the fields.
|
|
func (b *BlockBase) encodeHashableFields(w io.Writer) error {
|
|
if err := binary.Write(w, binary.LittleEndian, b.Version); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Write(w, binary.LittleEndian, b.PrevHash); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Write(w, binary.LittleEndian, b.MerkleRoot); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Write(w, binary.LittleEndian, b.Timestamp); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Write(w, binary.LittleEndian, b.Index); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Write(w, binary.LittleEndian, b.ConsensusData); err != nil {
|
|
return err
|
|
}
|
|
return binary.Write(w, binary.LittleEndian, b.NextConsensus)
|
|
}
|
|
|
|
// decodeHashableFields will only decode the fields used for hashing.
|
|
// see Hash() for more information about the fields.
|
|
func (b *BlockBase) decodeHashableFields(r io.Reader) error {
|
|
if err := binary.Read(r, binary.LittleEndian, &b.Version); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Read(r, binary.LittleEndian, &b.PrevHash); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Read(r, binary.LittleEndian, &b.MerkleRoot); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Read(r, binary.LittleEndian, &b.Timestamp); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Read(r, binary.LittleEndian, &b.Index); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Read(r, binary.LittleEndian, &b.ConsensusData); err != nil {
|
|
return err
|
|
}
|
|
if err := binary.Read(r, binary.LittleEndian, &b.NextConsensus); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Make the hash of the block here so we dont need to do this
|
|
// again.
|
|
return b.createHash()
|
|
}
|