Skip to content

Commit

Permalink
Add anti-fee-sniping option to commandline wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
mccwdev committed Mar 13, 2024
1 parent e125827 commit a05bf88
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 24 deletions.
2 changes: 1 addition & 1 deletion bitcoinlib/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +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")
anti_fee_sniping = Column(Boolean, default=True, doc="Set default locktime in transactions to avoid fee-sniping")
default_account_id = Column(Integer, doc="ID of default account for this wallet if multiple accounts are used")

__table_args__ = (
Expand Down
7 changes: 5 additions & 2 deletions bitcoinlib/tools/clw.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ def parse_args():
parser_new.add_argument('--yes', '-y', action='store_true', default=False,
help='Non-interactive mode, does not prompt for confirmation')
parser_new.add_argument('--quiet', '-q', action='store_true',
help='Quit mode, no output writen to console.')
help='Quiet mode, no output writen to console.')
parser_new.add_argument('--disable-anti-fee-sniping', action='store_true', default=False,
help='Disable anti-fee-sniping, and set locktime in all transaction to zero.')

group_wallet = parser.add_argument_group("Wallet Actions")
group_wallet.add_argument('--wallet-remove', action='store_true',
Expand Down Expand Up @@ -188,7 +190,8 @@ def create_wallet(wallet_name, args, db_uri, output_to):
passphrase = get_passphrase(args.passphrase_strength, args.yes, args.quiet)
key_list.append(HDKey.from_passphrase(passphrase, network=args.network))
return Wallet.create(wallet_name, key_list, sigs_required=sigs_required, network=args.network,
cosigner_id=args.cosigner_id, db_uri=db_uri, witness_type=args.witness_type)
cosigner_id=args.cosigner_id, db_uri=db_uri, witness_type=args.witness_type,
anti_fee_sniping=not(args.disable_anti_fee_sniping))
elif args.create_from_key:
from bitcoinlib.keys import get_key_format
import_key = args.create_from_key
Expand Down
22 changes: 11 additions & 11 deletions bitcoinlib/wallets.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def wallet_exists(wallet, db_uri=None, db_password=None):
def wallet_create_or_open(
name, keys='', owner='', network=None, account_id=0, purpose=None, scheme='bip32', sort_keys=True,
password='', witness_type=None, encoding=None, multisig=None, sigs_required=None, cosigner_id=None,
key_path=None, anti_fee_snipping=True, db_uri=None, db_cache_uri=None, db_password=None):
key_path=None, anti_fee_sniping=True, db_uri=None, db_cache_uri=None, db_password=None):
"""
Create a wallet with specified options if it doesn't exist, otherwise just open
Expand All @@ -124,7 +124,7 @@ def wallet_create_or_open(
else:
return Wallet.create(name, keys, owner, network, account_id, purpose, scheme, sort_keys,
password, witness_type, encoding, multisig, sigs_required, cosigner_id,
key_path, anti_fee_snipping, db_uri=db_uri, db_cache_uri=db_cache_uri,
key_path, anti_fee_sniping, db_uri=db_uri, db_cache_uri=db_cache_uri,
db_password=db_password)


Expand Down Expand Up @@ -1045,7 +1045,7 @@ 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,
anti_fee_snipping, db_uri, db_cache_uri, db_password):
anti_fee_sniping, db_uri, db_cache_uri, db_password):

db = Db(db_uri, db_password)
session = db.session
Expand Down Expand Up @@ -1085,7 +1085,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, anti_fee_snipping=anti_fee_snipping)
key_path=key_path, anti_fee_sniping=anti_fee_sniping)
session.add(new_wallet)
session.commit()
new_wallet_id = new_wallet.id
Expand Down Expand Up @@ -1124,7 +1124,7 @@ 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, anti_fee_snipping=True, db_uri=None, db_cache_uri=None,
cosigner_id=None, key_path=None, anti_fee_sniping=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 @@ -1196,8 +1196,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, which will make the network more secure. You could disable it to avoid transaction fingerprinting.
:type anti_fee_snipping: boolean
:param anti_fee_sniping: Set default locktime in transactions as current block height + 1 to avoid fee-sniping. Default is True, which will make the network more secure. You could disable it to avoid transaction fingerprinting.
:type anti_fee_sniping: 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 @@ -1316,7 +1316,7 @@ 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,
anti_fee_snipping=anti_fee_snipping, key_path=main_key_path, db_uri=db_uri,
anti_fee_sniping=anti_fee_sniping, key_path=main_key_path, db_uri=db_uri,
db_cache_uri=db_cache_uri, db_password=db_password)

if multisig:
Expand All @@ -1335,7 +1335,7 @@ 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,
anti_fee_snipping=anti_fee_snipping, db_uri=db_uri, db_cache_uri=db_cache_uri,
anti_fee_sniping=anti_fee_sniping, db_uri=db_uri, db_cache_uri=db_cache_uri,
db_password=db_password)
hdpm.cosigner.append(w)
wlt_cos_id += 1
Expand Down Expand Up @@ -1422,7 +1422,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
self.anti_fee_sniping = db_wlt.anti_fee_sniping
else:
raise WalletError("Wallet '%s' not found, please specify correct wallet ID or name." % wallet)

Expand Down Expand Up @@ -3716,7 +3716,7 @@ def transaction_create(self, output_arr, input_arr=None, input_key_id=None, acco

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

if not locktime and self.anti_fee_snipping:
if not locktime and self.anti_fee_sniping:
blockcount = srv.blockcount()
if blockcount:
transaction.locktime = blockcount + 1
Expand Down
14 changes: 9 additions & 5 deletions tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def test_tools_clw_create_multisig_wallet(self):
]
cmd_wlt_create = "%s %s new -w testms -m 2 2 %s -r -n testnet -d %s -o 0" % \
(self.python_executable, self.clw_executable, ' '.join(key_list), self.database_uri)
print(cmd_wlt_create)
cmd_wlt_delete = "%s %s -w testms --wallet-remove -d %s" % \
(self.python_executable, self.clw_executable, self.database_uri)
output_wlt_create = "2NBrLTapyFqU4Wo29xG4QeEt8kn38KVWRR"
Expand Down Expand Up @@ -276,11 +277,14 @@ def test_tools_wallet_multisig_cosigners(self):
'ZYXRnhWiS3jjHqgeZ')
pub_key3 = ('BC11mYrL5yBtMgaYxHEUg3anvLX3gcLi8hbtwbjymReCgGiP6hYifVMi96M3ejtvZpZbDvetBfbzgRxmu22ZkqP2i7yhFge'
'mSkHp7BRhoDubrQvs')
cmd_wlt_create1 = "%s %s new -w wlt_multisig_2_3_A -m 2 3 %s %s %s -d %s -n bitcoinlib_test -q" % \
(self.python_executable, self.clw_executable, pk1, pub_key2, pub_key3, self.database_uri)
cmd_wlt_create1 = ("%s %s new -w wlt_multisig_2_3_A -m 2 3 %s %s %s -d %s -n bitcoinlib_test -q "
"--disable-anti-fee-sniping") % \
(self.python_executable, self.clw_executable, pk1, pub_key2, pub_key3, self.database_uri)
Popen(cmd_wlt_create1, stdin=PIPE, stdout=PIPE, shell=True).communicate()
cmd_wlt_create2 = "%s %s new -w wlt_multisig_2_3_B -m 2 3 %s %s %s -d %s -n bitcoinlib_test -q" % \
(self.python_executable, self.clw_executable, pub_key1, pub_key2, pk3, self.database_uri)
cmd_wlt_create2 = ("%s %s new -w wlt_multisig_2_3_B -m 2 3 %s %s %s -d %s -n bitcoinlib_test -q "
"--disable-anti-fee-sniping") % \
(self.python_executable, self.clw_executable, pub_key1, pub_key2, pk3, self.database_uri)
print(cmd_wlt_create2)
Popen(cmd_wlt_create2, stdin=PIPE, stdout=PIPE, shell=True).communicate()

cmd_wlt_receive1 = "%s %s -w wlt_multisig_2_3_A -d %s -r -o 1 -q" % \
Expand Down Expand Up @@ -313,7 +317,7 @@ def test_tools_wallet_multisig_cosigners(self):
self.assertIn("'verified': True,", response)

filename = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'import_test.tx')
sign_import_tx_file = "%s %s -w wlt_multisig_2_3_B -d %s -o 1 --import-tx-file %s" % \
sign_import_tx_file = "%s %s -w wlt_multisig_2_3_B -d %s -o 1 --import-tx-file %s" % \
(self.python_executable, self.clw_executable, self.database_uri, filename)
output = Popen(sign_import_tx_file, stdin=PIPE, stdout=PIPE, shell=True).communicate()
response2 = normalize_string(output[0])
Expand Down
9 changes: 4 additions & 5 deletions tests/test_wallets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2266,20 +2266,19 @@ def test_wallet_transaction_replace_by_fee(self):
self.assertTrue(t2.replace_by_fee)
self.assertEqual(t2.inputs[0].sequence, SEQUENCE_REPLACE_BY_FEE)

def test_wallet_anti_fee_snipping(self):
w = wallet_create_or_open('antifeesnippingtestwallet', network='testnet', anti_fee_snipping=True,
db_uri=self.database_uri)
def test_wallet_anti_fee_sniping(self):
w = wallet_create_or_open('antifeesnipingtestwallet', network='testnet', db_uri=self.database_uri)
w.utxo_add(w.get_key().address, 1234567, os.urandom(32).hex(), 1)
t = w.send_to('tb1qrjtz22q59e76mhumy0p586cqukatw5vcd0xvvz', 123456)
block_height = Service(network='testnet').blockcount()
self.assertEqual(t.locktime, block_height+1)

w2 = wallet_create_or_open('antifeesnippingtestwallet2', network='testnet', anti_fee_snipping=True)
w2 = wallet_create_or_open('antifeesnipingtestwallet2', network='testnet', anti_fee_sniping=True)
w2.utxo_add(w2.get_key().address, 1234567, os.urandom(32).hex(), 1)
t = w2.send_to('tb1qrjtz22q59e76mhumy0p586cqukatw5vcd0xvvz', 123456, locktime=1901070183)
self.assertEqual(t.locktime, 1901070183)

w3 = wallet_create_or_open('antifeesnippingtestwallet3', network='testnet', anti_fee_snipping=False)
w3 = wallet_create_or_open('antifeesnipingtestwallet3', network='testnet', anti_fee_sniping=False)
w3.utxo_add(w3.get_key().address, 1234567, os.urandom(32).hex(), 1)
t = w3.send_to('tb1qrjtz22q59e76mhumy0p586cqukatw5vcd0xvvz', 123456)
self.assertEqual(t.locktime, 0)
Expand Down

0 comments on commit a05bf88

Please sign in to comment.