This commit is contained in:
Ed Tubbs 2021-10-13 20:14:09 -05:00
commit 84c5b57e48
38 changed files with 836 additions and 362 deletions

View file

@ -28,6 +28,7 @@ jobs:
fail-fast: false
matrix:
name:
- aarch64-linux
- armhf-linux
- i686-linux
- i686-win
@ -39,69 +40,93 @@ jobs:
include:
- name: i686-linux
host: i686-pc-linux-gnu
os: ubuntu-20.04
os: ubuntu-18.04
packages: g++-multilib bc python3-zmq
run-tests: true
check-security: true
check-symbols: true
dep-opts: "NO_QT=1"
config-opts: "--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++"
config-opts: "--enable-zmq --enable-glibc-back-compat LDFLAGS=-static-libstdc++"
goal: install
- name: armhf-linux
host: arm-linux-gnueabihf
os: ubuntu-20.04
os: ubuntu-18.04
packages: g++-arm-linux-gnueabihf
run-tests: false
check-security: true
check-symbols: false
dep-opts: "NO_QT=1"
config-opts: "--enable-glibc-back-compat --enable-reduce-exports --disable-tests"
config-opts: "--enable-glibc-back-compat --disable-tests LDFLAGS=-static-libstdc++"
goal: install
- name: aarch64-linux
host: aarch64-linux-gnu
os: ubuntu-18.04
packages: g++-aarch64-linux-gnu
run-tests: false
check-security: true
check-symbols: false
dep-opts: "NO_QT=1"
config-opts: "--enable-zmq --enable-glibc-back-compat --disable-tests LDFLAGS=-static-libstdc++"
goal: install
- name: x86_64-linux-nowallet
host: x86_64-unknown-linux-gnu
os: ubuntu-20.04
os: ubuntu-18.04
packages: python3
run-tests: true
check-security: true
check-symbols: true
dep-opts: "NO_WALLET=1"
config-opts: "--enable-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports --disable-wallet"
config-opts: "--enable-gui=qt5 --enable-glibc-back-compat --disable-wallet LDFLAGS=-static-libstdc++"
goal: install
- name: x86_64-linux-dbg
host: x86_64-unknown-linux-gnu
os: ubuntu-20.04
os: ubuntu-18.04
packages: bc python3-zmq
run-tests: true
check-security: true
check-symbols: false
dep-opts: "DEBUG=1"
config-opts: "--enable-gui=qt5 --enable-zmq --enable-glibc-back-compat --enable-reduce-exports CPPFLAGS=-DDEBUG_LOCKORDER"
config-opts: "--enable-gui=qt5 --enable-zmq --enable-glibc-back-compat CPPFLAGS=-DDEBUG_LOCKORDER"
goal: install
- name: i686-win
host: i686-w64-mingw32
arch: "i386"
os: ubuntu-20.04
packages: python3 nsis g++-mingw-w64-i686 wine bc wine-binfmt
os: ubuntu-18.04
packages: python3 nsis g++-mingw-w64-i686 wine-stable bc wine-binfmt
postinstall: |
sudo update-alternatives --set i686-w64-mingw32-gcc /usr/bin/i686-w64-mingw32-gcc-posix
sudo update-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix
sudo update-binfmts --import /usr/share/binfmts/wine
run-tests: true
check-security: true
check-symbols: false
dep-opts: ""
config-opts: "--enable-reduce-exports --enable-gui=qt5"
config-opts: "--enable-gui=qt5"
goal: install
- name: x86_64-win
host: x86_64-w64-mingw32
arch: "i386"
os: ubuntu-20.04
os: ubuntu-18.04
packages: python3 nsis g++-mingw-w64-x86-64 wine64 bc wine-binfmt
postinstall: |
sudo update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix
sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
sudo update-binfmts --import /usr/share/binfmts/wine
run-tests: true
check-security: true
check-symbols: false
dep-opts: ""
config-opts: "--enable-reduce-exports --enable-gui=qt5"
config-opts: "--enable-gui=qt5"
goal: install
- name: x86_64-macos
host: x86_64-apple-darwin11
os: ubuntu-18.04
packages: cmake imagemagick libcap-dev librsvg2-bin libz-dev libtiff-tools libtinfo5 python3-setuptools xorriso libtinfo5
run-tests: false
check-security: false
check-symbols: false
dep-opts: ""
config-opts: "--enable-gui=qt5 --enable-reduce-exports"
config-opts: "--enable-gui=qt5 --disable-tests"
goal: deploy
sdk: 10.11
- name: x86_64-linux-experimental
@ -175,7 +200,7 @@ jobs:
run: |
depends/${{ matrix.host }}/native/bin/ccache --max-size=$CCACHE_SIZE
./autogen.sh
./configure --prefix=`pwd`/depends/${{ matrix.host }} ${{ matrix.config-opts }} || ( cat config.log && false)
./configure --prefix=`pwd`/depends/${{ matrix.host }} ${{ matrix.config-opts }} --enable-reduce-exports || ( cat config.log && false)
make $MAKEJOBS ${{ matrix.goal }} || ( echo "Build failure. Verbose build follows." && make ${{ matrix.goal }} V=1 ; false )
- name: Run tests
@ -185,6 +210,14 @@ jobs:
qa/pull-tester/install-deps.sh
qa/pull-tester/rpc-tests.py --coverage
- name: Check security
if: ${{ matrix.check-security }}
run: make -C src check-security
- name: Check symbols
if: ${{ matrix.check-symbols }}
run: make -C src check-symbols
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:

View file

@ -21,7 +21,7 @@ on:
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
runs-on: ubuntu-18.04
permissions:
actions: read
contents: read

View file

@ -493,6 +493,8 @@ if test x$use_glibc_compat != xno; then
[ fdelt_type="long int"])
AC_MSG_RESULT($fdelt_type)
AC_DEFINE_UNQUOTED(FDELT_TYPE, $fdelt_type,[parameter and return value type for __fdelt_chk])
AX_CHECK_LINK_FLAG([[-Wl,--wrap=__divmoddi4]], [COMPAT_LDFLAGS="$COMPAT_LDFLAGS -Wl,--wrap=__divmoddi4"])
AX_CHECK_LINK_FLAG([[-Wl,--wrap=log2f]], [COMPAT_LDFLAGS="$COMPAT_LDFLAGS -Wl,--wrap=log2f"])
else
AC_SEARCH_LIBS([clock_gettime],[rt])
fi
@ -1106,6 +1108,7 @@ AC_SUBST(BITCOIN_CLI_NAME)
AC_SUBST(BITCOIN_TX_NAME)
AC_SUBST(RELDFLAGS)
AC_SUBST(COMPAT_LDFLAGS)
AC_SUBST(ERROR_CXXFLAGS)
AC_SUBST(HARDENED_CXXFLAGS)
AC_SUBST(HARDENED_CPPFLAGS)

View file

@ -48,7 +48,8 @@ MAX_VERSIONS = {
# Ignore symbols that are exported as part of every executable
IGNORE_EXPORTS = {
b'_edata', b'_end', b'_init', b'__bss_start', b'_fini', b'_IO_stdin_used'
b'_edata', b'_end', b'_init', b'__bss_start', b'_fini', b'_IO_stdin_used',
b'stdin', b'stdout', b'stderr'
}
READELF_CMD = os.getenv('READELF', '/usr/bin/readelf')
CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt')

View file

@ -2,21 +2,21 @@
name: "dogecoin-linux-1.14"
enable_cache: true
suites:
- "trusty"
- "bionic"
architectures:
- "amd64"
packages:
- "curl"
- "g++-aarch64-linux-gnu"
- "g++-4.8-aarch64-linux-gnu"
- "gcc-4.8-aarch64-linux-gnu"
- "g++-7-aarch64-linux-gnu"
- "gcc-7-aarch64-linux-gnu"
- "binutils-aarch64-linux-gnu"
- "g++-arm-linux-gnueabihf"
- "g++-4.8-arm-linux-gnueabihf"
- "gcc-4.8-arm-linux-gnueabihf"
- "g++-7-arm-linux-gnueabihf"
- "gcc-7-arm-linux-gnueabihf"
- "binutils-arm-linux-gnueabihf"
- "g++-4.8-multilib"
- "gcc-4.8-multilib"
- "g++-7-multilib"
- "gcc-7-multilib"
- "binutils-gold"
- "git-core"
- "pkg-config"

View file

@ -1,7 +1,7 @@
---
name: "dogecoin-dmg-signer"
suites:
- "trusty"
- "bionic"
architectures:
- "amd64"
packages:

View file

@ -2,7 +2,7 @@
name: "dogecoin-osx-1.14"
enable_cache: true
suites:
- "trusty"
- "bionic"
architectures:
- "amd64"
packages:

View file

@ -1,7 +1,7 @@
---
name: "dogecoin-win-signer"
suites:
- "trusty"
- "bionic"
architectures:
- "amd64"
packages:

View file

@ -2,7 +2,7 @@
name: "dogecoin-win-1.14"
enable_cache: true
suites:
- "trusty"
- "bionic"
architectures:
- "amd64"
packages:
@ -21,6 +21,7 @@ packages:
- "zip"
- "ca-certificates"
- "python"
- "rename"
remotes:
- "url": "https://github.com/dogecoin/dogecoin.git"
"dir": "dogecoin"
@ -29,7 +30,7 @@ script: |
WRAP_DIR=$HOME/wrapped
HOSTS="i686-w64-mingw32 x86_64-w64-mingw32"
CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests"
FAKETIME_HOST_PROGS="g++ ar ranlib nm windres strip objcopy"
FAKETIME_HOST_PROGS="ar ranlib nm windres strip objcopy"
FAKETIME_PROGS="date makensis zip"
HOST_CFLAGS="-O2 -g"
HOST_CXXFLAGS="-O2 -g"
@ -70,21 +71,13 @@ script: |
done
}
function create_per-host_linker_wrapper {
# This is only needed for trusty, as the mingw linker leaks a few bytes of
# heap, causing non-determinism. See discussion in https://github.com/bitcoin/bitcoin/pull/6900
function create_per-host_compiler_wrapper {
# -posix variant is required for c++11 threading.
for i in $HOSTS; do
mkdir -p ${WRAP_DIR}/${i}
for prog in collect2; do
echo '#!/bin/bash' > ${WRAP_DIR}/${i}/${prog}
REAL=$(${i}-gcc -print-prog-name=${prog})
echo "export MALLOC_PERTURB_=255" >> ${WRAP_DIR}/${i}/${prog}
echo "${REAL} \$@" >> $WRAP_DIR/${i}/${prog}
chmod +x ${WRAP_DIR}/${i}/${prog}
done
for prog in gcc g++; do
echo '#!/bin/bash' > ${WRAP_DIR}/${i}-${prog}
echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog}
echo "REAL=\`which -a ${i}-${prog}-posix | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog}
echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog}
echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog}
echo "export COMPILER_PATH=${WRAP_DIR}/${i}" >> ${WRAP_DIR}/${i}-${prog}
@ -98,7 +91,7 @@ script: |
export PATH_orig=${PATH}
create_global_faketime_wrappers "2000-01-01 12:00:00"
create_per-host_faketime_wrappers "2000-01-01 12:00:00"
create_per-host_linker_wrapper "2000-01-01 12:00:00"
create_per-host_compiler_wrapper "2000-01-01 12:00:00"
export PATH=${WRAP_DIR}:${PATH}
cd dogecoin
@ -112,7 +105,7 @@ script: |
export PATH=${PATH_orig}
create_global_faketime_wrappers "${REFERENCE_DATETIME}"
create_per-host_faketime_wrappers "${REFERENCE_DATETIME}"
create_per-host_linker_wrapper "${REFERENCE_DATETIME}"
create_per-host_compiler_wrapper "${REFERENCE_DATETIME}"
export PATH=${WRAP_DIR}:${PATH}
# Create the release tarball using (arbitrarily) the first host

View file

@ -5,7 +5,7 @@ $(package)_file_name=$(package)-$($(package)_version).tar.bz2
$(package)_sha256_hash=3a3bb2c4e15ffb433f2032f50a5b5a92558206822e22bfe8cbe339af4aa82f88
define $(package)_set_vars
$(package)_config_opts=--without-zlib --without-png --disable-static
$(package)_config_opts=--without-zlib --without-png --without-harfbuzz --without-bzip2 --disable-static
$(package)_config_opts_linux=--with-pic
endef

View file

@ -73,6 +73,7 @@ $(package)_config_opts += -prefix $(host_prefix)
$(package)_config_opts += -qt-libpng
$(package)_config_opts += -qt-libjpeg
$(package)_config_opts += -qt-pcre
$(package)_config_opts += -qt-harfbuzz
$(package)_config_opts += -system-zlib
$(package)_config_opts += -reduce-exports
$(package)_config_opts += -static

View file

@ -4,7 +4,6 @@ $(package)_download_path=https://github.com/zeromq/libzmq/releases/download/v$($
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=c593001a89f5a85dd2ddf564805deb860e02471171b3f204944857336295c3e5
$(package)_patches=remove_libstd_link.patch clock-unused-nsecs.patch 0002-disable-pthread_set_name_np.patch
$(package)_patches+=trusty-add-win32-inethdr.patch
define $(package)_set_vars
$(package)_config_opts=--without-documentation --disable-shared --without-libsodium --disable-curve
@ -16,7 +15,6 @@ define $(package)_preprocess_cmds
patch -p1 < $($(package)_patch_dir)/clock-unused-nsecs.patch && \
patch -p1 < $($(package)_patch_dir)/remove_libstd_link.patch && \
patch -p1 < $($(package)_patch_dir)/0002-disable-pthread_set_name_np.patch && \
patch -p1 < $($(package)_patch_dir)/trusty-add-win32-inethdr.patch && \
cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub config
endef

View file

@ -1,11 +0,0 @@
diff -dur a/src/windows.hpp b/src/windows.hpp
--- a/src/windows.hpp 2021-07-16 20:31:22.997078113 +0000
+++ b/src/windows.hpp 2021-07-16 20:33:21.525281189 +0000
@@ -52,6 +52,7 @@
#endif
#include <winsock2.h>
+#include <ws2ipdef.h>
#include <windows.h>
#include <mswsock.h>
#include <iphlpapi.h>

View file

@ -154,7 +154,7 @@ testScripts = [
'signmessages.py',
# 'nulldummy.py',
'import-rescan.py',
'harddustlimit.py',
'dustlimits.py',
'paytxfee.py',
'feelimit.py',
# While fee bumping should work in Doge, these tests depend on free transactions, which we don't support.
@ -164,6 +164,7 @@ testScripts = [
'listsinceblock.py',
'p2p-leaktests.py',
'replace-by-fee.py',
'p2p-policy.py',
]
if ENABLE_ZMQ:
testScripts.append('zmq_test.py')

View file

@ -279,20 +279,32 @@ def test_locked_wallet_fails(rbf_node, dest_address):
def test_dogecoin_wallet_minchange(rbf_node, dest_address):
input = Decimal("10.00000000")
min_change = Decimal("0.03000000")
min_fee = Decimal("0.01000000")
bumpfee = Decimal("0.001")
est_tx_size = Decimal("0.193")
discard_threshold = Decimal("1.00000000") # DEFAULT_DISCARD_THRESHOLD
min_fee = Decimal("0.01000000") # DEFAULT_TRANSACTION_FEE
min_change = discard_threshold + 2 * min_fee # MIN_CHANGE
bumpfee = Decimal("0.001") # WALLET_INCREMENTAL_RELAY_FEE
est_tx_size = Decimal("0.226") # 1 in, 2 out
# create a transaction with minimum fees
destamount = input - min_change - min_fee * est_tx_size
rbfid = spend_one_input(rbf_node,
input,
{dest_address: destamount,
get_change_address(rbf_node): min_change})
# bump the fee with the default incremental fee; this should add 0.001 DOGE
bumped_tx = rbf_node.bumpfee(rbfid)
assert_equal(bumped_tx["fee"], min_fee * est_tx_size + bumpfee)
newfee = int((input - destamount - min_fee - bumpfee / 2 ) * 100000000)
# bump the fee to only have a change output with the discard threshold
# plus half the incremental fee
newfee = int((input - destamount - discard_threshold - bumpfee / 2 ) * 100000000)
bumped_tx = rbf_node.bumpfee(bumped_tx["txid"], {"totalFee": newfee})
assert_equal(bumped_tx["fee"], input - destamount - min_fee - bumpfee / 2)
assert_equal(bumped_tx["fee"], input - destamount - discard_threshold - bumpfee / 2)
# now bump with the default incremental fee again; as the resulting change
# output will be under the discard threshold, this must discard all change
# to fee
bumped_tx = rbf_node.bumpfee(bumped_tx["txid"])
assert_equal(bumped_tx["fee"], input - destamount)
rbf_node.settxfee(Decimal("0.00000000"))

166
qa/rpc-tests/dustlimits.py Normal file
View file

@ -0,0 +1,166 @@
#!/usr/bin/env python3
# Copyright (c) 2021 The Dogecoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Dust limit QA test.
# Tests nodes with differing mempool/relay dust limits
"""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from decimal import Decimal
class DustLimitTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 4
# set up receiving addresses outside of nodes' wallets
self.recv_1 = "n4LRQGEKcyRCXqD2MH3ompyMTJKitxu1WP"
self.recv_2 = "n1eAe5K2AQUtbmMxVzWnGAyq4hkWJdse2x"
# seed moneys
self.seed = 100
def setup_nodes(self, split=False):
nodes = []
# 1.10.0-like node with only a soft dust limit
nodes.append(start_node(0, self.options.tmpdir,
["-acceptnonstdtxn=0", "-dustlimit=1", "-harddustlimit=0.0", "-minrelaytxfee=1", "-debug"]))
# 1.14.2-like node with only a hard dust limit
nodes.append(start_node(1, self.options.tmpdir,
["-acceptnonstdtxn=0", "-dustlimit=1", "-harddustlimit=1", "-minrelaytxfee=1", "-debug"]))
# 1.14.5-like node with a lower, different hard and soft dust limit
nodes.append(start_node(2, self.options.tmpdir,
["-acceptnonstdtxn=0", "-dustlimit=0.01", "-harddustlimit=0.001", "-debug"]))
# node that should accept everything
nodes.append(start_node(3, self.options.tmpdir,
["-acceptnonstdtxn=0", "-dustlimit=0.0", "-harddustlimit=0.0", "-minrelaytxfee=0.00000001", "-debug"]))
return nodes
def setup_network(self, split = False):
self.nodes = self.setup_nodes()
# connect everything to everything
for s in range(0, self.num_nodes):
for t in range(s+1, self.num_nodes):
connect_nodes_bi(self.nodes, s, t)
self.is_network_split = False
self.sync_all()
def run_test(self):
# set up 10 seeded addresses for node 0-2
addrs = []
for i in range(3):
for _ in range(10):
addrs.append(self.nodes[i].getnewaddress())
# mine some blocks and prepare some coins
self.nodes[2].generate(1)
self.sync_all()
self.nodes[0].generate(101)
self.sync_all()
for addr in addrs:
self.nodes[0].sendtoaddress(addr, self.seed)
self.nodes[0].generate(1)
self.sync_all()
# create dusty transactions
txids = [
self.send_dusty_tx(self.nodes[1], Decimal("1"), Decimal("1")), # goes to all
self.send_dusty_tx(self.nodes[0], Decimal("0.9"), Decimal("2")), # goes to 3/4
self.send_dusty_tx(self.nodes[0], Decimal("0.0009"), Decimal("2")), # goes to 3/4
self.send_dusty_tx(self.nodes[2], Decimal("0.001"), Decimal("2")), # goes to 3/4
self.send_dusty_tx(self.nodes[2], Decimal("0.001"), Decimal("0.02")), # goes to 2/4
]
# nodes do not accept dust under their hard dust limit
# no matter how much fee is paid
self.get_dust_rejection(self.nodes[2], Decimal("0.0009"), Decimal("5"))
self.get_dust_rejection(self.nodes[1], Decimal("0.9"), Decimal("5"))
# wait 15 seconds to sync mempools
time.sleep(15)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 4) # 4 of 5
assert_equal(self.nodes[1].getmempoolinfo()['size'], 1) # 1 of 5
assert_equal(self.nodes[2].getmempoolinfo()['size'], 4) # 4 of 5
assert_equal(self.nodes[3].getmempoolinfo()['size'], 5) # all
# check each tx
i = 0
for checktx in [[0,1,2,3], [0], [0,1,3,4], [0,1,2,3,4]]:
for idx in checktx:
assert(txids[idx] in self.nodes[i].getrawmempool())
i += 1
# mining the 1 tx known to node 1
self.nodes[1].generate(1)
sync_blocks(self.nodes)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 3) # 3 of 4
assert_equal(self.nodes[1].getmempoolinfo()['size'], 0) # none left
assert_equal(self.nodes[2].getmempoolinfo()['size'], 3) # 3 of 4
assert_equal(self.nodes[3].getmempoolinfo()['size'], 4) # all
# check each tx
i = 0
for checktx in [[1,2,3], [], [1,3,4], [1,2,3,4]]:
for idx in checktx:
assert(txids[idx] in self.nodes[i].getrawmempool())
i += 1
# mine the 3 tx known to node 0
self.nodes[0].generate(1)
sync_blocks(self.nodes)
# now only the last tx is left
assert_equal(self.nodes[0].getmempoolinfo()['size'], 0) # none left
assert_equal(self.nodes[1].getmempoolinfo()['size'], 0) # none left
assert_equal(self.nodes[2].getmempoolinfo()['size'], 1) # all
assert_equal(self.nodes[3].getmempoolinfo()['size'], 1) # all
# check each tx on the remaining nodes
for i in [2,3]:
assert(txids[4] in self.nodes[i].getrawmempool())
# after mining the last tx from node 2, all nodes should have empty mempools
self.nodes[2].generate(1)
self.sync_all()
assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)
assert_equal(self.nodes[1].getmempoolinfo()['size'], 0)
assert_equal(self.nodes[2].getmempoolinfo()['size'], 0)
assert_equal(self.nodes[3].getmempoolinfo()['size'], 0)
print("such success. wow!")
def create_dusty_tx(self, n, dust, fee):
minAmount = 5 * (dust + fee)
avail = n.listunspent(0, 1000, [], True, {'minimumAmount': minAmount})[0]
inputs = [ {'txid': avail['txid'], 'vout': avail['vout']} ]
outputs = { self.recv_1 : avail['amount'] - fee - dust , self.recv_2: dust }
rawtx = n.createrawtransaction(inputs, outputs)
return n.signrawtransaction(rawtx)
def send_dusty_tx(self, n, dust, fee):
rawtx = self.create_dusty_tx(n, dust, fee)
return n.sendrawtransaction(rawtx['hex'])
def get_dust_rejection(self, n, dust, fee):
rawtx = self.create_dusty_tx(n, dust, fee)
assert_raises_jsonrpc(-26, "dust", n.sendrawtransaction, rawtx['hex'])
if __name__ == '__main__':
DustLimitTest().main()

View file

@ -200,7 +200,7 @@ class RawTransactionsTest(BitcoinTestFramework):
utx = get_unspent(self.nodes[2].listunspent(), 5)
inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']} ]
outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) }
outputs = { self.nodes[0].getnewaddress() : Decimal("3.9") }
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
@ -336,7 +336,7 @@ class RawTransactionsTest(BitcoinTestFramework):
############################################################
#compare fee of a standard pubkeyhash transaction with multiple outputs
inputs = []
outputs = {self.nodes[1].getnewaddress():1.1,self.nodes[1].getnewaddress():1.2,self.nodes[1].getnewaddress():0.1,self.nodes[1].getnewaddress():1.3,self.nodes[1].getnewaddress():0.2,self.nodes[1].getnewaddress():0.3}
outputs = {self.nodes[1].getnewaddress():1.1,self.nodes[1].getnewaddress():1.2,self.nodes[1].getnewaddress():1.1,self.nodes[1].getnewaddress():1.3,self.nodes[1].getnewaddress():1.2,self.nodes[1].getnewaddress():1.3}
rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
fundedTx = self.nodes[0].fundrawtransaction(rawTx)
#create same transaction over sendtoaddress
@ -507,14 +507,14 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all()
for i in range(0,20):
self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
for i in range(0,22):
self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1.1)
self.nodes[0].generate(1)
self.sync_all()
#fund a tx with ~20 small inputs
#fund a tx with ~20 small inputs, by spending in combination the 22 DOGE we just sent
inputs = []
outputs = {self.nodes[0].getnewaddress():0.15,self.nodes[0].getnewaddress():0.04}
outputs = {self.nodes[0].getnewaddress():16.0,self.nodes[0].getnewaddress():5.9}
rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
fundedTx = self.nodes[1].fundrawtransaction(rawTx)
@ -537,8 +537,8 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all()
for i in range(0,20):
self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
for i in range(0,22):
self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1.1)
self.nodes[0].generate(1)
self.sync_all()
@ -546,7 +546,7 @@ class RawTransactionsTest(BitcoinTestFramework):
oldBalance = self.nodes[0].getbalance()
inputs = []
outputs = {self.nodes[0].getnewaddress():0.15,self.nodes[0].getnewaddress():0.04}
outputs = {self.nodes[0].getnewaddress():16.0,self.nodes[0].getnewaddress():5.9}
rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
fundedTx = self.nodes[1].fundrawtransaction(rawTx)
fundedAndSignedTx = self.nodes[1].signrawtransaction(fundedTx['hex'])
@ -554,7 +554,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.sync_all()
self.nodes[0].generate(1)
self.sync_all()
assert_equal(oldBalance+Decimal('500000.19000'), self.nodes[0].getbalance()) #0.19+block reward
assert_equal(oldBalance+Decimal('500021.9000'), self.nodes[0].getbalance()) #2.19+block reward
#####################################################
# test fundrawtransaction with OP_RETURN and no vin #
@ -677,7 +677,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(len(self.nodes[3].listunspent(1)), 1)
inputs = []
outputs = {self.nodes[2].getnewaddress(): 1}
outputs = {self.nodes[2].getnewaddress(): 2}
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
result = [self.nodes[3].fundrawtransaction(rawtx), # uses min_relay_tx_fee (set by settxfee)
@ -700,7 +700,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(change[3] + result[3]['fee'], change[4])
inputs = []
outputs = {self.nodes[2].getnewaddress(): value for value in (1.0, 1.1, 1.2, 1.3)}
outputs = {self.nodes[2].getnewaddress(): value for value in (1.05, 1.1, 1.2, 1.3)}
keys = list(outputs.keys())
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)

View file

@ -1,82 +0,0 @@
#!/usr/bin/env python3
# Copyright (c) 2021 The Dogecoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Hard dust limit QA test.
# Tests nodes with differing -dustlimits
"""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from decimal import Decimal
class HardDustLimitTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 3
def setup_network(self, split=False):
self.nodes = []
self.nodes.append(start_node(0, self.options.tmpdir, ["-dustlimit=0.1", "-debug"]))
self.nodes.append(start_node(1, self.options.tmpdir, ["-dustlimit=1", "-debug"]))
self.nodes.append(start_node(2, self.options.tmpdir, ["-dustlimit=0.01", "-debug"]))
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
self.is_network_split=False
self.sync_all()
def run_test(self):
self.fee = Decimal("0.001")
self.seed = 1000
self.coinselector = {'minimumAmount': self.fee * 10, 'maximumAmount': self.seed}
# set up addresses
n0a1 = self.nodes[0].getnewaddress()
n0a2 = self.nodes[0].getnewaddress()
n0a3 = self.nodes[0].getnewaddress()
n1a1 = self.nodes[1].getnewaddress()
n2a1 = self.nodes[2].getnewaddress()
n2a2 = self.nodes[2].getnewaddress()
n2a3 = self.nodes[2].getnewaddress()
n2a4 = self.nodes[2].getnewaddress()
# mine some blocks and prepare some coins
self.nodes[2].generate(1)
self.sync_all()
self.nodes[0].generate(101)
self.sync_all()
self.nodes[0].sendtoaddress(n0a1, self.seed)
self.nodes[0].sendtoaddress(n2a1, self.seed)
self.sync_all()
self.nodes[0].generate(1)
self.sync_all()
# create dusty transactions
self.send_dusty_tx(self.nodes[2], n2a2, n0a2, Decimal("0.9"))
self.send_dusty_tx(self.nodes[2], n2a3, n0a3, Decimal("0.09"))
self.send_dusty_tx(self.nodes[0], n2a4, n1a1, Decimal("1"))
# wait 10 seconds to sync mempools
time.sleep(10)
assert_equal(self.nodes[2].getmempoolinfo()['size'], 3)
assert_equal(self.nodes[1].getmempoolinfo()['size'], 1)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 2)
def send_dusty_tx(self, n, addr1, addr2, dust):
avail = n.listunspent(0, 1000, [], True, self.coinselector)
inputs = [ {'txid': avail[0]['txid'], 'vout': avail[0]['vout']}]
outputs = { addr1 : avail[0]['amount'] - self.fee - dust , addr2: dust }
rawtx = n.createrawtransaction(inputs, outputs)
rawtx = n.signrawtransaction(rawtx)
n.sendrawtransaction(rawtx['hex'])
if __name__ == '__main__':
HardDustLimitTest().main()

View file

@ -58,13 +58,13 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
assert_equal(address_info['ismine'], False)
#Send funds to self
txnid1 = self.nodes[0].sendtoaddress(address1, 0.1)
txnid1 = self.nodes[0].sendtoaddress(address1, 10)
rawtxn1 = self.nodes[0].gettransaction(txnid1)['hex']
txnid2 = self.nodes[0].sendtoaddress(address2, 0.05)
txnid2 = self.nodes[0].sendtoaddress(address2, 5)
rawtxn2 = self.nodes[0].gettransaction(txnid2)['hex']
txnid3 = self.nodes[0].sendtoaddress(address3, 0.025)
txnid3 = self.nodes[0].sendtoaddress(address3, 2.5)
rawtxn3 = self.nodes[0].gettransaction(txnid3)['hex']
self.nodes[0].generate(1)
@ -85,15 +85,15 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
self.nodes[1].importaddress(address2, "add2", False)
result2 = self.nodes[1].importprunedfunds(rawtxn2, proof2)
balance2 = self.nodes[1].getbalance("add2", 0, True)
assert_equal(balance2, Decimal('0.05'))
assert_equal(balance2, Decimal('5.0'))
#Import with private key with no rescan
self.nodes[1].importprivkey(address3_privkey, "add3", False)
result3 = self.nodes[1].importprunedfunds(rawtxn3, proof3)
balance3 = self.nodes[1].getbalance("add3", 0, False)
assert_equal(balance3, Decimal('0.025'))
assert_equal(balance3, Decimal('2.5'))
balance3 = self.nodes[1].getbalance("*", 0, True)
assert_equal(balance3, Decimal('0.075'))
assert_equal(balance3, Decimal('7.5'))
#Addresses Test - after import
address_info = self.nodes[1].validateaddress(address1)
@ -110,11 +110,11 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
assert_raises_jsonrpc(-8, "Transaction does not exist in wallet.", self.nodes[1].removeprunedfunds, txnid1)
balance1 = self.nodes[1].getbalance("*", 0, True)
assert_equal(balance1, Decimal('0.075'))
assert_equal(balance1, Decimal('7.5'))
self.nodes[1].removeprunedfunds(txnid2)
balance2 = self.nodes[1].getbalance("*", 0, True)
assert_equal(balance2, Decimal('0.025'))
assert_equal(balance2, Decimal('2.5'))
self.nodes[1].removeprunedfunds(txnid3)
balance3 = self.nodes[1].getbalance("*", 0, True)

201
qa/rpc-tests/p2p-policy.py Normal file
View file

@ -0,0 +1,201 @@
#!/usr/bin/env python3
# Copyright (c) 2021 The Dogecoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""P2P Policies QA test
# Tests relay and mempool acceptance policies from p2p perspective
"""
from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
class TestNode(NodeConnCB):
def __init__(self):
NodeConnCB.__init__(self)
self.connection = None
self.ping_counter = 1
self.last_pong = msg_pong()
self.txinvs = {}
self.rejects = []
def add_connection(self, conn):
self.connection = conn
# Track transaction invs for wait_for_tx_inv
def on_inv(self, conn, message):
for i in message.inv:
if (i.type == 1):
self.txinvs[format(i.hash, '064x')] = True
# Track pongs for sync_with_ping
def on_pong(self, conn, message):
self.last_pong = message
# Track reject messages
def on_reject(self, conn, message):
self.rejects.append(message)
# wait for verack to make sure the node accepts our connection attempt
def wait_for_verack(self):
def veracked():
return self.verack_received
return wait_until(veracked, timeout=10)
# Wait until we have received an inv of a specific tx
def wait_for_tx_inv(self, hash, timeout=30):
def have_received_tx_inv():
try:
return self.txinvs[hash]
except KeyError as e:
return False
return wait_until(have_received_tx_inv, timeout=timeout)
# Send a ping message and wait until we get the pong message back
def sync_with_ping(self, timeout=30):
def received_pong():
return (self.last_pong.nonce == self.ping_counter)
self.connection.send_message(msg_ping(nonce=self.ping_counter))
success = wait_until(received_pong, timeout=timeout)
self.ping_counter += 1
return success
class P2PPolicyTests(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 1
self.utxo = []
# a private key and corresponding address and p2pkh output script
self.srcPrivKey = "cRhVU6TU1qHfRg3ee59yqg7ifhREKPLPPk8eccrrAEEY74bY1dCY"
self.srcAddr = "mmMP9oKFdADezYzduwJFcLNmmi8JHUKdx9"
self.srcOutScript = "76a91440015860f45d48eeeb2224dce3ad94ba91763e1e88ac"
# valid regtest address that no one has the key to
self.tgtAddr = "mkwDHkWXF8x6aFtdGVm5E9PVC7yPY8cb4r"
def create_testnode(self, node_idx=0):
node = TestNode()
conn = NodeConn('127.0.0.1', p2p_port(node_idx), self.nodes[node_idx], node)
node.add_connection(conn)
return node
def setup_network(self):
self.nodes = []
# a Dogecoin Core node that behaves similar to mainnet policies
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-acceptnonstdtxn=0"]))
# custom testnodes
self.sendNode = self.create_testnode() # to send tx from
self.recvNode = self.create_testnode() # to check relay from
# start networking and handshake the mininodes
NetworkThread().start()
self.sendNode.wait_for_verack()
self.recvNode.wait_for_verack()
def run_test(self):
self.nodes[0].generate(101)
### test constants ###
koinu = Decimal("0.00000001") # 1 Koinu expressed in DOGE
ten = Decimal("10.0") # uniform 10 DOGE seed moneys
### parameters from fee policy ###
relay_fee = Decimal("0.001") # DEFAULT_MIN_RELAY_TX_FEE
soft_dust_limit = Decimal("0.01") # DEFAULT_DUST_LIMIT
relay_fee_per_byte = relay_fee / 1000
# create a bunch of UTXO with seed money from the Dogecoin Core wallet
for i in range(10):
inputs = [self.nodes[0].listunspent()[0]]
outputs = { self.srcAddr : ten }
tx = self.nodes[0].createrawtransaction(inputs, outputs)
signed = self.nodes[0].signrawtransaction(tx)
txid = self.nodes[0].sendrawtransaction(signed['hex'], True)
self.utxo.append(txid)
self.nodes[0].generate(1)
# test legacy output of 1 DOGE output and 1 DOGE fee
output = { self.tgtAddr : 1, self.srcAddr: 8 }
self.run_relay_test(output, None)
# test exact relay fee rate
output = { self.tgtAddr: ten - relay_fee_per_byte * 192}
tx = self.run_relay_test(output, None)
# test too low relay fee rate
output = { self.tgtAddr: ten - relay_fee_per_byte * 191 + koinu }
tx = self.run_relay_test(output, 66) # 66 = too low fee
# test exact dust limit
change = ten - soft_dust_limit - relay_fee_per_byte * 226
output = { self.tgtAddr : soft_dust_limit, self.srcAddr: change}
self.run_relay_test(output, None)
# test soft dust limit with sufficient fee
amount = soft_dust_limit - koinu
change = ten - amount - relay_fee_per_byte * 226 - soft_dust_limit
output = { self.tgtAddr : amount, self.srcAddr: change }
self.run_relay_test(output, None)
# test soft dust limit with insufficient fee
amount = soft_dust_limit - koinu
change = ten - amount - relay_fee_per_byte * 225 - soft_dust_limit + koinu
output = { self.tgtAddr : amount, self.srcAddr: change }
self.run_relay_test(output, 66)
# test a 1 koinu output with sufficient fee
amount = koinu
change = ten - amount - relay_fee_per_byte * 226 - soft_dust_limit
output = { self.tgtAddr : amount, self.srcAddr: change }
self.run_relay_test(output, 64) # 64 = dust
# test a 1 koinu output with insufficient fee
amount = koinu
change = ten - amount - relay_fee_per_byte * 225 - soft_dust_limit + koinu
output = { self.tgtAddr : amount, self.srcAddr: change }
self.run_relay_test(output, 64)
# test mempool acceptance and relay outcomes
def run_relay_test(self, output, expected_reject_code):
num_rejects = len(self.sendNode.rejects)
tx = self.spend_utxo(output)
self.sendNode.sync_with_ping(timeout=10)
if (expected_reject_code is None):
# test that the tx got relayed
assert_equal(self.recvNode.wait_for_tx_inv(tx.hash), True)
assert_equal(len(self.sendNode.rejects), num_rejects)
else:
# test that there was a rejection received with the correct code
assert_greater_than(len(self.sendNode.rejects), num_rejects)
assert_equal(self.sendNode.rejects[-1].code, expected_reject_code)
return tx
# spend seed money with a key not in the Dogecoin Core wallet.
def spend_utxo(self, output):
# construct the transaction using Dogecoin Core raw tx APIs
input = [{ "txid": self.utxo.pop(), "vout": 0, "scriptPubKey": self.srcOutScript }]
rawtx = self.nodes[0].createrawtransaction(input, output)
signed_tx = self.nodes[0].signrawtransaction(rawtx, input, [self.srcPrivKey])
# import the signed tx into a format the mininode client understands
# and send the tx from there rather than from Dogecoin Core, to test
# mempool acceptance as it would happen on mainnet: through relay
tx = FromHex(CTransaction(), signed_tx['hex'])
tx.rehash()
self.sendNode.connection.send_message(msg_tx(tx))
return tx
if __name__ == '__main__':
P2PPolicyTests().main()

View file

@ -356,7 +356,10 @@ libdogecoin_util_a_SOURCES = \
$(BITCOIN_CORE_H)
if GLIBC_BACK_COMPAT
libdogecoin_util_a_SOURCES += compat/glibc_compat.cpp
AM_LDFLAGS += $(COMPAT_LDFLAGS)
endif
# cli: shared between bitcoin-cli and bitcoin-qt

View file

@ -7,6 +7,7 @@
#endif
#include <cstddef>
#include <cstdint>
#if defined(HAVE_SYS_SELECT_H)
#include <sys/select.h>
@ -27,3 +28,47 @@ extern "C" FDELT_TYPE __fdelt_warn(FDELT_TYPE a)
return a / __NFDBITS;
}
extern "C" FDELT_TYPE __fdelt_chk(FDELT_TYPE) __attribute__((weak, alias("__fdelt_warn")));
#if defined(__i386__) || defined(__arm__)
extern "C" int64_t __udivmoddi4(uint64_t u, uint64_t v, uint64_t* rp);
extern "C" int64_t __wrap___divmoddi4(int64_t u, int64_t v, int64_t* rp)
{
int32_t c1 = 0, c2 = 0;
int64_t uu = u, vv = v;
int64_t w;
int64_t r;
if (uu < 0) {
c1 = ~c1, c2 = ~c2, uu = -uu;
}
if (vv < 0) {
c1 = ~c1, vv = -vv;
}
w = __udivmoddi4(uu, vv, (uint64_t*)&r);
if (c1)
w = -w;
if (c2)
r = -r;
*rp = r;
return w;
}
#endif
extern "C" float log2f_old(float x);
#ifdef __i386__
__asm(".symver log2f_old,log2f@GLIBC_2.1");
#elif defined(__amd64__)
__asm(".symver log2f_old,log2f@GLIBC_2.2.5");
#elif defined(__arm__)
__asm(".symver log2f_old,log2f@GLIBC_2.4");
#elif defined(__aarch64__)
__asm(".symver log2f_old,log2f@GLIBC_2.17");
#endif
extern "C" float __wrap_log2f(float x)
{
return log2f_old(x);
}

View file

@ -92,7 +92,7 @@ CAmount GetDogecoinMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool
}
CAmount nMinFee = ::minRelayTxFeeRate.GetFee(nBytes);
nMinFee += GetDogecoinDustFee(tx.vout, ::minRelayTxFeeRate);
nMinFee += GetDogecoinDustFee(tx.vout, nDustLimit);
if (fAllowFree)
{
@ -109,13 +109,14 @@ CAmount GetDogecoinMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool
return nMinFee;
}
CAmount GetDogecoinDustFee(const std::vector<CTxOut> &vout, CFeeRate &baseFeeRate) {
CAmount GetDogecoinDustFee(const std::vector<CTxOut> &vout, const CAmount dustLimit) {
CAmount nFee = 0;
// To limit dust spam, add base fee for each output less than a COIN
// To limit dust spam, add the dust limit for each output
// less than the (soft) dustlimit
BOOST_FOREACH(const CTxOut& txout, vout)
if (txout.IsDust(::minRelayTxFeeRate))
nFee += baseFeeRate.GetFeePerK();
if (txout.IsDust(dustLimit))
nFee += dustLimit;
return nFee;
}

View file

@ -28,6 +28,6 @@ CFeeRate GetDogecoinWalletFeeRate();
CAmount GetDogecoinMinWalletFee(unsigned int nBytes_);
#endif // ENABLE_WALLET
CAmount GetDogecoinMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree);
CAmount GetDogecoinDustFee(const std::vector<CTxOut> &vout, CFeeRate &baseFeeRate);
CAmount GetDogecoinDustFee(const std::vector<CTxOut> &vout, const CAmount dustLimit);
#endif // BITCOIN_DOGECOIN_FEES_H

View file

@ -473,9 +473,9 @@ std::string HelpMessage(HelpMessageMode mode)
if (showDebug) {
strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !Params(CBaseChainParams::TESTNET).RequireStandard()));
strUsage += HelpMessageOpt("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)));
strUsage += HelpMessageOpt("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost about 1/3 of its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)));
}
strUsage += HelpMessageOpt("-dustlimit=<amt>", strprintf(_("Amount under which a transaction output is considered dust, in %s (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_DUST_LIMIT)));
strUsage += HelpMessageOpt("-harddustlimit=<amt>", strprintf(_("Amount under which a transaction output is considered non-standard and will not be accepted or relayed, in %s (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_HARD_DUST_LIMIT)));
strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions for relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP));
strUsage += HelpMessageOpt("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER));
strUsage += HelpMessageOpt("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY));
@ -1029,29 +1029,34 @@ bool AppInitParameterInteraction()
return InitError(AmountErrMsg("blockmintxfee", GetArg("-blockmintxfee", "")));
}
// Feerate used to define dust. Shouldn't be changed lightly as old
// implementations may inadvertently create non-standard transactions
if (IsArgSet("-dustrelayfee"))
{
CAmount n = 0;
if (!ParseMoney(GetArg("-dustrelayfee", ""), n) || 0 == n)
return InitError(AmountErrMsg("dustrelayfee", GetArg("-dustrelayfee", "")));
dustRelayFee = CFeeRate(n);
}
fRequireStandard = !GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
if (chainparams.RequireStandard() && !fRequireStandard)
return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString()));
nBytesPerSigOp = GetArg("-bytespersigop", nBytesPerSigOp);
if (IsArgSet("-harddustlimit"))
{
CAmount n = nHardDustLimit;
if (!ParseMoney(GetArg("-harddustlimit", ""), n))
return InitError(AmountErrMsg("harddustlimit", GetArg("-harddustlimit", "")));
nHardDustLimit = n;
}
if (IsArgSet("-dustlimit"))
{
CAmount n = nDustLimit;
if (!ParseMoney(GetArg("-dustlimit", ""), n))
return InitError(AmountErrMsg("dustlimit", GetArg("-dustlimit", "")));
nDustLimit = n;
}
if (nDustLimit < nHardDustLimit)
{
nDustLimit = nHardDustLimit;
LogPrintf("Increasing -dustlimit to %s to match -harddustlimit\n", FormatMoney(nHardDustLimit));
}
#ifdef ENABLE_WALLET
if (!CWallet::ParameterInteraction())
return false;
@ -1347,7 +1352,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
if (!mapMultiArgs.count("-bind") && !mapMultiArgs.count("-whitebind")) {
struct in_addr inaddr_any;
inaddr_any.s_addr = INADDR_ANY;
fBound |= Bind(connman, CService(in6addr_any, GetListenPort()), BF_NONE);
fBound |= Bind(connman, CService((in6_addr)IN6ADDR_ANY_INIT, GetListenPort()), BF_NONE);
fBound |= Bind(connman, CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE);
}
if (!fBound)

View file

@ -105,7 +105,7 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes
else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) {
reason = "bare-multisig";
return false;
} else if (txout.IsDust(dustRelayFee)) {
} else if (txout.IsDust(nHardDustLimit)) {
reason = "dust";
return false;
}
@ -207,9 +207,9 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
}
CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE);
CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
CAmount nDustLimit = DEFAULT_DUST_LIMIT;
CAmount nHardDustLimit = DEFAULT_HARD_DUST_LIMIT;
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost)
{

View file

@ -61,18 +61,23 @@ static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100;
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80;
/** The maximum size of a standard witnessScript */
static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
/** Min feerate for defining dust. Historically this has been the same as the
* minRelayTxFee, however changing the dust limit changes which transactions are
* standard and should be done with care and ideally rarely. It makes sense to
* only increase the dust limit after prior releases were already not creating
* outputs below the new threshold */
static const CAmount DUST_RELAY_TX_FEE = RECOMMENDED_MIN_TX_FEE / 1000;
/**
* Dogecoin: Default dust limit that is evaluated when considering whether a
* transaction output is required to pay additional fee for relay and inclusion
* in blocks. Overridden by -dustlimit
*/
static const CAmount DEFAULT_DUST_LIMIT = RECOMMENDED_MIN_TX_FEE;
/**
* Dogecoin: Default hard dust limit that is evaluated when considering whether
* a transaction is standard. Transactions under this limit will not be accepted
* to the mempool and thus not relayed. Can be overridden by -harddustlimit
*
* Changing the hard dust limit changes which transactions are standard and
* should be done with care and ideally rarely. It makes sense to only increase
* this limit after prior releases were already not creating outputs below the
* new threshold
*/
static const CAmount DEFAULT_HARD_DUST_LIMIT = DEFAULT_DUST_LIMIT / 10;
/**
* Standard script verification flags that standard transactions will comply
@ -125,6 +130,7 @@ extern CFeeRate incrementalRelayFee;
extern CFeeRate dustRelayFee;
extern unsigned int nBytesPerSigOp;
extern CAmount nDustLimit;
extern CAmount nHardDustLimit;
/** Compute the virtual transaction size (weight reinterpreted as bytes). */
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost);

View file

@ -206,6 +206,15 @@ public:
return (nValue < GetDustThreshold(minRelayTxFeeRate));
}
// Dogecoin: allow comparison against different dustlimit parameters
bool IsDust(const CAmount dustLimit) const
{
if (scriptPubKey.IsUnspendable())
return false;
return (nValue < dustLimit);
}
friend bool operator==(const CTxOut& a, const CTxOut& b)
{
return (a.nValue == b.nValue &&

View file

@ -433,7 +433,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
{
CTxOut txout(amount, (CScript)std::vector<unsigned char>(24, 0));
txDummy.vout.push_back(txout);
if (txout.IsDust(dustRelayFee))
if (txout.IsDust(CWallet::discardThreshold))
fDust = true;
}
}
@ -543,13 +543,13 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
nChange -= nPayFee;
// Never create dust outputs; if we would, just add the dust to the fee.
if (nChange > 0 && nChange < MIN_CHANGE)
if (nChange > 0 && nChange < CWallet::GetMinChange())
{
CTxOut txout(nChange, (CScript)std::vector<unsigned char>(24, 0));
if (txout.IsDust(dustRelayFee))
if (txout.IsDust(CWallet::discardThreshold))
{
if (CoinControlDialog::fSubtractFeeFromAmount) // dust-change will be raised until no dust
nChange = txout.GetDustThreshold(dustRelayFee);
nChange = CWallet::discardThreshold;
else
{
nPayFee += nChange;

View file

@ -18,6 +18,10 @@
#include "script/standard.h"
#include "util.h"
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
#endif
#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
@ -248,13 +252,15 @@ QString formatBitcoinURI(const SendCoinsRecipient &info)
return ret;
}
#ifdef ENABLE_WALLET
bool isDust(const QString& address, const CAmount& amount)
{
CTxDestination dest = CBitcoinAddress(address.toStdString()).Get();
CScript script = GetScriptForDestination(dest);
CTxOut txOut(amount, script);
return txOut.IsDust(dustRelayFee);
return txOut.IsDust(CWallet::discardThreshold);
}
#endif
QString HtmlEscape(const QString& str, bool fMultiLine)
{

View file

@ -51,8 +51,11 @@ namespace GUIUtil
bool parseBitcoinURI(QString uri, SendCoinsRecipient *out);
QString formatBitcoinURI(const SendCoinsRecipient &info);
//Dogecoin: need wallet to establish dust from a wallet perspective
#ifdef ENABLE_WALLET
// Returns true if given address+amount meets "dust" definition
bool isDust(const QString& address, const CAmount& amount);
#endif
// HTML escaping for rich text controls
QString HtmlEscape(const QString& str, bool fMultiLine=false);

View file

@ -580,8 +580,8 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen
// Extract and check amounts
CTxOut txOut(sendingTo.second, sendingTo.first);
if (txOut.IsDust(dustRelayFee)) {
Q_EMIT message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).")
if (txOut.IsDust(CWallet::discardThreshold)) {
Q_EMIT message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (below discard threshold).")
.arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)),
CClientUIInterface::MSG_ERROR);

View file

@ -681,35 +681,53 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vin[0].prevout.n = 1;
t.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
t.vout.resize(1);
t.vout[0].nValue = COIN;
CKey key;
key.MakeNewKey(true);
t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
std::string reason;
// Standard: 1 DOGE
t.vout[0].nValue = COIN;
BOOST_CHECK(IsStandardTx(t, reason));
// Dogecoin: Dust is totally different in Dogecoin, disable these tests
// Check dust with default relay fee:
/* CAmount nDustThreshold = 182 * dustRelayFee.GetFeePerK()/1000 * 3;
BOOST_CHECK_EQUAL(nDustThreshold, 546);
// dust:
t.vout[0].nValue = nDustThreshold - 1;
// Non-standard (1 koinu):
t.vout[0].nValue = 1;
BOOST_CHECK(!IsStandardTx(t, reason));
// not dust:
t.vout[0].nValue = nDustThreshold;
// Non-standard (below hard dust):
t.vout[0].nValue = nHardDustLimit - 1;
BOOST_CHECK(!IsStandardTx(t, reason));
// Standard (at hard dust):
t.vout[0].nValue = nHardDustLimit;
BOOST_CHECK(IsStandardTx(t, reason));
// Check dust with odd relay fee to verify rounding:
// nDustThreshold = 182 * 1234 / 1000 * 3
dustRelayFee = CFeeRate(1234);
// dust:
t.vout[0].nValue = 672 - 1;
// Standard (below soft dust but above hard):
t.vout[0].nValue = nDustLimit - 1;
BOOST_CHECK(IsStandardTx(t, reason));
// Standard (at soft dust):
t.vout[0].nValue = nDustLimit;
BOOST_CHECK(IsStandardTx(t, reason));
// Lowering limits:
CAmount nPrevHardDustLimit = nHardDustLimit;
CAmount nPrevDustLimit = nDustLimit;
nHardDustLimit = nHardDustLimit / 2;
nDustLimit = nPrevHardDustLimit;
// Standard:
t.vout[0].nValue = nDustLimit - 1;
BOOST_CHECK(IsStandardTx(t, reason));
// Non-standard:
t.vout[0].nValue = nHardDustLimit - 1;
BOOST_CHECK(!IsStandardTx(t, reason));
// not dust:
t.vout[0].nValue = 672;
BOOST_CHECK(IsStandardTx(t, reason)); */
dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
nHardDustLimit = nPrevHardDustLimit;
nDustLimit = nPrevDustLimit;
t.vout[0].scriptPubKey = CScript() << OP_1;
BOOST_CHECK(!IsStandardTx(t, reason));

View file

@ -59,10 +59,13 @@ static const CAmount DEFAULT_MIN_RELAY_TX_FEE = RECOMMENDED_MIN_TX_FEE / 10;
//! -maxtxfee default
//rnicoll: 8/2021 scaled down as recommended fee is lowered
static const CAmount DEFAULT_TRANSACTION_MAXFEE = RECOMMENDED_MIN_TX_FEE * 10000;
//! Discourage users to set fees higher than this amount (in satoshis) per kB
//mlumin: 5/2021 adjusted downward for fee revisions
//rnicoll: 8/2021 scale further down as recommended fee is lowered
static const CAmount HIGH_TX_FEE_PER_KB = RECOMMENDED_MIN_TX_FEE * 100;
/* Dogecoin: Set the high tx fee to be higher than the default values
* implemented by the wallet.
*/
static const CAmount HIGH_TX_FEE_PER_KB = RECOMMENDED_MIN_TX_FEE * 1000;
//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis)
//mlumin: 5/2021 adjusted max upward in terms of coin
static const CAmount HIGH_MAX_TX_FEE = 100 * HIGH_TX_FEE_PER_KB;

View file

@ -2947,7 +2947,7 @@ UniValue bumpfee(const JSONRPCRequest& request)
// If the output would become dust, discard it (converting the dust to fee)
poutput->nValue -= nDelta;
if (poutput->nValue <= poutput->GetDustThreshold(::dustRelayFee)) {
if (poutput->nValue <= CWallet::discardThreshold) {
LogPrint("rpc", "Bumping fee and discarding dust output\n");
nNewFee += poutput->nValue;
tx.vout.erase(tx.vout.begin() + nOutput);

View file

@ -93,139 +93,139 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
// with an empty wallet we can't even pay one coin
BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
add_coin(1*COIN, 4); // add a new 1 coin output
add_coin(10*COIN, 4); // add a new 10 coin output
// with only a new 1 coin output, we still can't find a mature 1 coin output
BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
// with only a new 1 coin output, we still can't find a mature 10 coin output
BOOST_CHECK(!wallet.SelectCoinsMinConf( 10 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
// but we can find a new 1 coin output
BOOST_CHECK( wallet.SelectCoinsMinConf( 1 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * COIN);
// but we can find a new 10 coin output
BOOST_CHECK( wallet.SelectCoinsMinConf( 10 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 10 * COIN);
add_coin(2*COIN); // add a mature 2 coin output
add_coin(20*COIN); // add a mature 20 coin output
// we can't make 3 coins of mature outputs
BOOST_CHECK(!wallet.SelectCoinsMinConf( 3 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
// we can't make 30 coins of mature outputs
BOOST_CHECK(!wallet.SelectCoinsMinConf( 30 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
// we can make 3 coin of new outputs
BOOST_CHECK( wallet.SelectCoinsMinConf( 3 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 3 * COIN);
// we can make 30 coin of new outputs
BOOST_CHECK( wallet.SelectCoinsMinConf( 30 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 30 * COIN);
add_coin(5*COIN); // add a mature 5 coin output,
add_coin(10*COIN, 3, true); // a new 10 coin output sent from one of our own addresses
add_coin(20*COIN); // and a mature 20 coin output
add_coin(50*COIN); // add a mature 50 coin output,
add_coin(100*COIN, 3, true); // a new 100 coin output sent from one of our own addresses
add_coin(200*COIN); // and a mature 200 coin output
// now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
// now we have new: 10+100=110 (of which 100 was self-sent), and mature: 20+50+200=270. total = 380
// we can't make 38 coins only if we disallow new output:
BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
// we can't even make 37 coins if we don't allow new output even if they're from us
BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * COIN, 6, 6, 0, vCoins, setCoinsRet, nValueRet));
// but we can make 37 coins if we accept new output from ourself
BOOST_CHECK( wallet.SelectCoinsMinConf(37 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 37 * COIN);
// and we can make 38 coins if we accept all new output
BOOST_CHECK( wallet.SelectCoinsMinConf(38 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 38 * COIN);
// we can't make 380 coins only if we disallow new output:
BOOST_CHECK(!wallet.SelectCoinsMinConf(380 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
// we can't even make 370 coins if we don't allow new output even if they're from us
BOOST_CHECK(!wallet.SelectCoinsMinConf(380 * COIN, 6, 6, 0, vCoins, setCoinsRet, nValueRet));
// but we can make 370 coins if we accept new output from ourself
BOOST_CHECK( wallet.SelectCoinsMinConf(370 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 370 * COIN);
// and we can make 380 coins if we accept all new output
BOOST_CHECK( wallet.SelectCoinsMinConf(380 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 380 * COIN);
// try making 34 coins from 1,2,5,10,20 - we can't do it exactly
BOOST_CHECK( wallet.SelectCoinsMinConf(34 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 35 * COIN); // but 35 coins is closest
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible)
// try making 340 coins from 10,20,50,100,200 - we can't do it exactly
BOOST_CHECK( wallet.SelectCoinsMinConf(340 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 350 * COIN); // but 350 coins is closest
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 200+100+50. it's incredibly unlikely the 10 or 20 got included (but possible)
// when we try making 7 coins, the smaller outputs (1,2,5) are enough. We should see just 2+5
BOOST_CHECK( wallet.SelectCoinsMinConf( 7 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 7 * COIN);
// when we try making 70 coins, the smaller outputs (10,20,50) are enough. We should see just 20+50
BOOST_CHECK( wallet.SelectCoinsMinConf( 70 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 70 * COIN);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// when we try making 8 coins, the smaller outputs (1,2,5) are exactly enough.
BOOST_CHECK( wallet.SelectCoinsMinConf( 8 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK(nValueRet == 8 * COIN);
// when we try making 80 coins, the smaller outputs (10,20,50) are exactly enough.
BOOST_CHECK( wallet.SelectCoinsMinConf( 80 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK(nValueRet == 80 * COIN);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// when we try making 9 coins, no subset of smaller outputs is enough, and we get the next bigger output (10)
BOOST_CHECK( wallet.SelectCoinsMinConf( 9 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 10 * COIN);
// when we try making 90 coins, no subset of smaller outputs is enough, and we get the next bigger output (100)
BOOST_CHECK( wallet.SelectCoinsMinConf( 90 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 100 * COIN);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
// now clear out the wallet and start again to test choosing between subsets of smaller coins and the next biggest coin
empty_wallet();
add_coin( 6*COIN);
add_coin( 7*COIN);
add_coin( 8*COIN);
add_coin(20*COIN);
add_coin(30*COIN); // now we have 6+7+8+20+30 = 71 coins total
add_coin( 60*COIN);
add_coin( 70*COIN);
add_coin( 80*COIN);
add_coin(200*COIN);
add_coin(300*COIN); // now we have 60+70+80+200+300 = 710 coins total
// check that we have 71 and not 72
BOOST_CHECK( wallet.SelectCoinsMinConf(71 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK(!wallet.SelectCoinsMinConf(72 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
// check that we have 710 and not 711
BOOST_CHECK( wallet.SelectCoinsMinConf(710 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK(!wallet.SelectCoinsMinConf(711 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
// now try making 16 coins. the best smaller outputs can do is 6+7+8 = 21; not as good at the next biggest output, 20
BOOST_CHECK( wallet.SelectCoinsMinConf(16 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 20 * COIN); // we should get 20 in one output
// now try making 160 coins. the best smaller outputs can do is 60+70+80 = 210; not as good at the next biggest output, 200
BOOST_CHECK( wallet.SelectCoinsMinConf(160 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 200 * COIN); // we should get 200 in one output
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
add_coin( 5*COIN); // now we have 5+6+7+8+20+30 = 75 coins total
add_coin( 50*COIN); // now we have 50+60+70+80+200+300 = 750 coins total
// now if we try making 16 coins again, the smaller outputs can make 5+6+7 = 18 coins, better than the next biggest output, 20
BOOST_CHECK( wallet.SelectCoinsMinConf(16 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 18 * COIN); // we should get 18 in 3 outputs
// now if we try making 160 coins again, the smaller outputs can make 50+60+70 = 18 coins, better than the next biggest output, 200
BOOST_CHECK( wallet.SelectCoinsMinConf(160 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 180 * COIN); // we should get 180 in 3 outputs
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
add_coin( 18*COIN); // now we have 5+6+7+8+18+20+30
add_coin( 180*COIN); // now we have 50+60+70+80+180+200+300
// and now if we try making 16 coins again, the smaller outputs can make 5+6+7 = 18 coins, the same as the next biggest output, 18
BOOST_CHECK( wallet.SelectCoinsMinConf(16 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 18 * COIN); // we should get 18 in 1 output
// and now if we try making 16 coins again, the smaller outputs can make 50+60+70 = 180 coins, the same as the next biggest output, 180
BOOST_CHECK( wallet.SelectCoinsMinConf(160 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 180 * COIN); // we should get 180 in 1 output
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest output wins
// now try making 11 coins. we should get 5+6
BOOST_CHECK( wallet.SelectCoinsMinConf(11 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 11 * COIN);
// now try making 110 coins. we should get 50+60
BOOST_CHECK( wallet.SelectCoinsMinConf(110 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 110 * COIN);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// check that the smallest bigger output is used
add_coin( 100*COIN);
add_coin( 200*COIN);
add_coin( 300*COIN);
add_coin( 400*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 coins
BOOST_CHECK( wallet.SelectCoinsMinConf(95 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 100 * COIN); // we should get 100 coins in 1 output
add_coin( 1000*COIN);
add_coin( 2000*COIN);
add_coin( 3000*COIN);
add_coin( 4000*COIN); // now we have 50+60+70+80+180+200+300+1000+2000+3000+4000 = 10940 coins
BOOST_CHECK( wallet.SelectCoinsMinConf(950 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1000 * COIN); // we should get 1000 coins in 1 output
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
BOOST_CHECK( wallet.SelectCoinsMinConf(195 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 200 * COIN); // we should get 200 coins in 1 output
BOOST_CHECK( wallet.SelectCoinsMinConf(1950 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 2000 * COIN); // we should get 2000 coins in 1 output
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
// empty the wallet and start again, now with fractions of a coin, to test small change avoidance
empty_wallet();
add_coin(MIN_CHANGE * 1 / 10);
add_coin(MIN_CHANGE * 2 / 10);
add_coin(MIN_CHANGE * 3 / 10);
add_coin(MIN_CHANGE * 4 / 10);
add_coin(MIN_CHANGE * 5 / 10);
add_coin(CWallet::GetMinChange() * 1 / 10);
add_coin(CWallet::GetMinChange() * 2 / 10);
add_coin(CWallet::GetMinChange() * 3 / 10);
add_coin(CWallet::GetMinChange() * 4 / 10);
add_coin(CWallet::GetMinChange() * 5 / 10);
// try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE
// we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly
BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE);
// try making 1 * GetMinChange() from the 1.5 * GetMinChange()
// we'll get change smaller than GetMinChange() whatever happens, so can expect GetMinChange() exactly
BOOST_CHECK( wallet.SelectCoinsMinConf(CWallet::GetMinChange(), 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, CWallet::GetMinChange());
// but if we add a bigger output, small change is avoided
add_coin(1111*MIN_CHANGE);
add_coin(1111*CWallet::GetMinChange());
// try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CWallet::GetMinChange(), 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * CWallet::GetMinChange()); // we should get the exact amount
// if we add more small output:
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 7 / 10);
add_coin(CWallet::GetMinChange() * 6 / 10);
add_coin(CWallet::GetMinChange() * 7 / 10);
// and try again to make 1.0 * MIN_CHANGE
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// and try again to make 1.0 * GetMinChange()
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CWallet::GetMinChange(), 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * CWallet::GetMinChange()); // we should get the exact amount
// run the 'mtgox' test (see http://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
// they tried to consolidate 10 50k outputs into one 500k output, and ended up with 50k in change
@ -237,43 +237,43 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten outputs
// if there's not enough in the smaller coins to make at least 1 * MIN_CHANGE change (0.5+0.6+0.7 < 1.0+1.0),
// if there's not enough in the smaller coins to make at least 1 * GetMinChange() change (0.5+0.6+0.7 < 1.0+1.0),
// we need to try finding an exact subset anyway
// sometimes it will fail, and so we use the next biggest output:
empty_wallet();
add_coin(MIN_CHANGE * 5 / 10);
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 7 / 10);
add_coin(1111 * MIN_CHANGE);
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger output
add_coin(CWallet::GetMinChange() * 5 / 10);
add_coin(CWallet::GetMinChange() * 6 / 10);
add_coin(CWallet::GetMinChange() * 7 / 10);
add_coin(1111 * CWallet::GetMinChange());
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CWallet::GetMinChange(), 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1111 * CWallet::GetMinChange()); // we get the bigger output
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
// but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0)
empty_wallet();
add_coin(MIN_CHANGE * 4 / 10);
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 8 / 10);
add_coin(1111 * MIN_CHANGE);
BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount
add_coin(CWallet::GetMinChange() * 4 / 10);
add_coin(CWallet::GetMinChange() * 6 / 10);
add_coin(CWallet::GetMinChange() * 8 / 10);
add_coin(1111 * CWallet::GetMinChange());
BOOST_CHECK( wallet.SelectCoinsMinConf(CWallet::GetMinChange(), 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, CWallet::GetMinChange()); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two outputs 0.4+0.6
// test avoiding small change
empty_wallet();
add_coin(MIN_CHANGE * 5 / 100);
add_coin(MIN_CHANGE * 1);
add_coin(MIN_CHANGE * 100);
add_coin(CWallet::GetMinChange() * 5 / 100);
add_coin(CWallet::GetMinChange() * 1);
add_coin(CWallet::GetMinChange() * 100);
// trying to make 100.01 from these three outputs
BOOST_CHECK(wallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE * 10105 / 100); // we should get all outputs
BOOST_CHECK(wallet.SelectCoinsMinConf(CWallet::GetMinChange() * 10001 / 100, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, CWallet::GetMinChange() * 10105 / 100); // we should get all outputs
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// but if we try to make 99.9, we should take the bigger of the two small outputs to avoid small change
BOOST_CHECK(wallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE);
BOOST_CHECK(wallet.SelectCoinsMinConf(CWallet::GetMinChange() * 9990 / 100, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 101 * CWallet::GetMinChange());
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// test with many inputs
@ -283,9 +283,9 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
for (uint16_t j = 0; j < 676; j++)
add_coin(amt);
BOOST_CHECK(wallet.SelectCoinsMinConf(20*CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
if (amt - 20*CENT < MIN_CHANGE) {
if (amt - 20*CENT < CWallet::GetMinChange()) {
// needs more than one input:
uint16_t returnSize = std::ceil((20.0 * CENT + MIN_CHANGE)/amt);
uint16_t returnSize = std::ceil((20.0 * CENT + CWallet::GetMinChange())/amt);
CAmount returnValue = amt * returnSize;
BOOST_CHECK_EQUAL(nValueRet, returnValue);
BOOST_CHECK_EQUAL(setCoinsRet.size(), returnSize);
@ -522,15 +522,17 @@ BOOST_AUTO_TEST_CASE(GetMinimumFee_dust_test)
CAmount nMinTxFee = COIN / 100;
// Confirm dust penalty fees are added on
CAmount nDustPenalty = COIN / 100;
// Because this is ran by the wallet, this takes the discardThreshold,
// not the dust limit
CAmount nDustPenalty = COIN;
BOOST_CHECK_EQUAL(CWallet::GetMinimumFee(tx, 963, 0, pool), nDustPenalty + (nMinTxFee * 0.963));
BOOST_CHECK_EQUAL(CWallet::GetMinimumFee(tx, 1000, 0, pool), nDustPenalty + (nMinTxFee * 1.000));
BOOST_CHECK_EQUAL(CWallet::GetMinimumFee(tx, 1999, 0, pool), nDustPenalty + (nMinTxFee * 1.999));
// change the hard dust limit
// change the discard threshold
nDustLimit = COIN / 1000;
CWallet::discardThreshold = COIN / 1000;
// Confirm dust penalty fees are not added
@ -538,7 +540,7 @@ BOOST_AUTO_TEST_CASE(GetMinimumFee_dust_test)
BOOST_CHECK_EQUAL(CWallet::GetMinimumFee(tx, 1000, 0, pool), nMinTxFee * 1.000);
BOOST_CHECK_EQUAL(CWallet::GetMinimumFee(tx, 1999, 0, pool), nMinTxFee * 1.999);
nDustLimit = COIN;
CWallet::discardThreshold = COIN;
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -58,6 +58,12 @@ CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE);
* Override with -fallbackfee
*/
CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);
/**
* Dogecoin: Effective dust limit for the wallet
* - Outputs smaller than this get rejected
* - Change smaller than this gets discarded to fee
*/
CAmount CWallet::discardThreshold = DEFAULT_DISCARD_THRESHOLD;
/** @defgroup mapWallet
*
@ -2173,6 +2179,14 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns
}
}
// Dogecoin: MIN_CHANGE as a function of discardThreshold and minTxFee(1000)
// Makes the wallet change output minimums configurable instead of hardcoded
// defaults.
CAmount CWallet::GetMinChange()
{
return discardThreshold + minTxFee.GetFeePerK() * MIN_CHANGE_FEE_MULTIPLIER;
}
bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, vector<COutput> vCoins,
set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const
{
@ -2212,7 +2226,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
nValueRet += coin.first;
return true;
}
else if (n < nTargetValue + MIN_CHANGE)
else if (n < nTargetValue + GetMinChange())
{
vValue.push_back(coin);
nTotalLower += n;
@ -2249,13 +2263,13 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
CAmount nBest;
ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest);
if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE)
ApproximateBestSubset(vValue, nTotalLower, nTargetValue + MIN_CHANGE, vfBest, nBest);
if (nBest != nTargetValue && nTotalLower >= nTargetValue + GetMinChange())
ApproximateBestSubset(vValue, nTotalLower, nTargetValue + GetMinChange(), vfBest, nBest);
// If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
// or the next bigger coin is closer), return the bigger coin
if (coinLowestLarger.second.first &&
((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger.first <= nBest))
((nBest != nTargetValue && nBest < nTargetValue + GetMinChange()) || coinLowestLarger.first <= nBest))
{
setCoinsRet.insert(coinLowestLarger.second);
nValueRet += coinLowestLarger.first;
@ -2500,7 +2514,15 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
}
}
if (txout.IsDust(dustRelayFee))
/*
* Dogecoin: check all outputs against the discard threshold
* to make sure that the wallet's dust policy gets
* followed rather than the current relay rules,
* because the larger network may settle on a
* higher hard limit than the current version's
* soft limit.
*/
if (txout.IsDust(discardThreshold))
{
if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
{
@ -2578,16 +2600,16 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// We do not move dust-change to fees, because the sender would end up paying more than requested.
// This would be against the purpose of the all-inclusive feature.
// So instead we raise the change and deduct from the recipient.
if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee))
if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(discardThreshold))
{
CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue;
newTxOut.nValue += nDust; // raise change until no more dust
CAmount changeDelta = discardThreshold - newTxOut.nValue;
newTxOut.nValue += changeDelta; // raise change until we reach the discard threshold
for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient
{
if (vecSend[i].fSubtractFeeFromAmount)
{
txNew.vout[i].nValue -= nDust;
if (txNew.vout[i].IsDust(dustRelayFee))
txNew.vout[i].nValue -= changeDelta;
if (txNew.vout[i].IsDust(discardThreshold))
{
strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
return false;
@ -2597,9 +2619,9 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
}
}
// Never create dust outputs; if we would, just
// add the dust to the fee.
if (newTxOut.IsDust(dustRelayFee))
// Never create change under the discard threshold;
// if we would, just discard the change to the fee.
if (newTxOut.IsDust(discardThreshold))
{
nChangePosInOut = -1;
nFeeRet += nChange;
@ -2721,7 +2743,16 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet;
vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut;
// Only reduce change if remaining amount is still a large enough output.
if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) {
/* Dogecoin: this has been changed from a static MIN_FINAL_CHANGE that
* followed DEFAULT_DISCARD_THRESHOLD to instead use the configurable
* discard threshold.
*
* Note:
* If GetMinChange() ever becomes configurable or otherwise changes to no
* longer be derived from DEFAULT_DISCARD_THRESHOLD, then this check
* must be adapted.
*/
if (change_position->nValue >= discardThreshold + additionalFeeNeeded) {
change_position->nValue -= additionalFeeNeeded;
nFeeRet += additionalFeeNeeded;
break; // Done, able to increase fee from change
@ -2852,8 +2883,8 @@ bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB *pwa
CAmount CWallet::GetRequiredFee(const CMutableTransaction& tx, unsigned int nTxBytes)
{
// Dogecoin: Add an increased fee for each dust output
return std::max(minTxFee.GetFee(nTxBytes) + GetDogecoinDustFee(tx.vout, minTxFee), ::minRelayTxFeeRate.GetFee(nTxBytes));
// Dogecoin: Add an increased fee for each output that is lower than the discard threshold
return std::max(minTxFee.GetFee(nTxBytes) + GetDogecoinDustFee(tx.vout, discardThreshold), ::minRelayTxFeeRate.GetFee(nTxBytes));
}
CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
@ -3620,6 +3651,8 @@ std::string CWallet::GetWalletHelpString(bool showDebug)
std::string strUsage = HelpMessageGroup(_("Wallet options:"));
strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls"));
strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE));
strUsage += HelpMessageOpt("-discardthreshold=<amt>", strprintf(_("The minimum transaction output size (in %s) used to validate wallet transactions and discard change (to fee) (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_THRESHOLD)));
strUsage += HelpMessageOpt("-fallbackfee=<amt>", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)));
strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"),
@ -3953,6 +3986,22 @@ bool CWallet::ParameterInteraction()
GetArg("-maxtxfee", ""), ::minRelayTxFeeRate.ToString()));
}
}
if (IsArgSet("-discardthreshold"))
{
CAmount nDiscardThreshold = 0;
if (!ParseMoney(GetArg("-discardthreshold", ""), nDiscardThreshold))
return InitError(AmountErrMsg("discardthreshold", GetArg("-discardthreshold", "")));
if (nDiscardThreshold < nDustLimit)
{
return InitError(strprintf(_("Invalid amount for -discardthreshold=<amount>: '%s' (must be at least the dust limit of %s to prevent stuck transactions)"),
GetArg("-discardthreshold", ""), FormatMoney(nDustLimit)));
}
if (nDiscardThreshold > HIGH_TX_FEE_PER_KB)
InitWarning(_("-discardthreshold is set very high! This is the output amount that the wallet will discard (to fee) if it is smaller than this setting."));
CWallet::discardThreshold = nDiscardThreshold;
}
nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS);

View file

@ -56,6 +56,14 @@ static const CAmount DEFAULT_TRANSACTION_FEE = RECOMMENDED_MIN_TX_FEE;
static const CAmount DEFAULT_FALLBACK_FEE = RECOMMENDED_MIN_TX_FEE;
//! -mintxfee default
static const CAmount DEFAULT_TRANSACTION_MINFEE = RECOMMENDED_MIN_TX_FEE;
//! -discardthreshold default
/* 1.14.5: set the wallet's discard threshold to 1 DOGE because that's what 97%
* of the network currently implements as the hard dust limit. This
* value can be changed when a significant portion of the relay network
* and miners have adopted a different hard dust limit.
*/
static const CAmount DEFAULT_DISCARD_THRESHOLD = COIN;
//! minimum recommended increment for BIP 125 replacement txs
/*
* Dogecoin: Scaled to 1/10th of the recommended transaction fee to make RBF
@ -66,11 +74,10 @@ static const CAmount DEFAULT_TRANSACTION_MINFEE = RECOMMENDED_MIN_TX_FEE;
* This way, replacements for fee bumps are transient rather than persisted.
*/
static const CAmount WALLET_INCREMENTAL_RELAY_FEE = RECOMMENDED_MIN_TX_FEE / 10;
/*
* Dogecoin: Creating change outputs at exactly the dustlimit is counter-
* productive because it leaves no space to bump the fee up, so we make the
* MIN_CHANGE parameter higher than the MIN_FINAL_CHANGE parameter.
* minimum change higher than the discard threshold.
*
* When RBF is not a default policy, we need to scale for both that and CPFP,
* to have a facility for those that did not manually enable RBF, yet need to
@ -85,24 +92,18 @@ static const CAmount WALLET_INCREMENTAL_RELAY_FEE = RECOMMENDED_MIN_TX_FEE / 10;
* or transaction size, we assume that most transactions are < 1kb, leading
* to the following when planning for a replacements with 2x original fee:
*
* RBF: MIN_CHANGE = dust limit + min fee or
* CPFP: MIN_CHANGE = dust limit + 2 * min fee * 0.147 + min fee
* RBF: min change = discardThreshold + minTxFee(1000) or
* CPFP: min change = discardThreshold + 2 * minTxFee(147) + minTxFee(1000)
*
* Where the CPFP requirement is higher than the RBF one to lead to the same
* result.
*
* This can be rounded up to the nearest multiple of RECOMMENDED_MIN_TX_FEE as:
* This can be rounded up to the nearest multiple of minTxFee(1000) as:
*
* MIN_CHANGE = DEFAULT_DUST_LIMIT + 2 * RECOMMENDED_MIN_TX_FEE
*
* The MIN_FINAL_CHANGE parameter can stay equal to DEFAULT_DUST_LIMIT as this
* influences when the wallet will discard all remaining dust as fee instead of
* change.
* min change = discardThreshold + 2 * minTxFee(1000)
*/
//! target minimum change amount
static const CAmount MIN_CHANGE = DEFAULT_DUST_LIMIT + 2 * RECOMMENDED_MIN_TX_FEE;
//! final minimum change amount after paying for fees
static const CAmount MIN_FINAL_CHANGE = DEFAULT_DUST_LIMIT;
//! target minimum change fee multiplier
static const CAmount MIN_CHANGE_FEE_MULTIPLIER = 2;
//! Default for -spendzeroconfchange
static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true;
@ -793,6 +794,13 @@ public:
static CFeeRate minTxFee;
static CFeeRate fallbackFee;
static CAmount discardThreshold;
/**
* Minimum change as a function of discardThreshold
*/
static CAmount GetMinChange();
/**
* Estimate the minimum fee considering user set parameters
* and the required fee