diff --git a/decred/decred/dcr/account.py b/decred/decred/dcr/account.py index be4ba7ee..6af1fdfa 100644 --- a/decred/decred/dcr/account.py +++ b/decred/decred/dcr/account.py @@ -7,6 +7,7 @@ from decred import DecredError from decred.crypto import crypto, opcode from decred.dcr import addrlib +from decred.dcr.txscript import DefaultRelayFeePerKb from decred.util import encode, helpers from decred.util.encode import BuildyBytes, ByteArray, unblobCheck @@ -801,6 +802,7 @@ def __init__( self.coinID = BIPID self.netID = netID self.netParams = nets.parse(netID) + self.relayFee = int(DefaultRelayFeePerKb) # For external addresses, the cursor can sit on the last seen address, # so start the lastSeen at the 0th external address. This is necessary # because the currentAddress method grabs the address at the current @@ -856,6 +858,7 @@ def blob(acct): .addData(encode.intToBytes(acct.cursorExt, signed=True)) .addData(acct.cursorInt) .addData(acct.gapLimit) + .addData(acct.relayFee) .b ) @@ -863,7 +866,7 @@ def blob(acct): def unblob(b): """Satisfies the encode.Blobber API""" ver, d = encode.decodeBlob(b) - unblobCheck("Account", ver, len(d), {0: 8}) + unblobCheck("Account", ver, len(d), {0: 9}) iFunc = encode.intFromBytes @@ -876,6 +879,7 @@ def unblob(b): acct.cursorExt = iFunc(d[5], signed=True) acct.cursorInt = iFunc(d[6]) acct.gapLimit = iFunc(d[7]) + acct.relayFee = iFunc(d[8]) return acct @staticmethod diff --git a/decred/decred/wallet/accounts.py b/decred/decred/wallet/accounts.py index bb47ffa5..b5868ea8 100644 --- a/decred/decred/wallet/accounts.py +++ b/decred/decred/wallet/accounts.py @@ -145,6 +145,18 @@ def setNode(self, node): for acct in self.accounts.values(): acct.setNode(node) + def setRelayFee(self, idx, fee): + """ + Save the relay fee for account at index. + + Args: + idx (int): The account's index. + fee (int): The relay fee in smallest unit/kb. + """ + acct = self.accounts[idx] + acct.relayFee = fee + self.acctDB[idx] = acct + def coinKey(self, cryptoKey): """ Decrypt the coin-type extended key. diff --git a/tinywallet/tinywallet/screens.py b/tinywallet/tinywallet/screens.py index 341422a0..6112b213 100644 --- a/tinywallet/tinywallet/screens.py +++ b/tinywallet/tinywallet/screens.py @@ -18,6 +18,7 @@ from decred.crypto import crypto from decred.dcr import constants as DCR, nets from decred.dcr.blockchain import LocalNode +from decred.dcr.txscript import DefaultRelayFeePerKb from decred.dcr.vsp import VotingServiceProvider from decred.util import chains, database, helpers from decred.wallet.wallet import Wallet @@ -475,7 +476,9 @@ def __init__(self, acctMgr, acct, assetScreen): self.canGoHome = False self.ticketStats = None self.balance = None - self.settingsScreen = AccountSettingsScreen(self.saveName) + self.settingsScreen = AccountSettingsScreen( + self.account.relayFee, self.saveName, self.setRelayFee + ) self.stakeScreen = StakingScreen(acct) self.wgt.setFixedSize( TinyDialog.maxWidth * 0.9, @@ -716,6 +719,15 @@ def saveName(self, newName): self.assetScreen.doButtons() app.home() + def setRelayFee(self, relayFee): + """ + Changes and saves the relayFee of the account. + + Args: + int: The new relayFee. + """ + self.acctMgr.setRelayFee(self.account.idx, relayFee) + def stackAndSync(self): """ Start syncing the account. @@ -1832,7 +1844,7 @@ class AccountSettingsScreen(Screen): Account settings screen. """ - def __init__(self, saveName): + def __init__(self, relayFee, saveName, setRelayFee): """ Args: saveName (function): A callback function to be called after the user @@ -1846,6 +1858,7 @@ def __init__(self, saveName): TinyDialog.maxHeight * 0.9 - TinyDialog.topMenuHeight, ) self.saveName = saveName + self.setRelayFee = setRelayFee gear = SVGWidget("gear", h=20) lbl = Q.makeLabel("Account Settings", 22) @@ -1854,6 +1867,11 @@ def __init__(self, saveName): self.layout.addStretch(1) + def insertSpace(): + spacer = Q.makeLabel("", 5) + spacer.setContentsMargins(0, 5, 0, 0) + grid.addWidget(spacer, row, 0, 1, 4) + # ACCOUNT NAME wgt, grid = Q.makeWidget(QtWidgets.QWidget, Q.GRID) @@ -1869,8 +1887,67 @@ def __init__(self, saveName): bttn.clicked.connect(self.nameChangeClicked) grid.addWidget(bttn, row, 1) + # RELAY FEE + + # Set string constants. + self.lowStr = "low" + self.defaultStr = "default" + self.highStr = "high" + + # Set values that are considered low, default, and high. + self.feeRates = { + self.lowStr: 4000, + self.defaultStr: int(DefaultRelayFeePerKb), + self.highStr: 50000, + } + self.feeLvl = "" + + # Find the current level. Warn if the current level isn't one of the + # three. + for k, v in self.feeRates.items(): + if v == relayFee: + self.feeLvl = k + break + else: + log.warn(f"non standard relay fee set: {relayFee}") + + # Helper to check the current value. + def setChecked(btn): + if self.feeLvl == btn.text(): + btn.setChecked(True) + + row += 1 + insertSpace() + row += 1 + lbl = Q.makeLabel("Relay Fee", 14, Q.ALIGN_LEFT) + grid.addWidget(lbl, row, 0) + self.feeLbl = lbl = Q.makeLabel("", 14, Q.ALIGN_LEFT) + self.setFeeLbl(relayFee) + grid.addWidget(lbl, row, 1) + row += 1 + btn1 = QtWidgets.QRadioButton(self.lowStr) + Q.setProperties(btn1, fontFamily="Roboto", fontSize=14) + setChecked(btn1) + btn1.toggled.connect(lambda: self.relayFeeChangeClicked(self.lowStr)) + btn2 = QtWidgets.QRadioButton(self.defaultStr) + Q.setProperties(btn2, fontFamily="Roboto", fontSize=14) + setChecked(btn2) + btn2.toggled.connect(lambda: self.relayFeeChangeClicked(self.defaultStr)) + btn3 = QtWidgets.QRadioButton(self.highStr) + Q.setProperties(btn3, fontFamily="Roboto", fontSize=14) + setChecked(btn3) + btn3.toggled.connect(lambda: self.relayFeeChangeClicked(self.highStr)) + wgt, _ = Q.makeRow(btn1, btn2, btn3) + grid.addWidget(wgt, row, 0, 1, -1) + self.layout.addStretch(1) + def setFeeLbl(self, fee): + """ + Set the displayed fee as atoms/byte. + """ + self.feeLbl.setText(f"{fee//1000} atoms/byte") + def nameChangeClicked(self, e): """ Qt slot for nameField.clicked signal. @@ -1882,6 +1959,20 @@ def nameChangeClicked(self, e): self.nameField.setText("") self.saveName(newName) + def relayFeeChangeClicked(self, feeLvl): + """ + Qt slot connected to relay fee radio button clicked signal. Changes the + relay fee to feeLvl. + """ + if self.feeLvl == feeLvl: + return + self.feeLvl = feeLvl + fee = self.feeRates[feeLvl] + self.setRelayFee(fee) + self.setFeeLbl(fee) + log.info(f"relay fee changed to {fee} atoms/kb") + app.appWindow.showSuccess(f"using {feeLvl} fees") + class NewAccountScreen(Screen): """