-
Notifications
You must be signed in to change notification settings - Fork 87
/
Copy pathblock.go
162 lines (139 loc) · 4.26 KB
/
block.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright (c) 2019-2020 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
// Package parser deserializes blocks from zcashd.
package parser
import (
"fmt"
"github.com/pkg/errors"
"github.com/zcash/lightwalletd/parser/internal/bytestring"
"github.com/zcash/lightwalletd/walletrpc"
)
// 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()
}
// 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
}
// see https://github.com/zcash/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 !coinbaseScript.ReadScriptInt64(&heightNum) {
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
}