Merge branch '1.14.5-dev' of https://github.com/dogecoin/dogecoin
This commit is contained in:
commit
84c5b57e48
63
.github/workflows/ci.yml
vendored
63
.github/workflows/ci.yml
vendored
|
@ -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:
|
||||
|
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
|
@ -21,7 +21,7 @@ on:
|
|||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-18.04
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: "dogecoin-dmg-signer"
|
||||
suites:
|
||||
- "trusty"
|
||||
- "bionic"
|
||||
architectures:
|
||||
- "amd64"
|
||||
packages:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name: "dogecoin-osx-1.14"
|
||||
enable_cache: true
|
||||
suites:
|
||||
- "trusty"
|
||||
- "bionic"
|
||||
architectures:
|
||||
- "amd64"
|
||||
packages:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: "dogecoin-win-signer"
|
||||
suites:
|
||||
- "trusty"
|
||||
- "bionic"
|
||||
architectures:
|
||||
- "amd64"
|
||||
packages:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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')
|
||||
|
|
|
@ -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
166
qa/rpc-tests/dustlimits.py
Normal 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()
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
|
@ -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
201
qa/rpc-tests/p2p-policy.py
Normal 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()
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
29
src/init.cpp
29
src/init.cpp
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue