Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

staking: Add revocations #30

Merged
merged 8 commits into from
Jan 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions pydecred/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
support.
"""

from tinydecred.crypto.bytearray import decodeBA
from tinydecred.crypto import opcode, crypto
from tinydecred.wallet.accounts import Account
from tinydecred.util import tinyjson, helpers
from tinydecred.crypto.crypto import AddressSecpPubKey, CrazyKeyError
Expand Down Expand Up @@ -422,6 +424,41 @@ def purchaseTickets(self, qty, price):
self.tickets.extend([tx.txid() for tx in txs[1]])
return txs[1]

def revokeTickets(self):
"""
Iterate through missed and expired tickets and revoke them.

Returns:
bool: whether or not an error occured.
"""
revocableTickets = (
utxo.txid for utxo in self.utxos.values() if utxo.isRevocableTicket()
)
txs = (self.blockchain.tx(txid) for txid in revocableTickets)
for tx in txs:
redeemHash = crypto.AddressScriptHash(
self.net.ScriptHashAddrID,
txscript.extractStakeScriptHash(tx.txOut[0].pkScript, opcode.OP_SSTX),
)
redeemScript = next(
(
decodeBA(p.purchaseInfo.script)
for p in self.stakePools
if p.purchaseInfo.ticketAddress == redeemHash.string()
),
None,
)
if not redeemScript:
raise Exception("did not find redeem script for hash %s" % redeemHash)

keysource = KeySource(
# This will need to change when we start using different
# addresses for voting.
priv=lambda _: self._votingKey,
internal=lambda: "",
)
self.blockchain.revokeTicket(tx, keysource, redeemScript)

def sync(self, blockchain, signals):
"""
Synchronize the UTXO set with the server. This should be the first
Expand Down
48 changes: 46 additions & 2 deletions pydecred/dcrdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import atexit
import websocket
from tinydecred.util import tinyjson, helpers, database, tinyhttp
from tinydecred.crypto import crypto
from tinydecred.crypto import crypto, opcode
from tinydecred.crypto.bytearray import ByteArray
from tinydecred.wallet.api import InsufficientFundsError
from tinydecred.pydecred import txscript, calc
Expand Down Expand Up @@ -704,10 +704,19 @@ def isLiveTicket(self):
isLiveTicket will return True if this is a live ticket.

Returns:
bool. True if this is a live ticket.
bool: True if this is a live ticket.
"""
return self.tinfo and self.tinfo.status in ("immature", "live")

def isRevocableTicket(self):
"""
Returns True if this is an expired or missed ticket.

Returns:
bool: True if this is expired or missed ticket.
"""
return self.tinfo and self.tinfo.status in ("expired", "missed")


tinyjson.register(UTXO, "dcr.UTXO")

Expand Down Expand Up @@ -1626,3 +1635,38 @@ def purchaseTickets(self, keysource, utxosource, req):
)
)
return (splitTx, tickets), splitSpent, internalOutputs

def revokeTicket(self, tx, keysource, redeemScript):
"""
Revoke a ticket by signing the supplied redeem script and broadcasting the raw transaction.

Args:
tx (object): the msgTx of the ticket purchase.
keysource (object): a KeySource object that holds a function to get the private key used for signing.
redeemScript (byte-like): the 1-of-2 multisig script that delegates voting rights for the ticket.
buck54321 marked this conversation as resolved.
Show resolved Hide resolved

Returns:
MsgTx: the signed revocation.
"""

revocation = txscript.makeRevocation(tx, self.relayFee())

signedScript = txscript.signTxOutput(
self.params,
revocation,
0,
redeemScript,
txscript.SigHashAll,
keysource,
revocation.txIn[0].signatureScript,
crypto.STEcdsaSecp256k1,
)

# Append the redeem script to the signature
signedScript += txscript.addData(redeemScript)
revocation.txIn[0].signatureScript = signedScript

self.broadcast(revocation.txHex())

log.info("published revocation %s" % revocation.txid())
return revocation
Loading