146 lines
4.3 KiB
Go
146 lines
4.3 KiB
Go
// 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
|
|
}
|