Skip to content

Commit

Permalink
Use anti-fee-snipping and set locktime to current block+1 by default …
Browse files Browse the repository at this point in the history
…in wallets
  • Loading branch information
Cryp Toon committed Mar 12, 2024
1 parent 9732b2b commit b1e5d98
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 12 deletions.
1 change: 1 addition & 0 deletions bitcoinlib/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ class DbWallet(Base):
"* If accounts are used, the account level must be 3. I.e.: m/purpose/coin_type/account/ "
"* All keys must be hardened, except for change, address_index or cosigner_id "
" Max length of path is 8 levels")
anti_fee_snipping = Column(Boolean, default=True, doc="Set default locktime in transactions to avoid fee-snipping")
default_account_id = Column(Integer, doc="ID of default account for this wallet if multiple accounts are used")

__table_args__ = (
Expand Down
4 changes: 2 additions & 2 deletions bitcoinlib/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1089,9 +1089,9 @@ def __init__(self, inputs=None, outputs=None, locktime=0, version=None,
:type version: bytes, int
:param network: Network, leave empty for default network
:type network: str, Network
:param fee: Fee in smallest denominator (ie Satoshi) for complete transaction
:param fee: Fee in the smallest denominator (ie Satoshi) for complete transaction
:type fee: int
:param fee_per_kb: Fee in smallest denominator per kilobyte. Specify when exact transaction size is not known.
:param fee_per_kb: Fee in the smallest denominator per kilobyte. Specify when exact transaction size is not known.
:type fee_per_kb: int
:param size: Transaction size in bytes
:type size: int
Expand Down
34 changes: 24 additions & 10 deletions bitcoinlib/wallets.py
Original file line number Diff line number Diff line change
Expand Up @@ -874,7 +874,8 @@ def store(self):
wallet_id=self.hdwallet.wallet_id, txid=bytes.fromhex(self.txid), block_height=self.block_height,
size=self.size, confirmations=self.confirmations, date=self.date, fee=self.fee, status=self.status,
input_total=self.input_total, output_total=self.output_total, network_name=self.network.name,
raw=self.rawtx, verified=self.verified, account_id=self.account_id)
raw=self.rawtx, verified=self.verified, account_id=self.account_id, locktime=self.locktime,
version=self.version_int, coinbase=self.coinbase, index=self.index)
sess.add(new_tx)
self.hdwallet._commit()
txidn = new_tx.id
Expand All @@ -890,6 +891,7 @@ def store(self):
db_tx.network_name = self.network.name if self.network.name else db_tx.name
db_tx.raw = self.rawtx if self.rawtx else db_tx.raw
db_tx.verified = self.verified
db_tx.locktime = self.locktime
self.hdwallet._commit()

assert txidn
Expand Down Expand Up @@ -1041,8 +1043,8 @@ class Wallet(object):

@classmethod
def _create(cls, name, key, owner, network, account_id, purpose, scheme, parent_id, sort_keys,
witness_type, encoding, multisig, sigs_required, cosigner_id, key_path, db_uri, db_cache_uri,
db_password):
witness_type, encoding, multisig, sigs_required, cosigner_id, key_path,
anti_fee_snipping, db_uri, db_cache_uri, db_password):

db = Db(db_uri, db_password)
session = db.session
Expand Down Expand Up @@ -1082,7 +1084,7 @@ def _create(cls, name, key, owner, network, account_id, purpose, scheme, parent_
new_wallet = DbWallet(name=name, owner=owner, network_name=network, purpose=purpose, scheme=scheme,
sort_keys=sort_keys, witness_type=witness_type, parent_id=parent_id, encoding=encoding,
multisig=multisig, multisig_n_required=sigs_required, cosigner_id=cosigner_id,
key_path=key_path)
key_path=key_path, anti_fee_snipping=anti_fee_snipping)
session.add(new_wallet)
session.commit()
new_wallet_id = new_wallet.id
Expand Down Expand Up @@ -1121,7 +1123,8 @@ def _commit(self):
@classmethod
def create(cls, name, keys=None, owner='', network=None, account_id=0, purpose=0, scheme='bip32',
sort_keys=True, password='', witness_type=None, encoding=None, multisig=None, sigs_required=None,
cosigner_id=None, key_path=None, db_uri=None, db_cache_uri=None, db_password=None):
cosigner_id=None, key_path=None, anti_fee_snipping=True, db_uri=None, db_cache_uri=None,
db_password=None):
"""
Create Wallet and insert in database. Generate masterkey or import key when specified.
Expand Down Expand Up @@ -1192,6 +1195,8 @@ def create(cls, name, keys=None, owner='', network=None, account_id=0, purpose=0
* All keys must be hardened, except for change, address_index or cosigner_id
* Max length of path is 8 levels
:type key_path: list, str
:param anti_fee_snipping: Set default locktime in transactions as current block height + 1 to avoid fee-snipping. Default is True
:type anti_fee_snipping: boolean
:param db_uri: URI of the database for wallets, wallet transactions and keys
:type db_uri: str
:param db_cache_uri: URI of the cache database. If not specified the default cache database is used when using sqlite, for other databasetypes the cache database is merged with the wallet database (db_uri)
Expand Down Expand Up @@ -1310,7 +1315,8 @@ def create(cls, name, keys=None, owner='', network=None, account_id=0, purpose=0
hdpm = cls._create(name, key, owner=owner, network=network, account_id=account_id, purpose=purpose,
scheme=scheme, parent_id=None, sort_keys=sort_keys, witness_type=witness_type,
encoding=encoding, multisig=multisig, sigs_required=sigs_required, cosigner_id=cosigner_id,
key_path=main_key_path, db_uri=db_uri, db_cache_uri=db_cache_uri, db_password=db_password)
anti_fee_snipping=anti_fee_snipping, key_path=main_key_path, db_uri=db_uri,
db_cache_uri=db_cache_uri, db_password=db_password)

if multisig:
wlt_cos_id = 0
Expand All @@ -1328,7 +1334,8 @@ def create(cls, name, keys=None, owner='', network=None, account_id=0, purpose=0
purpose=hdpm.purpose, scheme=scheme, parent_id=hdpm.wallet_id, sort_keys=sort_keys,
witness_type=hdpm.witness_type, encoding=encoding, multisig=True,
sigs_required=None, cosigner_id=wlt_cos_id, key_path=c_key_path,
db_uri=db_uri, db_cache_uri=db_cache_uri, db_password=db_password)
anti_fee_snipping=anti_fee_snipping, db_uri=db_uri, db_cache_uri=db_cache_uri,
db_password=db_password)
hdpm.cosigner.append(w)
wlt_cos_id += 1
# hdpm._dbwallet = hdpm.session.query(DbWallet).filter(DbWallet.id == hdpm.wallet_id)
Expand Down Expand Up @@ -1414,6 +1421,7 @@ def __init__(self, wallet, db_uri=None, db_cache_uri=None, session=None, main_ke
self.depth_public_master = self.key_path.index(hardened_keys[-1])
self.key_depth = len(self.key_path) - 1
self.last_updated = None
self.anti_fee_snipping = db_wlt.anti_fee_snipping
else:
raise WalletError("Wallet '%s' not found, please specify correct wallet ID or name." % wallet)

Expand Down Expand Up @@ -3706,6 +3714,12 @@ def transaction_create(self, output_arr, input_arr=None, input_key_id=None, acco
transaction.add_output(value, addr)

srv = Service(network=network, providers=self.providers, cache_uri=self.db_cache_uri)

if not locktime and self.anti_fee_snipping:
blockcount = srv.blockcount()
if blockcount:
transaction.locktime = blockcount + 1

transaction.fee_per_kb = None
if isinstance(fee, int):
fee_estimate = fee
Expand All @@ -3725,10 +3739,10 @@ def transaction_create(self, output_arr, input_arr=None, input_key_id=None, acco

# Add inputs
sequence = 0xffffffff
if 0 < transaction.locktime < 0xffffffff:
sequence = SEQUENCE_ENABLE_LOCKTIME
elif replace_by_fee:
if replace_by_fee:
sequence = SEQUENCE_REPLACE_BY_FEE
elif 0 < transaction.locktime < 0xffffffff:
sequence = SEQUENCE_ENABLE_LOCKTIME
amount_total_input = 0
if input_arr is None:
selected_utxos = self.select_inputs(amount_total_output + fee_estimate, transaction.network.dust_amount,
Expand Down
Binary file modified tests/bitcoinlib_encrypted.db
Binary file not shown.

0 comments on commit b1e5d98

Please sign in to comment.