Skip to content

Commit

Permalink
Add individual ticket setting for VoteBits via the RPC
Browse files Browse the repository at this point in the history
This update allows the per-ticket setting of the VoteBits field so
that different VoteBits settings can be user selected in stake pools.
It is backwards compatible with the former VoteBits selection method
on the command line. Tickets stored before this update will continue
to use the VoteBits selection from the command line. Tickets stored
after this update will be initialized with the VoteBits selection from
the command line and then be able to be updated by the RPC. After
updating, any votes cast with that ticket will be assigned the
per-ticket, user selected VoteBits instead of the default VoteBits.
  • Loading branch information
cjepson committed Feb 17, 2016
1 parent cc4956b commit 6a938f9
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 20 deletions.
49 changes: 49 additions & 0 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1338,6 +1338,7 @@ var rpcHandlers = map[string]struct {
"getseed": {handler: GetSeed},
"getticketmaxprice": {handler: GetTicketMaxPrice},
"gettickets": {handler: GetTickets},
"getticketvotebits": {handler: GetTicketVoteBits},
"gettransaction": {handler: GetTransaction},
"getwalletfee": {handler: GetWalletFee},
"help": {handler: Help},
Expand All @@ -1362,6 +1363,7 @@ var rpcHandlers = map[string]struct {
"sendtossrtx": {handler: SendToSSRtx},
"setgenerate": {handler: SetGenerate},
"setticketmaxprice": {handler: SetTicketMaxPrice},
"setticketvotebits": {handler: SetTicketVoteBits},
"settxfee": {handler: SetTxFee},
"signmessage": {handler: SignMessage},
"signrawtransaction": {handler: SignRawTransaction},
Expand Down Expand Up @@ -2396,6 +2398,35 @@ func GetTickets(w *wallet.Wallet, chainSvr *chain.Client,
return &dcrjson.GetTicketsResult{ticketsStr}, nil
}

// GetTicketVoteBits fetches the per-ticket voteBits for a given ticket from
// a ticket hash. If the voteBits are unset, it returns the default voteBits.
// Otherwise, it returns the voteBits it finds. Missing tickets return an
// error.
func GetTicketVoteBits(w *wallet.Wallet, chainSvr *chain.Client,
icmd interface{}) (interface{}, error) {
cmd := icmd.(*dcrjson.GetTicketVoteBitsCmd)
ticket, err := chainhash.NewHashFromStr(cmd.TxHash)
if err != nil {
return nil, err
}

set, voteBits, err := w.StakeMgr.SStxVoteBits(ticket)
if err != nil {
return nil, err
}
if !set {
return &dcrjson.GetTicketVoteBitsResult{
VoteBits: w.VoteBits,
VoteBitsExt: "",
}, nil
}

return &dcrjson.GetTicketVoteBitsResult{
VoteBits: voteBits,
VoteBitsExt: "",
}, nil
}

// GetTransaction handles a gettransaction request by returning details about
// a single transaction saved by wallet.
func GetTransaction(w *wallet.Wallet, chainSvr *chain.Client,
Expand Down Expand Up @@ -3688,6 +3719,24 @@ func SetTicketMaxPrice(w *wallet.Wallet, chainSvr *chain.Client,
return nil, nil
}

// SetTicketVoteBits sets the per-ticket voteBits for a given ticket from
// a ticket hash. Missing tickets return an error.
func SetTicketVoteBits(w *wallet.Wallet, chainSvr *chain.Client,
icmd interface{}) (interface{}, error) {
cmd := icmd.(*dcrjson.SetTicketVoteBitsCmd)
ticket, err := chainhash.NewHashFromStr(cmd.TxHash)
if err != nil {
return nil, err
}

err = w.StakeMgr.UpdateSStxVoteBits(ticket, cmd.VoteBits)
if err != nil {
return nil, err
}

return nil, nil
}

// SetTxFee sets the transaction fee per kilobyte added to transactions.
func SetTxFee(w *wallet.Wallet, chainSvr *chain.Client,
icmd interface{}) (interface{}, error) {
Expand Down
2 changes: 1 addition & 1 deletion wallet/chainntfns.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ func (w *Wallet) addRelevantTx(rec *wtxmgr.TxRecord,
}

if insert {
err := w.StakeMgr.InsertSStx(tx)
err := w.StakeMgr.InsertSStx(tx, w.VoteBits)
if err != nil {
log.Errorf("Failed to insert SStx %v"+
"into the stake store.", tx.Sha())
Expand Down
2 changes: 1 addition & 1 deletion wallet/createtx.go
Original file line number Diff line number Diff line change
Expand Up @@ -1384,7 +1384,7 @@ func (w *Wallet) purchaseTicket(req purchaseTicketRequest) (interface{},
// automatically insert it.
if _, err := w.Manager.Address(ticketAddr); err == nil {
if w.ticketAddress == nil {
err = w.StakeMgr.InsertSStx(txTemp)
err = w.StakeMgr.InsertSStx(txTemp, w.VoteBits)
if err != nil {
return nil, fmt.Errorf("Failed to insert SStx %v"+
"into the stake store", txTemp.Sha())
Expand Down
100 changes: 91 additions & 9 deletions wstakemgr/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,18 @@ func deserializeSStxRecord(serializedSStxRecord []byte) (*sstxRecord, error) {
// Pretend to read the pkScrLoc for the 0th output pkScript.
curPos += int32Size

// Pretend to read the intended votebits length (uint8).
// Read the intended voteBits and extended voteBits length (uint8).
record.voteBitsSet = false
voteBitsLen := uint8(serializedSStxRecord[curPos])
if voteBitsLen != 0 {
record.voteBitsSet = true
}
curPos += int8Size

// Pretend to read the intended votebits (75 bytes).
// Read the assumed 2 byte VoteBits and pretend to read the
// extended votebits (75 bytes).
record.voteBits = byteOrder.Uint16(
serializedSStxRecord[curPos : curPos+int16Size])
curPos += stake.MaxSingleBytePushLength

// Prepare a buffer for the msgTx.
Expand Down Expand Up @@ -230,7 +238,7 @@ func deserializeSStxTicketScriptHash(serializedSStxRecord []byte) ([]byte, error
}

// serializeSSTxRecord returns the serialization of the passed txrecord row.
func serializeSStxRecord(record *sstxRecord) ([]byte, error) {
func serializeSStxRecord(record *sstxRecord, voteBits uint16) ([]byte, error) {
msgTx := record.tx.MsgTx()
msgTxSize := int64(msgTx.SerializeSize())

Expand Down Expand Up @@ -269,10 +277,13 @@ func serializeSStxRecord(record *sstxRecord) ([]byte, error) {
byteOrder.PutUint32(buf[curPos:curPos+int32Size], uint32(pkScrLoc[0]))
curPos += int32Size

// Skip the section for intended votebits length (uint8).
// Write the intended votebits length (uint8). Hardcode the uint16
// size for now. TODO Allow for extended voteBits.
buf[curPos] = byte(int16Size)
curPos += int8Size

// Skip the section for intended votebits (75 bytes).
// Write the first two bytes for the intended votebits (75 bytes max).
byteOrder.PutUint16(buf[curPos:curPos+int16Size], voteBits)
curPos += stake.MaxSingleBytePushLength

// Serialize and write transaction.
Expand Down Expand Up @@ -579,12 +590,83 @@ func fetchSStxRecordSStxTicketScriptHash(tx walletdb.Tx,
return deserializeSStxTicketScriptHash(val)
}

// fetchSStxRecordVoteBits fetches an individual ticket's intended voteBits
// which are used to override the default voteBits when voting.
func fetchSStxRecordVoteBits(tx walletdb.Tx, hash *chainhash.Hash) (bool, uint16,
error) {
bucket := tx.RootBucket().Bucket(sstxRecordsBucketName)

key := hash.Bytes()
val := bucket.Get(key)
if val == nil {
str := fmt.Sprintf("missing sstx record for hash '%s'", hash.String())
return false, 0, stakeStoreError(ErrSStxNotFound, str, nil)
}
valLen := len(val)
valCopy := make([]byte, valLen, valLen)
copy(valCopy, val)

// Move the cursor to the voteBits position and rewrite it.
curPos := 0
curPos += int64Size
curPos += int32Size

// Read the intended votebits length (uint8). If it is unset, return now.
voteBitsLen := uint8(val[curPos])
if voteBitsLen == 0 {
return false, 0, nil
}
curPos += int8Size

// Read the first two bytes for the intended votebits.
voteBits := byteOrder.Uint16(valCopy[curPos : curPos+int16Size])

return true, voteBits, nil
}

// updateSStxRecordVoteBits updates an individual ticket's intended voteBits
// which are used to override the default voteBits when voting.
func updateSStxRecordVoteBits(tx walletdb.Tx, hash *chainhash.Hash,
voteBits uint16) error {
bucket := tx.RootBucket().Bucket(sstxRecordsBucketName)

key := hash.Bytes()
val := bucket.Get(key)
if val == nil {
str := fmt.Sprintf("missing sstx record for hash '%s'", hash.String())
return stakeStoreError(ErrSStxNotFound, str, nil)
}
valLen := len(val)
valCopy := make([]byte, valLen, valLen)
copy(valCopy, val)

// Move the cursor to the voteBits position and rewrite it.
curPos := 0
curPos += int64Size
curPos += int32Size

// Write the intended votebits length (uint8). Hardcode the uint16
// size for now. TODO Allow for extended voteBits.
valCopy[curPos] = byte(int16Size)
curPos += int8Size

// Write the first two bytes for the intended votebits.
byteOrder.PutUint16(valCopy[curPos:curPos+int16Size], voteBits)

err := bucket.Put(key, valCopy)
if err != nil {
str := fmt.Sprintf("failed to update sstxrecord votebits for '%s'", hash)
return stakeStoreError(ErrDatabase, str, err)
}
return nil
}

// updateSStxRecord updates a sstx record in the sstx records bucket.
func updateSStxRecord(tx walletdb.Tx, record *sstxRecord) error {
func updateSStxRecord(tx walletdb.Tx, record *sstxRecord, voteBits uint16) error {
bucket := tx.RootBucket().Bucket(sstxRecordsBucketName)

// Write the serialized txrecord keyed by the tx hash.
serializedSStxRecord, err := serializeSStxRecord(record)
serializedSStxRecord, err := serializeSStxRecord(record, voteBits)
if err != nil {
str := fmt.Sprintf("failed to serialize sstxrecord '%s'", record.tx.Sha())
return stakeStoreError(ErrDatabase, str, err)
Expand All @@ -598,8 +680,8 @@ func updateSStxRecord(tx walletdb.Tx, record *sstxRecord) error {
}

// putSStxRecord inserts a given SStx record to the SStxrecords bucket.
func putSStxRecord(tx walletdb.Tx, record *sstxRecord) error {
return updateSStxRecord(tx, record)
func putSStxRecord(tx walletdb.Tx, record *sstxRecord, voteBits uint16) error {
return updateSStxRecord(tx, record, voteBits)
}

// fetchSSGenRecords retrieves SSGen records from the SSGenRecords bucket with
Expand Down
Loading

0 comments on commit 6a938f9

Please sign in to comment.