mirror of
https://github.com/FlipsideCrypto/dcrd.git
synced 2026-02-06 10:56:47 +00:00
Add new setticketsvotebits command
The dcrwallet command to set multiple tickets vote bits fields at the same time has been added. Functions to encode/decode relevant hex strings (as passed over JSON) were also added, along with tests.
This commit is contained in:
parent
f26d1e4b34
commit
cc25399f6a
@ -176,6 +176,13 @@ var (
|
||||
rangeLimitMax = uint16(63)
|
||||
)
|
||||
|
||||
// VoteBits is a field representing the mandatory 2-byte field of voteBits along
|
||||
// with the optional 73-byte extended field for votes.
|
||||
type VoteBits struct {
|
||||
Bits uint16
|
||||
ExtendedBits []byte
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Accessory Stake Functions
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
@ -569,6 +569,22 @@ func NewSetTicketVoteBitsCmd(txHash string, voteBits uint16, voteBitsExt *string
|
||||
}
|
||||
}
|
||||
|
||||
// SetTicketsVoteBitsCmd is a type handling custom marshaling and
|
||||
// unmarshaling of setticketsvotebits JSON RPC commands.
|
||||
type SetTicketsVoteBitsCmd struct {
|
||||
TxHashes string
|
||||
VoteBitsBytes string
|
||||
}
|
||||
|
||||
// NewSetTicketsVoteBitsCmd creates a new instance of the setticketsvotebits
|
||||
// command.
|
||||
func NewSetTicketsVoteBitsCmd(txHashes string, voteBitsBytes string) *SetTicketsVoteBitsCmd {
|
||||
return &SetTicketsVoteBitsCmd{
|
||||
TxHashes: txHashes,
|
||||
VoteBitsBytes: voteBitsBytes,
|
||||
}
|
||||
}
|
||||
|
||||
// SignRawTransactionsCmd defines the signrawtransactions JSON-RPC command.
|
||||
type SignRawTransactionsCmd struct {
|
||||
RawTxs []string
|
||||
@ -651,6 +667,7 @@ func init() {
|
||||
MustRegisterCmd("setticketfee", (*SetTicketFeeCmd)(nil), flags)
|
||||
MustRegisterCmd("setticketmaxprice", (*SetTicketMaxPriceCmd)(nil), flags)
|
||||
MustRegisterCmd("setticketvotebits", (*SetTicketVoteBitsCmd)(nil), flags)
|
||||
MustRegisterCmd("setticketsvotebits", (*SetTicketsVoteBitsCmd)(nil), flags)
|
||||
MustRegisterCmd("signrawtransactions", (*SignRawTransactionsCmd)(nil), flags)
|
||||
MustRegisterCmd("stakepooluserinfo", (*StakePoolUserInfoCmd)(nil), flags)
|
||||
MustRegisterCmd("walletinfo", (*WalletInfoCmd)(nil), flags)
|
||||
|
||||
@ -5,8 +5,11 @@
|
||||
package dcrjson
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/decred/dcrd/blockchain/stake"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
@ -44,3 +47,87 @@ func DecodeConcatenatedHashes(hashes string) ([]chainhash.Hash, error) {
|
||||
}
|
||||
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.MaxSingleBytePushLength-2 {
|
||||
return "", fmt.Errorf("extended votebits too long (got %v, want "+
|
||||
"%v max", len(voteBitsSlice[i].ExtendedBits),
|
||||
stake.MaxSingleBytePushLength-2)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -5,9 +5,12 @@
|
||||
package dcrjson_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/decred/dcrd/blockchain/stake"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrjson"
|
||||
)
|
||||
@ -46,3 +49,113 @@ func TestDecodeConcatenatedHashes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeConcatenatedVoteBits(t *testing.T) {
|
||||
testVbs := []stake.VoteBits{
|
||||
stake.VoteBits{Bits: 0, ExtendedBits: []byte{}},
|
||||
stake.VoteBits{Bits: 0, ExtendedBits: []byte{0x00}},
|
||||
stake.VoteBits{Bits: 0x1223, ExtendedBits: []byte{0x01, 0x02, 0x03, 0x04}},
|
||||
stake.VoteBits{Bits: 0xaaaa, ExtendedBits: []byte{0x01, 0x02, 0x03, 0x04, 0x05}},
|
||||
}
|
||||
encodedResults, err := dcrjson.EncodeConcatenatedVoteBits(testVbs)
|
||||
if err != nil {
|
||||
t.Fatalf("Encode failed: %v", err)
|
||||
}
|
||||
|
||||
expectedEncoded := []byte{
|
||||
0x02, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x06,
|
||||
0x23, 0x12, 0x01, 0x02,
|
||||
0x03, 0x04, 0x07, 0xaa,
|
||||
0xaa, 0x01, 0x02, 0x03,
|
||||
0x04, 0x05,
|
||||
}
|
||||
|
||||
encodedResultsStr, _ := hex.DecodeString(encodedResults)
|
||||
if !bytes.Equal(expectedEncoded, encodedResultsStr) {
|
||||
t.Fatalf("Encoded votebits `%x` does not match expected `%x`",
|
||||
encodedResults, expectedEncoded)
|
||||
}
|
||||
|
||||
// Test too long voteBits extended.
|
||||
testVbs = []stake.VoteBits{
|
||||
stake.VoteBits{Bits: 0, ExtendedBits: bytes.Repeat([]byte{0x00}, 74)},
|
||||
}
|
||||
_, err = dcrjson.EncodeConcatenatedVoteBits(testVbs)
|
||||
if err == nil {
|
||||
t.Fatalf("expected too long error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeConcatenatedVoteBits(t *testing.T) {
|
||||
encodedBytes := []byte{
|
||||
0x03, 0x00, 0x00, 0x00,
|
||||
0x06, 0x23, 0x12, 0x01,
|
||||
0x02, 0x03, 0x04, 0x07,
|
||||
0xaa, 0xaa, 0x01, 0x02,
|
||||
0x03, 0x04, 0x05,
|
||||
}
|
||||
encodedBytesStr := hex.EncodeToString(encodedBytes)
|
||||
|
||||
expectedVbs := []stake.VoteBits{
|
||||
stake.VoteBits{Bits: 0, ExtendedBits: []byte{0x00}},
|
||||
stake.VoteBits{Bits: 0x1223, ExtendedBits: []byte{0x01, 0x02, 0x03, 0x04}},
|
||||
stake.VoteBits{Bits: 0xaaaa, ExtendedBits: []byte{0x01, 0x02, 0x03, 0x04, 0x05}},
|
||||
}
|
||||
|
||||
decodedSlice, err :=
|
||||
dcrjson.DecodeConcatenatedVoteBits(encodedBytesStr)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error decoding votebits: %v", err.Error())
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedVbs, decodedSlice) {
|
||||
t.Fatalf("Decoded votebits `%v` does not match expected `%v`",
|
||||
decodedSlice, expectedVbs)
|
||||
}
|
||||
|
||||
// Test short read.
|
||||
encodedBytes = []byte{
|
||||
0x03, 0x00, 0x00, 0x00,
|
||||
0x06, 0x23, 0x12, 0x01,
|
||||
0x02, 0x03, 0x04, 0x07,
|
||||
0xaa, 0xaa, 0x01, 0x02,
|
||||
0x03, 0x04,
|
||||
}
|
||||
encodedBytesStr = hex.EncodeToString(encodedBytes)
|
||||
|
||||
decodedSlice, err = dcrjson.DecodeConcatenatedVoteBits(encodedBytesStr)
|
||||
if err == nil {
|
||||
t.Fatalf("expected short read error")
|
||||
}
|
||||
|
||||
// Test too long read.
|
||||
encodedBytes = []byte{
|
||||
0x03, 0x00, 0x00, 0x00,
|
||||
0x06, 0x23, 0x12, 0x01,
|
||||
0x02, 0x03, 0x04, 0x07,
|
||||
0xaa, 0xaa, 0x01, 0x02,
|
||||
0x03, 0x04, 0x05, 0x06,
|
||||
}
|
||||
encodedBytesStr = hex.EncodeToString(encodedBytes)
|
||||
|
||||
decodedSlice, err = dcrjson.DecodeConcatenatedVoteBits(encodedBytesStr)
|
||||
if err == nil {
|
||||
t.Fatalf("expected corruption error")
|
||||
}
|
||||
|
||||
// Test invalid length.
|
||||
encodedBytes = []byte{
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x06, 0x23, 0x12, 0x01,
|
||||
0x02, 0x03, 0x04, 0x07,
|
||||
0xaa, 0xaa, 0x01, 0x02,
|
||||
0x03, 0x04, 0x05, 0x06,
|
||||
}
|
||||
encodedBytesStr = hex.EncodeToString(encodedBytes)
|
||||
|
||||
decodedSlice, err = dcrjson.DecodeConcatenatedVoteBits(encodedBytesStr)
|
||||
if err == nil {
|
||||
t.Fatalf("expected corruption error")
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user