forked from decred/dcrd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
parse.go
145 lines (126 loc) · 4.29 KB
/
parse.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
// Copyright (c) 2016 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package dcrjson
import (
"encoding/binary"
"encoding/hex"
"fmt"
"github.com/decred/dcrd/blockchain/stake"
"github.com/decred/dcrd/chaincfg/chainhash"
)
// EncodeConcatenatedHashes serializes a slice of chainhash.Hash values into a
// string of hex-encoded bytes.
func EncodeConcatenatedHashes(hashSlice []chainhash.Hash) string {
length := len(hashSlice) * chainhash.HashSize
hashBytes := make([]byte, 0, length)
for i := range hashSlice {
hashBytes = append(hashBytes, hashSlice[i][:]...)
}
return hex.EncodeToString(hashBytes)
}
// DecodeConcatenatedHashes return a slice of contiguous chainhash.Hash objects
// created by decoding a single string of concatenated hex-encoded hashes.
//
// These hashes must NOT be the byte reversed string encoding that is typically
// used for block and transaction hashes, or each resulting hash will also be
// reversed.
//
// The length of the string must be evenly divisible by twice the hash size in
// order for the parameter to be valid. This function assumes the input is from
// a JSON-RPC request and any errors will be of type *RPCError with an
// ErrRPCInvalidParameter or ErrRPCDecodedHexString error code.
func DecodeConcatenatedHashes(hashes string) ([]chainhash.Hash, error) {
numHashes := len(hashes) / (2 * chainhash.HashSize)
if numHashes*2*chainhash.HashSize != len(hashes) {
return nil, &RPCError{
Code: ErrRPCInvalidParameter,
Message: "Hashes is not evenly divisible by the hash size",
}
}
decoded := make([]chainhash.Hash, numHashes)
hashSrcCpy := make([]byte, 2*chainhash.HashSize)
for i, b := 0, 0; b < len(hashes); i, b = i+1, b+2*chainhash.HashSize {
copy(hashSrcCpy, hashes[b:])
_, err := hex.Decode(decoded[i][:], hashSrcCpy)
if err != nil {
return nil, &RPCError{
Code: ErrRPCDecodeHexString,
Message: "Parameter contains invalid hexadecimal " +
"encoding: " + string(hashSrcCpy),
}
}
}
return decoded, nil
}
// EncodeConcatenatedVoteBits encodes a slice of VoteBits into a serialized byte
// slice. The entirety of the voteBits are encoded individually in series as
// follows:
//
// Size Description
// 1 byte Length of the concatenated voteBits in bytes
// 2 bytes Vote bits
// up to 73 bytes Extended vote bits
//
// The result may be concatenated into a slice and then passed to callers
func EncodeConcatenatedVoteBits(voteBitsSlice []stake.VoteBits) (string, error) {
length := 0
for i := range voteBitsSlice {
if len(voteBitsSlice[i].ExtendedBits) > stake.SSGenVoteBitsExtendedMaxSize {
return "", fmt.Errorf("extended votebits too long (got %v, want "+
"%v max", len(voteBitsSlice[i].ExtendedBits),
stake.SSGenVoteBitsExtendedMaxSize)
}
length += 1 + 2 + len(voteBitsSlice[i].ExtendedBits)
}
vbBytes := make([]byte, length)
offset := 0
for i := range voteBitsSlice {
vbBytes[offset] = 2 + uint8(len(voteBitsSlice[i].ExtendedBits))
offset++
binary.LittleEndian.PutUint16(vbBytes[offset:offset+2],
voteBitsSlice[i].Bits)
offset += 2
copy(vbBytes[offset:], voteBitsSlice[i].ExtendedBits[:])
offset += len(voteBitsSlice[i].ExtendedBits)
}
return hex.EncodeToString(vbBytes), nil
}
// DecodeConcatenatedVoteBits decodes a string encoded as a slice of concatenated
// voteBits and extended voteBits, and returns the slice of DecodedVoteBits to
// the caller.
func DecodeConcatenatedVoteBits(voteBitsString string) ([]stake.VoteBits, error) {
asBytes, err := hex.DecodeString(voteBitsString)
if err != nil {
return nil, err
}
var dvbs []stake.VoteBits
cursor := 0
for {
var dvb stake.VoteBits
length := int(asBytes[cursor])
if length < 2 {
return nil, &RPCError{
Code: ErrRPCInvalidParameter,
Message: "invalid length byte for votebits (short)",
}
}
if cursor+length >= len(asBytes) {
return nil, &RPCError{
Code: ErrRPCInvalidParameter,
Message: "cursor read past memory when decoding " +
"votebits",
}
}
cursor++
dvb.Bits = binary.LittleEndian.Uint16(asBytes[cursor : cursor+2])
cursor += 2
dvb.ExtendedBits = asBytes[cursor : cursor+length-2]
cursor += length - 2
dvbs = append(dvbs, dvb)
if cursor == len(asBytes) {
break
}
}
return dvbs, nil
}