Backend server for SDL
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

229 lines
6.7 KiB

package parser
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"log"
"math/big"
"git.hush.is/duke/lightwalletd/parser/internal/bytestring"
"github.com/pkg/errors"
)
const (
serBlockHeaderMinusEquihashSize = 140 // size of a serialized block header minus the Equihash solution
equihashSizeMainnet = 1344 // size of a mainnet / testnet Equihash solution in bytes
)
// A block header as defined in version 2018.0-beta-29 of the Zcash Protocol Spec.
type rawBlockHeader struct {
// The block version number indicates which set of block validation rules
// to follow. The current and only defined block version number for Zcash
// is 4.
Version int32
// A SHA-256d hash in internal byte order of the previous block's header. This
// ensures no previous block can be changed without also changing this block's
// header.
HashPrevBlock []byte
// A SHA-256d hash in internal byte order. The merkle root is derived from
// the hashes of all transactions included in this block, ensuring that
// none of those transactions can be modified without modifying the header.
HashMerkleRoot []byte
// [Pre-Sapling] A reserved field which should be ignored.
// [Sapling onward] The root LEBS2OSP_256(rt) of the Sapling note
// commitment tree corresponding to the final Sapling treestate of this
// block.
HashFinalSaplingRoot []byte
// The block time is a Unix epoch time (UTC) when the miner started hashing
// the header (according to the miner).
Time uint32
// An encoded version of the target threshold this block's header hash must
// be less than or equal to, in the same nBits format used by Bitcoin.
NBitsBytes []byte
// An arbitrary field that miners can change to modify the header hash in
// order to produce a hash less than or equal to the target threshold.
Nonce []byte
// The Equihash solution. In the wire format, this is a
// CompactSize-prefixed value.
Solution []byte
}
type blockHeader struct {
*rawBlockHeader
cachedHash []byte
targetThreshold *big.Int
}
func CompactLengthPrefixedLen(val []byte) int {
length := len(val)
if length < 253 {
return 1 + length
} else if length <= 0xffff {
return 1 + 2 + length
} else if length <= 0xffffffff {
return 1 + 4 + length
} else {
return 1 + 8 + length
}
}
func WriteCompactLengthPrefixed(buf *bytes.Buffer, val []byte) error {
length := len(val)
if length < 253 {
binary.Write(buf, binary.LittleEndian, uint8(length))
binary.Write(buf, binary.LittleEndian, val)
} else if length <= 0xffff {
binary.Write(buf, binary.LittleEndian, byte(253))
binary.Write(buf, binary.LittleEndian, uint16(length))
binary.Write(buf, binary.LittleEndian, val)
} else if length <= 0xffffffff {
binary.Write(buf, binary.LittleEndian, byte(254))
binary.Write(buf, binary.LittleEndian, uint32(length))
binary.Write(buf, binary.LittleEndian, val)
} else {
binary.Write(buf, binary.LittleEndian, byte(255))
binary.Write(buf, binary.LittleEndian, uint64(length))
binary.Write(buf, binary.LittleEndian, val)
}
return nil
}
func (hdr *rawBlockHeader) GetSize() int {
return serBlockHeaderMinusEquihashSize + CompactLengthPrefixedLen(hdr.Solution)
}
func (hdr *rawBlockHeader) MarshalBinary() ([]byte, error) {
headerSize := hdr.GetSize()
backing := make([]byte, 0, headerSize)
buf := bytes.NewBuffer(backing)
binary.Write(buf, binary.LittleEndian, hdr.Version)
binary.Write(buf, binary.LittleEndian, hdr.HashPrevBlock)
binary.Write(buf, binary.LittleEndian, hdr.HashMerkleRoot)
binary.Write(buf, binary.LittleEndian, hdr.HashFinalSaplingRoot)
binary.Write(buf, binary.LittleEndian, hdr.Time)
binary.Write(buf, binary.LittleEndian, hdr.NBitsBytes)
binary.Write(buf, binary.LittleEndian, hdr.Nonce)
WriteCompactLengthPrefixed(buf, hdr.Solution)
return backing[:headerSize], nil
}
func NewBlockHeader() *blockHeader {
return &blockHeader{
rawBlockHeader: new(rawBlockHeader),
}
}
// ParseFromSlice parses the block header struct from the provided byte slice,
// advancing over the bytes read. If successful it returns the rest of the
// slice, otherwise it returns the input slice unaltered along with an error.
func (hdr *blockHeader) ParseFromSlice(in []byte) (rest []byte, err error) {
s := bytestring.String(in)
// Primary parsing layer: sort the bytes into things
if ok := s.ReadInt32(&hdr.Version); !ok {
return in, errors.New("could not read header version")
}
if ok := s.ReadBytes(&hdr.HashPrevBlock, 32); !ok {
return in, errors.New("could not read HashPrevBlock")
}
if ok := s.ReadBytes(&hdr.HashMerkleRoot, 32); !ok {
return in, errors.New("could not read HashMerkleRoot")
}
if ok := s.ReadBytes(&hdr.HashFinalSaplingRoot, 32); !ok {
return in, errors.New("could not read HashFinalSaplingRoot")
}
if ok := s.ReadUint32(&hdr.Time); !ok {
return in, errors.New("could not read timestamp")
}
if ok := s.ReadBytes(&hdr.NBitsBytes, 4); !ok {
return in, errors.New("could not read NBits bytes")
}
if ok := s.ReadBytes(&hdr.Nonce, 32); !ok {
return in, errors.New("could not read Nonce bytes")
}
if ok := s.ReadCompactLengthPrefixed((*bytestring.String)(&hdr.Solution)); !ok {
return in, errors.New("could not read CompactSize-prefixed Equihash solution")
}
// TODO: interpret the bytes
//hdr.targetThreshold = parseNBits(hdr.NBitsBytes)
return []byte(s), nil
}
func parseNBits(b []byte) *big.Int {
byteLen := int(b[0])
targetBytes := make([]byte, byteLen)
copy(targetBytes, b[1:])
// If high bit set, return a negative result. This is in the Bitcoin Core
// test vectors even though Bitcoin itself will never produce or interpret
// a difficulty lower than zero.
if b[1]&0x80 != 0 {
targetBytes[0] &= 0x7F
target := new(big.Int).SetBytes(targetBytes)
target.Neg(target)
return target
}
return new(big.Int).SetBytes(targetBytes)
}
// GetDisplayHash returns the bytes of a block hash in big-endian order.
func (hdr *blockHeader) GetDisplayHash() []byte {
if hdr.cachedHash != nil {
return hdr.cachedHash
}
serializedHeader, err := hdr.MarshalBinary()
if err != nil {
log.Fatalf("error marshaling block header: %v", err)
return nil
}
// SHA256d
digest := sha256.Sum256(serializedHeader)
digest = sha256.Sum256(digest[:])
// Reverse byte order
for i := 0; i < len(digest)/2; i++ {
j := len(digest) - 1 - i
digest[i], digest[j] = digest[j], digest[i]
}
hdr.cachedHash = digest[:]
return hdr.cachedHash
}
// GetEncodableHash returns the bytes of a block hash in little-endian wire order.
func (hdr *blockHeader) GetEncodableHash() []byte {
serializedHeader, err := hdr.MarshalBinary()
if err != nil {
log.Fatalf("error marshaling block header: %v", err)
return nil
}
// SHA256d
digest := sha256.Sum256(serializedHeader)
digest = sha256.Sum256(digest[:])
return digest[:]
}