Lite wallet server https://hush.is
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.
 
 
 

185 lines
4.7 KiB

// Copyright (c) 2019-2024 Duke Leto and The Hush developers
// Copyright (c) 2019-2020 The Zcash developers
// Distributed under the GPLv3 software license
// Package parser deserializes blocks from hushd
package parser
import (
"fmt"
"git.hush.is/hush/lightwalletd/parser/internal/bytestring"
"git.hush.is/hush/lightwalletd/walletrpc"
"github.com/pkg/errors"
)
// Block represents a full block (not a compact block).
type Block struct {
hdr *BlockHeader
vtx []*Transaction
height int
}
// NewBlock constructs a block instance.
func NewBlock() *Block {
return &Block{height: -1}
}
// GetVersion returns a block's version number (current 4)
func (b *Block) GetVersion() int {
return int(b.hdr.Version)
}
// GetTxCount returns the number of transactions in the block,
// including the coinbase transaction (minimum 1).
func (b *Block) GetTxCount() int {
return len(b.vtx)
}
// Transactions returns the list of the block's transactions.
func (b *Block) Transactions() []*Transaction {
// TODO: these should NOT be mutable
return b.vtx
}
// GetDisplayHash returns the block hash in big-endian display order.
func (b *Block) GetDisplayHash() []byte {
return b.hdr.GetDisplayHash()
}
// TODO: encode hash endianness in a type?
// GetEncodableHash returns the block hash in little-endian wire order.
func (b *Block) GetEncodableHash() []byte {
return b.hdr.GetEncodableHash()
}
/*
func (b *Block) GetDisplayPrevHash() []byte {
rhash := make([]byte, len(b.hdr.HashPrevBlock))
copy(rhash, b.hdr.HashPrevBlock)
// Reverse byte order
for i := 0; i < len(rhash)/2; i++ {
j := len(rhash) - 1 - i
rhash[i], rhash[j] = rhash[j], rhash[i]
}
return rhash
}
*/
// GetDisplayPrevHash returns the block's previous hash in big-endian format.
func (b *Block) GetDisplayPrevHash() []byte {
return b.hdr.GetDisplayPrevHash()
}
// HasSaplingTransactions indicates if the block contains any Sapling tx.
func (b *Block) HasSaplingTransactions() bool {
for _, tx := range b.vtx {
if tx.HasShieldedElements() {
return true
}
}
return false
}
/*
func (b *Block) HasSaplingTransactions() bool {
for _, tx := range b.vtx {
if tx.HasSaplingTransactions() {
return true
}
}
return false
}
*/
// see https://github.com/adityapk00/lightwalletd/issues/17#issuecomment-467110828
const genesisTargetDifficulty = 520617983
// GetHeight() extracts the block height from the coinbase transaction. See
// BIP34. Returns block height on success, or -1 on error.
func (b *Block) GetHeight() int {
if b.height != -1 {
return b.height
}
coinbaseScript := bytestring.String(b.vtx[0].transparentInputs[0].ScriptSig)
var heightNum int64
if ok := coinbaseScript.ReadScriptInt64(&heightNum); !ok {
return -1
}
if heightNum < 0 {
return -1
}
// uint32 should last us a while (Nov 2018)
if heightNum > int64(^uint32(0)) {
return -1
}
blockHeight := uint32(heightNum)
if blockHeight == genesisTargetDifficulty {
blockHeight = 0
}
b.height = int(blockHeight)
return int(blockHeight)
}
// GetPrevHash returns the hash of the block's previous block (little-endian).
func (b *Block) GetPrevHash() []byte {
return b.hdr.HashPrevBlock
}
// ToCompact returns the compact representation of the full block.
func (b *Block) ToCompact() *walletrpc.CompactBlock {
compactBlock := &walletrpc.CompactBlock{
//TODO ProtoVersion: 1,
Height: uint64(b.GetHeight()),
PrevHash: b.hdr.HashPrevBlock,
Hash: b.GetEncodableHash(),
Time: b.hdr.Time,
}
// Only Sapling transactions have a meaningful compact encoding
saplingTxns := make([]*walletrpc.CompactTx, 0, len(b.vtx))
for idx, tx := range b.vtx {
if tx.HasShieldedElements() {
saplingTxns = append(saplingTxns, tx.ToCompact(idx))
}
}
compactBlock.Vtx = saplingTxns
return compactBlock
}
// ParseFromSlice deserializes a block from the given data stream
// and returns a slice to the remaining data. The caller should verify
// there is no remaining data if none is expected.
func (b *Block) ParseFromSlice(data []byte) (rest []byte, err error) {
hdr := NewBlockHeader()
data, err = hdr.ParseFromSlice(data)
if err != nil {
return nil, errors.Wrap(err, "parsing block header")
}
s := bytestring.String(data)
var txCount int
if !s.ReadCompactSize(&txCount) {
return nil, errors.New("could not read tx_count")
}
data = []byte(s)
vtx := make([]*Transaction, 0, txCount)
var i int
for i = 0; i < txCount && len(data) > 0; i++ {
tx := NewTransaction()
data, err = tx.ParseFromSlice(data)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("parsing transaction %d", i))
}
vtx = append(vtx, tx)
}
if i < txCount {
return nil, errors.New("parsing block transactions: not enough data")
}
b.hdr = hdr
b.vtx = vtx
return data, nil
}