2018-09-27 17:52:40 -07:00
|
|
|
package parser
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
2019-01-07 11:20:47 -08:00
|
|
|
"github.com/zcash-hackworks/lightwalletd/parser/internal/bytestring"
|
|
|
|
"github.com/zcash-hackworks/lightwalletd/walletrpc"
|
2018-09-27 17:52:40 -07:00
|
|
|
)
|
|
|
|
|
2019-06-06 17:44:13 -07:00
|
|
|
type Block struct {
|
2018-12-14 18:54:33 -08:00
|
|
|
hdr *blockHeader
|
|
|
|
vtx []*Transaction
|
2018-12-14 18:54:59 -08:00
|
|
|
height int
|
2018-09-27 17:52:40 -07:00
|
|
|
}
|
|
|
|
|
2019-06-06 17:44:13 -07:00
|
|
|
func NewBlock() *Block {
|
|
|
|
return &Block{height: -1}
|
2018-09-27 17:52:40 -07:00
|
|
|
}
|
|
|
|
|
2019-06-06 17:44:13 -07:00
|
|
|
func (b *Block) GetVersion() int {
|
2018-09-28 17:44:34 -07:00
|
|
|
return int(b.hdr.Version)
|
|
|
|
}
|
|
|
|
|
2019-06-06 17:44:13 -07:00
|
|
|
func (b *Block) GetTxCount() int {
|
2018-09-28 17:44:34 -07:00
|
|
|
return len(b.vtx)
|
|
|
|
}
|
|
|
|
|
2019-06-06 17:44:13 -07:00
|
|
|
func (b *Block) Transactions() []*Transaction {
|
2018-12-14 18:54:33 -08:00
|
|
|
// TODO: these should NOT be mutable
|
|
|
|
return b.vtx
|
|
|
|
}
|
|
|
|
|
2018-12-04 15:21:53 -08:00
|
|
|
// GetDisplayHash returns the block hash in big-endian display order.
|
2019-06-06 17:44:13 -07:00
|
|
|
func (b *Block) GetDisplayHash() []byte {
|
2018-12-04 15:21:53 -08:00
|
|
|
return b.hdr.GetDisplayHash()
|
2018-11-16 16:37:31 -08:00
|
|
|
}
|
|
|
|
|
2018-12-04 15:21:53 -08:00
|
|
|
// TODO: encode hash endianness in a type?
|
|
|
|
|
2018-12-10 22:38:57 -08:00
|
|
|
// GetEncodableHash returns the block hash in little-endian wire order.
|
2019-06-06 17:44:13 -07:00
|
|
|
func (b *Block) GetEncodableHash() []byte {
|
2018-12-10 22:38:57 -08:00
|
|
|
return b.hdr.GetEncodableHash()
|
2018-11-16 16:37:31 -08:00
|
|
|
}
|
|
|
|
|
2019-06-28 10:22:22 -07:00
|
|
|
func (b *Block) GetDisplayPrevHash() []byte {
|
2019-11-01 12:47:32 -07:00
|
|
|
return b.hdr.GetDisplayPrevHash()
|
2019-06-28 10:22:22 -07:00
|
|
|
}
|
|
|
|
|
2019-06-06 17:44:13 -07:00
|
|
|
func (b *Block) HasSaplingTransactions() bool {
|
2018-11-17 19:02:49 -08:00
|
|
|
for _, tx := range b.vtx {
|
|
|
|
if tx.HasSaplingTransactions() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-02-25 10:57:06 -08:00
|
|
|
// see https://github.com/zcash-hackworks/lightwalletd/issues/17#issuecomment-467110828
|
|
|
|
const genesisTargetDifficulty = 520617983
|
|
|
|
|
2018-11-16 16:02:56 -08:00
|
|
|
// GetHeight() extracts the block height from the coinbase transaction. See
|
|
|
|
// BIP34. Returns block height on success, or -1 on error.
|
2019-06-06 17:44:13 -07:00
|
|
|
func (b *Block) GetHeight() int {
|
2018-12-14 18:54:59 -08:00
|
|
|
if b.height != -1 {
|
|
|
|
return b.height
|
|
|
|
}
|
2018-11-16 16:02:56 -08:00
|
|
|
coinbaseScript := bytestring.String(b.vtx[0].transparentInputs[0].ScriptSig)
|
2019-05-04 03:37:00 -07:00
|
|
|
var heightNum int64
|
2019-10-29 17:35:13 -07:00
|
|
|
if !coinbaseScript.ReadScriptInt64(&heightNum) {
|
2018-11-16 16:02:56 -08:00
|
|
|
return -1
|
|
|
|
}
|
2019-05-04 03:37:00 -07:00
|
|
|
if heightNum < 0 {
|
2018-11-16 16:02:56 -08:00
|
|
|
return -1
|
|
|
|
}
|
|
|
|
// uint32 should last us a while (Nov 2018)
|
2019-05-14 07:26:50 -07:00
|
|
|
if heightNum > int64(^uint32(0)) {
|
|
|
|
return -1
|
|
|
|
}
|
2019-05-04 03:37:00 -07:00
|
|
|
blockHeight := uint32(heightNum)
|
2019-02-25 10:57:06 -08:00
|
|
|
|
|
|
|
if blockHeight == genesisTargetDifficulty {
|
|
|
|
blockHeight = 0
|
|
|
|
}
|
|
|
|
|
2018-12-14 18:54:59 -08:00
|
|
|
b.height = int(blockHeight)
|
2018-11-16 16:02:56 -08:00
|
|
|
return int(blockHeight)
|
|
|
|
}
|
|
|
|
|
2019-07-09 15:52:35 -07:00
|
|
|
func (b *Block) GetPrevHash() []byte {
|
|
|
|
return b.hdr.HashPrevBlock
|
|
|
|
}
|
|
|
|
|
2019-06-06 17:44:13 -07:00
|
|
|
func (b *Block) ToCompact() *walletrpc.CompactBlock {
|
2019-01-07 11:20:47 -08:00
|
|
|
compactBlock := &walletrpc.CompactBlock{
|
2018-12-04 15:21:53 -08:00
|
|
|
//TODO ProtoVersion: 1,
|
2019-10-30 09:43:51 -07:00
|
|
|
Height: uint64(b.GetHeight()),
|
2019-05-04 04:18:42 -07:00
|
|
|
PrevHash: b.hdr.HashPrevBlock,
|
2019-10-30 09:43:51 -07:00
|
|
|
Hash: b.GetEncodableHash(),
|
|
|
|
Time: b.hdr.Time,
|
2018-11-16 16:37:31 -08:00
|
|
|
}
|
2018-12-11 13:58:43 -08:00
|
|
|
|
|
|
|
// Only Sapling transactions have a meaningful compact encoding
|
2019-01-07 11:20:47 -08:00
|
|
|
saplingTxns := make([]*walletrpc.CompactTx, 0, len(b.vtx))
|
2018-11-16 16:37:31 -08:00
|
|
|
for idx, tx := range b.vtx {
|
2018-12-11 13:58:43 -08:00
|
|
|
if tx.HasSaplingTransactions() {
|
|
|
|
saplingTxns = append(saplingTxns, tx.ToCompact(idx))
|
|
|
|
}
|
2018-11-16 16:37:31 -08:00
|
|
|
}
|
2018-12-11 13:58:43 -08:00
|
|
|
compactBlock.Vtx = saplingTxns
|
2018-11-16 16:37:31 -08:00
|
|
|
return compactBlock
|
|
|
|
}
|
|
|
|
|
2019-06-06 17:44:13 -07:00
|
|
|
func (b *Block) ParseFromSlice(data []byte) (rest []byte, err error) {
|
2018-09-27 17:52:40 -07:00
|
|
|
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
|
2019-10-29 17:35:13 -07:00
|
|
|
if !s.ReadCompactSize(&txCount) {
|
2018-09-27 17:52:40 -07:00
|
|
|
return nil, errors.New("could not read tx_count")
|
|
|
|
}
|
|
|
|
data = []byte(s)
|
|
|
|
|
2018-12-14 18:54:33 -08:00
|
|
|
vtx := make([]*Transaction, 0, txCount)
|
2018-09-27 17:52:40 -07:00
|
|
|
for i := 0; 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)
|
|
|
|
}
|
|
|
|
|
|
|
|
b.hdr = hdr
|
|
|
|
b.vtx = vtx
|
|
|
|
|
|
|
|
return data, nil
|
|
|
|
}
|