mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-18 13:17:55 +02:00
Merge #515: [0.17] Cleaned up CA
ac642808aSet correct ports for Liquid network params (Steven Roose)6491b4da5Use upstream Shuffle which doesn't cause DEBUG panic (Gregory Sanders)de56cd82bmove fee estimation test to extended tests, takes too long (Gregory Sanders)1f062da26Fix compilation of certain builds (Gregory Sanders)7d86c7d97remove leftover asset debug printing (Gregory Sanders)1fec77f46Add elements transaction unit test data to EXTRA_DIST (Gregory Sanders)665d9ebe0Whitespace and tab linting (Gregory Sanders)30552459dSpelling linter (Gregory Sanders)0148024ddFunctional test linting (Gregory Sanders)242515fd6LogPrints need \n (Gregory Sanders)b4c9ccb8fremove duplicate includes in test_bitcoin (Gregory Sanders)8371cfcdaFixup blind.h guard (Gregory Sanders)bc9a6104aFixup wallet log print (Gregory Sanders)f56de12cdRemove all but one circular dep: coinselection <-> wallet (Gregory Sanders)5c3481a91Lint RPC args (Gregory Sanders)04a8f2e05Document blindedprefix arg (Gregory Sanders)a687dd9a3Add assets and issuance functional tests (Steven Roose)96af43b18Add confidential transactions functional test (Steven Roose)39020cdc7Adapt existing functional tests to CA (Steven Roose)6113bf89c[BROKEN] Add fee outputs to functional tests (Steven Roose)b48aea828[BROKEN] Account for assets in all balance inspection in tests (Steven Roose)43f8b459d[BROKEN] Adapt bitcoin_functional tests to CA (Steven Roose)5f9f578ee[BROKEN] Unbreak Qt code after CA (Steven Roose)f7920d77e[BROKEN] Add issuance wallet RPCs (Steven Roose)a361dda7a[BROKEN] Add CA wallet RPCs (Steven Roose)b6397d695[BROKEN] Adapt wallet unit tests to CA (Steven Roose)d3ab44de4[BROKEN] Adapt wallet to CA (Steven Roose)49e808373[BROKEN] Add raw issuance RPCs (Steven Roose)a03239dd3[BROKEN] Add raw tx RPCs for CA (Steven Roose)6038c37c7[BROKEN] Add unit test for blinding logic (Steven Roose)aa33202bd[BROKEN] Add blinding logic (Steven Roose)cd5662664[BROKEN] Adapt existing unit tests to CA (Steven Roose)f4553d4b6[BROKEN] Adapt existing benchmarks to CA (Steven Roose)703db5b42[BROKEN] Adapt existing RPCs to CA (Steven Roose)6493ad902Adapt bitcoin-tx to CA (Steven Roose)0b5066143[BROKEN] Add CA validation (Steven Roose)d53479c9f[BROKEN] Introduce CA in data structures (Steven Roose)04d06917dRename con_elementswitness to con_elementsmode (Steven Roose)484d83c37Add new arguments (Steven Roose)fde9fe101Add basic issuance boilerplate (Steven Roose)f81122f94Add asset and value blinding functionality (Steven Roose)8b8256d9fAdd ConfidentialCommitment-based types (Steven Roose)d0322f7f7Add initial issuance boilerplate (Steven Roose)fd411bb17Enable surjection proof module in secp256k1-zkp (Steven Roose)84954c811Add CAssetsDir (Steven Roose)10773c6c1Add CAsset and CAmountMap (Steven Roose)14af0b653Implement blech32 addresses, expose over RPC (Gregory Sanders)0f2878036Add blech32 implementation and API (Steven Roose)9cb2fa051Add blech32 python implementation with diff annotation with segwit_addr.py (Gregory Sanders)17a22f149Add support for blinded addresses (Steven Roose)00fba7771Add liquidv1 chainparams (Gregory Sanders)045e61569Add CKey::ECDH (Steven Roose)aa375e66cReplace CScriptID and CKeyID in CTxDestination with dedicated type (Gregory Sanders)3e7cff1e4Disable tests that are not relevant for Elements (Steven Roose)3a0b3b70aAdd tweakfedpeg and compatbility test (Gregory Sanders)36a5d5d0fFix bug in calculate_contract for Liquid (Steven Roose)77ba5eec4RPC params convertion cleanup (Steven Roose)ce19a5f6cAdd secure reporting document, link in README (Gregory Sanders) Pull request description: Cleaned up version of https://github.com/ElementsProject/elements/pull/503. Tree-SHA512: fb82a182a49384dd4be5838c147f6049f3b554f6d861681df46d83819c39c460b469cb68a8f6c47a2e8d3c17e439fea8818f8bca03b200027e6a127875376892
This commit is contained in:
commit
e9f6e82bbf
198 changed files with 11649 additions and 1931 deletions
|
|
@ -269,6 +269,8 @@ EXTRA_DIST += \
|
|||
test/util/data/txcreatesignv1.hex \
|
||||
test/util/data/txcreatesignv1.json \
|
||||
test/util/data/txcreatesignv2.hex \
|
||||
test/util/data/txcreatemultisig4_elements.hex \
|
||||
test/util/data/txcreatemultisig4_elements.json \
|
||||
test/util/rpcauth-test.py
|
||||
|
||||
CLEANFILES = $(OSX_DMG) $(BITCOIN_WIN_INSTALLER)
|
||||
|
|
|
|||
|
|
@ -74,3 +74,7 @@ Translations are periodically pulled from Transifex and merged into the git repo
|
|||
pull from Transifex would automatically overwrite them again.
|
||||
|
||||
Translators should also subscribe to the [mailing list](https://groups.google.com/forum/#!forum/bitcoin-translators).
|
||||
|
||||
Secure Reporting
|
||||
------------------
|
||||
See [our vulnerability reporting guide](SECURITY.md)
|
||||
894
SECURITY.md
Normal file
894
SECURITY.md
Normal file
|
|
@ -0,0 +1,894 @@
|
|||
# Security
|
||||
|
||||
The maintainers of Elements take security very seriously and are committed to addressing any disclosed security vulnerabilities quickly and carefully. If you find a security vulnerability, please report it to us following the steps described here.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Privately and confidentially send us a description of the vulnerability that you have discovered using an encrypted and authenticated channel. PGP encrypted email is preferred. Our contact information is given below.
|
||||
|
||||
In your report, please include as much information as you can, including:
|
||||
|
||||
* a description of the vulnerability and how it could be exploited
|
||||
* its potential impact (e.g. privacy leak, denial of service, theft of funds)
|
||||
* steps or code for reproducing it
|
||||
* a proposed patch for remedying it
|
||||
|
||||
Also, provide us with a secure means to contact you with any follow up questions we might have.
|
||||
|
||||
## Considerations
|
||||
|
||||
Please take care not to violate the privacy of users in your report. For example, stack traces or exploit scripts sent to us should never contain private keys or personally identifiable information.
|
||||
|
||||
Give us at least one week to investigate the vulnerability you found and up to 90 days to fix it. Also, please give us reasonable advanced notice if at any point you intend to disclose the vulnerability to anyone else.
|
||||
|
||||
In general, please investigate and report bugs in a way that makes a reasonable, good faith effort not to be disruptive or harmful to us, this software's users, or the users of dependent projects.
|
||||
|
||||
We will take care to inform the maintainers of dependent projects.
|
||||
|
||||
## How to Contact Us
|
||||
|
||||
Greg Sanders
|
||||
gsanders@blockstream.com
|
||||
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1
|
||||
|
||||
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
|
||||
=bKfQ
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
|
||||
## Escalation
|
||||
|
||||
If there is no response from the authors within 3 days, please escalate your vulnerability report by emailing security@blockstream.com using this PGP key:
|
||||
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBFv/XdQBEAC2iS1uQij2AJSnvQZxScnqf6v0db63QDbS6GjH5PndQ8cF0szv
|
||||
YJYCFBigkzj4BkKxbJJlnfPW6Jl3SfzCGDvBW3IYuB3S10InDqJFYcM1ZemWCGAs
|
||||
HA48NDfB4AIBIFH09H4dUE/J6yAdhX/+Qa/bjOhiwrCFVE2pVtMN8aTFnaLzxCP+
|
||||
fWZUaPrPv84B7uxEdLM77wIhsN+16FAr1qS42NfKDDolBAs//Bmv5fkNC7lzAVCf
|
||||
MA/QEcNlAvButPrNyZU3t25maUv5hhKUDdJ2G/iACf8tVgp+ygmD8NHQMLPSaFqa
|
||||
O5wy77Fd5OyX3Gii/E8MtPEsePViwecwJqc/3UXBx7zTRou2gxLikVFTnJb+Jit9
|
||||
F2kcljhCjHGxsuhf4Zr6zu+RTHHDgdBmpt4t1HA2jft/40r+uWQjL/rNP+01HgZj
|
||||
4OLHkSI5VfJsXRn1EqOGpBIzR56f0GaxA0jluQMfkE9PTMxg5+YbrGgdot3l7pQ3
|
||||
+mqMu3aim2EYZZHTsMCRt4j4pRn5g4BZan+w7STfA7rIMJu/MjP3G4s+IFMPVRki
|
||||
QLwktZSD+x2M9iIsOD4YVheMKtU6WRroFeCkXzIzLYwCuZ4ym/JFJMH+Keuyo254
|
||||
5hcymw+ivmPP+xuuoP1npQioRH4RKpfDgskABv8+t5rteV4BtUIWL33A/wARAQAB
|
||||
tDlCbG9ja3N0cmVhbSBTZWN1cml0eSBSZXBvcnRpbmcgPHNlY3VyaXR5QGJsb2Nr
|
||||
c3RyZWFtLmNvbT6JAk4EEwEKADgWIQQRdlQtqY5x4TNyLvdKyMyIaESi1gUCW/9d
|
||||
1AIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRBKyMyIaESi1lcQD/9HZmtP
|
||||
XhKtwC92zTsT5Xqt/K4ckaiJRaUlHeFtfkHpTdXIUFIJjZ1w1JJAWLtRf58MY45U
|
||||
5DAOOYQptoXiy4USZkIMH1uBtFSAvyCUXH5cDWK1347G5rUg6Ry8Cxe+wzXOlxfr
|
||||
f/9Vs28z+awfIrvk50sj4QW+mMlS69VwuHUl5CJ+BtcqQWQO85ummQxQq8rMw7rD
|
||||
AwkftqiMKz+YLw5/xECyiXDDdQr66kdkglbQGgiciS7HNo0SQ2XqTNcGZkRA3lmv
|
||||
HYCchZpgr9qxfnLjgVddJB+iNTwFZ7AQ7ZBlYWvu5UIMweuEz+yB7WGbQZLOsRZ8
|
||||
OaIPmZ150VX0sQYeXYhoFrraNW6obFqsSklnQbsfw6KsCaFvYhNZgHf177YlrAzq
|
||||
puR53H1sOjOQq8pnbjyf4XLhAGMC65LydWtkQK77m46kOBZad9UGg2WKg/SY+3pF
|
||||
WWdP7vlsR7oJyElEQfUwBsT16K/6kenyagQ6CzqnF/X+W7P1STndpBJp4lD0RfaD
|
||||
v6UyqxPYhUuQ24jP5jm8+RtS+OGB00czY2cVSDgjYVuU80WsW+Qt0XtLKeoVYdCb
|
||||
TaKgreicqbz0Afr9hbPIieW2wbQnYlRPjprTVhhGsxlaUb7Kcz7fapliJrKBFgy5
|
||||
odUljZ+iSompuiYtFhVYA8e6sx0pRUGOopnkGLkCDQRb/13UARAA3WAlRv6DofgG
|
||||
xu+L2ePZb1OCQTkn4Eq+24veGibPvlqFJivF1ebctUtxiKVsz0dXtWcAYk7Rh2I/
|
||||
xsEGxIzhjr5VLVOdldM5AgJna6WPvOA4sPXjdy47R71NfEQfg9Svv93mmkpbJsL3
|
||||
NuHxvpoeO4A9JrFfwn7WJevXOiUWdKJ+nn0ZPwjYle6i27OfIojyVmZVQEiHC/Il
|
||||
LxQEYaNDalAorjnn0b7X7S3Z8pMAb8HqD0RTXXed9LPgbasARyND2I2xy1txUDPI
|
||||
Qcq6tIbryGYlegEHuvsE31zRPoNjnXkwABb6qBkUUiZMbRJCYOQXSo7Z2tasKHIJ
|
||||
I/FnIj8dmT/IXDb9KiWr8wziGLdgnZx3QZGt5P0LIMFKrfXMNJO7EmO1QMbgZFgk
|
||||
JPhJ0o61PvMaVLMQVoxD6K7bKOzI2t4LTA0l5RxuMcadu8G13YzgVXX44Cac1qUn
|
||||
xriMzk62HXdSeZozcO/IRN7Kdw2bB++5EVYTQN1EEhIymXVUrBg2pXvLSXalg+kp
|
||||
0BhLVHcbTI51mKz8GY9NUShFI7ZEzxzzltcEA+F5TLrPMgT+tx+QvjDdGWIhWycI
|
||||
KW53hjKiGolhpG9Kqo9ogtCO2a3r6JspO0z+54/EF5rS2LI13pqk0qNgoYMYqChe
|
||||
XU8BJdZ9siCooQ+3o+Y/9TkQWSAwnWkAEQEAAYkCNgQYAQoAIBYhBBF2VC2pjnHh
|
||||
M3Iu90rIzIhoRKLWBQJb/13UAhsMAAoJEErIzIhoRKLWGhoP/jFfwRrda1RNR6OY
|
||||
NHOIa4x4PtjDuYwDYgI5X2NQXlglyOTWouKjY1eu7LRoQSS5blD7BA9GHhYRDBL/
|
||||
0NQo/EQn3JFoitGWs07Bry0A4DTOz0H7wRqVXtN+Ck13QdEemq+suLE+PcbRJ4Ei
|
||||
ANoNVgSRGqYO683oXEzGgzF+FXXPbcRTNHwvV8LgmUioe2cgHX3Q2PC3gUTmnNkq
|
||||
IhWirlT5cQVSLS2IzsP903uq8VtHl7lXLkS6Ba3CmwLoHYfhurGQNR6Av2WPgL2D
|
||||
oY8NOxPdz9QxBUzUVObiMm3UfD/eTF73NAmNJRDqYzpY/l54ZyxLFjlfXRpwKrx/
|
||||
islwezx+2fzns5u4xwdywVHzvgsmbXMIDdNTaTS8BDaKbAopLmbmuTnnTbJXWFbb
|
||||
mQ2/GHcB0mKuXDkzt+7JMQ0NHtrGC3qvEtnTXZGXr3uIhFDkJSOoaH68dqq5++pz
|
||||
GtT+aiv3L120r0pSSyTgbPsrqSlWgXEuJ4uzt3j69J0Qek0YrL0EDxHdnGPW4+fv
|
||||
AZiq1RFG8MHOy0Obahed5uqlzXCNtroHdgSQeR+6IkODSsEd+hVdXJs/hjcWLNG5
|
||||
VNztar/H4BSwlhKbgvFivOzhj8x5TNoqMM95G8Ew/5idiT/YQgsA6lcwsEZ78t9O
|
||||
lTHPj4G8vH5F/zIFb+uQNSlKzuH+
|
||||
=8mAH
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
Your help in maintaining and improving the security of this software is deeply appreciated. When the vulnerability you have reported is patched here and in downstream dependencies, we will publicly disclose it. Let us know if you would like to remain anonymous or would like to be acknowledged. We would be happy to acknowledge your contributions to closing the vulnerability you found in the release notes, source code and/or documentation in this repository.
|
||||
|
|
@ -256,7 +256,7 @@ if test "x$enable_debug" = xyes; then
|
|||
# Prefer -Og, fall back to -O0 if that is unavailable.
|
||||
AX_CHECK_COMPILE_FLAG(
|
||||
[-Og],
|
||||
[[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -Og"]],
|
||||
[[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -O0"]],
|
||||
[AX_CHECK_COMPILE_FLAG([-O0],[[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -O0"]],,[[$CXXFLAG_WERROR]])],
|
||||
[[$CXXFLAG_WERROR]])
|
||||
|
||||
|
|
@ -1459,7 +1459,7 @@ if test x$need_bundled_univalue = xyes; then
|
|||
AC_CONFIG_SUBDIRS([src/univalue])
|
||||
fi
|
||||
|
||||
ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-module-recovery --disable-jni --enable-experimental --enable-module-whitelist --enable-module-rangeproof --enable-module-generator"
|
||||
ac_configure_args="${ac_configure_args} --disable-shared --with-pic --with-bignum=no --enable-module-recovery --disable-jni --enable-experimental --enable-module-whitelist --enable-module-rangeproof --enable-module-generator --enable-module-surjectionproof --enable-module-ecdh"
|
||||
AC_CONFIG_SUBDIRS([src/secp256k1])
|
||||
|
||||
AC_OUTPUT
|
||||
|
|
|
|||
|
|
@ -92,8 +92,12 @@ endif
|
|||
BITCOIN_CORE_H = \
|
||||
addrdb.h \
|
||||
addrman.h \
|
||||
asset.h \
|
||||
assetsdir.h \
|
||||
base58.h \
|
||||
bech32.h \
|
||||
blech32.h \
|
||||
blind.h \
|
||||
bloom.h \
|
||||
blockencodings.h \
|
||||
blockfilter.h \
|
||||
|
|
@ -111,6 +115,7 @@ BITCOIN_CORE_H = \
|
|||
compat/endian.h \
|
||||
compat/sanity.h \
|
||||
compressor.h \
|
||||
confidential_validation.h \
|
||||
consensus/consensus.h \
|
||||
consensus/tx_verify.h \
|
||||
core_io.h \
|
||||
|
|
@ -126,6 +131,7 @@ BITCOIN_CORE_H = \
|
|||
interfaces/handler.h \
|
||||
interfaces/node.h \
|
||||
interfaces/wallet.h \
|
||||
issuance.h \
|
||||
key.h \
|
||||
key_io.h \
|
||||
keystore.h \
|
||||
|
|
@ -223,12 +229,14 @@ libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
|||
libbitcoin_server_a_SOURCES = \
|
||||
addrdb.cpp \
|
||||
addrman.cpp \
|
||||
assetsdir.cpp \
|
||||
bloom.cpp \
|
||||
blockencodings.cpp \
|
||||
blockfilter.cpp \
|
||||
block_proof.cpp \
|
||||
chain.cpp \
|
||||
checkpoints.cpp \
|
||||
confidential_validation.cpp \
|
||||
consensus/tx_verify.cpp \
|
||||
httprpc.cpp \
|
||||
httpserver.cpp \
|
||||
|
|
@ -290,6 +298,7 @@ endif
|
|||
libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
|
||||
libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
libbitcoin_wallet_a_SOURCES = \
|
||||
assetsdir.cpp \
|
||||
interfaces/wallet.cpp \
|
||||
wallet/coincontrol.cpp \
|
||||
wallet/crypter.cpp \
|
||||
|
|
@ -358,6 +367,7 @@ libbitcoin_consensus_a_SOURCES = \
|
|||
amount.h \
|
||||
arith_uint256.cpp \
|
||||
arith_uint256.h \
|
||||
asset.cpp \
|
||||
consensus/merkle.cpp \
|
||||
consensus/merkle.h \
|
||||
consensus/params.h \
|
||||
|
|
@ -367,6 +377,8 @@ libbitcoin_consensus_a_SOURCES = \
|
|||
prevector.h \
|
||||
primitives/block.cpp \
|
||||
primitives/block.h \
|
||||
primitives/confidential.cpp \
|
||||
primitives/confidential.h \
|
||||
primitives/txwitness.cpp \
|
||||
primitives/txwitness.h \
|
||||
primitives/transaction.cpp \
|
||||
|
|
@ -403,11 +415,14 @@ libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
|||
libbitcoin_common_a_SOURCES = \
|
||||
base58.cpp \
|
||||
bech32.cpp \
|
||||
blech32.cpp \
|
||||
blind.cpp \
|
||||
chainparams.cpp \
|
||||
coins.cpp \
|
||||
compressor.cpp \
|
||||
core_read.cpp \
|
||||
core_write.cpp \
|
||||
issuance.cpp \
|
||||
key.cpp \
|
||||
key_io.cpp \
|
||||
keystore.cpp \
|
||||
|
|
|
|||
|
|
@ -96,7 +96,8 @@ BITCOIN_TESTS =\
|
|||
test/util_tests.cpp \
|
||||
test/validation_block_tests.cpp \
|
||||
test/versionbits_tests.cpp \
|
||||
test/pegin_spent_tests.cpp
|
||||
test/pegin_spent_tests.cpp \
|
||||
test/blind_tests.cpp
|
||||
# ELEMENTS IN THE END
|
||||
|
||||
if ENABLE_PROPERTY_TESTS
|
||||
|
|
|
|||
149
src/asset.cpp
Normal file
149
src/asset.cpp
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
|
||||
#include <asset.h>
|
||||
|
||||
CAmountMap& operator+=(CAmountMap& a, const CAmountMap& b)
|
||||
{
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = b.begin(); it != b.end(); ++it)
|
||||
a[it->first] += it->second;
|
||||
return a;
|
||||
}
|
||||
|
||||
CAmountMap& operator-=(CAmountMap& a, const CAmountMap& b)
|
||||
{
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = b.begin(); it != b.end(); ++it)
|
||||
a[it->first] -= it->second;
|
||||
return a;
|
||||
}
|
||||
|
||||
CAmountMap operator+(const CAmountMap& a, const CAmountMap& b)
|
||||
{
|
||||
CAmountMap c;
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = a.begin(); it != a.end(); ++it)
|
||||
c[it->first] += it->second;
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = b.begin(); it != b.end(); ++it)
|
||||
c[it->first] += it->second;
|
||||
return c;
|
||||
}
|
||||
|
||||
CAmountMap operator-(const CAmountMap& a, const CAmountMap& b)
|
||||
{
|
||||
CAmountMap c;
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = a.begin(); it != a.end(); ++it)
|
||||
c[it->first] += it->second;
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = b.begin(); it != b.end(); ++it)
|
||||
c[it->first] -= it->second;
|
||||
return c;
|
||||
}
|
||||
|
||||
bool operator<(const CAmountMap& a, const CAmountMap& b)
|
||||
{
|
||||
bool smallerElement = false;
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = b.begin(); it != b.end(); ++it) {
|
||||
CAmount aValue = a.count(it->first) ? a.find(it->first)->second : 0;
|
||||
if (aValue > it->second)
|
||||
return false;
|
||||
if (aValue < it->second)
|
||||
smallerElement = true;
|
||||
}
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = a.begin(); it != a.end(); ++it) {
|
||||
CAmount bValue = b.count(it->first) ? b.find(it->first)->second : 0;
|
||||
if (it->second > bValue)
|
||||
return false;
|
||||
if (it->second < bValue)
|
||||
smallerElement = true;
|
||||
}
|
||||
return smallerElement;
|
||||
}
|
||||
|
||||
bool operator<=(const CAmountMap& a, const CAmountMap& b)
|
||||
{
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = b.begin(); it != b.end(); ++it) {
|
||||
CAmount aValue = a.count(it->first) ? a.find(it->first)->second : 0;
|
||||
if (aValue > it->second)
|
||||
return false;
|
||||
}
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = a.begin(); it != a.end(); ++it) {
|
||||
CAmount bValue = b.count(it->first) ? b.find(it->first)->second : 0;
|
||||
if (it->second > bValue)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator>(const CAmountMap& a, const CAmountMap& b)
|
||||
{
|
||||
bool largerElement = false;
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = b.begin(); it != b.end(); ++it) {
|
||||
CAmount aValue = a.count(it->first) ? a.find(it->first)->second : 0;
|
||||
if (aValue < it->second)
|
||||
return false;
|
||||
if (aValue > it->second)
|
||||
largerElement = true;
|
||||
}
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = a.begin(); it != a.end(); ++it) {
|
||||
CAmount bValue = b.count(it->first) ? b.find(it->first)->second : 0;
|
||||
if (it->second < bValue)
|
||||
return false;
|
||||
if (it->second > bValue)
|
||||
largerElement = true;
|
||||
}
|
||||
return largerElement;
|
||||
}
|
||||
|
||||
bool operator>=(const CAmountMap& a, const CAmountMap& b)
|
||||
{
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = b.begin(); it != b.end(); ++it) {
|
||||
if ((a.count(it->first) ? a.find(it->first)->second : 0) < it->second)
|
||||
return false;
|
||||
}
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = a.begin(); it != a.end(); ++it) {
|
||||
if (it->second < (b.count(it->first) ? b.find(it->first)->second : 0))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator==(const CAmountMap& a, const CAmountMap& b)
|
||||
{
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = a.begin(); it != a.end(); ++it) {
|
||||
if ((b.count(it->first) ? b.find(it->first)->second : 0) != it->second)
|
||||
return false;
|
||||
}
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = b.begin(); it != b.end(); ++it) {
|
||||
if ((a.count(it->first) ? a.find(it->first)->second : 0) != it->second)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const CAmountMap& a, const CAmountMap& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
bool hasNegativeValue(const CAmountMap& amount)
|
||||
{
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = amount.begin(); it != amount.end(); ++it) {
|
||||
if (it->second < 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasNonPostiveValue(const CAmountMap& amount)
|
||||
{
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = amount.begin(); it != amount.end(); ++it) {
|
||||
if (it->second <= 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CAmount valueFor(const CAmountMap& mapValue, const CAsset& asset) {
|
||||
CAmountMap::const_iterator it = mapValue.find(asset);
|
||||
if (it != mapValue.end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
return CAmount(0);
|
||||
}
|
||||
}
|
||||
108
src/asset.h
Normal file
108
src/asset.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
|
||||
#ifndef BITCOIN_ASSET_H
|
||||
#define BITCOIN_ASSET_H
|
||||
|
||||
#include <uint256.h>
|
||||
|
||||
#include <amount.h>
|
||||
#include <serialize.h>
|
||||
|
||||
/**
|
||||
* Native Asset Issuance
|
||||
*
|
||||
* An asset identifier tag, a 256 bits serialized hash (sha256) defined
|
||||
* by the issuance transaction from which the output’s coins are derived.
|
||||
* Each output contains coins from a single asset/currency.
|
||||
* For the host currency, the similarly-calculated hash of the chain’s genesis
|
||||
* block is used instead.
|
||||
**/
|
||||
struct CAsset {
|
||||
uint256 id;
|
||||
|
||||
CAsset() { }
|
||||
explicit CAsset(const uint256& idIn) : id(idIn) { }
|
||||
explicit CAsset(const std::vector<unsigned char>& vchIDIn) : id(vchIDIn) { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(id);
|
||||
}
|
||||
|
||||
bool IsNull() const { return id.IsNull(); }
|
||||
void SetNull() { id.SetNull(); }
|
||||
|
||||
unsigned char* begin() { return id.begin(); }
|
||||
unsigned char* end() { return id.end(); }
|
||||
const unsigned char* begin() const { return id.begin(); }
|
||||
const unsigned char* end() const { return id.end(); }
|
||||
|
||||
std::string GetHex() const { return id.GetHex(); }
|
||||
void SetHex(const std::string& str) { id.SetHex(str); }
|
||||
|
||||
friend bool operator==(const CAsset& a, const CAsset& b)
|
||||
{
|
||||
return a.id == b.id;
|
||||
}
|
||||
|
||||
friend bool operator!=(const CAsset& a, const CAsset& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
friend bool operator<(const CAsset& a, const CAsset& b)
|
||||
{
|
||||
return a.id < b.id;
|
||||
}
|
||||
};
|
||||
|
||||
/** Used for consensus fee and general wallet accounting*/
|
||||
typedef std::map<CAsset, CAmount> CAmountMap;
|
||||
|
||||
CAmountMap& operator+=(CAmountMap& a, const CAmountMap& b);
|
||||
CAmountMap& operator-=(CAmountMap& a, const CAmountMap& b);
|
||||
CAmountMap operator+(const CAmountMap& a, const CAmountMap& b);
|
||||
CAmountMap operator-(const CAmountMap& a, const CAmountMap& b);
|
||||
|
||||
// WARNING: Comparisons are only looking for *complete* ordering.
|
||||
// For strict inequality checks, if any entry would fail the non-strict
|
||||
// inequality, the comparison will fail. Therefore it is possible
|
||||
// that all inequality comparison checks may fail.
|
||||
// Therefore if >/< fails against a CAmountMap(), this means there
|
||||
// are all zeroes or one or more negative values.
|
||||
//
|
||||
// Examples: 1A + 2B <= 1A + 2B + 1C
|
||||
// and 1A + 2B < 1A + 2B + 1C
|
||||
// but
|
||||
// !(1A + 2B == 1A + 2B + 1C)
|
||||
//-------------------------------------
|
||||
// 1A + 2B == 1A + 2B
|
||||
// and 1A + 2B <= 1A + 2B
|
||||
// but
|
||||
// !(1A + 2B < 1A + 2B)
|
||||
//-------------------------------------
|
||||
// !(1A + 2B == 2B - 1C)
|
||||
// !(1A + 2B >= 2B - 1C)
|
||||
// ...
|
||||
// !(1A + 2B < 2B - 1C)
|
||||
// and 1A + 2B != 2B - 1C
|
||||
bool operator<(const CAmountMap& a, const CAmountMap& b);
|
||||
bool operator<=(const CAmountMap& a, const CAmountMap& b);
|
||||
bool operator>(const CAmountMap& a, const CAmountMap& b);
|
||||
bool operator>=(const CAmountMap& a, const CAmountMap& b);
|
||||
bool operator==(const CAmountMap& a, const CAmountMap& b);
|
||||
bool operator!=(const CAmountMap& a, const CAmountMap& b);
|
||||
|
||||
inline bool MoneyRange(const CAmountMap& mapValue) {
|
||||
for(CAmountMap::const_iterator it = mapValue.begin(); it != mapValue.end(); it++) {
|
||||
if (it->second < 0 || it->second > MAX_MONEY) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CAmount valueFor(const CAmountMap& mapValue, const CAsset& asset);
|
||||
|
||||
#endif // BITCOIN_AMOUNT_H
|
||||
99
src/assetsdir.cpp
Normal file
99
src/assetsdir.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (c) 2017-2017 The Elements Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <assetsdir.h>
|
||||
#include <chainparams.h>
|
||||
|
||||
#include <tinyformat.h>
|
||||
#include <utilstrencodings.h>
|
||||
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
|
||||
void CAssetsDir::Set(const CAsset& asset, const AssetMetadata& metadata)
|
||||
{
|
||||
// No asset or label repetition
|
||||
if (GetLabel(asset) != "")
|
||||
throw std::runtime_error(strprintf("duplicated asset '%s'", asset.GetHex()));
|
||||
if (GetAsset(metadata.GetLabel()) != CAsset())
|
||||
throw std::runtime_error(strprintf("duplicated label '%s'", metadata.GetLabel()));
|
||||
|
||||
mapAssetMetadata[asset] = metadata;
|
||||
mapAssets[metadata.GetLabel()] = asset;
|
||||
}
|
||||
|
||||
void CAssetsDir::SetHex(const std::string& assetHex, const std::string& label)
|
||||
{
|
||||
if (!IsHex(assetHex) || assetHex.size() != 64)
|
||||
throw std::runtime_error("The asset must be hex string of length 64");
|
||||
|
||||
const std::vector<std::string> protectedLabels = {"", "*", "bitcoin", "Bitcoin", "btc"};
|
||||
for (std::string proLabel : protectedLabels) {
|
||||
if (label == proLabel) {
|
||||
throw std::runtime_error(strprintf("'%s' label is protected", proLabel));
|
||||
}
|
||||
}
|
||||
Set(CAsset(uint256S(assetHex)), AssetMetadata(label));
|
||||
}
|
||||
|
||||
void CAssetsDir::InitFromStrings(const std::vector<std::string>& assetsToInit, const std::string& pegged_asset_name)
|
||||
{
|
||||
for (std::string strToSplit : assetsToInit) {
|
||||
std::vector<std::string> vAssets;
|
||||
boost::split(vAssets, strToSplit, boost::is_any_of(":"));
|
||||
if (vAssets.size() != 2) {
|
||||
throw std::runtime_error("-assetdir parameters malformed, expecting asset:label");
|
||||
}
|
||||
SetHex(vAssets[0], vAssets[1]);
|
||||
}
|
||||
// Set "bitcoin" to the pegged asset for tests
|
||||
Set(Params().GetConsensus().pegged_asset, AssetMetadata(pegged_asset_name));
|
||||
}
|
||||
|
||||
CAsset CAssetsDir::GetAsset(const std::string& label) const
|
||||
{
|
||||
auto it = mapAssets.find(label);
|
||||
if (it != mapAssets.end())
|
||||
return it->second;
|
||||
return CAsset();
|
||||
}
|
||||
|
||||
AssetMetadata CAssetsDir::GetMetadata(const CAsset& asset) const
|
||||
{
|
||||
auto it = mapAssetMetadata.find(asset);
|
||||
if (it != mapAssetMetadata.end())
|
||||
return it->second;
|
||||
return AssetMetadata("");
|
||||
}
|
||||
|
||||
std::string CAssetsDir::GetLabel(const CAsset& asset) const
|
||||
{
|
||||
return GetMetadata(asset).GetLabel();
|
||||
}
|
||||
|
||||
std::vector<CAsset> CAssetsDir::GetKnownAssets() const
|
||||
{
|
||||
std::vector<CAsset> knownAssets;
|
||||
for (auto it = mapAssets.begin(); it != mapAssets.end(); it++) {
|
||||
knownAssets.push_back(it->second);
|
||||
}
|
||||
return knownAssets;
|
||||
}
|
||||
|
||||
CAsset GetAssetFromString(const std::string& strasset) {
|
||||
CAsset asset = gAssetsDir.GetAsset(strasset);
|
||||
if (asset.IsNull() && strasset.size() == 64 && IsHex(strasset)) {
|
||||
asset = CAsset(uint256S(strasset));
|
||||
}
|
||||
return asset;
|
||||
}
|
||||
|
||||
// GLOBAL:
|
||||
CAssetsDir _gAssetsDir;
|
||||
const CAssetsDir& gAssetsDir = _gAssetsDir;
|
||||
|
||||
void InitGlobalAssetDir(const std::vector<std::string>& assetsToInit, const std::string& pegged_asset_name)
|
||||
{
|
||||
_gAssetsDir.InitFromStrings(assetsToInit, pegged_asset_name);
|
||||
}
|
||||
60
src/assetsdir.h
Normal file
60
src/assetsdir.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
|
||||
#ifndef BITCOIN_ASSETSDIR_H
|
||||
#define BITCOIN_ASSETSDIR_H
|
||||
|
||||
#include <asset.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
class AssetMetadata
|
||||
{
|
||||
std::string label;
|
||||
public:
|
||||
AssetMetadata() : label("") {};
|
||||
AssetMetadata(std::string _label) : label(_label) {};
|
||||
|
||||
const std::string& GetLabel() const
|
||||
{
|
||||
return label;
|
||||
}
|
||||
};
|
||||
|
||||
class CAssetsDir
|
||||
{
|
||||
std::map<CAsset, AssetMetadata> mapAssetMetadata;
|
||||
std::map<std::string, CAsset> mapAssets;
|
||||
|
||||
void Set(const CAsset& asset, const AssetMetadata& metadata);
|
||||
void SetHex(const std::string& assetHex, const std::string& label);
|
||||
public:
|
||||
void InitFromStrings(const std::vector<std::string>& assetsToInit, const std::string& pegged_asset_name);
|
||||
|
||||
/**
|
||||
* @param label A label string
|
||||
* @return asset id corresponding to the asset label
|
||||
*/
|
||||
CAsset GetAsset(const std::string& label) const;
|
||||
|
||||
AssetMetadata GetMetadata(const CAsset& asset) const;
|
||||
|
||||
/** @return the label associated to the asset id */
|
||||
std::string GetLabel(const CAsset& asset) const;
|
||||
|
||||
std::vector<CAsset> GetKnownAssets() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns asset id corresponding to the given asset expression, which is either an asset label or a hex value.
|
||||
* @param strasset A label string or a hex value corresponding to an asset
|
||||
* @return The asset ID for the given expression
|
||||
*/
|
||||
CAsset GetAssetFromString(const std::string& strasset);
|
||||
|
||||
// GLOBAL:
|
||||
class CAssetsDir;
|
||||
|
||||
extern const CAssetsDir& gAssetsDir;
|
||||
|
||||
void InitGlobalAssetDir(const std::vector<std::string>& assetsToInit, const std::string& pegged_asset_name);
|
||||
|
||||
#endif // BITCOIN_ASSETSDIR_H
|
||||
|
|
@ -95,7 +95,7 @@ static void AssembleBlock(benchmark::State& state)
|
|||
tx.vin.push_back(MineBlock(SCRIPT_PUB));
|
||||
tx.witness.vtxinwit.resize(1);
|
||||
tx.witness.vtxinwit.back().scriptWitness = witness;
|
||||
tx.vout.emplace_back(1337, SCRIPT_PUB);
|
||||
tx.vout.emplace_back(CTxOut(CAsset(), 1337, SCRIPT_PUB));
|
||||
if (NUM_BLOCKS - b >= COINBASE_MATURITY)
|
||||
txs.at(b) = MakeTransactionRef(tx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
|
|||
|
||||
dummyTransactions[1].vout.resize(2);
|
||||
dummyTransactions[1].vout[0].nValue = 21 * CENT;
|
||||
dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
|
||||
dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[2].GetPubKey()));
|
||||
dummyTransactions[1].vout[1].nValue = 22 * CENT;
|
||||
dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
|
||||
dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(PKHash(key[3].GetPubKey()));
|
||||
AddCoins(coinsRet, dummyTransactions[1], 0);
|
||||
|
||||
return dummyTransactions;
|
||||
|
|
|
|||
|
|
@ -45,11 +45,12 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::State& state)
|
|||
// Make insecure_rand here so that each iteration is identical.
|
||||
FastRandomContext insecure_rand(true);
|
||||
CCheckQueueControl<PrevectorJob> control(&queue);
|
||||
std::vector<std::vector<PrevectorJob>> vBatches(BATCHES);
|
||||
std::vector<std::vector<PrevectorJob*>> vBatches(BATCHES);
|
||||
for (auto& vChecks : vBatches) {
|
||||
vChecks.reserve(BATCH_SIZE);
|
||||
for (size_t x = 0; x < BATCH_SIZE; ++x)
|
||||
vChecks.emplace_back(insecure_rand);
|
||||
for (size_t x = 0; x < BATCH_SIZE; ++x) {
|
||||
vChecks.emplace_back(new PrevectorJob(insecure_rand));
|
||||
}
|
||||
control.Add(vChecks);
|
||||
}
|
||||
// control waits for completion by RAII, but
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
#include <bench/bench.h>
|
||||
#include <wallet/wallet.h>
|
||||
#include <wallet/coinselection.h>
|
||||
#include <asset.h>
|
||||
#include <policy/policy.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
|
|
@ -47,12 +49,23 @@ static void CoinSelection(benchmark::State& state)
|
|||
const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0);
|
||||
while (state.KeepRunning()) {
|
||||
std::set<CInputCoin> setCoinsRet;
|
||||
CAmount nValueRet;
|
||||
CAmountMap mapValueRet;
|
||||
bool bnb_used;
|
||||
bool success = wallet.SelectCoinsMinConf(1003 * COIN, filter_standard, groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used);
|
||||
CAmountMap mapValue;
|
||||
mapValue[::policyAsset] = 1003 * COIN;
|
||||
bool success = wallet.SelectCoinsMinConf(mapValue, filter_standard, groups, setCoinsRet, mapValueRet, coin_selection_params, bnb_used);
|
||||
assert(success);
|
||||
assert(nValueRet == 1003 * COIN);
|
||||
assert(mapValueRet[::policyAsset] == 1003 * COIN);
|
||||
assert(setCoinsRet.size() == 2);
|
||||
|
||||
/* std::set<std::pair<const CWalletTx*, unsigned int> > setCoinsRet;
|
||||
CAmountMap nValueRet;
|
||||
CAmountMap mapValue;
|
||||
mapValue[CAsset()] = 1003 * COIN;
|
||||
bool success = wallet.SelectCoinsMinConf(mapValue, 1, 6, 0, vCoins, setCoinsRet, nValueRet);
|
||||
assert(success);
|
||||
assert(nValueRet[CAsset()] == 1003 * COIN);
|
||||
assert(setCoinsRet.size() == 2);*/
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,10 +97,12 @@ static void VerifyScriptBench(benchmark::State& state)
|
|||
#if defined(HAVE_CONSENSUS_LIB)
|
||||
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
|
||||
stream << txSpend;
|
||||
CDataStream streamVal(SER_NETWORK, PROTOCOL_VERSION);
|
||||
streamVal << txCredit.vout[0].nValue;
|
||||
int csuccess = bitcoinconsensus_verify_script_with_amount(
|
||||
txCredit.vout[0].scriptPubKey.data(),
|
||||
txCredit.vout[0].scriptPubKey.size(),
|
||||
txCredit.vout[0].nValue,
|
||||
(const unsigned char*)&streamVal[0], streamVal.size(),
|
||||
(const unsigned char*)stream.data(), stream.size(), 0, flags, nullptr);
|
||||
assert(csuccess == 1);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <config/bitcoin-config.h>
|
||||
#endif
|
||||
|
||||
#include <asset.h>
|
||||
#include <clientversion.h>
|
||||
#include <coins.h>
|
||||
#include <consensus/consensus.h>
|
||||
|
|
@ -46,7 +47,7 @@ static void SetupBitcoinTxArgs()
|
|||
gArgs.AddArg("in=TXID:VOUT(:SEQUENCE_NUMBER)", "Add input to TX", false, OptionsCategory::COMMANDS);
|
||||
gArgs.AddArg("locktime=N", "Set TX lock time to N", false, OptionsCategory::COMMANDS);
|
||||
gArgs.AddArg("nversion=N", "Set TX version to N", false, OptionsCategory::COMMANDS);
|
||||
gArgs.AddArg("outaddr=VALUE:ADDRESS", "Add address-based output to TX", false, OptionsCategory::COMMANDS);
|
||||
gArgs.AddArg("outaddr=VALUE:ADDRESS(:ASSET)", "Add address-based output to TX", false, OptionsCategory::COMMANDS);
|
||||
gArgs.AddArg("outdata=[VALUE:]DATA", "Add data-based output to TX", false, OptionsCategory::COMMANDS);
|
||||
gArgs.AddArg("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", "Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS. "
|
||||
"Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output. "
|
||||
|
|
@ -67,6 +68,8 @@ static void SetupBitcoinTxArgs()
|
|||
gArgs.AddArg("load=NAME:FILENAME", "Load JSON file FILENAME into register NAME", false, OptionsCategory::REGISTER_COMMANDS);
|
||||
gArgs.AddArg("set=NAME:JSON-STRING", "Set register NAME to given JSON-STRING", false, OptionsCategory::REGISTER_COMMANDS);
|
||||
|
||||
gArgs.AddArg("-serialization=TYPE", "Sets the serialization of transactions. ELEMENTS or BITCOIN are the two valid options.", false, OptionsCategory::REGISTER_COMMANDS);
|
||||
|
||||
// Hidden
|
||||
gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN);
|
||||
gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN);
|
||||
|
|
@ -270,7 +273,7 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn
|
|||
std::vector<std::string> vStrInputParts;
|
||||
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
|
||||
|
||||
if (vStrInputParts.size() != 2)
|
||||
if (vStrInputParts.size() < 2)
|
||||
throw std::runtime_error("TX output missing or too many separators");
|
||||
|
||||
// Extract and validate VALUE
|
||||
|
|
@ -284,8 +287,22 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn
|
|||
}
|
||||
CScript scriptPubKey = GetScriptForDestination(destination);
|
||||
|
||||
// extract and validate ASSET
|
||||
CAsset asset;
|
||||
if (!g_con_elementsmode && vStrInputParts.size() == 3) {
|
||||
throw std::runtime_error("TX output asset type invalid for BITCOIN serialization");
|
||||
}
|
||||
if (vStrInputParts.size() == 3) {
|
||||
asset = CAsset(uint256S(vStrInputParts[2]));
|
||||
if (asset.IsNull()) {
|
||||
throw std::runtime_error("invalid TX output asset type");
|
||||
}
|
||||
} else {
|
||||
asset = Params().GetConsensus().pegged_asset;
|
||||
}
|
||||
|
||||
// construct TxOut, append to transaction output list
|
||||
CTxOut txout(value, scriptPubKey);
|
||||
CTxOut txout(asset, CConfidentialValue(value), scriptPubKey);
|
||||
tx.vout.push_back(txout);
|
||||
}
|
||||
|
||||
|
|
@ -325,11 +342,11 @@ static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& str
|
|||
}
|
||||
if (bScriptHash) {
|
||||
// Get the ID for the script, and then construct a P2SH destination for it.
|
||||
scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
|
||||
scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
|
||||
}
|
||||
|
||||
// construct TxOut, append to transaction output list
|
||||
CTxOut txout(value, scriptPubKey);
|
||||
CTxOut txout(Params().GetConsensus().pegged_asset, CConfidentialValue(value), scriptPubKey);
|
||||
tx.vout.push_back(txout);
|
||||
}
|
||||
|
||||
|
|
@ -399,11 +416,11 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s
|
|||
"redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
|
||||
}
|
||||
// Get the ID for the script, and then construct a P2SH destination for it.
|
||||
scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
|
||||
scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
|
||||
}
|
||||
|
||||
// construct TxOut, append to transaction output list
|
||||
CTxOut txout(value, scriptPubKey);
|
||||
CTxOut txout(Params().GetConsensus().pegged_asset, CConfidentialValue(value), scriptPubKey);
|
||||
tx.vout.push_back(txout);
|
||||
}
|
||||
|
||||
|
|
@ -411,26 +428,46 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strIn
|
|||
{
|
||||
CAmount value = 0;
|
||||
|
||||
// separate [VALUE:]DATA in string
|
||||
size_t pos = strInput.find(':');
|
||||
// separate [VALUE:]DATA[:ASSET] in string
|
||||
std::vector<std::string> vStrInputParts;
|
||||
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
|
||||
|
||||
if (pos==0)
|
||||
// Check that there are enough parameters
|
||||
if (vStrInputParts[0].empty())
|
||||
throw std::runtime_error("TX output value not specified");
|
||||
|
||||
if (pos != std::string::npos) {
|
||||
// Extract and validate VALUE
|
||||
value = ExtractAndValidateValue(strInput.substr(0, pos));
|
||||
if (vStrInputParts.size()>3)
|
||||
throw std::runtime_error("too many separators");
|
||||
|
||||
std::vector<unsigned char> data;
|
||||
CAsset asset(Params().GetConsensus().pegged_asset);
|
||||
|
||||
if (vStrInputParts.size()==1) {
|
||||
std::string strData = vStrInputParts[0];
|
||||
if (!IsHex(strData))
|
||||
throw std::runtime_error("invalid TX output data");
|
||||
data = ParseHex(strData);
|
||||
|
||||
} else {
|
||||
value = ExtractAndValidateValue(vStrInputParts[0]);
|
||||
std::string strData = vStrInputParts[1];
|
||||
if (!IsHex(strData))
|
||||
throw std::runtime_error("invalid TX output data");
|
||||
data = ParseHex(strData);
|
||||
|
||||
if (vStrInputParts.size()==3) {
|
||||
std::string strAsset = vStrInputParts[2];
|
||||
if (!IsHex(strAsset))
|
||||
throw std::runtime_error("invalid TX output asset type");
|
||||
|
||||
asset.SetHex(strAsset);
|
||||
if (asset.IsNull()) {
|
||||
throw std::runtime_error("invalid TX output asset type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// extract and validate DATA
|
||||
std::string strData = strInput.substr(pos + 1, std::string::npos);
|
||||
|
||||
if (!IsHex(strData))
|
||||
throw std::runtime_error("invalid TX output data");
|
||||
|
||||
std::vector<unsigned char> data = ParseHex(strData);
|
||||
|
||||
CTxOut txout(value, CScript() << OP_RETURN << data);
|
||||
CTxOut txout(asset, CConfidentialValue(value), CScript() << OP_RETURN << data);
|
||||
tx.vout.push_back(txout);
|
||||
}
|
||||
|
||||
|
|
@ -471,11 +508,11 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str
|
|||
throw std::runtime_error(strprintf(
|
||||
"redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
|
||||
}
|
||||
scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
|
||||
scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey));
|
||||
}
|
||||
|
||||
// construct TxOut, append to transaction output list
|
||||
CTxOut txout(value, scriptPubKey);
|
||||
CTxOut txout(Params().GetConsensus().pegged_asset, CConfidentialValue(value), scriptPubKey);
|
||||
tx.vout.push_back(txout);
|
||||
}
|
||||
|
||||
|
|
@ -612,7 +649,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
|
|||
newcoin.out.scriptPubKey = scriptPubKey;
|
||||
newcoin.out.nValue = 0;
|
||||
if (prevOut.exists("amount")) {
|
||||
newcoin.out.nValue = AmountFromValue(prevOut["amount"]);
|
||||
newcoin.out.nValue = CConfidentialValue(AmountFromValue(prevOut["amount"]));
|
||||
}
|
||||
newcoin.nHeight = 1;
|
||||
view.AddCoin(out, std::move(newcoin), true);
|
||||
|
|
@ -642,7 +679,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
|
|||
continue;
|
||||
}
|
||||
const CScript& prevPubKey = coin.out.scriptPubKey;
|
||||
const CAmount& amount = coin.out.nValue;
|
||||
const CConfidentialValue& amount = coin.out.nValue;
|
||||
|
||||
SignatureData sigdata = DataFromTransaction(mergedTx, i, coin.out);
|
||||
// Only sign SIGHASH_SINGLE if there's a corresponding output:
|
||||
|
|
@ -812,6 +849,16 @@ static int CommandLineRawTx(int argc, char* argv[])
|
|||
value = arg.substr(eqpos + 1);
|
||||
}
|
||||
|
||||
std::string serialization = gArgs.GetArg("-serialization", "ELEMENTS");
|
||||
if (serialization == "ELEMENTS") {
|
||||
g_con_elementsmode = true;
|
||||
} else if (serialization == "BITCOIN") {
|
||||
g_con_elementsmode = false;
|
||||
} else {
|
||||
PrintExceptionContinue(nullptr, "Invalid serialization argument");
|
||||
throw;
|
||||
}
|
||||
|
||||
MutateTx(tx, key, value);
|
||||
}
|
||||
|
||||
|
|
|
|||
199
src/blech32.cpp
Normal file
199
src/blech32.cpp
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
// Copyright (c) 2017 Pieter Wuille
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <blech32.h>
|
||||
|
||||
/*
|
||||
* IMPORTANT NOTE: Comments below may largely pertain for bech32, not blech32.
|
||||
* Some of these magic constants have changes.
|
||||
* See liquid_addr.py for compact difference from bech32
|
||||
* TODO: Update comments
|
||||
*/
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
typedef std::vector<uint8_t> data;
|
||||
|
||||
/** The Blech32 character set for encoding. */
|
||||
const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||
|
||||
/** The Blech32 character set for decoding. */
|
||||
const int8_t CHARSET_REV[128] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
|
||||
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
||||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
|
||||
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
||||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
/** Concatenate two byte arrays. */
|
||||
data Cat(data x, const data& y)
|
||||
{
|
||||
x.insert(x.end(), y.begin(), y.end());
|
||||
return x;
|
||||
}
|
||||
|
||||
/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to
|
||||
* make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher
|
||||
* bits correspond to earlier values. */
|
||||
uint32_t PolyMod(const data& v)
|
||||
{
|
||||
// The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an
|
||||
// implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) =
|
||||
// 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that
|
||||
// [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...].
|
||||
|
||||
// The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of
|
||||
// v(x) mod g(x), where g(x) is the Blech32 generator,
|
||||
// x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way
|
||||
// that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a
|
||||
// window of 1023 characters. Among the various possible BCH codes, one was selected to in
|
||||
// fact guarantee detection of up to 4 errors within a window of 89 characters.
|
||||
|
||||
// Note that the coefficients are elements of GF(32), here represented as decimal numbers
|
||||
// between {}. In this finite field, addition is just XOR of the corresponding numbers. For
|
||||
// example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires
|
||||
// treating the bits of values themselves as coefficients of a polynomial over a smaller field,
|
||||
// GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} =
|
||||
// (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a
|
||||
// = a^3 + 1 (mod a^5 + a^3 + 1) = {9}.
|
||||
|
||||
// During the course of the loop below, `c` contains the bitpacked coefficients of the
|
||||
// polynomial constructed from just the values of v that were processed so far, mod g(x). In
|
||||
// the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of
|
||||
// v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value
|
||||
// for `c`.
|
||||
uint64_t c = 1;
|
||||
for (const auto v_i : v) {
|
||||
// We want to update `c` to correspond to a polynomial with one extra term. If the initial
|
||||
// value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to
|
||||
// correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to
|
||||
// process. Simplifying:
|
||||
// c'(x) = (f(x) * x + v_i) mod g(x)
|
||||
// ((f(x) mod g(x)) * x + v_i) mod g(x)
|
||||
// (c(x) * x + v_i) mod g(x)
|
||||
// If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute
|
||||
// c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x)
|
||||
// = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x)
|
||||
// = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i
|
||||
// If we call (x^6 mod g(x)) = k(x), this can be written as
|
||||
// c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x)
|
||||
|
||||
// First, determine the value of c0:
|
||||
uint8_t c0 = c >> 55; // ELEMENTS: 25->55
|
||||
|
||||
// Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i:
|
||||
c = ((c & 0x7fffffffffffff) << 5) ^ v_i; // ELEMENTS 0x1ffffff->0x7fffffffffffff
|
||||
|
||||
// Finally, for each set bit n in c0, conditionally add {2^n}k(x):
|
||||
if (c0 & 1) c ^= 0x7d52fba40bd886; // ELEMENTS
|
||||
if (c0 & 2) c ^= 0x5e8dbf1a03950c; // ELEMENTS
|
||||
if (c0 & 4) c ^= 0x1c3a3c74072a18; // ELEMENTS
|
||||
if (c0 & 8) c ^= 0x385d72fa0e5139; // ELEMENTS
|
||||
if (c0 & 16) c ^= 0x7093e5a608865b; // ELEMENTS
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/** Convert to lower case. */
|
||||
inline unsigned char LowerCase(unsigned char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c;
|
||||
}
|
||||
|
||||
/** Expand a HRP for use in checksum computation. */
|
||||
data ExpandHRP(const std::string& hrp)
|
||||
{
|
||||
data ret;
|
||||
ret.reserve(hrp.size() + 90);
|
||||
ret.resize(hrp.size() * 2 + 1);
|
||||
for (size_t i = 0; i < hrp.size(); ++i) {
|
||||
unsigned char c = hrp[i];
|
||||
ret[i] = c >> 5;
|
||||
ret[i + hrp.size() + 1] = c & 0x1f;
|
||||
}
|
||||
ret[hrp.size()] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Verify a checksum. */
|
||||
bool VerifyChecksum(const std::string& hrp, const data& values)
|
||||
{
|
||||
// PolyMod computes what value to xor into the final values to make the checksum 0. However,
|
||||
// if we required that the checksum was 0, it would be the case that appending a 0 to a valid
|
||||
// list of values would result in a new valid list. For that reason, Blech32 requires the
|
||||
// resulting checksum to be 1 instead.
|
||||
return PolyMod(Cat(ExpandHRP(hrp), values)) == 1;
|
||||
}
|
||||
|
||||
/** Create a checksum. */
|
||||
data CreateChecksum(const std::string& hrp, const data& values)
|
||||
{
|
||||
data enc = Cat(ExpandHRP(hrp), values);
|
||||
enc.resize(enc.size() + 12); // ELEMENTS: Append 6->12 zeroes
|
||||
uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes.
|
||||
data ret(12); // ELEMENTS: 6->12
|
||||
for (size_t i = 0; i < 12; ++i) { // ELEMENTS: 6->12
|
||||
// Convert the 5-bit groups in mod to checksum values.
|
||||
ret[i] = (mod >> (5 * (11 - i))) & 31; // ELEMENTS: 5->11
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace blech32
|
||||
{
|
||||
|
||||
/** Encode a Blech32 string. */
|
||||
std::string Encode(const std::string& hrp, const data& values) {
|
||||
data checksum = CreateChecksum(hrp, values);
|
||||
data combined = Cat(values, checksum);
|
||||
std::string ret = hrp + '1';
|
||||
ret.reserve(ret.size() + combined.size());
|
||||
for (const auto c : combined) {
|
||||
ret += CHARSET[c];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Decode a Blech32 string. */
|
||||
std::pair<std::string, data> Decode(const std::string& str) {
|
||||
bool lower = false, upper = false;
|
||||
for (size_t i = 0; i < str.size(); ++i) {
|
||||
unsigned char c = str[i];
|
||||
if (c >= 'a' && c <= 'z') lower = true;
|
||||
else if (c >= 'A' && c <= 'Z') upper = true;
|
||||
else if (c < 33 || c > 126) return {};
|
||||
}
|
||||
if (lower && upper) return {};
|
||||
size_t pos = str.rfind('1');
|
||||
if (str.size() > 1000 || pos == str.npos || pos == 0 || pos + 13 > str.size()) { // ELEMENTS: 90->1000, 7->13
|
||||
return {};
|
||||
}
|
||||
data values(str.size() - 1 - pos);
|
||||
for (size_t i = 0; i < str.size() - 1 - pos; ++i) {
|
||||
unsigned char c = str[i + pos + 1];
|
||||
int8_t rev = CHARSET_REV[c];
|
||||
|
||||
if (rev == -1) {
|
||||
return {};
|
||||
}
|
||||
values[i] = rev;
|
||||
}
|
||||
std::string hrp;
|
||||
for (size_t i = 0; i < pos; ++i) {
|
||||
hrp += LowerCase(str[i]);
|
||||
}
|
||||
if (!VerifyChecksum(hrp, values)) {
|
||||
return {};
|
||||
}
|
||||
return {hrp, data(values.begin(), values.end() - 12)};
|
||||
}
|
||||
|
||||
} // namespace blech32
|
||||
30
src/blech32.h
Normal file
30
src/blech32.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) 2017 Pieter Wuille
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
// Bech32 is a string encoding format used in newer address types.
|
||||
// The output consists of a human-readable part (alphanumeric), a
|
||||
// separator character (1), and a base32 data section, the last
|
||||
// 6 characters of which are a checksum.
|
||||
//
|
||||
// For more information, see BIP 173.
|
||||
|
||||
#ifndef BITCOIN_BLECH32_H
|
||||
#define BITCOIN_BLECH32_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace blech32
|
||||
{
|
||||
|
||||
/** Encode a Bech32 string. Returns the empty string in case of failure. */
|
||||
std::string Encode(const std::string& hrp, const std::vector<uint8_t>& values);
|
||||
|
||||
/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */
|
||||
std::pair<std::string, std::vector<uint8_t>> Decode(const std::string& str);
|
||||
|
||||
} // namespace blech32
|
||||
|
||||
#endif // BITCOIN_BLECH32_H
|
||||
579
src/blind.cpp
Normal file
579
src/blind.cpp
Normal file
|
|
@ -0,0 +1,579 @@
|
|||
// Copyright (c) 2017-2019 The Elements Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <blind.h>
|
||||
|
||||
#include <hash.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <primitives/confidential.h>
|
||||
#include <issuance.h>
|
||||
#include <random.h>
|
||||
#include <util.h>
|
||||
|
||||
static secp256k1_context* secp256k1_blind_context = NULL;
|
||||
|
||||
class Blind_ECC_Init {
|
||||
public:
|
||||
Blind_ECC_Init() {
|
||||
assert(secp256k1_blind_context == NULL);
|
||||
|
||||
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
assert(ctx != NULL);
|
||||
|
||||
secp256k1_blind_context = ctx;
|
||||
}
|
||||
|
||||
~Blind_ECC_Init() {
|
||||
secp256k1_context *ctx = secp256k1_blind_context;
|
||||
secp256k1_blind_context = NULL;
|
||||
|
||||
if (ctx) {
|
||||
secp256k1_context_destroy(ctx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static Blind_ECC_Init ecc_init_on_load;
|
||||
|
||||
bool UnblindConfidentialPair(const CKey& blinding_key, const CConfidentialValue& conf_value, const CConfidentialAsset& conf_asset, const CConfidentialNonce& nonce_commitment, const CScript& committedScript, const std::vector<unsigned char>& vchRangeproof, CAmount& amount_out, uint256& blinding_factor_out, CAsset& asset_out, uint256& asset_blinding_factor_out)
|
||||
{
|
||||
if (!blinding_key.IsValid() || vchRangeproof.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
CPubKey ephemeral_key(nonce_commitment.vchCommitment);
|
||||
if (nonce_commitment.vchCommitment.size() > 0 && !ephemeral_key.IsFullyValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ECDH or not depending on if nonce commitment is non-empty
|
||||
uint256 nonce;
|
||||
bool blank_nonce = false;
|
||||
if (nonce_commitment.vchCommitment.size() > 0) {
|
||||
nonce = blinding_key.ECDH(ephemeral_key);
|
||||
CSHA256().Write(nonce.begin(), 32).Finalize(nonce.begin());
|
||||
} else {
|
||||
// Use blinding key directly, and don't commit to a scriptpubkey
|
||||
// This is used for issuance inputs.
|
||||
blank_nonce = true;
|
||||
nonce = uint256(std::vector<unsigned char>(blinding_key.begin(), blinding_key.end()));
|
||||
}
|
||||
|
||||
// API-prescribed sidechannel maximum size, though we only use 64 bytes
|
||||
unsigned char msg[4096] = {0};
|
||||
// 32 bytes of asset type, 32 bytes of asset blinding factor in sidechannel
|
||||
size_t msg_size = 64;
|
||||
|
||||
// If value is unblinded, we don't support unblinding just the asset
|
||||
if (!conf_value.IsCommitment()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Valid asset commitment?
|
||||
secp256k1_generator observed_gen;
|
||||
if (conf_asset.IsCommitment()) {
|
||||
if (secp256k1_generator_parse(secp256k1_blind_context, &observed_gen, &conf_asset.vchCommitment[0]) != 1)
|
||||
return false;
|
||||
} else if (conf_asset.IsExplicit()) {
|
||||
if (secp256k1_generator_generate(secp256k1_blind_context, &observed_gen, conf_asset.GetAsset().begin()) != 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Valid value commitment?
|
||||
secp256k1_pedersen_commitment value_commit;
|
||||
if (secp256k1_pedersen_commitment_parse(secp256k1_blind_context, &value_commit, conf_value.vchCommitment.data()) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rewind rangeproof
|
||||
uint64_t min_value, max_value, amount;
|
||||
if (!secp256k1_rangeproof_rewind(secp256k1_blind_context, blinding_factor_out.begin(), &amount, msg, &msg_size, nonce.begin(), &min_value, &max_value, &value_commit, &vchRangeproof[0], vchRangeproof.size(), (committedScript.size() && !blank_nonce)? &committedScript.front(): NULL, blank_nonce ? 0 : committedScript.size(), &observed_gen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Value sidechannel must be a transaction-valid amount (should be belt-and-suspenders check)
|
||||
if (amount > (uint64_t)MAX_MONEY || !MoneyRange((CAmount)amount)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convenience pointers to starting point of each recovered 32 byte message
|
||||
unsigned char *asset_type = msg;
|
||||
unsigned char *asset_blinder = msg+32;
|
||||
|
||||
// Asset sidechannel of asset type + asset blinder
|
||||
secp256k1_generator recalculated_gen;
|
||||
if (msg_size != 64 || secp256k1_generator_generate_blinded(secp256k1_blind_context, &recalculated_gen, asset_type, asset_blinder) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Serialize both generators then compare
|
||||
unsigned char observed_generator[33];
|
||||
unsigned char derived_generator[33];
|
||||
secp256k1_generator_serialize(secp256k1_blind_context, observed_generator, &observed_gen);
|
||||
secp256k1_generator_serialize(secp256k1_blind_context, derived_generator, &recalculated_gen);
|
||||
if (memcmp(observed_generator, derived_generator, sizeof(observed_generator))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
amount_out = (CAmount)amount;
|
||||
asset_out = CAsset(std::vector<unsigned char>(asset_type, asset_type+32));
|
||||
asset_blinding_factor_out = uint256(std::vector<unsigned char>(asset_blinder, asset_blinder+32));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create surjection proof
|
||||
bool SurjectOutput(CTxOutWitness& txoutwit, const std::vector<secp256k1_fixed_asset_tag>& surjection_targets, const std::vector<secp256k1_generator>& target_asset_generators, const std::vector<uint256 >& target_asset_blinders, const std::vector<const unsigned char*> asset_blindptrs, const secp256k1_generator& output_asset_gen, const CAsset& asset)
|
||||
{
|
||||
int ret;
|
||||
// 1 to 3 targets
|
||||
size_t nInputsToSelect = std::min((size_t)3, surjection_targets.size());
|
||||
unsigned char randseed[32];
|
||||
GetStrongRandBytes(randseed, 32);
|
||||
size_t input_index;
|
||||
secp256k1_surjectionproof proof;
|
||||
secp256k1_fixed_asset_tag tag;
|
||||
memcpy(&tag, asset.begin(), 32);
|
||||
// Find correlation between asset tag and listed input tags
|
||||
if (secp256k1_surjectionproof_initialize(secp256k1_blind_context, &proof, &input_index, &surjection_targets[0], surjection_targets.size(), nInputsToSelect, &tag, 100, randseed) == 0) {
|
||||
return false;
|
||||
}
|
||||
// Using the input chosen, build proof
|
||||
ret = secp256k1_surjectionproof_generate(secp256k1_blind_context, &proof, target_asset_generators.data(), target_asset_generators.size(), &output_asset_gen, input_index, target_asset_blinders[input_index].begin(), asset_blindptrs[asset_blindptrs.size()-1]);
|
||||
assert(ret == 1);
|
||||
// Double-check answer
|
||||
ret = secp256k1_surjectionproof_verify(secp256k1_blind_context, &proof, target_asset_generators.data(), target_asset_generators.size(), &output_asset_gen);
|
||||
assert(ret != 0);
|
||||
|
||||
// Serialize into output witness structure
|
||||
size_t output_len = secp256k1_surjectionproof_serialized_size(secp256k1_blind_context, &proof);
|
||||
txoutwit.vchSurjectionproof.resize(output_len);
|
||||
secp256k1_surjectionproof_serialize(secp256k1_blind_context, &txoutwit.vchSurjectionproof[0], &output_len, &proof);
|
||||
assert(output_len == txoutwit.vchSurjectionproof.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Creates ECDH nonce commitment using ephemeral key and output_pubkey
|
||||
uint256 GenerateOutputRangeproofNonce(CTxOut& out, const CPubKey output_pubkey)
|
||||
{
|
||||
// Generate ephemeral key for ECDH nonce generation
|
||||
CKey ephemeral_key;
|
||||
ephemeral_key.MakeNewKey(true);
|
||||
CPubKey ephemeral_pubkey = ephemeral_key.GetPubKey();
|
||||
assert(ephemeral_pubkey.size() == CConfidentialNonce::nCommittedSize);
|
||||
out.nNonce.vchCommitment.resize(ephemeral_pubkey.size());
|
||||
memcpy(&out.nNonce.vchCommitment[0], &ephemeral_pubkey[0], ephemeral_pubkey.size());
|
||||
// Generate nonce
|
||||
uint256 nonce = ephemeral_key.ECDH(output_pubkey);
|
||||
CSHA256().Write(nonce.begin(), 32).Finalize(nonce.begin());
|
||||
return nonce;
|
||||
}
|
||||
|
||||
bool GenerateRangeproof(std::vector<unsigned char>& rangeproof, const std::vector<unsigned char*>& value_blindptrs, const uint256& nonce, const CAmount amount, const CScript& scriptPubKey, const secp256k1_pedersen_commitment& value_commit, const secp256k1_generator& gen, const CAsset& asset, std::vector<const unsigned char*>& asset_blindptrs)
|
||||
{
|
||||
// Prep range proof
|
||||
size_t nRangeProofLen = 5134;
|
||||
rangeproof.resize(nRangeProofLen);
|
||||
|
||||
// Compose sidechannel message to convey asset info (ID and asset blinds)
|
||||
unsigned char asset_message[64];
|
||||
memcpy(asset_message, asset.begin(), 32);
|
||||
memcpy(asset_message+32, asset_blindptrs[asset_blindptrs.size()-1], 32);
|
||||
|
||||
// Sign rangeproof
|
||||
// If min_value is 0, scriptPubKey must be unspendable
|
||||
int res = secp256k1_rangeproof_sign(secp256k1_blind_context, rangeproof.data(), &nRangeProofLen, scriptPubKey.IsUnspendable() ? 0 : 1, &value_commit, value_blindptrs.back(), nonce.begin(), std::min(std::max((int)gArgs.GetArg("-ct_exponent", 0), -1),18), std::min(std::max((int)gArgs.GetArg("-ct_bits", 36), 1), 51), amount, asset_message, sizeof(asset_message), scriptPubKey.size() ? &scriptPubKey.front() : NULL, scriptPubKey.size(), &gen);
|
||||
rangeproof.resize(nRangeProofLen);
|
||||
return (res == 1);
|
||||
}
|
||||
|
||||
void BlindAsset(CConfidentialAsset& conf_asset, secp256k1_generator& asset_gen, const CAsset& asset, const unsigned char* asset_blindptr)
|
||||
{
|
||||
conf_asset.vchCommitment.resize(CConfidentialAsset::nCommittedSize);
|
||||
int ret = secp256k1_generator_generate_blinded(secp256k1_blind_context, &asset_gen, asset.begin(), asset_blindptr);
|
||||
assert(ret == 1);
|
||||
ret = secp256k1_generator_serialize(secp256k1_blind_context, conf_asset.vchCommitment.data(), &asset_gen);
|
||||
assert(ret != 0);
|
||||
}
|
||||
|
||||
void CreateValueCommitment(CConfidentialValue& conf_value, secp256k1_pedersen_commitment& value_commit, const unsigned char* value_blindptr, const secp256k1_generator& asset_gen, const CAmount amount)
|
||||
{
|
||||
int ret;
|
||||
conf_value.vchCommitment.resize(CConfidentialValue::nCommittedSize);
|
||||
ret = secp256k1_pedersen_commit(secp256k1_blind_context, &value_commit, value_blindptr, amount, &asset_gen);
|
||||
assert(ret != 0);
|
||||
secp256k1_pedersen_commitment_serialize(secp256k1_blind_context, conf_value.vchCommitment.data(), &value_commit);
|
||||
assert(conf_value.IsValid());
|
||||
}
|
||||
|
||||
size_t GetNumIssuances(const CTransaction& tx)
|
||||
{
|
||||
unsigned int num_issuances = 0;
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
||||
if (!tx.vin[i].assetIssuance.IsNull()) {
|
||||
if (!tx.vin[i].assetIssuance.nAmount.IsNull()) {
|
||||
num_issuances++;
|
||||
}
|
||||
if (!tx.vin[i].assetIssuance.nInflationKeys.IsNull()) {
|
||||
num_issuances++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return num_issuances;
|
||||
}
|
||||
|
||||
int BlindTransaction(std::vector<uint256 >& input_value_blinding_factors, const std::vector<uint256 >& input_asset_blinding_factors, const std::vector<CAsset >& input_assets, const std::vector<CAmount >& input_amounts, std::vector<uint256 >& out_val_blind_factors, std::vector<uint256 >& out_asset_blind_factors, const std::vector<CPubKey>& output_pubkeys, const std::vector<CKey>& issuance_blinding_privkey, const std::vector<CKey>& token_blinding_privkey, CMutableTransaction& tx, std::vector<std::vector<unsigned char> >* auxiliary_generators)
|
||||
{
|
||||
// Sanity check input data and output_pubkey size, clear other output data
|
||||
assert(tx.vout.size() >= output_pubkeys.size());
|
||||
assert(tx.vin.size()+GetNumIssuances(tx) >= issuance_blinding_privkey.size());
|
||||
assert(tx.vin.size()+GetNumIssuances(tx) >= token_blinding_privkey.size());
|
||||
out_val_blind_factors.clear();
|
||||
out_val_blind_factors.resize(tx.vout.size());
|
||||
out_asset_blind_factors.clear();
|
||||
out_asset_blind_factors.resize(tx.vout.size());
|
||||
assert(tx.vin.size() == input_value_blinding_factors.size());
|
||||
assert(tx.vin.size() == input_asset_blinding_factors.size());
|
||||
assert(tx.vin.size() == input_assets.size());
|
||||
assert(tx.vin.size() == input_amounts.size());
|
||||
if (auxiliary_generators) {
|
||||
assert(auxiliary_generators->size() >= tx.vin.size());
|
||||
}
|
||||
|
||||
std::vector<unsigned char*> value_blindptrs;
|
||||
std::vector<const unsigned char*> asset_blindptrs;
|
||||
std::vector<uint64_t> blinded_amounts;
|
||||
value_blindptrs.reserve(tx.vout.size() + tx.vin.size());
|
||||
asset_blindptrs.reserve(tx.vout.size() + tx.vin.size());
|
||||
|
||||
int ret;
|
||||
int num_blind_attempts = 0, num_issuance_blind_attempts = 0, num_blinded = 0;
|
||||
|
||||
//Surjection proof prep
|
||||
|
||||
// Needed to surj init, only matches to output asset matters, rest can be garbage
|
||||
std::vector<secp256k1_fixed_asset_tag> surjection_targets;
|
||||
|
||||
// Needed to construct the proof itself. Generators must match final transaction to be valid
|
||||
std::vector<secp256k1_generator> target_asset_generators;
|
||||
surjection_targets.resize(tx.vin.size()*3);
|
||||
target_asset_generators.resize(tx.vin.size()*3);
|
||||
|
||||
// input_asset_blinding_factors is only for inputs, not for issuances(0 by def)
|
||||
// but we need to create surjection proofs against this list so we copy and insert 0's
|
||||
// where issuances occur.
|
||||
std::vector<uint256> target_asset_blinders;
|
||||
|
||||
size_t totalTargets = 0;
|
||||
for (size_t i = 0; i < tx.vin.size(); i++) {
|
||||
// For each input we either need the asset/blinds or the generator
|
||||
if (input_assets[i].IsNull()) {
|
||||
// If non-empty generator exists, parse
|
||||
if (auxiliary_generators) {
|
||||
// Parse generator here
|
||||
ret = secp256k1_generator_parse(secp256k1_blind_context, &target_asset_generators[totalTargets], &(*auxiliary_generators)[i][0]);
|
||||
if (ret != 1) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
ret = secp256k1_generator_generate_blinded(secp256k1_blind_context, &target_asset_generators[totalTargets], input_assets[i].begin(), input_asset_blinding_factors[i].begin());
|
||||
assert(ret == 1);
|
||||
}
|
||||
memcpy(&surjection_targets[totalTargets], input_assets[i].begin(), 32);
|
||||
target_asset_blinders.push_back(input_asset_blinding_factors[i]);
|
||||
totalTargets++;
|
||||
|
||||
// Create target generators for issuances
|
||||
CAssetIssuance& issuance = tx.vin[i].assetIssuance;
|
||||
uint256 entropy;
|
||||
CAsset asset;
|
||||
CAsset token;
|
||||
if (!issuance.IsNull()) {
|
||||
if (issuance.nAmount.IsCommitment() || issuance.nInflationKeys.IsCommitment()) {
|
||||
return -1;
|
||||
}
|
||||
// New Issuance
|
||||
if (issuance.assetBlindingNonce.IsNull()) {
|
||||
bool blind_issuance = (token_blinding_privkey.size() > i && token_blinding_privkey[i].IsValid()) ? true : false;
|
||||
GenerateAssetEntropy(entropy, tx.vin[i].prevout, issuance.assetEntropy);
|
||||
CalculateAsset(asset, entropy);
|
||||
CalculateReissuanceToken(token, entropy, blind_issuance);
|
||||
} else {
|
||||
CalculateAsset(asset, issuance.assetEntropy);
|
||||
}
|
||||
|
||||
if (!issuance.nAmount.IsNull()) {
|
||||
memcpy(&surjection_targets[totalTargets], asset.begin(), 32);
|
||||
ret = secp256k1_generator_generate(secp256k1_blind_context, &target_asset_generators[totalTargets], asset.begin());
|
||||
assert(ret != 0);
|
||||
// Issuance asset cannot be blinded by definition
|
||||
target_asset_blinders.push_back(uint256());
|
||||
totalTargets++;
|
||||
}
|
||||
if (!issuance.nInflationKeys.IsNull()) {
|
||||
assert(!token.IsNull());
|
||||
memcpy(&surjection_targets[totalTargets], token.begin(), 32);
|
||||
ret = secp256k1_generator_generate(secp256k1_blind_context, &target_asset_generators[totalTargets], token.begin());
|
||||
assert(ret != 0);
|
||||
// Issuance asset cannot be blinded by definition
|
||||
target_asset_blinders.push_back(uint256());
|
||||
totalTargets++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auxiliary_generators) {
|
||||
// Process any additional targets from auxiliary_generators
|
||||
// we know nothing about it other than the generator itself
|
||||
for (size_t i = tx.vin.size(); i < auxiliary_generators->size(); i++) {
|
||||
ret = secp256k1_generator_parse(secp256k1_blind_context, &target_asset_generators[totalTargets], &(*auxiliary_generators)[i][0]);
|
||||
if (ret != 1) {
|
||||
return -1;
|
||||
}
|
||||
memset(&surjection_targets[totalTargets], 0, 32);
|
||||
target_asset_blinders.push_back(uint256());
|
||||
totalTargets++;
|
||||
}
|
||||
}
|
||||
|
||||
// Resize the target surjection lists to how many actually exist
|
||||
assert(totalTargets == target_asset_blinders.size());
|
||||
surjection_targets.resize(totalTargets);
|
||||
target_asset_generators.resize(totalTargets);
|
||||
|
||||
//Total blinded inputs that you own (that you are balancing against)
|
||||
int num_known_input_blinds = 0;
|
||||
//Number of outputs and issuances to blind
|
||||
int num_to_blind = 0;
|
||||
|
||||
// Make sure witness lengths are correct
|
||||
tx.witness.vtxoutwit.resize(tx.vout.size());
|
||||
tx.witness.vtxinwit.resize(tx.vin.size());
|
||||
|
||||
size_t txoutwitsize = tx.witness.vtxoutwit.size();
|
||||
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
|
||||
if (!input_value_blinding_factors[nIn].IsNull() || !input_asset_blinding_factors[nIn].IsNull()) {
|
||||
if (input_amounts[nIn] < 0) {
|
||||
return -1;
|
||||
}
|
||||
value_blindptrs.push_back(input_value_blinding_factors[nIn].begin());
|
||||
asset_blindptrs.push_back(input_asset_blinding_factors[nIn].begin());
|
||||
blinded_amounts.push_back(input_amounts[nIn]);
|
||||
num_known_input_blinds++;
|
||||
}
|
||||
|
||||
// Count number of issuance pseudo-inputs to blind
|
||||
CAssetIssuance& issuance = tx.vin[nIn].assetIssuance;
|
||||
if (!issuance.IsNull()) {
|
||||
// Marked for blinding
|
||||
if (issuance_blinding_privkey.size() > nIn && issuance_blinding_privkey[nIn].IsValid()) {
|
||||
if(issuance.nAmount.IsExplicit() && tx.witness.vtxinwit[nIn].vchIssuanceAmountRangeproof.empty()) {
|
||||
num_to_blind++;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (token_blinding_privkey.size() > nIn && token_blinding_privkey[nIn].IsValid()) {
|
||||
if(issuance.nInflationKeys.IsExplicit() && tx.witness.vtxinwit[nIn].vchInflationKeysRangeproof.empty()) {
|
||||
num_to_blind++;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t nOut = 0; nOut < output_pubkeys.size(); nOut++) {
|
||||
if (output_pubkeys[nOut].IsValid()) {
|
||||
// Keys must be valid and outputs completely unblinded or else call fails
|
||||
if (!output_pubkeys[nOut].IsFullyValid() ||
|
||||
(!tx.vout[nOut].nValue.IsExplicit() || !tx.vout[nOut].nAsset.IsExplicit()) ||
|
||||
(txoutwitsize > nOut && !tx.witness.vtxoutwit[nOut].IsNull())
|
||||
|| tx.vout[nOut].IsFee()) {
|
||||
return -1;
|
||||
}
|
||||
num_to_blind++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Running total of newly blinded outputs
|
||||
static const unsigned char diff_zero[32] = {0};
|
||||
assert(num_to_blind <= 10000); // More than 10k outputs? Stop spamming.
|
||||
unsigned char blind[10000][32];
|
||||
unsigned char asset_blind[10000][32];
|
||||
secp256k1_pedersen_commitment value_commit;
|
||||
secp256k1_generator asset_gen;
|
||||
CAsset asset;
|
||||
|
||||
// First blind issuance pseudo-inputs
|
||||
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
|
||||
for (size_t nPseudo = 0; nPseudo < 2; nPseudo++) {
|
||||
if ((nPseudo == 0 && issuance_blinding_privkey.size() > nIn && issuance_blinding_privkey[nIn].IsValid()) ||
|
||||
(nPseudo == 1 && token_blinding_privkey.size() > nIn && token_blinding_privkey[nIn].IsValid())) {
|
||||
num_blind_attempts++;
|
||||
num_issuance_blind_attempts++;
|
||||
CAssetIssuance& issuance = tx.vin[nIn].assetIssuance;
|
||||
// First iteration does issuance asset, second inflation keys
|
||||
CConfidentialValue& conf_value = nPseudo ? issuance.nInflationKeys : issuance.nAmount;
|
||||
if (conf_value.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
CAmount amount = conf_value.GetAmount();
|
||||
blinded_amounts.push_back(amount);
|
||||
|
||||
// Derive the asset of the issuance asset/token
|
||||
if (issuance.assetBlindingNonce.IsNull()) {
|
||||
uint256 entropy;
|
||||
GenerateAssetEntropy(entropy, tx.vin[nIn].prevout, issuance.assetEntropy);
|
||||
if (nPseudo == 0) {
|
||||
CalculateAsset(asset, entropy);
|
||||
} else {
|
||||
bool blind_issuance = (token_blinding_privkey.size() > nIn && token_blinding_privkey[nIn].IsValid()) ? true : false;
|
||||
CalculateReissuanceToken(asset, entropy, blind_issuance);
|
||||
}
|
||||
} else {
|
||||
if (nPseudo == 0) {
|
||||
CalculateAsset(asset, issuance.assetEntropy);
|
||||
} else {
|
||||
// Re-issuance only has one pseudo-input maximum
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill out the value blinders and blank asset blinder
|
||||
GetStrongRandBytes(&blind[num_blind_attempts-1][0], 32);
|
||||
// Issuances are not asset-blinded
|
||||
memset(&asset_blind[num_blind_attempts-1][0], 0, 32);
|
||||
value_blindptrs.push_back(&blind[num_blind_attempts-1][0]);
|
||||
asset_blindptrs.push_back(&asset_blind[num_blind_attempts-1][0]);
|
||||
|
||||
if (num_blind_attempts == num_to_blind) {
|
||||
// All outputs we own are unblinded, we don't support this type of blinding
|
||||
// though it is possible. No privacy gained here, incompatible with secp api
|
||||
return num_blinded;
|
||||
}
|
||||
|
||||
if (tx.witness.vtxinwit.size() <= nIn) {
|
||||
tx.witness.vtxinwit.resize(tx.vin.size());
|
||||
}
|
||||
CTxInWitness& txinwit = tx.witness.vtxinwit[nIn];
|
||||
|
||||
// Create unblinded generator. We throw away all but `asset_gen`
|
||||
CConfidentialAsset conf_asset;
|
||||
BlindAsset(conf_asset, asset_gen, asset, asset_blindptrs.back());
|
||||
|
||||
// Create value commitment
|
||||
CreateValueCommitment(conf_value, value_commit, value_blindptrs.back(), asset_gen, amount);
|
||||
|
||||
// nonce should just be blinding key
|
||||
uint256 nonce = nPseudo ? uint256(std::vector<unsigned char>(token_blinding_privkey[nIn].begin(), token_blinding_privkey[nIn].end())) : uint256(std::vector<unsigned char>(issuance_blinding_privkey[nIn].begin(), issuance_blinding_privkey[nIn].end()));
|
||||
|
||||
// Generate rangeproof, no script committed for issuances
|
||||
bool rangeresult = GenerateRangeproof((nPseudo ? txinwit.vchInflationKeysRangeproof : txinwit.vchIssuanceAmountRangeproof), value_blindptrs, nonce, amount, CScript(), value_commit, asset_gen, asset, asset_blindptrs);
|
||||
assert(rangeresult);
|
||||
|
||||
// Successfully blinded this issuance
|
||||
num_blinded++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This section of code *only* deals with unblinded outputs
|
||||
// that we want to blind
|
||||
for (size_t nOut = 0; nOut < output_pubkeys.size(); nOut++) {
|
||||
if (output_pubkeys[nOut].IsFullyValid()) {
|
||||
CTxOut& out = tx.vout[nOut];
|
||||
num_blind_attempts++;
|
||||
CConfidentialAsset& conf_asset = out.nAsset;
|
||||
CConfidentialValue& conf_value = out.nValue;
|
||||
CAmount amount = conf_value.GetAmount();
|
||||
asset = out.nAsset.GetAsset();
|
||||
blinded_amounts.push_back(conf_value.GetAmount());
|
||||
|
||||
GetStrongRandBytes(&blind[num_blind_attempts-1][0], 32);
|
||||
GetStrongRandBytes(&asset_blind[num_blind_attempts-1][0], 32);
|
||||
value_blindptrs.push_back(&blind[num_blind_attempts-1][0]);
|
||||
asset_blindptrs.push_back(&asset_blind[num_blind_attempts-1][0]);
|
||||
|
||||
// Last blinding factor r' is set as -(output's (vr + r') - input's (vr + r')).
|
||||
// Before modifying the transaction or return arguments we must
|
||||
// ensure the final blinding factor to not be its corresponding -vr (aka unblinded),
|
||||
// or 0, in the case of 0-value output, insisting on additional output to blind.
|
||||
if (num_blind_attempts == num_to_blind) {
|
||||
|
||||
// Can't successfully blind in this case, since -vr = r
|
||||
// This check is assuming blinds are generated randomly
|
||||
// Adversary would need to create all input blinds
|
||||
// therefore would already know all your summed output amount anyways.
|
||||
if (num_blind_attempts == 1 && num_known_input_blinds == 0) {
|
||||
return num_blinded;
|
||||
}
|
||||
|
||||
// Generate value we intend to insert
|
||||
ret = secp256k1_pedersen_blind_generator_blind_sum(secp256k1_blind_context, &blinded_amounts[0], &asset_blindptrs[0], &value_blindptrs[0], num_blind_attempts + num_known_input_blinds, num_issuance_blind_attempts + num_known_input_blinds);
|
||||
assert(ret);
|
||||
|
||||
// Resulting blinding factor can sometimes be 0
|
||||
// where inputs are the negations of each other
|
||||
// and the unblinded value of the output is 0.
|
||||
// e.g. 1 unblinded input to 2 blinded outputs,
|
||||
// then spent to 1 unblinded output. (vr + r')
|
||||
// becomes just (r'), if this is 0, we can just
|
||||
// abort and not blind and the math adds up.
|
||||
// Count as success(to signal caller that nothing wrong) and return early
|
||||
if (memcmp(diff_zero, &blind[num_blind_attempts-1][0], 32) == 0) {
|
||||
return ++num_blinded;
|
||||
}
|
||||
}
|
||||
|
||||
CTxOutWitness& txoutwit = tx.witness.vtxoutwit[nOut];
|
||||
|
||||
out_val_blind_factors[nOut] = uint256(std::vector<unsigned char>(value_blindptrs[value_blindptrs.size()-1], value_blindptrs[value_blindptrs.size()-1]+32));
|
||||
out_asset_blind_factors[nOut] = uint256(std::vector<unsigned char>(asset_blindptrs[asset_blindptrs.size()-1], asset_blindptrs[asset_blindptrs.size()-1]+32));
|
||||
|
||||
//Blind the asset ID
|
||||
BlindAsset(conf_asset, asset_gen, asset, asset_blindptrs.back());
|
||||
|
||||
// Create value commitment
|
||||
CreateValueCommitment(conf_value, value_commit, value_blindptrs.back(), asset_gen, amount);
|
||||
|
||||
// Generate nonce for rewind by owner
|
||||
uint256 nonce = GenerateOutputRangeproofNonce(out, output_pubkeys[nOut]);
|
||||
|
||||
// Generate rangeproof
|
||||
bool rangeresult = GenerateRangeproof(txoutwit.vchRangeproof, value_blindptrs, nonce, amount, out.scriptPubKey, value_commit, asset_gen, asset, asset_blindptrs);
|
||||
assert(rangeresult);
|
||||
|
||||
// Create surjection proof for this output
|
||||
if (!SurjectOutput(txoutwit, surjection_targets, target_asset_generators, target_asset_blinders, asset_blindptrs, asset_gen, asset)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Successfully blinded this output
|
||||
num_blinded++;
|
||||
}
|
||||
}
|
||||
|
||||
return num_blinded;
|
||||
}
|
||||
|
||||
void RawFillBlinds(CMutableTransaction& tx, std::vector<uint256>& output_value_blinds, std::vector<uint256>& output_asset_blinds, std::vector<CPubKey>& output_pubkeys) {
|
||||
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
|
||||
// Any place-holder blinding pubkeys are extracted
|
||||
if (tx.vout[nOut].nValue.IsExplicit()) {
|
||||
CPubKey pubkey(tx.vout[nOut].nNonce.vchCommitment);
|
||||
if (pubkey.IsFullyValid()) {
|
||||
output_pubkeys.push_back(pubkey);
|
||||
} else {
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
}
|
||||
}
|
||||
// No way to unblind anything, just fill out
|
||||
output_value_blinds.push_back(uint256());
|
||||
output_asset_blinds.push_back(uint256());
|
||||
}
|
||||
// We cannot unwind issuance inputs because there is no nonce placeholder for pubkeys
|
||||
}
|
||||
63
src/blind.h
Normal file
63
src/blind.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) 2017-2019 The Elements Core developers
|
||||
// // Distributed under the MIT software license, see the accompanying
|
||||
// // file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_BLIND_H
|
||||
#define BITCOIN_BLIND_H
|
||||
|
||||
#include <key.h>
|
||||
#include <pubkey.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <primitives/confidential.h>
|
||||
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_rangeproof.h>
|
||||
#include <secp256k1_surjectionproof.h>
|
||||
|
||||
//! ELEMENTS: 36-bit rangeproof size
|
||||
static const size_t DEFAULT_RANGEPROOF_SIZE = 2893;
|
||||
|
||||
/*
|
||||
* Unblind a pair of confidential asset and value.
|
||||
* Note that unblinded data will only be outputted if *BOTH* asset and value could be unblinded.
|
||||
*
|
||||
* blinding_key is used to create the nonce to rewind the rangeproof in conjunction with the nNonce commitment. In the case of a 0-length nNonce, the blinding key is directly used as the nonce.
|
||||
* Currently there is only a sidechannel message in the rangeproof so a valid rangeproof must
|
||||
* be included in the pair to recover value and asset data.
|
||||
*/
|
||||
bool UnblindConfidentialPair(const CKey& blinding_key, const CConfidentialValue& conf_value, const CConfidentialAsset& conf_asset, const CConfidentialNonce& nonce_commitment, const CScript& committedScript, const std::vector<unsigned char>& vchRangeproof, CAmount& amount_out, uint256& blinding_factor_out, CAsset& asset_out, uint256& asset_blinding_factor_out);
|
||||
|
||||
bool GenerateRangeproof(std::vector<unsigned char>& rangeproof, const std::vector<unsigned char*>& value_blindptrs, const uint256& nonce, const CAmount amount, const CScript& scriptPubKey, const secp256k1_pedersen_commitment& value_commit, const secp256k1_generator& gen, const CAsset& asset, std::vector<const unsigned char*>& asset_blindptrs);
|
||||
|
||||
bool SurjectOutput(CTxOutWitness& txoutwit, const std::vector<secp256k1_fixed_asset_tag>& surjection_targets, const std::vector<secp256k1_generator>& target_asset_generators, const std::vector<uint256 >& target_asset_blinders, const std::vector<const unsigned char*> asset_blindptrs, const secp256k1_generator& output_asset_gen, const CAsset& asset);
|
||||
|
||||
uint256 GenerateOutputRangeproofNonce(CTxOut& out, const CPubKey output_pubkey);
|
||||
|
||||
void BlindAsset(CConfidentialAsset& conf_asset, secp256k1_generator& asset_gen, const CAsset& asset, const unsigned char* asset_blindptr);
|
||||
|
||||
void CreateValueCommitment(CConfidentialValue& conf_value, secp256k1_pedersen_commitment& value_commit, const unsigned char* value_blindptr, const secp256k1_generator& asset_gen, const CAmount amount);
|
||||
|
||||
/* Returns the number of outputs that were successfully blinded.
|
||||
* In many cases a `0` can be fixed by adding an additional output.
|
||||
* @param[in] input_blinding_factors - A vector of input blinding factors that will be used to create the balanced output blinding factors
|
||||
* @param[in] input_asset_blinding_factors - A vector of input asset blinding factors that will be used to create the balanced output blinding factors
|
||||
* @param[in] input_assets - the asset of each corresponding input
|
||||
* @param[in] input_amounts - the unblinded amounts of each input. Required for owned blinded inputs
|
||||
* @param[in/out] output_blinding_factors - A vector of blinding factors. New blinding factors will replace these values.
|
||||
* @param[in/out] output_asset_blinding_factors - A vector of asset blinding factors. New blinding factors will replace these values.
|
||||
* @param[in] output_pubkeys - If valid, corresponding output must be unblinded, and will result in fully blinded output, modifying the output blinding arguments as well.
|
||||
* @param[in] vBlindIssuanceAsset - List of keys to use as nonces for issuance asset blinding.
|
||||
* @param[in] vBlindIssuanceToken - List of keys to use as nonces for issuance token blinding.
|
||||
* @param[in/out] tx - The transaction to be modified.
|
||||
* @param[in] auxiliary_generators - a list of generators to create surjection proofs when inputs are not owned by caller. Passing in non-empty elements results in ignoring of other input arguments for that index
|
||||
*/
|
||||
int BlindTransaction(std::vector<uint256 >& input_value_blinding_factors, const std::vector<uint256 >& input_asset_blinding_factors, const std::vector<CAsset >& input_assets, const std::vector<CAmount >& input_amounts, std::vector<uint256 >& out_val_blind_factors, std::vector<uint256 >& out_asset_blind_factors, const std::vector<CPubKey>& output_pubkeys, const std::vector<CKey>& issuance_blinding_privkey, const std::vector<CKey>& token_blinding_privkey, CMutableTransaction& tx, std::vector<std::vector<unsigned char> >* auxiliary_generators = nullptr);
|
||||
|
||||
/*
|
||||
* Extract pubkeys from nonce commitment placeholders, fill out vector of blank output blinding data
|
||||
*/
|
||||
void RawFillBlinds(CMutableTransaction& tx, std::vector<uint256>& output_value_blinds, std::vector<uint256>& output_asset_blinds, std::vector<CPubKey>& output_pubkeys);
|
||||
|
||||
size_t GetNumIssuances(const CTransaction& tx);
|
||||
|
||||
#endif //BITCOIN_BLIND_H
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <chainparamsseeds.h>
|
||||
#include <consensus/merkle.h>
|
||||
#include <issuance.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util.h>
|
||||
|
|
@ -47,10 +48,8 @@ static CBlock CreateGenesisBlock(const Consensus::Params& params, const CScript&
|
|||
CMutableTransaction txNew;
|
||||
txNew.nVersion = 1;
|
||||
txNew.vin.resize(1);
|
||||
txNew.vout.resize(1);
|
||||
txNew.vin[0].scriptSig = genesisScriptSig;
|
||||
txNew.vout[0].nValue = genesisReward;
|
||||
txNew.vout[0].scriptPubKey = genesisOutputScript;
|
||||
txNew.vout.push_back(CTxOut(CAsset(), genesisReward, genesisOutputScript));
|
||||
|
||||
CBlock genesis;
|
||||
genesis.nTime = nTime;
|
||||
|
|
@ -85,23 +84,6 @@ static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits
|
|||
return CreateGenesisBlock(params, genesisScriptSig, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward);
|
||||
}
|
||||
|
||||
/** Add an issuance transaction to the genesis block. Typically used to pre-issue
|
||||
* the policyAsset of a blockchain. The genesis block is not actually validated,
|
||||
* so this transaction simply has to match issuance structure. */
|
||||
static void AppendInitialIssuance(CBlock& genesis_block, const COutPoint& prevout, const int64_t asset_values, const CScript& issuance_destination) {
|
||||
|
||||
// Note: Genesis block isn't actually validated, outputs are entered into utxo db only
|
||||
CMutableTransaction txNew;
|
||||
txNew.nVersion = 1;
|
||||
txNew.vin.resize(1);
|
||||
txNew.vin[0].prevout = prevout;
|
||||
|
||||
txNew.vout.push_back(CTxOut(asset_values, issuance_destination));
|
||||
|
||||
genesis_block.vtx.push_back(MakeTransactionRef(std::move(txNew)));
|
||||
genesis_block.hashMerkleRoot = BlockMerkleRoot(genesis_block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main network
|
||||
*/
|
||||
|
|
@ -144,11 +126,14 @@ public:
|
|||
|
||||
consensus.genesis_subsidy = 50*COIN;
|
||||
consensus.connect_genesis_outputs = false;
|
||||
consensus.subsidy_asset = CAsset();
|
||||
anyonecanspend_aremine = false;
|
||||
enforce_pak = false;
|
||||
multi_data_permitted = false;
|
||||
consensus.has_parent_chain = false;
|
||||
g_signed_blocks = false;
|
||||
g_con_elementsmode = false;
|
||||
g_con_blockheightinheader = false;
|
||||
|
||||
/**
|
||||
* The message start string is designed to be unlikely to occur in normal data.
|
||||
|
|
@ -187,6 +172,7 @@ public:
|
|||
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4};
|
||||
|
||||
bech32_hrp = "bc";
|
||||
blech32_hrp = blech32_hrp;
|
||||
|
||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
|
||||
|
||||
|
|
@ -266,10 +252,14 @@ public:
|
|||
|
||||
consensus.genesis_subsidy = 50*COIN;
|
||||
consensus.connect_genesis_outputs = false;
|
||||
consensus.subsidy_asset = CAsset();
|
||||
anyonecanspend_aremine = false;
|
||||
enforce_pak = false;
|
||||
multi_data_permitted = false;
|
||||
consensus.has_parent_chain = false;
|
||||
g_signed_blocks = false;
|
||||
g_con_elementsmode = false;
|
||||
g_con_blockheightinheader = false;
|
||||
|
||||
pchMessageStart[0] = 0x0b;
|
||||
pchMessageStart[1] = 0x11;
|
||||
|
|
@ -298,6 +288,7 @@ public:
|
|||
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
|
||||
|
||||
bech32_hrp = "tb";
|
||||
blech32_hrp = blech32_hrp;
|
||||
|
||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
|
||||
|
||||
|
|
@ -362,10 +353,14 @@ public:
|
|||
|
||||
consensus.genesis_subsidy = 50*COIN;
|
||||
consensus.connect_genesis_outputs = false;
|
||||
consensus.subsidy_asset = CAsset();
|
||||
anyonecanspend_aremine = false;
|
||||
enforce_pak = false;
|
||||
multi_data_permitted = false;
|
||||
consensus.has_parent_chain = false;
|
||||
g_signed_blocks = false;
|
||||
g_con_elementsmode = false;
|
||||
g_con_blockheightinheader = false;
|
||||
|
||||
pchMessageStart[0] = 0xfa;
|
||||
pchMessageStart[1] = 0xbf;
|
||||
|
|
@ -407,6 +402,7 @@ public:
|
|||
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
|
||||
|
||||
bech32_hrp = "bcrt";
|
||||
blech32_hrp = blech32_hrp;
|
||||
|
||||
/* enable fallback fee on regtest */
|
||||
m_fallback_fee_enabled = true;
|
||||
|
|
@ -486,6 +482,7 @@ class CCustomParams : public CRegTestParams {
|
|||
m_fallback_fee_enabled = args.GetBoolArg("-fallback_fee_enabled", m_fallback_fee_enabled);
|
||||
|
||||
bech32_hrp = args.GetArg("-bech32_hrp", bech32_hrp);
|
||||
blech32_hrp = args.GetArg("-blech32_hrp", "el");
|
||||
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1, args.GetArg("-pubkeyprefix", 111));
|
||||
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1, args.GetArg("-scriptprefix", 196));
|
||||
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1, args.GetArg("-secretprefix", 239));
|
||||
|
|
@ -527,7 +524,7 @@ class CCustomParams : public CRegTestParams {
|
|||
// Note: These globals are needed to avoid circular dependencies.
|
||||
// Default to true for custom chains.
|
||||
g_con_blockheightinheader = args.GetBoolArg("-con_blockheightinheader", true);
|
||||
g_con_elementswitness = args.GetBoolArg("-con_elementswitness", true);
|
||||
g_con_elementsmode = args.GetBoolArg("-con_elementsmode", true);
|
||||
|
||||
// No subsidy for custom chains by default
|
||||
consensus.genesis_subsidy = args.GetArg("-con_blocksubsidy", 0);
|
||||
|
|
@ -565,6 +562,30 @@ class CCustomParams : public CRegTestParams {
|
|||
base58Prefixes[PARENT_PUBKEY_ADDRESS] = std::vector<unsigned char>(1, args.GetArg("-parentpubkeyprefix", 111));
|
||||
base58Prefixes[PARENT_SCRIPT_ADDRESS] = std::vector<unsigned char>(1, args.GetArg("-parentscriptprefix", 196));
|
||||
parent_bech32_hrp = args.GetArg("-parent_bech32_hrp", "bcrt");
|
||||
parent_blech32_hrp = args.GetArg("-parent_bech32_hrp", "bcrt");
|
||||
|
||||
base58Prefixes[BLINDED_ADDRESS] = std::vector<unsigned char>(1, args.GetArg("-blindedprefix", 4));
|
||||
|
||||
// Calculate pegged Bitcoin asset
|
||||
std::vector<unsigned char> commit = CommitToArguments(consensus, strNetworkID);
|
||||
uint256 entropy;
|
||||
GenerateAssetEntropy(entropy, COutPoint(uint256(commit), 0), parentGenesisBlockHash);
|
||||
|
||||
// Elements serialization uses derivation, bitcoin serialization uses 0x00
|
||||
if (g_con_elementsmode) {
|
||||
CalculateAsset(consensus.pegged_asset, entropy);
|
||||
} else {
|
||||
assert(consensus.pegged_asset == CAsset());
|
||||
}
|
||||
|
||||
consensus.parent_pegged_asset.SetHex(args.GetArg("-con_parent_pegged_asset", "0x00"));
|
||||
initial_reissuance_tokens = args.GetArg("-initialreissuancetokens", 0);
|
||||
|
||||
// Subsidy asset, like policyAsset, defaults to the pegged_asset
|
||||
consensus.subsidy_asset = consensus.pegged_asset;
|
||||
if (gArgs.IsArgSet("-subsidyasset")) {
|
||||
consensus.subsidy_asset = CAsset(uint256S(gArgs.GetArg("-subsidyasset", "0x00")));
|
||||
}
|
||||
|
||||
// END ELEMENTS fields
|
||||
|
||||
|
|
@ -583,8 +604,8 @@ class CCustomParams : public CRegTestParams {
|
|||
// Intended compatibility with Liquid v1 and elements-0.14.1
|
||||
std::vector<unsigned char> commit = CommitToArguments(consensus, strNetworkID);
|
||||
genesis = CreateGenesisBlock(consensus, CScript(commit), CScript(OP_RETURN), 1296688602, 2, 0x207fffff, 1, 0);
|
||||
if (initialFreeCoins != 0) {
|
||||
AppendInitialIssuance(genesis, COutPoint(uint256(commit), 0), initialFreeCoins, CScript() << OP_TRUE);
|
||||
if (initialFreeCoins != 0 || initial_reissuance_tokens != 0) {
|
||||
AppendInitialIssuance(genesis, COutPoint(uint256(commit), 0), parentGenesisBlockHash, (initialFreeCoins > 0) ? 1 : 0, initialFreeCoins, (initial_reissuance_tokens > 0) ? 1 : 0, initial_reissuance_tokens, CScript() << OP_TRUE);
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error(strprintf("Invalid -genesis_style (%s)", consensus.genesis_style));
|
||||
|
|
@ -601,6 +622,145 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Liquid v1
|
||||
*/
|
||||
class CLiquidV1Params : public CChainParams {
|
||||
public:
|
||||
CLiquidV1Params()
|
||||
{
|
||||
|
||||
strNetworkID = "liquidv1";
|
||||
consensus.nSubsidyHalvingInterval = 150;
|
||||
consensus.BIP16Exception = uint256();
|
||||
consensus.BIP34Height = 0;
|
||||
consensus.BIP34Hash = uint256();
|
||||
consensus.BIP65Height = 0;
|
||||
consensus.BIP66Height = 0;
|
||||
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
||||
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks;
|
||||
consensus.nPowTargetSpacing = 60; // Minute block assumption
|
||||
consensus.fPowAllowMinDifficultyBlocks = true;
|
||||
consensus.fPowNoRetargeting = true;
|
||||
consensus.nRuleChangeActivationThreshold = 108;
|
||||
consensus.nMinerConfirmationWindow = 144;
|
||||
|
||||
consensus.nMinimumChainWork = uint256();
|
||||
consensus.defaultAssumeValid = uint256();
|
||||
|
||||
nPruneAfterHeight = 1000;
|
||||
fDefaultConsistencyChecks = false;
|
||||
fRequireStandard = true;
|
||||
fMineBlocksOnDemand = false;
|
||||
m_fallback_fee_enabled = false; // TODO Will this break stuff?
|
||||
|
||||
bech32_hrp = "ex"; // ex(plicit)
|
||||
blech32_hrp = "lq"; // l(i)q(uid)
|
||||
parent_bech32_hrp = "bc";
|
||||
parent_blech32_hrp = "bc"; // Doesn't exist but...
|
||||
|
||||
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1, 57);
|
||||
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1, 39);
|
||||
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1, 128);
|
||||
base58Prefixes[BLINDED_ADDRESS]= std::vector<unsigned char>(1,12);
|
||||
|
||||
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E};
|
||||
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4};
|
||||
|
||||
base58Prefixes[PARENT_PUBKEY_ADDRESS] = std::vector<unsigned char>(1,0);
|
||||
base58Prefixes[PARENT_SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5);
|
||||
|
||||
pchMessageStart[0] = 0xfa;
|
||||
pchMessageStart[1] = 0xbf;
|
||||
pchMessageStart[2] = 0xb5;
|
||||
pchMessageStart[3] = 0xda;
|
||||
|
||||
nDefaultPort = 7042;
|
||||
|
||||
vSeeds.clear();
|
||||
vSeeds.emplace_back("seed.liquidnetwork.io");
|
||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_liquidv1, pnSeed6_liquidv1 + ARRAYLEN(pnSeed6_liquidv1));
|
||||
|
||||
//
|
||||
// ELEMENTS fields
|
||||
|
||||
consensus.genesis_style = "elements"; // unused here but let's set it anyways
|
||||
|
||||
// Block signing encumberance script, default of 51 aka OP_TRUE
|
||||
std::vector<unsigned char> sign_bytes = ParseHex("5b21026a2a106ec32c8a1e8052e5d02a7b0a150423dbd9b116fc48d46630ff6e6a05b92102791646a8b49c2740352b4495c118d876347bf47d0551c01c4332fdc2df526f1a2102888bda53a424466b0451627df22090143bbf7c060e9eacb1e38426f6b07f2ae12102aee8967150dee220f613de3b239320355a498808084a93eaf39a34dcd62024852102d46e9259d0a0bb2bcbc461a3e68f34adca27b8d08fbe985853992b4b104e27412102e9944e35e5750ab621e098145b8e6cf373c273b7c04747d1aa020be0af40ccd62102f9a9d4b10a6d6c56d8c955c547330c589bb45e774551d46d415e51cd9ad5116321033b421566c124dfde4db9defe4084b7aa4e7f36744758d92806b8f72c2e943309210353dcc6b4cf6ad28aceb7f7b2db92a4bf07ac42d357adf756f3eca790664314b621037f55980af0455e4fb55aad9b85a55068bb6dc4740ea87276dc693f4598db45fa210384001daa88dabd23db878dbb1ce5b4c2a5fa72c3113e3514bf602325d0c37b8e21039056d089f2fe72dbc0a14780b4635b0dc8a1b40b7a59106325dd1bc45cc70493210397ab8ea7b0bf85bc7fc56bb27bf85e75502e94e76a6781c409f3f2ec3d1122192103b00e3b5b77884bf3cae204c4b4eac003601da75f96982ffcb3dcb29c5ee419b92103c1f3c0874cfe34b8131af34699589aacec4093399739ae352e8a46f80a6f68375fae");
|
||||
consensus.signblockscript = CScript(sign_bytes.begin(), sign_bytes.end());
|
||||
consensus.max_block_signature_size = 12*74; // 11 signatures plus wiggle room
|
||||
g_signed_blocks = true;
|
||||
|
||||
g_con_blockheightinheader = true;
|
||||
g_con_elementsmode = true;
|
||||
|
||||
consensus.genesis_subsidy = 0;
|
||||
|
||||
// All non-zero coinbase outputs must go to this scriptPubKey
|
||||
std::vector<unsigned char> man_bytes = ParseHex("76a914fc26751a5025129a2fd006c6fbfa598ddd67f7e188ac");
|
||||
consensus.mandatory_coinbase_destination = CScript(man_bytes.begin(), man_bytes.end()); // Blank script allows any coinbase destination
|
||||
|
||||
// Custom chains connect coinbase outputs to db by default
|
||||
consensus.connect_genesis_outputs = true;
|
||||
|
||||
initialFreeCoins = 0;
|
||||
|
||||
anyonecanspend_aremine = false;
|
||||
|
||||
consensus.has_parent_chain = true;
|
||||
|
||||
enforce_pak = true;
|
||||
|
||||
multi_data_permitted = true;
|
||||
|
||||
parentGenesisBlockHash = uint256S("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
|
||||
const bool parent_genesis_is_null = parentGenesisBlockHash == uint256();
|
||||
assert(consensus.has_parent_chain != parent_genesis_is_null);
|
||||
consensus.parentChainPowLimit = uint256S("0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff");
|
||||
consensus.parent_chain_signblockscript = CScript(); // It has PoW
|
||||
consensus.pegin_min_depth = 100;
|
||||
|
||||
const CScript default_script(CScript() << OP_TRUE);
|
||||
consensus.fedpegScript = StrHexToScriptWithDefault("745c87635b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc40102102f8a00b269f8c5e59c67d36db3cdc11b11b21f64b4bffb2815e9100d9aa8daf072103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5f6702c00fb275522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb5368ae", default_script);
|
||||
|
||||
|
||||
// Calculate pegged Bitcoin asset
|
||||
std::vector<unsigned char> commit = CommitToArguments(consensus, strNetworkID);
|
||||
uint256 entropy;
|
||||
GenerateAssetEntropy(entropy, COutPoint(uint256(commit), 0), parentGenesisBlockHash);
|
||||
|
||||
// Elements serialization uses derivation, bitcoin serialization uses 0x00
|
||||
if (g_con_elementsmode) {
|
||||
CalculateAsset(consensus.pegged_asset, entropy);
|
||||
} else {
|
||||
assert(consensus.pegged_asset == CAsset());
|
||||
}
|
||||
|
||||
consensus.parent_pegged_asset.SetHex("0x00"); // No parent pegged asset
|
||||
initial_reissuance_tokens = 0;
|
||||
|
||||
consensus.subsidy_asset = consensus.pegged_asset;
|
||||
|
||||
// CSV always active
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
||||
// Segwit, likewise
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
||||
|
||||
|
||||
// Finally, create genesis block
|
||||
genesis = CreateGenesisBlock(consensus, CScript(commit), CScript(OP_RETURN), 1296688602, 2, 0x207fffff, 1, 0);
|
||||
consensus.hashGenesisBlock = genesis.GetHash();
|
||||
assert(consensus.hashGenesisBlock.GetHex() == "1466275836220db2944ca059a3a10ef6fd2ea684b0688d2c379296888a206003");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
static std::unique_ptr<const CChainParams> globalChainParams;
|
||||
|
||||
const CChainParams &Params() {
|
||||
|
|
@ -617,6 +777,8 @@ std::unique_ptr<const CChainParams> CreateChainParams(const std::string& chain)
|
|||
return std::unique_ptr<CChainParams>(new CTestNetParams());
|
||||
else if (chain == CBaseChainParams::REGTEST)
|
||||
return std::unique_ptr<CChainParams>(new CRegTestParams(gArgs));
|
||||
else if (chain == CBaseChainParams::LIQUID1)
|
||||
return std::unique_ptr<CChainParams>(new CLiquidV1Params());
|
||||
|
||||
return std::unique_ptr<CChainParams>(new CCustomParams(chain, gArgs));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ public:
|
|||
EXT_PUBLIC_KEY,
|
||||
EXT_SECRET_KEY,
|
||||
// ELEMENTS
|
||||
BLINDED_ADDRESS,
|
||||
PARENT_PUBKEY_ADDRESS,
|
||||
PARENT_SCRIPT_ADDRESS,
|
||||
|
||||
|
|
@ -80,6 +81,7 @@ public:
|
|||
const std::vector<std::string>& DNSSeeds() const { return vSeeds; }
|
||||
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
|
||||
const std::string& Bech32HRP() const { return bech32_hrp; }
|
||||
const std::string& Blech32HRP() const { return blech32_hrp; }
|
||||
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
|
||||
const CCheckpointData& Checkpoints() const { return checkpointData; }
|
||||
const ChainTxData& TxData() const { return chainTxData; }
|
||||
|
|
@ -87,6 +89,7 @@ public:
|
|||
const uint256 ParentGenesisBlockHash() const { return parentGenesisBlockHash; }
|
||||
bool anyonecanspend_aremine;
|
||||
const std::string& ParentBech32HRP() const { return parent_bech32_hrp; }
|
||||
const std::string& ParentBlech32HRP() const { return parent_blech32_hrp; }
|
||||
bool GetEnforcePak() const { return enforce_pak; }
|
||||
bool GetMultiDataPermitted() const { return multi_data_permitted; }
|
||||
|
||||
|
|
@ -100,9 +103,11 @@ protected:
|
|||
std::vector<std::string> vSeeds;
|
||||
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
|
||||
std::string bech32_hrp;
|
||||
std::string blech32_hrp;
|
||||
std::string strNetworkID;
|
||||
CBlock genesis;
|
||||
CAmount initialFreeCoins;
|
||||
CAmount initial_reissuance_tokens;
|
||||
std::vector<SeedSpec6> vFixedSeeds;
|
||||
bool fDefaultConsistencyChecks;
|
||||
bool fRequireStandard;
|
||||
|
|
@ -113,6 +118,7 @@ protected:
|
|||
// ELEMENTS extra fields:
|
||||
uint256 parentGenesisBlockHash;
|
||||
std::string parent_bech32_hrp;
|
||||
std::string parent_blech32_hrp;
|
||||
bool enforce_pak;
|
||||
bool multi_data_permitted;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
const std::string CBaseChainParams::MAIN = "main";
|
||||
const std::string CBaseChainParams::TESTNET = "test";
|
||||
const std::string CBaseChainParams::REGTEST = "regtest";
|
||||
const std::string CBaseChainParams::LIQUID1 = "liquidv1";
|
||||
|
||||
void SetupChainParamsBaseOptions()
|
||||
{
|
||||
|
|
@ -26,7 +27,7 @@ void SetupChainParamsBaseOptions()
|
|||
gArgs.AddArg("-con_mandatorycoinbase", "All non-zero valued coinbase outputs must go to this scriptPubKey, if set.", false, OptionsCategory::ELEMENTS);
|
||||
gArgs.AddArg("-con_blocksubsidy", "Defines the amount of block subsidy to start with, at genesis block.", false, OptionsCategory::ELEMENTS);
|
||||
gArgs.AddArg("-con_connect_coinbase", "Connect outputs in genesis block to utxo database.", false, OptionsCategory::ELEMENTS);
|
||||
gArgs.AddArg("-con_elementswitness", "Use Elements-like instead of Core-like witness encoding. This is required for CA/CT. (default: true)", false, OptionsCategory::ELEMENTS);
|
||||
gArgs.AddArg("-con_elementsmode", "Use Elements-like instead of Core-like witness encoding. This is required for CA/CT. (default: true)", false, OptionsCategory::ELEMENTS);
|
||||
gArgs.AddArg("-con_blockheightinheader", "Whether the chain includes the block height directly in the header, for easier validation of block height in low-resource environments. (default: true)", false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-con_genesis_style=<style>", "Use genesis style <style> (default: elements). Results in genesis block compatibility with various networks. Allowed values: elements, bitcoin", true, OptionsCategory::ELEMENTS);
|
||||
gArgs.AddArg("-con_signed_blocks", "Signed blockchain. Uses input of `-signblockscript` to define what signatures are necessary to solve it.", false, OptionsCategory::CHAINPARAMS);
|
||||
|
|
@ -61,9 +62,11 @@ std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain
|
|||
return MakeUnique<CBaseChainParams>("testnet3", 18332, 8332);
|
||||
else if (chain == CBaseChainParams::REGTEST)
|
||||
return MakeUnique<CBaseChainParams>("regtest", 18443, 18332);
|
||||
else if (chain == CBaseChainParams::LIQUID1)
|
||||
return MakeUnique<CBaseChainParams>("liquidv1", 7041, 8332);
|
||||
|
||||
// ELEMENTS:
|
||||
return MakeUnique<CBaseChainParams>(chain, 7041, 18332);
|
||||
return MakeUnique<CBaseChainParams>(chain, 7040, 18332);
|
||||
}
|
||||
|
||||
void SelectBaseParams(const std::string& chain)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ public:
|
|||
static const std::string MAIN;
|
||||
static const std::string TESTNET;
|
||||
static const std::string REGTEST;
|
||||
static const std::string LIQUID1;
|
||||
|
||||
const std::string& DataDir() const { return strDataDir; }
|
||||
int RPCPort() const { return nRPCPort; }
|
||||
|
|
|
|||
|
|
@ -1287,4 +1287,15 @@ static SeedSpec6 pnSeed6_test[] = {
|
|||
{{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x47,0xb1,0xe4,0x55,0xd1,0xb0,0x14,0x3f,0xb6,0xdb}, 18333},
|
||||
{{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa0,0x60,0x9e,0x46,0x54,0xdb,0x61,0x3b,0xb2,0x6f}, 18333}
|
||||
};
|
||||
|
||||
static SeedSpec6 pnSeed6_liquidv1[] = {
|
||||
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xc4,0x10,0xfe}, 7042},
|
||||
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xed,0xb0,0x3f}, 7042},
|
||||
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xed,0x51,0x0e}, 7042},
|
||||
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xed,0x93,0x15}, 7042},
|
||||
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xe3,0x5f,0x6d}, 7042},
|
||||
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xe7,0x8d,0xad}, 7042},
|
||||
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x68,0xc4,0x30,0xb8}, 7042},
|
||||
{{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xe7,0x0a,0x93}, 7042}
|
||||
};
|
||||
#endif // BITCOIN_CHAINPARAMSSEEDS_H
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ private:
|
|||
|
||||
//! The queue of elements to be processed.
|
||||
//! As the order of booleans doesn't matter, it is used as a LIFO (stack)
|
||||
std::vector<T> queue;
|
||||
std::vector<T*> queue;
|
||||
|
||||
//! The number of workers (including the master) that are idle.
|
||||
int nIdle;
|
||||
|
|
@ -66,7 +66,7 @@ private:
|
|||
bool Loop(bool fMaster = false)
|
||||
{
|
||||
boost::condition_variable& cond = fMaster ? condMaster : condWorker;
|
||||
std::vector<T> vChecks;
|
||||
std::vector<T*> vChecks;
|
||||
vChecks.reserve(nBatchSize);
|
||||
unsigned int nNow = 0;
|
||||
bool fOk = true;
|
||||
|
|
@ -105,20 +105,18 @@ private:
|
|||
// * Try to account for idle jobs which will instantly start helping.
|
||||
// * Don't do batches smaller than 1 (duh), or larger than nBatchSize.
|
||||
nNow = std::max(1U, std::min(nBatchSize, (unsigned int)queue.size() / (nTotal + nIdle + 1)));
|
||||
vChecks.resize(nNow);
|
||||
for (unsigned int i = 0; i < nNow; i++) {
|
||||
// We want the lock on the mutex to be as short as possible, so swap jobs from the global
|
||||
// queue to the local batch vector instead of copying.
|
||||
vChecks[i].swap(queue.back());
|
||||
queue.pop_back();
|
||||
}
|
||||
vChecks.clear();
|
||||
vChecks.insert(vChecks.end(), queue.end() - nNow, queue.end());
|
||||
queue.resize(queue.size() - nNow);
|
||||
// Check whether we need to do work at all
|
||||
fOk = fAllOk;
|
||||
}
|
||||
// execute work
|
||||
for (T& check : vChecks)
|
||||
for (T* check : vChecks) {
|
||||
if (fOk)
|
||||
fOk = check();
|
||||
fOk = (*check)();
|
||||
delete check;
|
||||
}
|
||||
vChecks.clear();
|
||||
} while (true);
|
||||
}
|
||||
|
|
@ -143,13 +141,10 @@ public:
|
|||
}
|
||||
|
||||
//! Add a batch of checks to the queue
|
||||
void Add(std::vector<T>& vChecks)
|
||||
void Add(const std::vector<T*> vChecks)
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
for (T& check : vChecks) {
|
||||
queue.push_back(T());
|
||||
check.swap(queue.back());
|
||||
}
|
||||
queue.insert(queue.end(), vChecks.begin(), vChecks.end());
|
||||
nTodo += vChecks.size();
|
||||
if (vChecks.size() == 1)
|
||||
condWorker.notify_one();
|
||||
|
|
@ -159,6 +154,7 @@ public:
|
|||
|
||||
~CCheckQueue()
|
||||
{
|
||||
assert(queue.empty());
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -171,14 +167,14 @@ template <typename T>
|
|||
class CCheckQueueControl
|
||||
{
|
||||
private:
|
||||
CCheckQueue<T> * const pqueue;
|
||||
CCheckQueue<T>* const pqueue;
|
||||
bool fDone;
|
||||
|
||||
public:
|
||||
CCheckQueueControl() = delete;
|
||||
CCheckQueueControl(const CCheckQueueControl&) = delete;
|
||||
CCheckQueueControl& operator=(const CCheckQueueControl&) = delete;
|
||||
explicit CCheckQueueControl(CCheckQueue<T> * const pqueueIn) : pqueue(pqueueIn), fDone(false)
|
||||
explicit CCheckQueueControl(CCheckQueue<T>* const pqueueIn) : pqueue(pqueueIn), fDone(false)
|
||||
{
|
||||
// passed queue is supposed to be unused, or nullptr
|
||||
if (pqueue != nullptr) {
|
||||
|
|
@ -195,7 +191,7 @@ public:
|
|||
return fRet;
|
||||
}
|
||||
|
||||
void Add(std::vector<T>& vChecks)
|
||||
void Add(std::vector<T*> vChecks)
|
||||
{
|
||||
if (pqueue != nullptr)
|
||||
pqueue->Add(vChecks);
|
||||
|
|
|
|||
|
|
@ -124,7 +124,16 @@ bool CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static const Coin coinEmpty;
|
||||
// ELEMENTS:
|
||||
// Because g_con_elementsmode is only set after the moment coinEmpty is initialized,
|
||||
// we have to force set it to an empty coin without the default asset commitment.
|
||||
Coin generateEmptyCoin() {
|
||||
Coin coin;
|
||||
coin.out.nValue.vchCommitment.clear();
|
||||
coin.out.nAsset.vchCommitment.clear();
|
||||
return coin;
|
||||
}
|
||||
static const Coin coinEmpty = generateEmptyCoin();
|
||||
|
||||
const Coin& CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const {
|
||||
CCoinsMap::const_iterator it = FetchCoin(outpoint);
|
||||
|
|
@ -306,7 +315,8 @@ CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const
|
|||
|
||||
CAmount nResult = 0;
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
nResult += AccessCoin(tx.vin[i].prevout).out.nValue;
|
||||
// ELEMENTS: this method is for tests only, just naively add amounts
|
||||
nResult += AccessCoin(tx.vin[i].prevout).out.nValue.GetAmount();
|
||||
|
||||
return nResult;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,14 +95,42 @@ public:
|
|||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
if (!ser_action.ForRead()) {
|
||||
uint64_t nVal = CompressAmount(txout.nValue);
|
||||
READWRITE(VARINT(nVal));
|
||||
if (g_con_elementsmode) {
|
||||
if (!ser_action.ForRead()) {
|
||||
if (txout.nValue.IsExplicit()) {
|
||||
uint8_t b = 0;
|
||||
READWRITE(b);
|
||||
uint64_t nVal = CompressAmount(txout.nValue.GetAmount());
|
||||
READWRITE(VARINT(nVal));
|
||||
} else {
|
||||
uint8_t b = 1;
|
||||
READWRITE(b);
|
||||
READWRITE(txout.nValue);
|
||||
}
|
||||
} else {
|
||||
uint8_t type = 0;
|
||||
READWRITE(type);
|
||||
if (type == 0) {
|
||||
uint64_t nVal = 0;
|
||||
READWRITE(VARINT(nVal));
|
||||
txout.nValue = DecompressAmount(nVal);
|
||||
} else {
|
||||
READWRITE(txout.nValue);
|
||||
}
|
||||
}
|
||||
READWRITE(txout.nAsset);
|
||||
} else {
|
||||
uint64_t nVal = 0;
|
||||
READWRITE(VARINT(nVal));
|
||||
txout.nValue = DecompressAmount(nVal);
|
||||
if (!ser_action.ForRead()) {
|
||||
assert(txout.nValue.IsExplicit());
|
||||
uint64_t nVal = CompressAmount(txout.nValue.GetAmount());
|
||||
READWRITE(VARINT(nVal));
|
||||
} else {
|
||||
uint64_t nVal = 0;
|
||||
READWRITE(VARINT(nVal));
|
||||
txout.nValue = DecompressAmount(nVal);
|
||||
}
|
||||
}
|
||||
|
||||
CScriptCompressor cscript(REF(txout.scriptPubKey));
|
||||
READWRITE(cscript);
|
||||
}
|
||||
|
|
|
|||
425
src/confidential_validation.cpp
Normal file
425
src/confidential_validation.cpp
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
|
||||
#include <confidential_validation.h>
|
||||
#include <issuance.h>
|
||||
#include <pegins.h>
|
||||
#include <script/sigcache.h>
|
||||
#include <blind.h>
|
||||
|
||||
namespace {
|
||||
static secp256k1_context *secp256k1_ctx_verify_amounts;
|
||||
|
||||
class CSecp256k1Init {
|
||||
public:
|
||||
CSecp256k1Init() {
|
||||
assert(secp256k1_ctx_verify_amounts == NULL);
|
||||
secp256k1_ctx_verify_amounts = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
|
||||
assert(secp256k1_ctx_verify_amounts != NULL);
|
||||
}
|
||||
~CSecp256k1Init() {
|
||||
assert(secp256k1_ctx_verify_amounts != NULL);
|
||||
secp256k1_context_destroy(secp256k1_ctx_verify_amounts);
|
||||
secp256k1_ctx_verify_amounts = NULL;
|
||||
}
|
||||
};
|
||||
static CSecp256k1Init instance_of_csecp256k1;
|
||||
}
|
||||
|
||||
bool HasValidFee(const CTransaction& tx) {
|
||||
CAmountMap totalFee;
|
||||
for (unsigned int i = 0; i < tx.vout.size(); i++) {
|
||||
CAmount fee = 0;
|
||||
if (tx.vout[i].IsFee()) {
|
||||
fee = tx.vout[i].nValue.GetAmount();
|
||||
if (fee == 0 || !MoneyRange(fee))
|
||||
return false;
|
||||
totalFee[tx.vout[i].nAsset.GetAsset()] += fee;
|
||||
}
|
||||
}
|
||||
return MoneyRange(totalFee);
|
||||
}
|
||||
|
||||
CAmountMap GetFeeMap(const CTransaction& tx) {
|
||||
CAmountMap fee;
|
||||
for (const CTxOut& txout : tx.vout) {
|
||||
if (txout.IsFee()) {
|
||||
fee[txout.nAsset.GetAsset()] += txout.nValue.GetAmount();
|
||||
}
|
||||
}
|
||||
return fee;
|
||||
}
|
||||
|
||||
bool CRangeCheck::operator()() {
|
||||
if (val->IsExplicit()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!CachingRangeProofChecker(store).VerifyRangeProof(rangeproof, val->vchCommitment, assetCommitment, scriptPubKey, secp256k1_ctx_verify_amounts)) {
|
||||
error = SCRIPT_ERR_RANGEPROOF;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
bool CBalanceCheck::operator()() {
|
||||
if (!secp256k1_pedersen_verify_tally(secp256k1_ctx_verify_amounts, vpCommitsIn.data(), vpCommitsIn.size(), vpCommitsOut.data(), vpCommitsOut.size())) {
|
||||
error = SCRIPT_ERR_PEDERSEN_TALLY;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSurjectionCheck::operator()() {
|
||||
return CachingSurjectionProofChecker(store).VerifySurjectionProof(proof, vTags, gen, secp256k1_ctx_verify_amounts, wtxid);
|
||||
}
|
||||
|
||||
// Destroys the check in the case of no queue, or passes its ownership to the queue.
|
||||
ScriptError QueueCheck(std::vector<CCheck*>* queue, CCheck* check) {
|
||||
if (queue != NULL) {
|
||||
queue->push_back(check);
|
||||
return SCRIPT_ERR_OK;
|
||||
}
|
||||
bool success = (*check)();
|
||||
ScriptError err = check->GetScriptError();
|
||||
delete check;
|
||||
return success ? SCRIPT_ERR_OK : err;
|
||||
}
|
||||
|
||||
// Helper function for VerifyAmount(), not exported
|
||||
static bool VerifyIssuanceAmount(secp256k1_pedersen_commitment& value_commit, secp256k1_generator& asset_gen,
|
||||
const CAsset& asset, const CConfidentialValue& value, const std::vector<unsigned char>& rangeproof,
|
||||
std::vector<CCheck*>* checks, const bool store_result)
|
||||
{
|
||||
// This is used to add in the explicit values
|
||||
unsigned char explicit_blinds[32];
|
||||
memset(explicit_blinds, 0, sizeof(explicit_blinds));
|
||||
int ret;
|
||||
|
||||
ret = secp256k1_generator_generate(secp256k1_ctx_verify_amounts, &asset_gen, asset.begin());
|
||||
assert(ret == 1);
|
||||
|
||||
// Build value commitment
|
||||
if (value.IsExplicit()) {
|
||||
if (!MoneyRange(value.GetAmount()) || value.GetAmount() == 0) {
|
||||
return false;
|
||||
}
|
||||
if (!rangeproof.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
ret = secp256k1_pedersen_commit(secp256k1_ctx_verify_amounts, &value_commit, explicit_blinds, value.GetAmount(), &asset_gen);
|
||||
// The explicit_blinds are all 0, and the amount is not 0. So secp256k1_pedersen_commit does not fail.
|
||||
assert(ret == 1);
|
||||
} else if (value.IsCommitment()) {
|
||||
// Verify range proof
|
||||
std::vector<unsigned char> vchAssetCommitment(CConfidentialAsset::nExplicitSize);
|
||||
secp256k1_generator_serialize(secp256k1_ctx_verify_amounts, vchAssetCommitment.data(), &asset_gen);
|
||||
if (QueueCheck(checks, new CRangeCheck(&value, rangeproof, vchAssetCommitment, CScript(), store_result)) != SCRIPT_ERR_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (secp256k1_pedersen_commitment_parse(secp256k1_ctx_verify_amounts, &value_commit, value.vchCommitment.data()) != 1) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerifyAmounts(const std::vector<CTxOut>& inputs, const CTransaction& tx, std::vector<CCheck*>* checks, const bool store_result) {
|
||||
assert(!tx.IsCoinBase());
|
||||
assert(inputs.size() == tx.vin.size());
|
||||
|
||||
std::vector<secp256k1_pedersen_commitment> vData;
|
||||
std::vector<secp256k1_pedersen_commitment *> vpCommitsIn, vpCommitsOut;
|
||||
|
||||
vData.reserve((tx.vin.size() + tx.vout.size() + GetNumIssuances(tx)));
|
||||
secp256k1_pedersen_commitment *p = vData.data();
|
||||
secp256k1_pedersen_commitment commit;
|
||||
secp256k1_generator gen;
|
||||
// This is used to add in the explicit values
|
||||
unsigned char explicit_blinds[32] = {0};
|
||||
int ret;
|
||||
|
||||
uint256 wtxid(tx.GetWitnessHash());
|
||||
|
||||
// This list is used to verify surjection proofs.
|
||||
// Proofs must be constructed with the list being in
|
||||
// order of input and non-null issuance pseudo-inputs, with
|
||||
// input first, asset issuance second, reissuance token third.
|
||||
std::vector<secp256k1_generator> target_generators;
|
||||
target_generators.reserve(tx.vin.size() + GetNumIssuances(tx));
|
||||
|
||||
// Tally up value commitments, check balance
|
||||
for (size_t i = 0; i < tx.vin.size(); ++i) {
|
||||
const CConfidentialValue& val = inputs[i].nValue;
|
||||
const CConfidentialAsset& asset = inputs[i].nAsset;
|
||||
|
||||
if (val.IsNull() || asset.IsNull())
|
||||
return false;
|
||||
|
||||
if (asset.IsExplicit()) {
|
||||
ret = secp256k1_generator_generate(secp256k1_ctx_verify_amounts, &gen, asset.GetAsset().begin());
|
||||
assert(ret != 0);
|
||||
}
|
||||
else if (asset.IsCommitment()) {
|
||||
if (secp256k1_generator_parse(secp256k1_ctx_verify_amounts, &gen, &asset.vchCommitment[0]) != 1)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
target_generators.push_back(gen);
|
||||
|
||||
if (val.IsExplicit()) {
|
||||
if (!MoneyRange(val.GetAmount()))
|
||||
return false;
|
||||
|
||||
// Fails if val.GetAmount() == 0
|
||||
if (secp256k1_pedersen_commit(secp256k1_ctx_verify_amounts, &commit, explicit_blinds, val.GetAmount(), &gen) != 1)
|
||||
return false;
|
||||
} else if (val.IsCommitment()) {
|
||||
if (secp256k1_pedersen_commitment_parse(secp256k1_ctx_verify_amounts, &commit, &val.vchCommitment[0]) != 1)
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
vData.push_back(commit);
|
||||
vpCommitsIn.push_back(p);
|
||||
p++;
|
||||
|
||||
// Each transaction input may have up to two "pseudo-inputs" to add to the LHS
|
||||
// for (re)issuance and may require up to two rangeproof checks:
|
||||
// blinded value of the new assets being made
|
||||
// blinded value of the issuance tokens being made (only for initial issuance)
|
||||
const CAssetIssuance& issuance = tx.vin[i].assetIssuance;
|
||||
|
||||
// No issuances to process, continue to next input
|
||||
if (issuance.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CAsset assetID;
|
||||
CAsset assetTokenID;
|
||||
|
||||
// First construct the assets of the issuances and reissuance token
|
||||
// These are calculated differently depending on if initial issuance or followup
|
||||
|
||||
// New issuance, compute the asset ids
|
||||
if (issuance.assetBlindingNonce.IsNull()) {
|
||||
uint256 entropy;
|
||||
GenerateAssetEntropy(entropy, tx.vin[i].prevout, issuance.assetEntropy);
|
||||
CalculateAsset(assetID, entropy);
|
||||
// Null nAmount is considered explicit 0, so just check for commitment
|
||||
CalculateReissuanceToken(assetTokenID, entropy, issuance.nAmount.IsCommitment());
|
||||
} else {
|
||||
// Re-issuance
|
||||
// hashAssetIdentifier doubles as the entropy on reissuance
|
||||
CalculateAsset(assetID, issuance.assetEntropy);
|
||||
CalculateReissuanceToken(assetTokenID, issuance.assetEntropy, issuance.nAmount.IsCommitment());
|
||||
|
||||
// Must check that prevout is the blinded issuance token
|
||||
// prevout's asset tag = assetTokenID + assetBlindingNonce
|
||||
if (secp256k1_generator_generate_blinded(secp256k1_ctx_verify_amounts, &gen, assetTokenID.begin(), issuance.assetBlindingNonce.begin()) != 1) {
|
||||
return false;
|
||||
}
|
||||
// Serialize the generator for direct comparison
|
||||
unsigned char derived_generator[33];
|
||||
secp256k1_generator_serialize(secp256k1_ctx_verify_amounts, derived_generator, &gen);
|
||||
|
||||
// Belt-and-suspenders: Check that asset commitment from issuance input is correct size
|
||||
if (asset.vchCommitment.size() != sizeof(derived_generator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We have already checked the outputs' generator commitment for general validity, so directly compare serialized bytes
|
||||
if (memcmp(asset.vchCommitment.data(), derived_generator, sizeof(derived_generator))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Process issuance of asset
|
||||
|
||||
if (!issuance.nAmount.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
if (!issuance.nAmount.IsNull()) {
|
||||
// Note: This check disallows issuances in transactions with *no* witness data.
|
||||
// This can be relaxed in a future update as a HF by passing in an empty rangeproof
|
||||
// to `VerifyIssuanceAmount` instead.
|
||||
if (i >= tx.witness.vtxinwit.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!VerifyIssuanceAmount(commit, gen, assetID, issuance.nAmount, tx.witness.vtxinwit[i].vchIssuanceAmountRangeproof, checks, store_result)) {
|
||||
return false;
|
||||
}
|
||||
target_generators.push_back(gen);
|
||||
vData.push_back(commit);
|
||||
vpCommitsIn.push_back(p);
|
||||
p++;
|
||||
}
|
||||
|
||||
// Process issuance of reissuance tokens
|
||||
|
||||
if (!issuance.nInflationKeys.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
if (!issuance.nInflationKeys.IsNull()) {
|
||||
// Only initial issuance can have reissuance tokens
|
||||
if (!issuance.assetBlindingNonce.IsNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: This check disallows issuances in transactions with *no* witness data.
|
||||
// This can be relaxed in a future update as a HF by passing in an empty rangeproof
|
||||
// to `VerifyIssuanceAmount` instead.
|
||||
if (i >= tx.witness.vtxinwit.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!VerifyIssuanceAmount(commit, gen, assetTokenID, issuance.nInflationKeys, tx.witness.vtxinwit[i].vchInflationKeysRangeproof, checks, store_result)) {
|
||||
return false;
|
||||
}
|
||||
target_generators.push_back(gen);
|
||||
vData.push_back(commit);
|
||||
vpCommitsIn.push_back(p);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
{
|
||||
const CConfidentialValue& val = tx.vout[i].nValue;
|
||||
const CConfidentialAsset& asset = tx.vout[i].nAsset;
|
||||
if (!asset.IsValid())
|
||||
return false;
|
||||
if (!val.IsValid())
|
||||
return false;
|
||||
if (!tx.vout[i].nNonce.IsValid())
|
||||
return false;
|
||||
|
||||
if (asset.IsExplicit()) {
|
||||
ret = secp256k1_generator_generate(secp256k1_ctx_verify_amounts, &gen, asset.GetAsset().begin());
|
||||
assert(ret != 0);
|
||||
}
|
||||
else if (asset.IsCommitment()) {
|
||||
if (secp256k1_generator_parse(secp256k1_ctx_verify_amounts, &gen, &asset.vchCommitment[0]) != 1)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (val.IsExplicit()) {
|
||||
if (!MoneyRange(val.GetAmount()))
|
||||
return false;
|
||||
|
||||
if (val.GetAmount() == 0) {
|
||||
if (tx.vout[i].scriptPubKey.IsUnspendable()) {
|
||||
continue;
|
||||
} else {
|
||||
// No spendable 0-value outputs
|
||||
// Reason: A spendable output of 0 reissuance tokens would allow reissuance without reissuance tokens.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ret = secp256k1_pedersen_commit(secp256k1_ctx_verify_amounts, &commit, explicit_blinds, val.GetAmount(), &gen);
|
||||
// The explicit_blinds are all 0, and the amount is not 0. So secp256k1_pedersen_commit does not fail.
|
||||
assert(ret == 1);
|
||||
}
|
||||
else if (val.IsCommitment()) {
|
||||
if (secp256k1_pedersen_commitment_parse(secp256k1_ctx_verify_amounts, &commit, &val.vchCommitment[0]) != 1)
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
vData.push_back(commit);
|
||||
vpCommitsOut.push_back(p);
|
||||
p++;
|
||||
}
|
||||
|
||||
// Check balance
|
||||
if (QueueCheck(checks, new CBalanceCheck(vData, vpCommitsIn, vpCommitsOut)) != SCRIPT_ERR_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Range proofs
|
||||
for (size_t i = 0; i < tx.vout.size(); i++) {
|
||||
const CConfidentialValue& val = tx.vout[i].nValue;
|
||||
const CConfidentialAsset& asset = tx.vout[i].nAsset;
|
||||
std::vector<unsigned char> vchAssetCommitment = asset.vchCommitment;
|
||||
const CTxOutWitness* ptxoutwit = tx.witness.vtxoutwit.size() <= i? NULL: &tx.witness.vtxoutwit[i];
|
||||
if (val.IsExplicit())
|
||||
{
|
||||
if (ptxoutwit && !ptxoutwit->vchRangeproof.empty())
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
if (asset.IsExplicit()) {
|
||||
int ret = secp256k1_generator_generate(secp256k1_ctx_verify_amounts, &gen, asset.GetAsset().begin());
|
||||
assert(ret != 0);
|
||||
secp256k1_generator_serialize(secp256k1_ctx_verify_amounts, &vchAssetCommitment[0], &gen);
|
||||
}
|
||||
if (!ptxoutwit) {
|
||||
return false;
|
||||
}
|
||||
if (QueueCheck(checks, new CRangeCheck(&val, ptxoutwit->vchRangeproof, vchAssetCommitment, tx.vout[i].scriptPubKey, store_result)) != SCRIPT_ERR_OK) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Surjection proofs
|
||||
for (size_t i = 0; i < tx.vout.size(); i++)
|
||||
{
|
||||
const CConfidentialAsset& asset = tx.vout[i].nAsset;
|
||||
const CTxOutWitness* ptxoutwit = tx.witness.vtxoutwit.size() <= i? NULL: &tx.witness.vtxoutwit[i];
|
||||
// No need for surjection proof
|
||||
if (asset.IsExplicit()) {
|
||||
if (ptxoutwit && !ptxoutwit->vchSurjectionproof.empty()) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!ptxoutwit)
|
||||
return false;
|
||||
if (secp256k1_generator_parse(secp256k1_ctx_verify_amounts, &gen, &asset.vchCommitment[0]) != 1)
|
||||
return false;
|
||||
|
||||
secp256k1_surjectionproof proof;
|
||||
if (secp256k1_surjectionproof_parse(secp256k1_ctx_verify_amounts, &proof, &ptxoutwit->vchSurjectionproof[0], ptxoutwit->vchSurjectionproof.size()) != 1)
|
||||
return false;
|
||||
|
||||
if (QueueCheck(checks, new CSurjectionCheck(proof, target_generators, gen, wtxid, store_result)) != SCRIPT_ERR_OK) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerifyCoinbaseAmount(const CTransaction& tx, const CAmountMap& mapFees) {
|
||||
assert(tx.IsCoinBase());
|
||||
CAmountMap remaining = mapFees;
|
||||
for (unsigned int i = 0; i < tx.vout.size(); i++) {
|
||||
const CTxOut& out = tx.vout[i];
|
||||
if (!out.nValue.IsExplicit() || !out.nAsset.IsExplicit()) {
|
||||
return false;
|
||||
}
|
||||
if (!MoneyRange(out.nValue.GetAmount())) {
|
||||
return false;
|
||||
}
|
||||
if (g_con_elementsmode &&
|
||||
out.nValue.GetAmount() == 0 && !out.scriptPubKey.IsUnspendable()) {
|
||||
return false;
|
||||
}
|
||||
remaining[out.nAsset.GetAsset()] -= out.nValue.GetAmount();
|
||||
}
|
||||
return MoneyRange(remaining);
|
||||
}
|
||||
97
src/confidential_validation.h
Normal file
97
src/confidential_validation.h
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
|
||||
#ifndef BITCOIN_CONFIDENTIAL_VALIDATION_H
|
||||
#define BITCOIN_CONFIDENTIAL_VALIDATION_H
|
||||
|
||||
#include <amount.h>
|
||||
#include <asset.h>
|
||||
#include <coins.h>
|
||||
#include <primitives/confidential.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <script/script_error.h>
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_rangeproof.h>
|
||||
#include <secp256k1_surjectionproof.h>
|
||||
#include <uint256.h>
|
||||
|
||||
|
||||
// Check if explicit TX fees overflow or are negative
|
||||
bool HasValidFee(const CTransaction& tx);
|
||||
|
||||
// Compute the fee from the explicit fee outputs. Must call HasValidFee first
|
||||
CAmountMap GetFeeMap(const CTransaction& tx);
|
||||
|
||||
/**
|
||||
* ELEMENTS:
|
||||
* Closure representing one verification, either script or range checks.
|
||||
* Note that this stores references to the spending transaction
|
||||
*/
|
||||
class CCheck
|
||||
{
|
||||
protected:
|
||||
ScriptError error;
|
||||
|
||||
public:
|
||||
CCheck() : error(SCRIPT_ERR_UNKNOWN_ERROR) {}
|
||||
virtual ~CCheck() {}
|
||||
|
||||
virtual bool operator()() = 0;
|
||||
|
||||
ScriptError GetScriptError() const { return error; }
|
||||
};
|
||||
|
||||
/** Closure representing one output range check. */
|
||||
class CRangeCheck : public CCheck
|
||||
{
|
||||
private:
|
||||
const CConfidentialValue* val;
|
||||
const std::vector<unsigned char>& rangeproof;
|
||||
// *Must* be a commitment, not an explicit value
|
||||
const std::vector<unsigned char> assetCommitment;
|
||||
const CScript scriptPubKey;
|
||||
const bool store;
|
||||
|
||||
public:
|
||||
CRangeCheck(const CConfidentialValue* val_, const std::vector<unsigned char>& rangeproof_, const std::vector<unsigned char>& assetCommitment_, const CScript& scriptPubKey_, const bool storeIn) : val(val_), rangeproof(rangeproof_), assetCommitment(assetCommitment_), scriptPubKey(scriptPubKey_), store(storeIn) {}
|
||||
|
||||
bool operator()();
|
||||
};
|
||||
|
||||
/** Closure representing a transaction amount balance check. */
|
||||
class CBalanceCheck : public CCheck
|
||||
{
|
||||
private:
|
||||
std::vector<secp256k1_pedersen_commitment> vData;
|
||||
std::vector<secp256k1_pedersen_commitment *> vpCommitsIn, vpCommitsOut;
|
||||
|
||||
public:
|
||||
CBalanceCheck(std::vector<secp256k1_pedersen_commitment>& vData_, std::vector<secp256k1_pedersen_commitment*>& vpCommitsIn_, std::vector<secp256k1_pedersen_commitment*>& vpCommitsOut_) {
|
||||
vData.swap(vData_);
|
||||
vpCommitsIn.swap(vpCommitsIn_);
|
||||
vpCommitsOut.swap(vpCommitsOut_);
|
||||
}
|
||||
|
||||
bool operator()();
|
||||
};
|
||||
|
||||
class CSurjectionCheck : public CCheck
|
||||
{
|
||||
private:
|
||||
secp256k1_surjectionproof proof;
|
||||
std::vector<secp256k1_generator> vTags;
|
||||
secp256k1_generator gen;
|
||||
uint256 wtxid;
|
||||
const bool store;
|
||||
public:
|
||||
CSurjectionCheck(secp256k1_surjectionproof& proof_in, std::vector<secp256k1_generator>& tags_in, secp256k1_generator& gen_in, uint256& wtxid_in, const bool store_in) : proof(proof_in), vTags(tags_in), gen(gen_in), wtxid(wtxid_in), store(store_in) {}
|
||||
|
||||
bool operator()();
|
||||
};
|
||||
|
||||
ScriptError QueueCheck(std::vector<CCheck*>* queue, CCheck* check);
|
||||
|
||||
bool VerifyAmounts(const std::vector<CTxOut>& inputs, const CTransaction& tx, std::vector<CCheck*>* pvChecks, const bool cacheStore);
|
||||
|
||||
bool VerifyCoinbaseAmount(const CTransaction& tx, const CAmountMap& mapFees);
|
||||
|
||||
|
||||
#endif // BITCOIN_CONFIDENTIAL_VALIDATION_H
|
||||
|
|
@ -77,13 +77,17 @@ uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated)
|
|||
{
|
||||
std::vector<uint256> leaves;
|
||||
leaves.resize(block.vtx.size());
|
||||
leaves[0].SetNull(); // The witness hash of the coinbase is 0.
|
||||
for (size_t s = 1; s < block.vtx.size(); s++) {
|
||||
if (g_con_elementswitness) {
|
||||
if (g_con_elementsmode) {
|
||||
// Coinbase witness hash for inputs is just CTxInWitness().GetHash()
|
||||
for (size_t s = 0; s < block.vtx.size(); s++) {
|
||||
leaves[s] = block.vtx[s]->GetWitnessOnlyHash();
|
||||
} else {
|
||||
}
|
||||
return ComputeFastMerkleRoot(std::move(leaves));
|
||||
} else {
|
||||
leaves[0].SetNull(); // The witness hash of the coinbase is 0.
|
||||
for (size_t s = 1; s < block.vtx.size(); s++) {
|
||||
leaves[s] = block.vtx[s]->GetWitnessHash();
|
||||
}
|
||||
return ComputeMerkleRoot(std::move(leaves), mutated);
|
||||
}
|
||||
return ComputeMerkleRoot(std::move(leaves), mutated);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#ifndef BITCOIN_CONSENSUS_PARAMS_H
|
||||
#define BITCOIN_CONSENSUS_PARAMS_H
|
||||
|
||||
#include <asset.h>
|
||||
#include <uint256.h>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
|
|
@ -83,6 +84,7 @@ struct Params {
|
|||
// ELEMENTS CHAIN PARAMS
|
||||
CScript mandatory_coinbase_destination;
|
||||
CAmount genesis_subsidy;
|
||||
CAsset subsidy_asset;
|
||||
bool connect_genesis_outputs;
|
||||
bool has_parent_chain;
|
||||
uint256 parentChainPowLimit;
|
||||
|
|
@ -90,6 +92,8 @@ struct Params {
|
|||
CScript parent_chain_signblockscript;
|
||||
bool ParentChainHasPow() const { return parent_chain_signblockscript == CScript();}
|
||||
CScript fedpegScript;
|
||||
CAsset pegged_asset;
|
||||
CAsset parent_pegged_asset;
|
||||
// g_con_blockheightinheader global hack instead of proper arg due to circular dep
|
||||
std::string genesis_style;
|
||||
CScript signblockscript;
|
||||
|
|
|
|||
|
|
@ -190,15 +190,19 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
|
|||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
|
||||
|
||||
// Check for negative or overflow output values
|
||||
CAmount nValueOut = 0;
|
||||
CAmount nValueOutExplicit = 0;
|
||||
for (const auto& txout : tx.vout)
|
||||
{
|
||||
if (txout.nValue < 0)
|
||||
if (!txout.nValue.IsValid())
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-amount-invalid");
|
||||
if (!txout.nValue.IsExplicit())
|
||||
continue;
|
||||
if (txout.nValue.GetAmount() < 0)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
|
||||
if (txout.nValue > MAX_MONEY)
|
||||
if (txout.nValue.GetAmount() > MAX_MONEY)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
|
||||
nValueOut += txout.nValue;
|
||||
if (!MoneyRange(nValueOut))
|
||||
nValueOutExplicit += txout.nValue.GetAmount();
|
||||
if (!MoneyRange(nValueOutExplicit))
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
|
||||
}
|
||||
|
||||
|
|
@ -216,6 +220,12 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
|
|||
{
|
||||
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
|
||||
|
||||
for (unsigned int i = 0; i < tx.vout.size(); i++) {
|
||||
if (tx.vout[i].IsFee()) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-fee");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -228,7 +238,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
|
|||
}
|
||||
|
||||
namespace Consensus {
|
||||
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, std::set<std::pair<uint256, COutPoint>>& setPeginsSpent)
|
||||
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmountMap& fee_map, std::set<std::pair<uint256, COutPoint>>& setPeginsSpent, std::vector<CCheck*> *pvChecks, const bool cacheStore, bool fScriptChecks)
|
||||
{
|
||||
// are the actual inputs available?
|
||||
if (!inputs.HaveInputs(tx)) {
|
||||
|
|
@ -236,6 +246,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
|
|||
strprintf("%s: inputs missing/spent", __func__));
|
||||
}
|
||||
|
||||
std::vector<CTxOut> spent_inputs;
|
||||
CAmount nValueIn = 0;
|
||||
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
|
||||
const COutPoint &prevout = tx.vin[i].prevout;
|
||||
|
|
@ -243,7 +254,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
|
|||
// Check existence and validity of pegin witness
|
||||
std::string err;
|
||||
if (tx.witness.vtxinwit.size() <= i || !IsValidPeginWitness(tx.witness.vtxinwit[i].m_pegin_witness, prevout, err, true)) {
|
||||
return state.DoS(0, false, REJECT_PEGIN, "bad-pegin-witness");
|
||||
return state.DoS(0, false, REJECT_PEGIN, "bad-pegin-witness", false, err);
|
||||
}
|
||||
std::pair<uint256, COutPoint> pegin = std::make_pair(uint256(tx.witness.vtxinwit[i].m_pegin_witness.stack[2]), prevout);
|
||||
if (inputs.IsPeginSpent(pegin)) {
|
||||
|
|
@ -256,11 +267,12 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
|
|||
setPeginsSpent.insert(pegin);
|
||||
|
||||
// Tally the input amount.
|
||||
const CTxOut out = GetPeginOutputFromWitness(tx.witness.vtxinwit[i].m_pegin_witness);
|
||||
if (!MoneyRange(out.nValue)) {
|
||||
spent_inputs.push_back(GetPeginOutputFromWitness(tx.witness.vtxinwit[i].m_pegin_witness));
|
||||
const CTxOut& out = spent_inputs.back();
|
||||
nValueIn += out.nValue.GetAmount(); // Non-explicit already filtered by IsValidPeginWitness
|
||||
if (!MoneyRange(out.nValue.GetAmount())) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-pegin-inputvalue-outofrange");
|
||||
}
|
||||
nValueIn += out.nValue;
|
||||
} else {
|
||||
const Coin& coin = inputs.AccessCoin(prevout);
|
||||
assert(!coin.IsSpent());
|
||||
|
|
@ -271,29 +283,40 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
|
|||
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
|
||||
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
|
||||
}
|
||||
|
||||
// Check for negative or overflow input values
|
||||
nValueIn += coin.out.nValue;
|
||||
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
|
||||
spent_inputs.push_back(coin.out);
|
||||
if (coin.out.nValue.IsExplicit()) {
|
||||
nValueIn += coin.out.nValue.GetAmount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO(rebase) you need to replace these two blocks with the `VerifyAmounts` and `HasValidFee` methods
|
||||
const CAmount value_out = tx.GetValueOut();
|
||||
if (nValueIn < value_out) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
|
||||
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
|
||||
if (g_con_elementsmode) {
|
||||
// Tally transaction fees
|
||||
if (!HasValidFee(tx)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
|
||||
}
|
||||
|
||||
// Verify that amounts add up.
|
||||
if (fScriptChecks && !VerifyAmounts(spent_inputs, tx, pvChecks, cacheStore)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-ne-out", false, "value in != value out");
|
||||
}
|
||||
fee_map += GetFeeMap(tx);
|
||||
} else {
|
||||
const CAmount value_out = tx.GetValueOutMap()[CAsset()];
|
||||
if (nValueIn < value_out) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
|
||||
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
|
||||
}
|
||||
|
||||
// Tally transaction fees
|
||||
const CAmount txfee_aux = nValueIn - value_out;
|
||||
if (!MoneyRange(txfee_aux)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
|
||||
}
|
||||
|
||||
fee_map[CAsset()] += txfee_aux;
|
||||
}
|
||||
|
||||
// Tally transaction fees
|
||||
const CAmount txfee_aux = nValueIn - value_out;
|
||||
if (!MoneyRange(txfee_aux)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
|
||||
}
|
||||
|
||||
txfee = txfee_aux;
|
||||
return true;
|
||||
}
|
||||
}// namespace Consensus
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <amount.h>
|
||||
|
||||
#include <confidential_validation.h>
|
||||
#include <set>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
|
@ -27,10 +28,10 @@ namespace Consensus {
|
|||
/**
|
||||
* Check whether all inputs of this transaction are valid (no double spends and amounts)
|
||||
* This does not modify the UTXO set. This does not check scripts and sigs.
|
||||
* @param[out] txfee Set to the transaction fee if successful.
|
||||
* @param[out] fee_map Set to the transaction fee if successful.
|
||||
* Preconditions: tx.IsCoinBase() is false.
|
||||
*/
|
||||
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, std::set<std::pair<uint256, COutPoint>>& setPeginsSpent);
|
||||
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmountMap& fee_map, std::set<std::pair<uint256, COutPoint>>& setPeginsSpent, std::vector<CCheck*> *pvChecks, const bool cacheStore, bool fScriptChecks);
|
||||
} // namespace Consensus
|
||||
|
||||
/** Auxiliary functions for transaction validation (ideally should not be exposed) */
|
||||
|
|
|
|||
|
|
@ -108,11 +108,14 @@ static inline int64_t GetTransactionInputWeight(const CTransaction& tx, const si
|
|||
{
|
||||
// scriptWitness size is added here because witnesses and txins are split up in segwit serialization.
|
||||
assert(tx.witness.vtxinwit.size() > nIn);
|
||||
//TODO(rebase) only count CA/CT witnesses when g_con_elementswitness is true
|
||||
// ELEMENTS: This is only used for change size calculation in wallet, assert if
|
||||
// anything is unexpected for this call e.g. issuances, rangeproofs
|
||||
assert(tx.witness.vtxinwit[nIn].vchIssuanceAmountRangeproof.empty());
|
||||
assert(tx.witness.vtxinwit[nIn].vchInflationKeysRangeproof.empty());
|
||||
|
||||
return ::GetSerializeSize(tx.vin[nIn], PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1)
|
||||
+ ::GetSerializeSize(tx.vin[nIn], PROTOCOL_VERSION)
|
||||
+ ::GetSerializeSize(tx.witness.vtxinwit[nIn].scriptWitness.stack, PROTOCOL_VERSION)
|
||||
+ ::GetSerializeSize(tx.witness.vtxinwit[nIn].m_pegin_witness.stack, PROTOCOL_VERSION);
|
||||
+ ::GetSerializeSize(tx.witness.vtxinwit[nIn].scriptWitness.stack, PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
#endif // BITCOIN_CONSENSUS_VALIDATION_H
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#define BITCOIN_CORE_IO_H
|
||||
|
||||
#include <amount.h>
|
||||
#include <asset.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <consensus/consensus.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <issuance.h>
|
||||
#include <key_io.h>
|
||||
#include <script/script.h>
|
||||
#include <script/standard.h>
|
||||
|
|
@ -16,6 +17,32 @@
|
|||
#include <utilmoneystr.h>
|
||||
#include <utilstrencodings.h>
|
||||
|
||||
#include <secp256k1_rangeproof.h>
|
||||
|
||||
static secp256k1_context* secp256k1_blind_context = NULL;
|
||||
|
||||
class RPCRawTransaction_ECC_Init {
|
||||
public:
|
||||
RPCRawTransaction_ECC_Init() {
|
||||
assert(secp256k1_blind_context == NULL);
|
||||
|
||||
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
assert(ctx != NULL);
|
||||
|
||||
secp256k1_blind_context = ctx;
|
||||
}
|
||||
|
||||
~RPCRawTransaction_ECC_Init() {
|
||||
secp256k1_context *ctx = secp256k1_blind_context;
|
||||
secp256k1_blind_context = NULL;
|
||||
|
||||
if (ctx) {
|
||||
secp256k1_context_destroy(ctx);
|
||||
}
|
||||
}
|
||||
};
|
||||
static RPCRawTransaction_ECC_Init ecc_init_on_load;
|
||||
|
||||
UniValue ValueFromAmount(const CAmount& amount)
|
||||
{
|
||||
bool sign = amount < 0;
|
||||
|
|
@ -200,7 +227,7 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
|
|||
{
|
||||
entry.pushKV("txid", tx.GetHash().GetHex());
|
||||
entry.pushKV("hash", tx.GetWitnessHash().GetHex());
|
||||
if (g_con_elementswitness) {
|
||||
if (g_con_elementsmode) {
|
||||
entry.pushKV("wtxid", tx.GetWitnessHash().GetHex());
|
||||
entry.pushKV("withash", tx.GetWitnessOnlyHash().GetHex());
|
||||
}
|
||||
|
|
@ -223,29 +250,65 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
|
|||
o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true));
|
||||
o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
|
||||
in.pushKV("scriptSig", o);
|
||||
|
||||
if (tx.witness.vtxinwit.size() > i) {
|
||||
const CScriptWitness &scriptWitness = tx.witness.vtxinwit[i].scriptWitness;
|
||||
if (!scriptWitness.IsNull()) {
|
||||
UniValue txinwitness(UniValue::VARR);
|
||||
for (const auto &item : scriptWitness.stack) {
|
||||
txinwitness.push_back(HexStr(item.begin(), item.end()));
|
||||
}
|
||||
in.pushKV("txinwitness", txinwitness);
|
||||
}
|
||||
}
|
||||
|
||||
// ELEMENTS:
|
||||
in.pushKV("is_pegin", txin.m_is_pegin);
|
||||
if (tx.witness.vtxinwit.size() > i && !tx.witness.vtxinwit[i].m_pegin_witness.IsNull()) {
|
||||
UniValue pegin_witness(UniValue::VARR);
|
||||
for (const auto& item : tx.witness.vtxinwit[i].m_pegin_witness.stack) {
|
||||
pegin_witness.push_back(HexStr(item.begin(), item.end()));
|
||||
}
|
||||
in.pushKV("pegin_witness", pegin_witness);
|
||||
}
|
||||
}
|
||||
in.pushKV("sequence", (int64_t)txin.nSequence);
|
||||
|
||||
if (tx.witness.vtxinwit.size() > i) {
|
||||
const CScriptWitness &scriptWitness = tx.witness.vtxinwit[i].scriptWitness;
|
||||
if (!scriptWitness.IsNull()) {
|
||||
UniValue txinwitness(UniValue::VARR);
|
||||
for (const auto &item : scriptWitness.stack) {
|
||||
txinwitness.push_back(HexStr(item.begin(), item.end()));
|
||||
}
|
||||
in.pushKV("txinwitness", txinwitness);
|
||||
}
|
||||
}
|
||||
|
||||
// ELEMENTS:
|
||||
if (tx.witness.vtxinwit.size() > i && !tx.witness.vtxinwit[i].m_pegin_witness.IsNull()) {
|
||||
UniValue pegin_witness(UniValue::VARR);
|
||||
for (const auto& item : tx.witness.vtxinwit[i].m_pegin_witness.stack) {
|
||||
pegin_witness.push_back(HexStr(item.begin(), item.end()));
|
||||
}
|
||||
in.pushKV("pegin_witness", pegin_witness);
|
||||
}
|
||||
const CAssetIssuance& issuance = txin.assetIssuance;
|
||||
if (!issuance.IsNull()) {
|
||||
UniValue issue(UniValue::VOBJ);
|
||||
issue.pushKV("assetBlindingNonce", issuance.assetBlindingNonce.GetHex());
|
||||
CAsset asset;
|
||||
CAsset token;
|
||||
uint256 entropy;
|
||||
if (issuance.assetBlindingNonce.IsNull()) {
|
||||
GenerateAssetEntropy(entropy, txin.prevout, issuance.assetEntropy);
|
||||
issue.pushKV("assetEntropy", entropy.GetHex());
|
||||
CalculateAsset(asset, entropy);
|
||||
CalculateReissuanceToken(token, entropy, issuance.nAmount.IsCommitment());
|
||||
issue.pushKV("isreissuance", false);
|
||||
issue.pushKV("token", token.GetHex());
|
||||
}
|
||||
else {
|
||||
issue.pushKV("assetEntropy", issuance.assetEntropy.GetHex());
|
||||
issue.pushKV("isreissuance", true);
|
||||
CalculateAsset(asset, issuance.assetEntropy);
|
||||
}
|
||||
issue.pushKV("asset", asset.GetHex());
|
||||
|
||||
if (issuance.nAmount.IsExplicit()) {
|
||||
issue.pushKV("assetamount", ValueFromAmount(issuance.nAmount.GetAmount()));
|
||||
} else if (issuance.nAmount.IsCommitment()) {
|
||||
issue.pushKV("assetamountcommitment", HexStr(issuance.nAmount.vchCommitment));
|
||||
}
|
||||
if (issuance.nInflationKeys.IsExplicit()) {
|
||||
issue.pushKV("tokenamount", ValueFromAmount(issuance.nInflationKeys.GetAmount()));
|
||||
} else if (issuance.nInflationKeys.IsCommitment()) {
|
||||
issue.pushKV("tokenamountcommitment", HexStr(issuance.nInflationKeys.vchCommitment));
|
||||
}
|
||||
in.pushKV("issuance", issue);
|
||||
}
|
||||
// END ELEMENTS
|
||||
|
||||
vin.push_back(in);
|
||||
}
|
||||
entry.pushKV("vin", vin);
|
||||
|
|
@ -256,7 +319,37 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
|
|||
|
||||
UniValue out(UniValue::VOBJ);
|
||||
|
||||
out.pushKV("value", ValueFromAmount(txout.nValue));
|
||||
if (txout.nValue.IsExplicit()) {
|
||||
out.pushKV("value", ValueFromAmount(txout.nValue.GetAmount()));
|
||||
} else {
|
||||
int exp;
|
||||
int mantissa;
|
||||
uint64_t minv;
|
||||
uint64_t maxv;
|
||||
const CTxOutWitness* ptxoutwit = tx.witness.vtxoutwit.size() <= i? NULL: &tx.witness.vtxoutwit[i];
|
||||
if (ptxoutwit && secp256k1_rangeproof_info(secp256k1_blind_context, &exp, &mantissa, &minv, &maxv, &ptxoutwit->vchRangeproof[0], ptxoutwit->vchRangeproof.size())) {
|
||||
if (exp == -1) {
|
||||
out.pushKV("value", ValueFromAmount((CAmount)minv));
|
||||
} else {
|
||||
out.pushKV("value-minimum", ValueFromAmount((CAmount)minv));
|
||||
out.pushKV("value-maximum", ValueFromAmount((CAmount)maxv));
|
||||
}
|
||||
out.pushKV("ct-exponent", exp);
|
||||
out.pushKV("ct-bits", mantissa);
|
||||
}
|
||||
out.pushKV("valuecommitment", txout.nValue.GetHex());
|
||||
}
|
||||
if (g_con_elementsmode) {
|
||||
if (txout.nAsset.IsExplicit()) {
|
||||
out.pushKV("asset", txout.nAsset.GetAsset().GetHex());
|
||||
} else {
|
||||
out.pushKV("assetcommitment", txout.nAsset.GetHex());
|
||||
}
|
||||
|
||||
out.pushKV("commitmentnonce", txout.nNonce.GetHex());
|
||||
CPubKey pubkey(txout.nNonce.vchCommitment);
|
||||
out.pushKV("commitmentnonce_fully_valid", pubkey.IsFullyValid());
|
||||
}
|
||||
out.pushKV("n", (int64_t)i);
|
||||
|
||||
UniValue o(UniValue::VOBJ);
|
||||
|
|
|
|||
43
src/init.cpp
43
src/init.cpp
|
|
@ -64,6 +64,8 @@
|
|||
#include <openssl/crypto.h>
|
||||
|
||||
#include <primitives/pak.h> // CPAKList
|
||||
#include <assetsdir.h> // InitGlobalAssetDir
|
||||
#include <pegins.h>
|
||||
|
||||
#if ENABLE_ZMQ
|
||||
#include <zmq/zmqnotificationinterface.h>
|
||||
|
|
@ -508,6 +510,11 @@ void SetupServerArgs()
|
|||
gArgs.AddArg("-extpubkeyprefix", strprintf("The 4-byte prefix, in hex, of the chain's base58 extended public key encoding. (default: %s)", HexStr(defaultChainParams->Base58Prefix(CChainParams::EXT_PUBLIC_KEY))), false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-extprvkeyprefix", strprintf("The 4-byte prefix, in hex, of the chain's base58 extended private key encoding. (default: %s)", HexStr(defaultChainParams->Base58Prefix(CChainParams::EXT_SECRET_KEY))), false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-bech32_hrp", strprintf("The human-readable part of the chain's bech32 encoding. (default: %s)", defaultChainParams->Bech32HRP()), false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-blech32_hrp", strprintf("The human-readable part of the chain's blech32 encoding. Used in confidential addresses.(default: %s)", defaultChainParams->Blech32HRP()), false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-assetdir", "Entries of pet names of assets, in this format:asset=<hex>:<label>. There can be any number of entries.", false, OptionsCategory::ELEMENTS);
|
||||
gArgs.AddArg("-defaultpeggedassetname", "Default name of the pegged asset. (default: bitcoin)", false, OptionsCategory::ELEMENTS);
|
||||
gArgs.AddArg("-blindedaddresses", "Give blind addresses by default via getnewaddress and getrawchangeaddress. (default: -con_elementsmode value)", false, OptionsCategory::ELEMENTS);
|
||||
gArgs.AddArg("-blindedprefix", "The byte prefix, in decimal, of blinded addresses. (default: 4)", false, OptionsCategory::ELEMENTS);
|
||||
|
||||
#if HAVE_DECL_DAEMON
|
||||
gArgs.AddArg("-daemon", "Run in the background as a daemon and accept commands", false, OptionsCategory::OPTIONS);
|
||||
|
|
@ -534,6 +541,13 @@ void SetupServerArgs()
|
|||
gArgs.AddArg("-parentpubkeyprefix", strprintf("The byte prefix, in decimal, of the parent chain's base58 pubkey address. (default: %d)", 111), false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-parentscriptprefix", strprintf("The byte prefix, in decimal, of the parent chain's base58 script address. (default: %d)", 196), false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-parent_bech32_hrp", strprintf("The human-readable part of the parent chain's bech32 encoding. (default: %s)", "bc"), false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-parent_blech32_hrp", strprintf("The human-readable part of the parent chain's blech32 encoding. (default: %s)", "bc"), false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-con_parent_pegged_asset=<hex>", "Asset ID (hex) for pegged asset for when parent chain has CA. (default: 0x00)", false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-feeasset=<hex>", strprintf("Asset ID (hex) for mempool/relay fees (default: %s)", defaultChainParams->GetConsensus().pegged_asset.GetHex()), false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-subsidyasset=<hex>", strprintf("Asset ID (hex) for the block subsidy (default: %s)", defaultChainParams->GetConsensus().pegged_asset.GetHex()), false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-initialreissuancetokens=<n>", "The amount of reissuance tokens created in the genesis block. (default: 0)", false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-ct_bits", strprintf("The default number of hiding bits in a rangeproof. Will be exceeded to cover amounts exceeding the maximum hiding value. (default: %d)", 36), false, OptionsCategory::CHAINPARAMS);
|
||||
gArgs.AddArg("-ct_exponent", strprintf("The hiding exponent. (default: %s)", 0), false, OptionsCategory::CHAINPARAMS);
|
||||
|
||||
// Add the hidden options
|
||||
gArgs.AddHiddenArgs(hidden_args);
|
||||
|
|
@ -1161,6 +1175,13 @@ bool AppInitParameterInteraction()
|
|||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const std::string default_asset_name = gArgs.GetArg("-defaultpeggedassetname", "bitcoin");
|
||||
InitGlobalAssetDir(gArgs.GetArgs("-assetdir"), default_asset_name);
|
||||
} catch (const std::exception& e) {
|
||||
return InitError(strprintf("Error in -assetdir: %s\n", e.what()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1259,6 +1280,9 @@ bool AppInitMain()
|
|||
|
||||
InitSignatureCache();
|
||||
InitScriptExecutionCache();
|
||||
InitRangeproofCache();
|
||||
InitSurjectionproofCache();
|
||||
|
||||
|
||||
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
|
||||
if (nScriptCheckThreads) {
|
||||
|
|
@ -1282,6 +1306,9 @@ bool AppInitMain()
|
|||
RegisterZMQRPCCommands(tableRPC);
|
||||
#endif
|
||||
|
||||
// ELEMENTS:
|
||||
policyAsset = CAsset(uint256S(gArgs.GetArg("-feeasset", chainparams.GetConsensus().pegged_asset.GetHex())));
|
||||
|
||||
/* Start the RPC server already. It will be started in "warmup" mode
|
||||
* and not really process calls already (but it will signify connections
|
||||
* that the server is there and will be ready later). Warmup mode will
|
||||
|
|
@ -1820,7 +1847,21 @@ bool AppInitMain()
|
|||
}
|
||||
}
|
||||
|
||||
// ********************************************************* Step 14: finished
|
||||
// ********************************************************* Step 14: Check fedpeg
|
||||
// ELEMENTS:
|
||||
if (chainparams.GetConsensus().has_parent_chain) {
|
||||
// Will assert if not properly formatted
|
||||
const CScript& fedpeg_script = chainparams.GetConsensus().fedpegScript;
|
||||
unsigned int dummy_required;
|
||||
std::vector<std::vector<unsigned char>> dummy_keys;
|
||||
if (!MatchLiquidWatchman(fedpeg_script) &&
|
||||
fedpeg_script != CScript() << OP_TRUE &&
|
||||
!MatchMultisig(fedpeg_script, dummy_required, dummy_keys)) {
|
||||
return InitError(_("ERROR: Fedpegscript is not one of the accepted templates: OP_TRUE, CHECKMULTISIG, and Liquidv1"));
|
||||
}
|
||||
}
|
||||
|
||||
// ********************************************************* Step 15: finished
|
||||
|
||||
SetRPCWarmupFinished();
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ namespace {
|
|||
class PendingWalletTxImpl : public PendingWalletTx
|
||||
{
|
||||
public:
|
||||
explicit PendingWalletTxImpl(CWallet& wallet) : m_wallet(wallet), m_key(&wallet) {}
|
||||
explicit PendingWalletTxImpl(CWallet& wallet) : m_wallet(wallet) { m_keys.reserve(1); m_keys.emplace_back(new CReserveKey(&wallet)); }
|
||||
|
||||
const CTransaction& get() override { return *m_tx; }
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ public:
|
|||
{
|
||||
LOCK2(cs_main, m_wallet.cs_wallet);
|
||||
CValidationState state;
|
||||
if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_key, g_connman.get(), state)) {
|
||||
if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_keys, g_connman.get(), state)) {
|
||||
reject_reason = state.GetRejectReason();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ public:
|
|||
|
||||
CTransactionRef m_tx;
|
||||
CWallet& m_wallet;
|
||||
CReserveKey m_key;
|
||||
std::vector<std::unique_ptr<CReserveKey>> m_keys;
|
||||
};
|
||||
|
||||
//! Construct wallet tx struct.
|
||||
|
|
@ -224,7 +224,7 @@ public:
|
|||
{
|
||||
LOCK2(cs_main, m_wallet.cs_wallet);
|
||||
auto pending = MakeUnique<PendingWalletTxImpl>(m_wallet);
|
||||
if (!m_wallet.CreateTransaction(recipients, pending->m_tx, pending->m_key, fee, change_pos,
|
||||
if (!m_wallet.CreateTransaction(recipients, pending->m_tx, pending->m_keys, fee, change_pos,
|
||||
fail_reason, coin_control, sign)) {
|
||||
return {};
|
||||
}
|
||||
|
|
@ -355,8 +355,8 @@ public:
|
|||
num_blocks = ::chainActive.Height();
|
||||
return true;
|
||||
}
|
||||
CAmount getBalance() override { return m_wallet.GetBalance(); }
|
||||
CAmount getAvailableBalance(const CCoinControl& coin_control) override
|
||||
CAmountMap getBalance() override { return m_wallet.GetBalance(); }
|
||||
CAmountMap getAvailableBalance(const CCoinControl& coin_control) override
|
||||
{
|
||||
return m_wallet.GetAvailableBalance(&coin_control);
|
||||
}
|
||||
|
|
@ -370,12 +370,12 @@ public:
|
|||
LOCK2(::cs_main, m_wallet.cs_wallet);
|
||||
return m_wallet.IsMine(txout);
|
||||
}
|
||||
CAmount getDebit(const CTxIn& txin, isminefilter filter) override
|
||||
CAmountMap getDebit(const CTxIn& txin, isminefilter filter) override
|
||||
{
|
||||
LOCK2(::cs_main, m_wallet.cs_wallet);
|
||||
return m_wallet.GetDebit(txin, filter);
|
||||
}
|
||||
CAmount getCredit(const CTxOut& txout, isminefilter filter) override
|
||||
CAmountMap getCredit(const CTxOut& txout, isminefilter filter) override
|
||||
{
|
||||
LOCK2(::cs_main, m_wallet.cs_wallet);
|
||||
return m_wallet.GetCredit(txout, filter);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#define BITCOIN_INTERFACES_WALLET_H
|
||||
|
||||
#include <amount.h> // For CAmount
|
||||
#include <asset.h> // For CAmountMap
|
||||
#include <pubkey.h> // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation)
|
||||
#include <script/ismine.h> // For isminefilter, isminetype
|
||||
#include <script/standard.h> // For CTxDestination
|
||||
|
|
@ -196,10 +197,10 @@ public:
|
|||
virtual bool tryGetBalances(WalletBalances& balances, int& num_blocks) = 0;
|
||||
|
||||
//! Get balance.
|
||||
virtual CAmount getBalance() = 0;
|
||||
virtual CAmountMap getBalance() = 0;
|
||||
|
||||
//! Get available balance.
|
||||
virtual CAmount getAvailableBalance(const CCoinControl& coin_control) = 0;
|
||||
virtual CAmountMap getAvailableBalance(const CCoinControl& coin_control) = 0;
|
||||
|
||||
//! Return whether transaction input belongs to wallet.
|
||||
virtual isminetype txinIsMine(const CTxIn& txin) = 0;
|
||||
|
|
@ -208,10 +209,10 @@ public:
|
|||
virtual isminetype txoutIsMine(const CTxOut& txout) = 0;
|
||||
|
||||
//! Return debit amount if transaction input belongs to wallet.
|
||||
virtual CAmount getDebit(const CTxIn& txin, isminefilter filter) = 0;
|
||||
virtual CAmountMap getDebit(const CTxIn& txin, isminefilter filter) = 0;
|
||||
|
||||
//! Return credit amount if transaction input belongs to wallet.
|
||||
virtual CAmount getCredit(const CTxOut& txout, isminefilter filter) = 0;
|
||||
virtual CAmountMap getCredit(const CTxOut& txout, isminefilter filter) = 0;
|
||||
|
||||
//! Return AvailableCoins + LockedCoins grouped by wallet address.
|
||||
//! (put change in one group with wallet address)
|
||||
|
|
@ -309,13 +310,13 @@ struct WalletAddress
|
|||
//! Collection of wallet balances.
|
||||
struct WalletBalances
|
||||
{
|
||||
CAmount balance = 0;
|
||||
CAmount unconfirmed_balance = 0;
|
||||
CAmount immature_balance = 0;
|
||||
CAmountMap balance = CAmountMap();
|
||||
CAmountMap unconfirmed_balance = CAmountMap();
|
||||
CAmountMap immature_balance = CAmountMap();
|
||||
bool have_watch_only = false;
|
||||
CAmount watch_only_balance = 0;
|
||||
CAmount unconfirmed_watch_only_balance = 0;
|
||||
CAmount immature_watch_only_balance = 0;
|
||||
CAmountMap watch_only_balance = CAmountMap();
|
||||
CAmountMap unconfirmed_watch_only_balance = CAmountMap();
|
||||
CAmountMap immature_watch_only_balance = CAmountMap();
|
||||
|
||||
bool balanceChanged(const WalletBalances& prev) const
|
||||
{
|
||||
|
|
@ -334,9 +335,9 @@ struct WalletTx
|
|||
std::vector<isminetype> txout_is_mine;
|
||||
std::vector<CTxDestination> txout_address;
|
||||
std::vector<isminetype> txout_address_is_mine;
|
||||
CAmount credit;
|
||||
CAmount debit;
|
||||
CAmount change;
|
||||
CAmountMap credit;
|
||||
CAmountMap debit;
|
||||
CAmountMap change;
|
||||
int64_t time;
|
||||
std::map<std::string, std::string> value_map;
|
||||
bool is_coinbase;
|
||||
|
|
|
|||
82
src/issuance.cpp
Normal file
82
src/issuance.cpp
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
|
||||
#include <issuance.h>
|
||||
|
||||
#include <primitives/transaction.h>
|
||||
#include <amount.h>
|
||||
|
||||
void GenerateAssetEntropy(uint256& entropy, const COutPoint& prevout, const uint256& contracthash)
|
||||
{
|
||||
// E : entropy
|
||||
// I : prevout
|
||||
// C : contract
|
||||
// E = H( H(I) || H(C) )
|
||||
std::vector<uint256> leaves;
|
||||
leaves.reserve(2);
|
||||
leaves.push_back(SerializeHash(prevout, SER_GETHASH, 0));
|
||||
leaves.push_back(contracthash);
|
||||
entropy = ComputeFastMerkleRoot(leaves);
|
||||
}
|
||||
|
||||
void CalculateAsset(CAsset& asset, const uint256& entropy)
|
||||
{
|
||||
static const uint256 kZero = uint256S("0x0000000000000000000000000000000000000000000000000000000000000000");
|
||||
// H_a : asset tag
|
||||
// E : entropy
|
||||
// H_a = H( E || 0 )
|
||||
std::vector<uint256> leaves;
|
||||
leaves.reserve(2);
|
||||
leaves.push_back(entropy);
|
||||
leaves.push_back(kZero);
|
||||
asset = CAsset(ComputeFastMerkleRoot(leaves));
|
||||
}
|
||||
|
||||
void CalculateReissuanceToken(CAsset& reissuanceToken, const uint256& entropy, bool fConfidential)
|
||||
{
|
||||
static const uint256 kOne = uint256S("0x0000000000000000000000000000000000000000000000000000000000000001");
|
||||
static const uint256 kTwo = uint256S("0x0000000000000000000000000000000000000000000000000000000000000002");
|
||||
// H_a : asset reissuance tag
|
||||
// E : entropy
|
||||
// if not fConfidential:
|
||||
// H_a = H( E || 1 )
|
||||
// else
|
||||
// H_a = H( E || 2 )
|
||||
std::vector<uint256> leaves;
|
||||
leaves.reserve(2);
|
||||
leaves.push_back(entropy);
|
||||
leaves.push_back(fConfidential ? kTwo : kOne);
|
||||
reissuanceToken = CAsset(ComputeFastMerkleRoot(leaves));
|
||||
}
|
||||
|
||||
/** Add an issuance transaction to the genesis block. Typically used to pre-issue
|
||||
* the policyAsset of a blockchain. The genesis block is not actually validated,
|
||||
* so this transaction simply has to match issuance structure. */
|
||||
void AppendInitialIssuance(CBlock& genesis_block, const COutPoint& prevout, const uint256& contract, const int64_t asset_outputs, const int64_t asset_values, const int64_t reissuance_outputs, const int64_t reissuance_values, const CScript& issuance_destination) {
|
||||
uint256 entropy;
|
||||
GenerateAssetEntropy(entropy, prevout, contract);
|
||||
|
||||
CAsset asset;
|
||||
CalculateAsset(asset, entropy);
|
||||
|
||||
// Re-issuance of policyAsset is always unblinded
|
||||
CAsset reissuance;
|
||||
CalculateReissuanceToken(reissuance, entropy, false);
|
||||
|
||||
// Note: Genesis block isn't actually validated, outputs are entered into utxo db only
|
||||
CMutableTransaction txNew;
|
||||
txNew.nVersion = 1;
|
||||
txNew.vin.resize(1);
|
||||
txNew.vin[0].prevout = prevout;
|
||||
txNew.vin[0].assetIssuance.assetEntropy = contract;
|
||||
txNew.vin[0].assetIssuance.nAmount = CConfidentialValue(asset_values * asset_outputs);
|
||||
txNew.vin[0].assetIssuance.nInflationKeys = CConfidentialValue(reissuance_values * reissuance_outputs);
|
||||
|
||||
for (unsigned int i = 0; i < asset_outputs; i++) {
|
||||
txNew.vout.push_back(CTxOut(asset, CConfidentialValue(asset_values), issuance_destination));
|
||||
}
|
||||
for (unsigned int i = 0; i < reissuance_outputs; i++) {
|
||||
txNew.vout.push_back(CTxOut(reissuance, CConfidentialValue(reissuance_values), issuance_destination));
|
||||
}
|
||||
|
||||
genesis_block.vtx.push_back(MakeTransactionRef(std::move(txNew)));
|
||||
genesis_block.hashMerkleRoot = BlockMerkleRoot(genesis_block);
|
||||
}
|
||||
48
src/issuance.h
Normal file
48
src/issuance.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
#ifndef BITCOIN_ISSUANCE_H
|
||||
#define BITCOIN_ISSUANCE_H
|
||||
|
||||
#include <primitives/transaction.h>
|
||||
#include <amount.h>
|
||||
#include <hash.h>
|
||||
#include <consensus/merkle.h>
|
||||
|
||||
/**
|
||||
* Calculate the asset entropy from an COutPoint and a tx-author specified
|
||||
* Ricardian contract. See Definition 18 of the confidential assets paper.
|
||||
*
|
||||
* @param[out] entropy The asset entropy, which is used as input to
|
||||
* CalculateAsset and CalculateReissuanceToken.
|
||||
* @param[in] prevout Reference to the UTXO being spent.
|
||||
* @param[in] contracthash Root hash of the issuer-specified Ricardian
|
||||
* contract.
|
||||
*/
|
||||
void GenerateAssetEntropy(uint256& entropy, const COutPoint& prevout, const uint256& contracthash);
|
||||
|
||||
/**
|
||||
* Derive the asset from the entropy. See Definition 19 of the confidential
|
||||
* assets paper.
|
||||
*
|
||||
* @param[out] asset The nonce used as auxiliary input to the Pedersen
|
||||
* commitment setup to derive the unblinded asset tag.
|
||||
* @param[in] entropy The asset entropy returned by GenerateAssetEntropy.
|
||||
*/
|
||||
void CalculateAsset(CAsset& asset, const uint256& entropy);
|
||||
|
||||
/**
|
||||
* Derive the asset reissuance token asset from the entropy and reissuance
|
||||
* parameters (confidential or explicit). See Definition 21 of the confidential
|
||||
* assets paper.
|
||||
*
|
||||
* @param[out] reissuanceToken The nonce used as auxiliary input to the
|
||||
* Pedersen commitment setup to derive the
|
||||
* unblinded reissuance asset tag.
|
||||
* @param[in] entropy The asset entropy returned by GenerateAssetEntropy.
|
||||
* @param[in] fConfidential Set to true if the initial issuance was blinded,
|
||||
* false otherwise.
|
||||
*/
|
||||
void CalculateReissuanceToken(CAsset& reissuanceToken, const uint256& entropy, bool fConfidential);
|
||||
|
||||
void AppendInitialIssuance(CBlock& genesis_block, const COutPoint& prevout, const uint256& contract, const int64_t asset_outputs, const int64_t asset_values, const int64_t reissuance_outputs, const int64_t reissuance_values, const CScript& issuance_destination);
|
||||
|
||||
#endif // BITCOIN_ISSUANCE_H
|
||||
10
src/key.cpp
10
src/key.cpp
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_recovery.h>
|
||||
#include <secp256k1_ecdh.h>
|
||||
|
||||
static secp256k1_context* secp256k1_context_sign = nullptr;
|
||||
|
||||
|
|
@ -202,6 +203,15 @@ bool SigHasLowR(const secp256k1_ecdsa_signature* sig)
|
|||
return compact_sig[0] < 0x80;
|
||||
}
|
||||
|
||||
uint256 CKey::ECDH(const CPubKey& pubkey) const {
|
||||
assert(fValid);
|
||||
uint256 result;
|
||||
secp256k1_pubkey pkey;
|
||||
assert(secp256k1_ec_pubkey_parse(secp256k1_context_sign, &pkey, pubkey.begin(), pubkey.size()));
|
||||
assert(secp256k1_ecdh(secp256k1_context_sign, result.begin(), &pkey, begin(), NULL, NULL));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, bool grind, uint32_t test_case) const {
|
||||
if (!fValid)
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -110,6 +110,11 @@ public:
|
|||
*/
|
||||
CPubKey GetPubKey() const;
|
||||
|
||||
/**
|
||||
* Compute the ECDH exchange result using this private key and another public key.
|
||||
*/
|
||||
uint256 ECDH(const CPubKey& pubkey) const;
|
||||
|
||||
/**
|
||||
* Create a DER-serialized signature.
|
||||
* The test_case parameter tweaks the deterministic nonce.
|
||||
|
|
|
|||
129
src/key_io.cpp
129
src/key_io.cpp
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <base58.h>
|
||||
#include <bech32.h>
|
||||
#include <blech32.h>
|
||||
#include <chainparams.h>
|
||||
#include <script/script.h>
|
||||
#include <utilstrencodings.h>
|
||||
|
|
@ -29,16 +30,38 @@ private:
|
|||
public:
|
||||
explicit DestinationEncoder(const CChainParams& params, const bool for_parent) : m_params(params), for_parent(for_parent) {}
|
||||
|
||||
std::string operator()(const CKeyID& id) const
|
||||
std::string operator()(const PKHash& id) const
|
||||
{
|
||||
if (id.blinding_pubkey.IsFullyValid()) {
|
||||
assert(!for_parent);
|
||||
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::BLINDED_ADDRESS);
|
||||
// Blinded addresses have the actual address type prefix inside the payload.
|
||||
std::vector<unsigned char> prefix = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
|
||||
data.insert(data.end(), prefix.begin(), prefix.end());
|
||||
data.insert(data.end(), id.blinding_pubkey.begin(), id.blinding_pubkey.end());
|
||||
data.insert(data.end(), id.begin(), id.end());
|
||||
return EncodeBase58Check(data);
|
||||
}
|
||||
|
||||
CChainParams::Base58Type type = for_parent ? CChainParams::PARENT_PUBKEY_ADDRESS : CChainParams::PUBKEY_ADDRESS;
|
||||
std::vector<unsigned char> data = m_params.Base58Prefix(type);
|
||||
data.insert(data.end(), id.begin(), id.end());
|
||||
return EncodeBase58Check(data);
|
||||
}
|
||||
|
||||
std::string operator()(const CScriptID& id) const
|
||||
std::string operator()(const ScriptHash& id) const
|
||||
{
|
||||
if (id.blinding_pubkey.IsFullyValid()) {
|
||||
assert(!for_parent);
|
||||
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::BLINDED_ADDRESS);
|
||||
// Blinded addresses have the actual address type prefix inside the payload.
|
||||
std::vector<unsigned char> prefix = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
|
||||
data.insert(data.end(), prefix.begin(), prefix.end());
|
||||
data.insert(data.end(), id.blinding_pubkey.begin(), id.blinding_pubkey.end());
|
||||
data.insert(data.end(), id.begin(), id.end());
|
||||
return EncodeBase58Check(data);
|
||||
}
|
||||
|
||||
CChainParams::Base58Type type = for_parent ? CChainParams::PARENT_SCRIPT_ADDRESS : CChainParams::SCRIPT_ADDRESS;
|
||||
std::vector<unsigned char> data = m_params.Base58Prefix(type);
|
||||
data.insert(data.end(), id.begin(), id.end());
|
||||
|
|
@ -48,7 +71,15 @@ public:
|
|||
std::string operator()(const WitnessV0KeyHash& id) const
|
||||
{
|
||||
std::vector<unsigned char> data = {0};
|
||||
data.reserve(33);
|
||||
data.reserve(53);
|
||||
if (id.blinding_pubkey.IsFullyValid()) {
|
||||
std::vector<unsigned char> bytes(id.blinding_pubkey.begin(), id.blinding_pubkey.end());
|
||||
bytes.insert(bytes.end(), id.begin(), id.end());
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, bytes.begin(), bytes.end());
|
||||
const std::string& hrp = for_parent ? m_params.ParentBlech32HRP() : m_params.Blech32HRP();
|
||||
return blech32::Encode(hrp, data);
|
||||
}
|
||||
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
|
||||
const std::string& hrp = for_parent ? m_params.ParentBech32HRP() : m_params.Bech32HRP();
|
||||
return bech32::Encode(hrp, data);
|
||||
|
|
@ -58,6 +89,14 @@ public:
|
|||
{
|
||||
std::vector<unsigned char> data = {0};
|
||||
data.reserve(53);
|
||||
if (id.blinding_pubkey.IsFullyValid()) {
|
||||
std::vector<unsigned char> bytes(id.blinding_pubkey.begin(), id.blinding_pubkey.end());
|
||||
bytes.insert(bytes.end(), id.begin(), id.end());
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, bytes.begin(), bytes.end());
|
||||
const std::string& hrp = for_parent ? m_params.ParentBlech32HRP() : m_params.Blech32HRP();
|
||||
return blech32::Encode(hrp, data);
|
||||
}
|
||||
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
|
||||
const std::string& hrp = for_parent ? m_params.ParentBech32HRP() : m_params.Bech32HRP();
|
||||
return bech32::Encode(hrp, data);
|
||||
|
|
@ -70,6 +109,14 @@ public:
|
|||
}
|
||||
std::vector<unsigned char> data = {(unsigned char)id.version};
|
||||
data.reserve(1 + (id.length * 8 + 4) / 5);
|
||||
if (id.blinding_pubkey.IsFullyValid()) {
|
||||
std::vector<unsigned char> bytes(id.blinding_pubkey.begin(), id.blinding_pubkey.end());
|
||||
bytes.insert(bytes.end(), id.program, id.program + id.length);
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, bytes.begin(), bytes.end());
|
||||
const std::string& hrp = for_parent ? m_params.ParentBlech32HRP() : m_params.Blech32HRP();
|
||||
return blech32::Encode(hrp, data);
|
||||
}
|
||||
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.program, id.program + id.length);
|
||||
const std::string& hrp = for_parent ? m_params.ParentBech32HRP() : m_params.Bech32HRP();
|
||||
return bech32::Encode(hrp, data);
|
||||
|
|
@ -82,24 +129,46 @@ public:
|
|||
CTxDestination DecodeDestination(const std::string& str, const CChainParams& params, const bool for_parent)
|
||||
{
|
||||
std::vector<unsigned char> data;
|
||||
size_t pk_size = CPubKey::COMPRESSED_PUBLIC_KEY_SIZE;
|
||||
uint160 hash;
|
||||
if (DecodeBase58Check(str, data)) {
|
||||
// base58-encoded Bitcoin addresses.
|
||||
// Public-key-hash-addresses have version 0 (or 111 testnet).
|
||||
// The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
|
||||
|
||||
// Blinded addresses have two prefixes: first the blinded one, then the traditional one.
|
||||
const std::vector<unsigned char>& blinded_prefix = params.Base58Prefix(CChainParams::BLINDED_ADDRESS);
|
||||
|
||||
CChainParams::Base58Type type_pkh = for_parent ? CChainParams::PARENT_PUBKEY_ADDRESS : CChainParams::PUBKEY_ADDRESS;
|
||||
const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(type_pkh);
|
||||
if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) {
|
||||
std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin());
|
||||
return CKeyID(hash);
|
||||
return PKHash(hash);
|
||||
} else if (data.size() == hash.size() + blinded_prefix.size() + pubkey_prefix.size() + pk_size &&
|
||||
std::equal(blinded_prefix.begin(), blinded_prefix.end(), data.begin()) &&
|
||||
std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin() + blinded_prefix.size())) {
|
||||
auto payload_start = data.begin() + blinded_prefix.size() + pubkey_prefix.size();
|
||||
CPubKey pubkey;
|
||||
pubkey.Set(payload_start, payload_start + pk_size);
|
||||
std::copy(payload_start + pk_size, data.end(), hash.begin());
|
||||
return PKHash(hash, pubkey);
|
||||
}
|
||||
|
||||
// Script-hash-addresses have version 5 (or 196 testnet).
|
||||
// The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
|
||||
CChainParams::Base58Type type_sh = for_parent ? CChainParams::PARENT_SCRIPT_ADDRESS : CChainParams::SCRIPT_ADDRESS;
|
||||
const std::vector<unsigned char>& script_prefix = params.Base58Prefix(type_sh);
|
||||
if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) {
|
||||
std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin());
|
||||
return CScriptID(hash);
|
||||
return ScriptHash(hash);
|
||||
} else if (data.size() == hash.size() + blinded_prefix.size() + pubkey_prefix.size() + pk_size &&
|
||||
std::equal(blinded_prefix.begin(), blinded_prefix.end(), data.begin()) &&
|
||||
std::equal(script_prefix.begin(), script_prefix.end(), data.begin() + blinded_prefix.size())) {
|
||||
auto payload_start = data.begin() + blinded_prefix.size() + script_prefix.size();
|
||||
CPubKey pubkey;
|
||||
pubkey.Set(payload_start, payload_start + pk_size);
|
||||
std::copy(payload_start + pk_size, data.end(), hash.begin());
|
||||
return ScriptHash(hash, pubkey);
|
||||
}
|
||||
}
|
||||
data.clear();
|
||||
|
|
@ -138,6 +207,56 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
|
|||
return unk;
|
||||
}
|
||||
}
|
||||
// ELEMENTS confidential addresses: version + 8to5(ecdhkey || witness program)
|
||||
data.clear();
|
||||
auto blech = blech32::Decode(str);
|
||||
const std::string& bl_hrp = for_parent ? params.ParentBlech32HRP() : params.Blech32HRP();
|
||||
if (blech.second.size() > 0 && blech.first == bl_hrp) {
|
||||
// Blech32 decoding
|
||||
int version = blech.second[0]; // The first 5 bit symbol is the witness version (0-16)
|
||||
|
||||
data.reserve(((blech.second.size() - 1) * 5) / 8);
|
||||
|
||||
// The rest of the symbols are converted blinding pubkey and witness program bytes.
|
||||
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, blech.second.begin() + 1, blech.second.end())) {
|
||||
// Must be long enough for blinding key and other data taken below
|
||||
if (data.size() < 34) {
|
||||
return CNoDestination();
|
||||
}
|
||||
std::vector<unsigned char> pubkey_bytes(data.begin(), data.begin()+33);
|
||||
data = std::vector<unsigned char>(data.begin()+33, data.end());
|
||||
CPubKey blinding_pubkey(pubkey_bytes);
|
||||
if (version == 0) {
|
||||
{
|
||||
WitnessV0KeyHash keyid;
|
||||
if (data.size() == keyid.size()) {
|
||||
std::copy(data.begin(), data.end(), keyid.begin());
|
||||
keyid.blinding_pubkey = blinding_pubkey;
|
||||
return keyid;
|
||||
}
|
||||
}
|
||||
{
|
||||
WitnessV0ScriptHash scriptid;
|
||||
if (data.size() == scriptid.size()) {
|
||||
std::copy(data.begin(), data.end(), scriptid.begin());
|
||||
scriptid.blinding_pubkey = blinding_pubkey;
|
||||
return scriptid;
|
||||
}
|
||||
}
|
||||
return CNoDestination();
|
||||
}
|
||||
if (version > 16 || data.size() < 2 || data.size() > 40) {
|
||||
return CNoDestination();
|
||||
}
|
||||
WitnessUnknown unk;
|
||||
unk.version = version;
|
||||
std::copy(data.begin(), data.end(), unk.program);
|
||||
unk.blinding_pubkey = blinding_pubkey;
|
||||
unk.length = data.size();
|
||||
return unk;
|
||||
}
|
||||
}
|
||||
|
||||
return CNoDestination();
|
||||
}
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -178,16 +178,17 @@ CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest)
|
|||
{
|
||||
// Only supports destinations which map to single public keys, i.e. P2PKH,
|
||||
// P2WPKH, and P2SH-P2WPKH.
|
||||
if (auto id = boost::get<CKeyID>(&dest)) {
|
||||
return *id;
|
||||
if (auto id = boost::get<PKHash>(&dest)) {
|
||||
return CKeyID(*id);
|
||||
}
|
||||
if (auto witness_id = boost::get<WitnessV0KeyHash>(&dest)) {
|
||||
return CKeyID(*witness_id);
|
||||
}
|
||||
if (auto script_id = boost::get<CScriptID>(&dest)) {
|
||||
if (auto script_hash = boost::get<ScriptHash>(&dest)) {
|
||||
CScript script;
|
||||
CScriptID script_id(*script_hash);
|
||||
CTxDestination inner_dest;
|
||||
if (store.GetCScript(*script_id, script) && ExtractDestination(script, inner_dest)) {
|
||||
if (store.GetCScript(script_id, script) && ExtractDestination(script, inner_dest)) {
|
||||
if (auto inner_witness_id = boost::get<WitnessV0KeyHash>(&inner_dest)) {
|
||||
return CKeyID(*inner_witness_id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
|
|||
|
||||
// Pad block weight to account for OP_RETURN commitments with two compressed pubkeys
|
||||
for (const auto& commitment : commitments) {
|
||||
CTxOut output(0, commitment);
|
||||
CTxOut output(CAsset(), 0, commitment);
|
||||
nBlockWeight += ::GetSerializeSize(output, PROTOCOL_VERSION)*WITNESS_SCALE_FACTOR;
|
||||
}
|
||||
// END PAK
|
||||
|
|
@ -187,12 +187,23 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
|
|||
coinbaseTx.vin[0].prevout.SetNull();
|
||||
coinbaseTx.vout.resize(1);
|
||||
coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn;
|
||||
coinbaseTx.vout[0].nAsset = policyAsset;
|
||||
coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());
|
||||
if (g_con_elementsmode) {
|
||||
if(chainparams.GetConsensus().subsidy_asset != policyAsset) {
|
||||
// Only claim the subsidy if it's the same as the policy asset.
|
||||
coinbaseTx.vout[0].nValue = nFees;
|
||||
}
|
||||
// 0-value outputs must be unspendable
|
||||
if (coinbaseTx.vout[0].nValue.GetAmount() == 0) {
|
||||
coinbaseTx.vout[0].scriptPubKey = CScript() << OP_RETURN;
|
||||
}
|
||||
}
|
||||
coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
|
||||
// ELEMENTS: PAK
|
||||
// Add PAK transition commitments
|
||||
for (unsigned int i = 0; i < commitments.size(); i++) {
|
||||
coinbaseTx.vout.push_back(CTxOut(0, commitments[i]));
|
||||
coinbaseTx.vout.push_back(CTxOut(CAsset(), 0, commitments[i]));
|
||||
}
|
||||
// END PAK
|
||||
pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
|
||||
|
|
|
|||
|
|
@ -45,14 +45,14 @@ const std::string& FormatOutputType(OutputType type)
|
|||
CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
|
||||
{
|
||||
switch (type) {
|
||||
case OutputType::LEGACY: return key.GetID();
|
||||
case OutputType::LEGACY: return PKHash(key);
|
||||
case OutputType::P2SH_SEGWIT:
|
||||
case OutputType::BECH32: {
|
||||
if (!key.IsCompressed()) return key.GetID();
|
||||
CTxDestination witdest = WitnessV0KeyHash(key.GetID());
|
||||
if (!key.IsCompressed()) return PKHash(key);
|
||||
CTxDestination witdest = WitnessV0KeyHash(PKHash(key));
|
||||
CScript witprog = GetScriptForDestination(witdest);
|
||||
if (type == OutputType::P2SH_SEGWIT) {
|
||||
return CScriptID(witprog);
|
||||
return ScriptHash(witprog);
|
||||
} else {
|
||||
return witdest;
|
||||
}
|
||||
|
|
@ -61,12 +61,33 @@ CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
|
|||
}
|
||||
}
|
||||
|
||||
// Elements
|
||||
CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type, const CPubKey& blinding_pubkey)
|
||||
{
|
||||
switch (type) {
|
||||
case OutputType::LEGACY: return PKHash(key, blinding_pubkey);
|
||||
case OutputType::P2SH_SEGWIT:
|
||||
case OutputType::BECH32: {
|
||||
if (!key.IsCompressed()) return PKHash(key, blinding_pubkey);
|
||||
CTxDestination witdest = WitnessV0KeyHash(PKHash(key), blinding_pubkey);
|
||||
CScript witprog = GetScriptForDestination(witdest);
|
||||
if (type == OutputType::P2SH_SEGWIT) {
|
||||
return ScriptHash(witprog, blinding_pubkey);
|
||||
} else {
|
||||
return witdest;
|
||||
}
|
||||
}
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
|
||||
{
|
||||
CKeyID keyid = key.GetID();
|
||||
PKHash keyid(key);
|
||||
if (key.IsCompressed()) {
|
||||
CTxDestination segwit = WitnessV0KeyHash(keyid);
|
||||
CTxDestination p2sh = CScriptID(GetScriptForDestination(segwit));
|
||||
CTxDestination p2sh = ScriptHash(GetScriptForDestination(segwit));
|
||||
return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
|
||||
} else {
|
||||
return std::vector<CTxDestination>{std::move(keyid)};
|
||||
|
|
@ -80,19 +101,19 @@ CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript&
|
|||
// Note that scripts over 520 bytes are not yet supported.
|
||||
switch (type) {
|
||||
case OutputType::LEGACY:
|
||||
return CScriptID(script);
|
||||
return ScriptHash(script);
|
||||
case OutputType::P2SH_SEGWIT:
|
||||
case OutputType::BECH32: {
|
||||
CTxDestination witdest = WitnessV0ScriptHash(script);
|
||||
CScript witprog = GetScriptForDestination(witdest);
|
||||
// Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
|
||||
if (!IsSolvable(keystore, witprog)) return CScriptID(script);
|
||||
if (!IsSolvable(keystore, witprog)) return ScriptHash(script);
|
||||
// Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
|
||||
keystore.AddCScript(witprog);
|
||||
if (type == OutputType::BECH32) {
|
||||
return witdest;
|
||||
} else {
|
||||
return CScriptID(witprog);
|
||||
return ScriptHash(CScriptID(witprog));
|
||||
}
|
||||
}
|
||||
default: assert(false);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ const std::string& FormatOutputType(OutputType type);
|
|||
* The caller must make sure LearnRelatedScripts has been called beforehand.
|
||||
*/
|
||||
CTxDestination GetDestinationForKey(const CPubKey& key, OutputType);
|
||||
CTxDestination GetDestinationForKey(const CPubKey& key, OutputType, const CPubKey& blinding_pubkey);
|
||||
|
||||
/** Get all destinations (potentially) supported by the wallet for the given key. */
|
||||
std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
|
||||
|
|
|
|||
153
src/pegins.cpp
153
src/pegins.cpp
|
|
@ -54,18 +54,16 @@ bool GetAmountFromParentChainPegin(CAmount& amount, const Sidechain::Bitcoin::CT
|
|||
|
||||
bool GetAmountFromParentChainPegin(CAmount& amount, const CTransaction& txBTC, unsigned int nOut)
|
||||
{
|
||||
//if (!txBTC.vout[nOut].nValue.IsExplicit()) {
|
||||
// return false;
|
||||
//}
|
||||
//if (!txBTC.vout[nOut].nAsset.IsExplicit()) {
|
||||
// return false;
|
||||
//}
|
||||
//if (txBTC.vout[nOut].nAsset.GetAsset() != Params().GetConsensus().parent_pegged_asset) {
|
||||
// return false;
|
||||
//}
|
||||
//amount = txBTC.vout[nOut].nValue.GetAmount();
|
||||
//TODO(rebase) re-enable above for CA/CT
|
||||
amount = txBTC.vout[nOut].nValue;
|
||||
if (!txBTC.vout[nOut].nValue.IsExplicit()) {
|
||||
return false;
|
||||
}
|
||||
if (!txBTC.vout[nOut].nAsset.IsExplicit()) {
|
||||
return false;
|
||||
}
|
||||
if (txBTC.vout[nOut].nAsset.GetAsset() != Params().GetConsensus().parent_pegged_asset) {
|
||||
return false;
|
||||
}
|
||||
amount = txBTC.vout[nOut].nValue.GetAmount();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -75,67 +73,72 @@ CScript calculate_contract(const CScript& federation_script, const CScript& scri
|
|||
std::vector<std::vector<unsigned char> > solutions;
|
||||
unsigned int required;
|
||||
std::vector<std::vector<unsigned char>> keys;
|
||||
bool is_liquidv1_watchman = MatchLiquidWatchman(federation_script);
|
||||
// Sanity check federation_script only to match 3 templates
|
||||
if (federation_script != CScript() << OP_TRUE &&
|
||||
!MatchMultisig(federation_script, required, keys) &&
|
||||
!MatchLiquidWatchman(federation_script)) {
|
||||
assert(false);
|
||||
if (!is_liquidv1_watchman &&
|
||||
federation_script != CScript() << OP_TRUE &&
|
||||
!MatchMultisig(federation_script, required, keys)) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
CScript::const_iterator sdpc = federation_script.begin();
|
||||
std::vector<unsigned char> vch;
|
||||
opcodetype opcodeTmp;
|
||||
bool liquid_op_else_found = false;
|
||||
while (federation_script.GetOp(sdpc, opcodeTmp, vch))
|
||||
{
|
||||
CScript::const_iterator sdpc = federation_script.begin();
|
||||
std::vector<unsigned char> vch;
|
||||
opcodetype opcodeTmp;
|
||||
while (federation_script.GetOp(sdpc, opcodeTmp, vch))
|
||||
// For liquid watchman template, don't tweak emergency keys
|
||||
if (is_liquidv1_watchman && opcodeTmp == OP_ELSE) {
|
||||
liquid_op_else_found = true;
|
||||
}
|
||||
|
||||
size_t pub_len = 33;
|
||||
if (vch.size() == pub_len && !liquid_op_else_found)
|
||||
{
|
||||
size_t pub_len = 33;
|
||||
if (vch.size() == pub_len)
|
||||
{
|
||||
unsigned char tweak[32];
|
||||
CHMAC_SHA256(vch.data(), pub_len).Write(scriptPubKey.data(), scriptPubKey.size()).Finalize(tweak);
|
||||
int ret;
|
||||
secp256k1_pubkey watchman;
|
||||
secp256k1_pubkey tweaked;
|
||||
ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_validation, &watchman, vch.data(), pub_len);
|
||||
assert(ret == 1);
|
||||
ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_validation, &tweaked, vch.data(), pub_len);
|
||||
assert(ret == 1);
|
||||
// If someone creates a tweak that makes this fail, they broke SHA256
|
||||
ret = secp256k1_ec_pubkey_tweak_add(secp256k1_ctx_validation, &tweaked, tweak);
|
||||
assert(ret == 1);
|
||||
unsigned char new_pub[33];
|
||||
ret = secp256k1_ec_pubkey_serialize(secp256k1_ctx_validation, new_pub, &pub_len, &tweaked, SECP256K1_EC_COMPRESSED);
|
||||
assert(ret == 1);
|
||||
assert(pub_len == 33);
|
||||
unsigned char tweak[32];
|
||||
CHMAC_SHA256(vch.data(), pub_len).Write(scriptPubKey.data(), scriptPubKey.size()).Finalize(tweak);
|
||||
int ret;
|
||||
secp256k1_pubkey watchman;
|
||||
secp256k1_pubkey tweaked;
|
||||
ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_validation, &watchman, vch.data(), pub_len);
|
||||
assert(ret == 1);
|
||||
ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_validation, &tweaked, vch.data(), pub_len);
|
||||
assert(ret == 1);
|
||||
// If someone creates a tweak that makes this fail, they broke SHA256
|
||||
ret = secp256k1_ec_pubkey_tweak_add(secp256k1_ctx_validation, &tweaked, tweak);
|
||||
assert(ret == 1);
|
||||
unsigned char new_pub[33];
|
||||
ret = secp256k1_ec_pubkey_serialize(secp256k1_ctx_validation, new_pub, &pub_len, &tweaked, SECP256K1_EC_COMPRESSED);
|
||||
assert(ret == 1);
|
||||
assert(pub_len == 33);
|
||||
|
||||
// push tweaked pubkey
|
||||
std::vector<unsigned char> pub_vec(new_pub, new_pub + pub_len);
|
||||
scriptDestination << pub_vec;
|
||||
// push tweaked pubkey
|
||||
std::vector<unsigned char> pub_vec(new_pub, new_pub + pub_len);
|
||||
scriptDestination << pub_vec;
|
||||
|
||||
// Sanity checks to reduce pegin risk. If the tweaked
|
||||
// value flips a bit, we may lose pegin funds irretrievably.
|
||||
// We take the tweak, derive its pubkey and check that
|
||||
// `tweaked - watchman = tweak` to check the computation
|
||||
// two different ways
|
||||
secp256k1_pubkey tweaked2;
|
||||
ret = secp256k1_ec_pubkey_create(secp256k1_ctx_validation, &tweaked2, tweak);
|
||||
assert(ret);
|
||||
ret = secp256k1_ec_pubkey_negate(secp256k1_ctx_validation, &watchman);
|
||||
assert(ret);
|
||||
secp256k1_pubkey* pubkey_combined[2];
|
||||
pubkey_combined[0] = &watchman;
|
||||
pubkey_combined[1] = &tweaked;
|
||||
secp256k1_pubkey maybe_tweaked2;
|
||||
ret = secp256k1_ec_pubkey_combine(secp256k1_ctx_validation, &maybe_tweaked2, pubkey_combined, 2);
|
||||
assert(ret);
|
||||
assert(!memcmp(&maybe_tweaked2, &tweaked2, 64));
|
||||
// Sanity checks to reduce pegin risk. If the tweaked
|
||||
// value flips a bit, we may lose pegin funds irretrievably.
|
||||
// We take the tweak, derive its pubkey and check that
|
||||
// `tweaked - watchman = tweak` to check the computation
|
||||
// two different ways
|
||||
secp256k1_pubkey tweaked2;
|
||||
ret = secp256k1_ec_pubkey_create(secp256k1_ctx_validation, &tweaked2, tweak);
|
||||
assert(ret);
|
||||
ret = secp256k1_ec_pubkey_negate(secp256k1_ctx_validation, &watchman);
|
||||
assert(ret);
|
||||
secp256k1_pubkey* pubkey_combined[2];
|
||||
pubkey_combined[0] = &watchman;
|
||||
pubkey_combined[1] = &tweaked;
|
||||
secp256k1_pubkey maybe_tweaked2;
|
||||
ret = secp256k1_ec_pubkey_combine(secp256k1_ctx_validation, &maybe_tweaked2, pubkey_combined, 2);
|
||||
assert(ret);
|
||||
assert(!memcmp(&maybe_tweaked2, &tweaked2, 64));
|
||||
} else {
|
||||
// add to script untouched
|
||||
if (vch.size() > 0) {
|
||||
scriptDestination << vch;
|
||||
} else {
|
||||
// add to script untouched
|
||||
if (vch.size() > 0) {
|
||||
scriptDestination << vch;
|
||||
} else {
|
||||
scriptDestination << opcodeTmp;
|
||||
}
|
||||
scriptDestination << opcodeTmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +180,7 @@ static bool CheckPeginTx(const std::vector<unsigned char>& tx_data, T& pegtx, co
|
|||
// Check that the witness program matches the p2ch on the p2sh-p2wsh transaction output
|
||||
CScript tweaked_fedpegscript = calculate_contract(Params().GetConsensus().fedpegScript, claim_script);
|
||||
CScript witness_output(GetScriptForWitness(tweaked_fedpegscript));
|
||||
CScript expected_script(CScript() << OP_HASH160 << ToByteVector(CScriptID(witness_output)) << OP_EQUAL);
|
||||
CScript expected_script(CScript() << OP_HASH160 << ToByteVector(ScriptHash(CScriptID(witness_output))) << OP_EQUAL);
|
||||
if (pegtx->vout[prevout.n].scriptPubKey != expected_script) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -273,8 +276,7 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
|
|||
err_msg = "Asset type was not 32 bytes.";
|
||||
return false;
|
||||
}
|
||||
//TODO(rebase) CA
|
||||
//CAsset asset(stack[1]);
|
||||
CAsset asset(stack[1]);
|
||||
|
||||
// Get genesis blockhash
|
||||
if (stack[2].size() != 32) {
|
||||
|
|
@ -345,11 +347,10 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
|
|||
return false;
|
||||
}
|
||||
|
||||
//TODO(rebase) CA
|
||||
//// Check the asset type corresponds to a valid pegged asset (only one for now)
|
||||
//if (asset != Params().GetConsensus().pegged_asset) {
|
||||
// return false;
|
||||
//}
|
||||
// Check the asset type corresponds to a valid pegged asset (only one for now)
|
||||
if (asset != Params().GetConsensus().pegged_asset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finally, validate peg-in via rpc call
|
||||
if (check_depth && gArgs.GetBoolArg("-validatepegin", DEFAULT_VALIDATE_PEGIN)) {
|
||||
|
|
@ -367,9 +368,7 @@ CTxOut GetPeginOutputFromWitness(const CScriptWitness& pegin_witness) {
|
|||
CAmount value;
|
||||
stream >> value;
|
||||
|
||||
//TODO(rebase) CA
|
||||
//return CTxOut(CAsset(pegin_witness.stack[1]), value, CScript(pegin_witness.stack[3].begin(), pegin_witness.stack[3].end()));
|
||||
return CTxOut(value, CScript(pegin_witness.stack[3].begin(), pegin_witness.stack[3].end()));
|
||||
return CTxOut(CAsset(pegin_witness.stack[1]), CConfidentialValue(value), CScript(pegin_witness.stack[3].begin(), pegin_witness.stack[3].end()));
|
||||
}
|
||||
|
||||
bool MatchLiquidWatchman(const CScript& script)
|
||||
|
|
@ -441,5 +440,5 @@ bool MatchLiquidWatchman(const CScript& script)
|
|||
return false;
|
||||
}
|
||||
// No more pushes
|
||||
return (it + 1 == script.end());
|
||||
return (it == script.end());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
#include <utilstrencodings.h>
|
||||
#include <chainparams.h> // Peg-out enforcement
|
||||
|
||||
// ELEMENTS:
|
||||
CAsset policyAsset;
|
||||
|
||||
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
|
||||
{
|
||||
|
|
@ -53,7 +55,13 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
|
|||
|
||||
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
|
||||
{
|
||||
return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn));
|
||||
if (!txout.nValue.IsExplicit())
|
||||
return false; // FIXME
|
||||
if (!txout.nAsset.IsExplicit())
|
||||
return false;
|
||||
if (txout.IsFee())
|
||||
return false;
|
||||
return (txout.nValue.GetAmount() < GetDustThreshold(txout, dustRelayFeeIn));
|
||||
}
|
||||
|
||||
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
|
||||
|
|
@ -129,21 +137,19 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (whichType == TX_NULL_DATA) {
|
||||
}
|
||||
if (whichType == TX_NULL_DATA) {
|
||||
nDataOut++;
|
||||
if (params.GetEnforcePak() &&
|
||||
txout.scriptPubKey.IsPegoutScript(params.ParentGenesisBlockHash()) &&
|
||||
(!ScriptHasValidPAKProof(txout.scriptPubKey, params.ParentGenesisBlockHash()))) {
|
||||
// TODO(rebase) CT/CA check for asset type
|
||||
txout.nAsset.IsExplicit() && txout.nAsset.GetAsset() == Params().GetConsensus().pegged_asset &&
|
||||
(!ScriptHasValidPAKProof(txout.scriptPubKey, params.ParentGenesisBlockHash()))) {
|
||||
reason = "invalid-pegout-proof";
|
||||
return false;
|
||||
}
|
||||
} else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) {
|
||||
reason = "bare-multisig";
|
||||
return false;
|
||||
} else if (IsDust(txout, ::dustRelayFee)) {
|
||||
} else if ((txout.nAsset.IsExplicit() && txout.nAsset.GetAsset() == policyAsset) && IsDust(txout, ::dustRelayFee)) {
|
||||
reason = "dust";
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@
|
|||
class CCoinsViewCache;
|
||||
class CTxOut;
|
||||
|
||||
// ELEMENTS:
|
||||
extern CAsset policyAsset;
|
||||
|
||||
/** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/
|
||||
static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = MAX_BLOCK_WEIGHT - 4000;
|
||||
/** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/
|
||||
|
|
|
|||
34
src/primitives/confidential.cpp
Normal file
34
src/primitives/confidential.cpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
#include <primitives/confidential.h>
|
||||
|
||||
#include <tinyformat.h>
|
||||
|
||||
void CConfidentialAsset::SetToAsset(const CAsset& asset)
|
||||
{
|
||||
vchCommitment.clear();
|
||||
vchCommitment.reserve(nExplicitSize);
|
||||
vchCommitment.push_back(1);
|
||||
vchCommitment.insert(vchCommitment.end(), asset.begin(), asset.end());
|
||||
}
|
||||
|
||||
void CConfidentialValue::SetToAmount(const CAmount amount)
|
||||
{
|
||||
vchCommitment.resize(nExplicitSize);
|
||||
vchCommitment[0] = 1;
|
||||
WriteBE64(&vchCommitment[1], amount);
|
||||
}
|
||||
|
||||
std::string CAssetIssuance::ToString() const
|
||||
{
|
||||
std::string str;
|
||||
str += "CAssetIssuance(";
|
||||
str += assetBlindingNonce.ToString();
|
||||
str += ", ";
|
||||
str += assetEntropy.ToString();
|
||||
if (!nAmount.IsNull())
|
||||
str += strprintf(", amount=%s", (nAmount.IsExplicit() ? strprintf("%d.%08d", nAmount.GetAmount() / COIN, nAmount.GetAmount() % COIN) : std::string("CONFIDENTIAL")));
|
||||
if (!nInflationKeys.IsNull())
|
||||
str += strprintf(", inflationkeys=%s", (nInflationKeys.IsExplicit() ? strprintf("%d.%08d", nInflationKeys.GetAmount() / COIN, nInflationKeys.GetAmount() % COIN) : std::string("CONFIDENTIAL")));
|
||||
str += ")";
|
||||
return str;
|
||||
}
|
||||
224
src/primitives/confidential.h
Normal file
224
src/primitives/confidential.h
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
|
||||
#ifndef BITCOIN_PRIMITIVES_CONFIDENTIAL_H
|
||||
#define BITCOIN_PRIMITIVES_CONFIDENTIAL_H
|
||||
|
||||
#include <amount.h>
|
||||
#include <asset.h>
|
||||
#include <script/script.h>
|
||||
#include <serialize.h>
|
||||
#include <span.h>
|
||||
#include <uint256.h>
|
||||
#include <utilstrencodings.h>
|
||||
|
||||
extern bool g_con_elementsmode;
|
||||
|
||||
/**
|
||||
* Confidential assets, values, and nonces all share enough code in common
|
||||
* that it makes sense to define a common abstract base class. */
|
||||
template<size_t ExplicitSize, unsigned char PrefixA, unsigned char PrefixB>
|
||||
class CConfidentialCommitment
|
||||
{
|
||||
public:
|
||||
static const size_t nExplicitSize = ExplicitSize;
|
||||
static const size_t nCommittedSize = 33;
|
||||
|
||||
std::vector<unsigned char> vchCommitment;
|
||||
|
||||
CConfidentialCommitment() { SetNull(); }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
unsigned char version = vchCommitment.empty()? 0: vchCommitment[0];
|
||||
READWRITE(version);
|
||||
if (ser_action.ForRead()) {
|
||||
switch (version) {
|
||||
/* Null */
|
||||
case 0:
|
||||
vchCommitment.clear();
|
||||
return;
|
||||
/* Explicit value */
|
||||
case 1:
|
||||
vchCommitment.resize(nExplicitSize);
|
||||
break;
|
||||
/* Confidential commitment */
|
||||
case PrefixA:
|
||||
case PrefixB:
|
||||
vchCommitment.resize(nCommittedSize);
|
||||
break;
|
||||
/* Invalid serialization! */
|
||||
default:
|
||||
throw std::ios_base::failure("Unrecognized serialization prefix");
|
||||
}
|
||||
vchCommitment[0] = version;
|
||||
}
|
||||
if (vchCommitment.size() > 1) {
|
||||
READWRITE(REF(Span<unsigned char>(vchCommitment.data() + 1, vchCommitment.size()-1)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Null is the default state when no explicit asset or confidential
|
||||
* asset commitment has been set. */
|
||||
bool IsNull() const { return vchCommitment.empty(); }
|
||||
void SetNull() { vchCommitment.clear(); }
|
||||
|
||||
bool IsExplicit() const
|
||||
{
|
||||
return vchCommitment.size()==nExplicitSize && vchCommitment[0]==1;
|
||||
}
|
||||
|
||||
bool IsCommitment() const
|
||||
{
|
||||
return vchCommitment.size()==nCommittedSize && (vchCommitment[0]==PrefixA || vchCommitment[0]==PrefixB);
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return IsNull() || IsExplicit() || IsCommitment();
|
||||
}
|
||||
|
||||
std::string GetHex() const { return HexStr(vchCommitment); }
|
||||
|
||||
friend bool operator==(const CConfidentialCommitment& a, const CConfidentialCommitment& b)
|
||||
{
|
||||
return a.vchCommitment == b.vchCommitment;
|
||||
}
|
||||
|
||||
friend bool operator!=(const CConfidentialCommitment& a, const CConfidentialCommitment& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
/** A commitment to a blinded asset, or an explicit asset NUMS identifier */
|
||||
class CConfidentialAsset : public CConfidentialCommitment<33, 10, 11>
|
||||
{
|
||||
public:
|
||||
CConfidentialAsset() {
|
||||
SetNull();
|
||||
}
|
||||
CConfidentialAsset(CAsset asset) { SetToAsset(asset); }
|
||||
|
||||
void SetNull() {
|
||||
vchCommitment.clear();
|
||||
|
||||
// Set to dummy asset when not doing CA.
|
||||
if (!g_con_elementsmode) {
|
||||
SetToAsset(CAsset());
|
||||
}
|
||||
}
|
||||
|
||||
/* An explicit asset identifier is a 256-bit nothing-up-my-sleeve number
|
||||
* that used as auxiliary input to the Pedersen commitment setup to create
|
||||
* a generator which acts as the asset tag. */
|
||||
const CAsset& GetAsset() const
|
||||
{
|
||||
assert(IsExplicit());
|
||||
return *reinterpret_cast<const CAsset*>(&vchCommitment[1]);
|
||||
}
|
||||
void SetToAsset(const CAsset& asset);
|
||||
|
||||
};
|
||||
|
||||
/** A 33-byte commitment to a confidential value, or a 64-bit explicit value. */
|
||||
class CConfidentialValue : public CConfidentialCommitment<9, 8, 9>
|
||||
{
|
||||
public:
|
||||
CConfidentialValue() { SetNull(); }
|
||||
CConfidentialValue(CAmount nAmount) { SetToAmount(nAmount); }
|
||||
|
||||
/* An explicit value is called an amount. The first byte indicates it is
|
||||
* an explicit value, and the remaining 8 bytes is the value serialized as
|
||||
* a 64-bit big-endian integer. */
|
||||
CAmount GetAmount() const
|
||||
{
|
||||
if (!g_con_elementsmode && IsNull()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(IsExplicit());;
|
||||
return ReadBE64(&vchCommitment[1]);
|
||||
}
|
||||
void SetToAmount(CAmount nAmount);
|
||||
};
|
||||
|
||||
/**
|
||||
* A 33-byte data field that typically is used to convey to the
|
||||
* recipient the ECDH ephemeral key (an EC point) for deriving the
|
||||
* transaction output blinding factor. */
|
||||
class CConfidentialNonce : public CConfidentialCommitment<33, 2, 3>
|
||||
{
|
||||
public:
|
||||
CConfidentialNonce() { SetNull(); }
|
||||
};
|
||||
|
||||
/** A new asset issuance, or a reissuance (inflation) of an existing asset */
|
||||
class CAssetIssuance
|
||||
{
|
||||
public:
|
||||
// == 0
|
||||
// Indicates new asset issuance.
|
||||
// != 0
|
||||
// This is a revelation of the blinding factor for the input,
|
||||
// which shows that the input being spent is of the reissuance
|
||||
// capability type for the asset being inflated.
|
||||
uint256 assetBlindingNonce;
|
||||
|
||||
// New asset issuance:
|
||||
// This is a 32-byte nonce of no consensus-defined meaning,
|
||||
// but is used as additional entropy to the asset tag calculation.
|
||||
// This is used by higher-layer protocols for defining the
|
||||
// Ricardian contract governing the asset.
|
||||
// Existing asset reissuance:
|
||||
// The original asset entropy (combination of Ricardian contract
|
||||
// and outpoint used) which was used to generate the fixed asset
|
||||
// tag and reissuance tokens.
|
||||
uint256 assetEntropy;
|
||||
|
||||
// Both explicit and blinded issuance amounts are supported
|
||||
// (see class definition for CConfidentialValue for details).
|
||||
CConfidentialValue nAmount;
|
||||
|
||||
// If nonzero, specifies the number of asset issuance tokens to
|
||||
// generate. These tokens are made available to the outputs of the
|
||||
// generating transaction.
|
||||
CConfidentialValue nInflationKeys;
|
||||
|
||||
public:
|
||||
CAssetIssuance()
|
||||
{
|
||||
SetNull();
|
||||
}
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action)
|
||||
{
|
||||
READWRITE(assetBlindingNonce);
|
||||
READWRITE(assetEntropy);
|
||||
READWRITE(nAmount);
|
||||
READWRITE(nInflationKeys);
|
||||
}
|
||||
|
||||
void SetNull() { nAmount.SetNull(); nInflationKeys.SetNull(); }
|
||||
bool IsNull() const { return (nAmount.IsNull() && nInflationKeys.IsNull()); }
|
||||
|
||||
friend bool operator==(const CAssetIssuance& a, const CAssetIssuance& b)
|
||||
{
|
||||
return a.assetBlindingNonce == b.assetBlindingNonce &&
|
||||
a.assetEntropy == b.assetEntropy &&
|
||||
a.nAmount == b.nAmount &&
|
||||
a.nInflationKeys == b.nInflationKeys;
|
||||
}
|
||||
|
||||
friend bool operator!=(const CAssetIssuance& a, const CAssetIssuance& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
std::string ToString() const;
|
||||
};
|
||||
|
||||
#endif // BITCOIN_PRIMITIVES_CONFIDENTIAL_H
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
#include <tinyformat.h>
|
||||
#include <utilstrencodings.h>
|
||||
|
||||
bool g_con_elementswitness = false;
|
||||
bool g_con_elementsmode = false;
|
||||
|
||||
std::string COutPoint::ToString() const
|
||||
{
|
||||
|
|
@ -41,19 +41,27 @@ std::string CTxIn::ToString() const
|
|||
str += strprintf(", scriptSig=%s", HexStr(scriptSig).substr(0, 24));
|
||||
if (nSequence != SEQUENCE_FINAL)
|
||||
str += strprintf(", nSequence=%u", nSequence);
|
||||
if (!assetIssuance.IsNull())
|
||||
str += strprintf(", %s", assetIssuance.ToString());
|
||||
str += ")";
|
||||
return str;
|
||||
}
|
||||
|
||||
CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn)
|
||||
CTxOut::CTxOut(const CConfidentialAsset& nAssetIn, const CConfidentialValue& nValueIn, CScript scriptPubKeyIn)
|
||||
{
|
||||
nAsset = nAssetIn;
|
||||
nValue = nValueIn;
|
||||
scriptPubKey = scriptPubKeyIn;
|
||||
}
|
||||
|
||||
std::string CTxOut::ToString() const
|
||||
{
|
||||
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30));
|
||||
std::string strAsset;
|
||||
if (nAsset.IsExplicit())
|
||||
strAsset = strprintf("nAsset=%s, ", nAsset.GetAsset().GetHex());
|
||||
if (nAsset.IsCommitment())
|
||||
strAsset = std::string("nAsset=CONFIDENTIAL, ");
|
||||
return strprintf("CTxOut(%snValue=%s, scriptPubKey=%s)", strAsset, (nValue.IsExplicit() ? strprintf("%d.%08d", nValue.GetAmount() / COIN, nValue.GetAmount() % COIN) : std::string("CONFIDENTIAL")), HexStr(scriptPubKey).substr(0, 30));
|
||||
}
|
||||
|
||||
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {}
|
||||
|
|
@ -81,13 +89,14 @@ uint256 CTransaction::ComputeWitnessHash() const
|
|||
// ELEMENTS ONLY
|
||||
uint256 CTransaction::GetWitnessOnlyHash() const
|
||||
{
|
||||
assert(g_con_elementswitness);
|
||||
assert(g_con_elementsmode);
|
||||
|
||||
std::vector<uint256> leaves;
|
||||
leaves.reserve(std::max(vin.size(), vout.size()));
|
||||
/* Inputs */
|
||||
for (size_t i = 0; i < vin.size(); ++i) {
|
||||
const CTxInWitness& txinwit = witness.vtxinwit.size() <= i || vin[i].prevout.IsNull() ? CTxInWitness() : witness.vtxinwit[i];
|
||||
// Input has no witness OR is null input(coinbase)
|
||||
const CTxInWitness& txinwit = (witness.vtxinwit.size() <= i || vin[i].prevout.IsNull()) ? CTxInWitness() : witness.vtxinwit[i];
|
||||
leaves.push_back(txinwit.GetHash());
|
||||
}
|
||||
uint256 hashIn = ComputeFastMerkleRoot(leaves);
|
||||
|
|
@ -112,15 +121,19 @@ CTransaction::CTransaction(const CMutableTransaction& tx) :
|
|||
CTransaction::CTransaction(CMutableTransaction&& tx) :
|
||||
vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), witness(std::move(tx.witness)),hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {}
|
||||
|
||||
CAmount CTransaction::GetValueOut() const
|
||||
{
|
||||
CAmount nValueOut = 0;
|
||||
CAmountMap CTransaction::GetValueOutMap() const {
|
||||
|
||||
CAmountMap values;
|
||||
for (const auto& tx_out : vout) {
|
||||
nValueOut += tx_out.nValue;
|
||||
if (!MoneyRange(tx_out.nValue) || !MoneyRange(nValueOut))
|
||||
throw std::runtime_error(std::string(__func__) + ": value out of range");
|
||||
if (tx_out.nValue.IsExplicit() && tx_out.nAsset.IsExplicit()) {
|
||||
CAmountMap m;
|
||||
m[tx_out.nAsset.GetAsset()] = tx_out.nValue.GetAmount();
|
||||
values += m;
|
||||
if (!MoneyRange(tx_out.nValue.GetAmount()) || !MoneyRange(values[tx_out.nAsset.GetAsset()]))
|
||||
throw std::runtime_error(std::string(__func__) + ": value out of range");
|
||||
}
|
||||
}
|
||||
return nValueOut;
|
||||
return values;
|
||||
}
|
||||
|
||||
unsigned int CTransaction::GetTotalSize() const
|
||||
|
|
|
|||
|
|
@ -11,13 +11,14 @@
|
|||
#include <script/script.h>
|
||||
#include <serialize.h>
|
||||
#include <uint256.h>
|
||||
#include <primitives/confidential.h>
|
||||
#include <primitives/txwitness.h>
|
||||
|
||||
static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000;
|
||||
|
||||
// ELEMENTS:
|
||||
// Globals to avoid circular dependencies.
|
||||
extern bool g_con_elementswitness;
|
||||
extern bool g_con_elementsmode;
|
||||
|
||||
/** An outpoint - a combination of a transaction hash and an index n into its vout */
|
||||
class COutPoint
|
||||
|
|
@ -31,8 +32,7 @@ public:
|
|||
|
||||
/* If this flag is set, the CTxIn including this COutPoint has a
|
||||
* CAssetIssuance object. */
|
||||
//TODO(rebase) CA
|
||||
//static const uint32_t OUTPOINT_ISSUANCE_FLAG = (1 << 31);
|
||||
static const uint32_t OUTPOINT_ISSUANCE_FLAG = (1 << 31);
|
||||
|
||||
/* If this flag is set, the CTxIn including this COutPoint
|
||||
* is a peg-in input. */
|
||||
|
|
@ -93,6 +93,8 @@ public:
|
|||
//
|
||||
// ELEMENTS:
|
||||
|
||||
CAssetIssuance assetIssuance;
|
||||
|
||||
/* If this is set to true, the input is interpreted as a
|
||||
* peg-in claim and processed as such */
|
||||
bool m_is_pegin = false;
|
||||
|
|
@ -143,14 +145,13 @@ public:
|
|||
//
|
||||
// ELEMENTS:
|
||||
|
||||
//TODO(rebase) CA/CT
|
||||
//bool fHasAssetIssuance;
|
||||
bool fHasAssetIssuance;
|
||||
COutPoint outpoint;
|
||||
if (!ser_action.ForRead()) {
|
||||
if (prevout.n == (uint32_t) -1) {
|
||||
// Coinbase inputs do not have asset issuances attached
|
||||
// to them.
|
||||
// fHasAssetIssuance = false;
|
||||
fHasAssetIssuance = false;
|
||||
outpoint = prevout;
|
||||
} else {
|
||||
// The issuance and pegin bits can't be set as it is used to indicate
|
||||
|
|
@ -160,15 +161,15 @@ public:
|
|||
assert(!(prevout.n & ~COutPoint::OUTPOINT_INDEX_MASK));
|
||||
// The assetIssuance object is used to represent both new
|
||||
// asset generation and reissuance of existing asset types.
|
||||
// fHasAssetIssuance = !assetIssuance.IsNull();
|
||||
fHasAssetIssuance = !assetIssuance.IsNull();
|
||||
// The mode is placed in the upper bits of the outpoint's
|
||||
// index field. The IssuanceMode enum values are chosen to
|
||||
// make this as simple as a bitwise-OR.
|
||||
outpoint.hash = prevout.hash;
|
||||
outpoint.n = prevout.n & COutPoint::OUTPOINT_INDEX_MASK;
|
||||
// if (fHasAssetIssuance) {
|
||||
// outpoint.n |= COutPoint::OUTPOINT_ISSUANCE_FLAG;
|
||||
// }
|
||||
if (fHasAssetIssuance) {
|
||||
outpoint.n |= COutPoint::OUTPOINT_ISSUANCE_FLAG;
|
||||
}
|
||||
if (m_is_pegin) {
|
||||
outpoint.n |= COutPoint::OUTPOINT_PEGIN_FLAG;
|
||||
}
|
||||
|
|
@ -180,13 +181,13 @@ public:
|
|||
if (ser_action.ForRead()) {
|
||||
if (outpoint.n == (uint32_t) -1) {
|
||||
// No asset issuance for Coinbase inputs.
|
||||
// fHasAssetIssuance = false;
|
||||
fHasAssetIssuance = false;
|
||||
prevout = outpoint;
|
||||
m_is_pegin = false;
|
||||
} else {
|
||||
// The presence of the asset issuance object is indicated by
|
||||
// a bit set in the outpoint index field.
|
||||
// fHasAssetIssuance = !!(outpoint.n & COutPoint::OUTPOINT_ISSUANCE_FLAG);
|
||||
fHasAssetIssuance = !!(outpoint.n & COutPoint::OUTPOINT_ISSUANCE_FLAG);
|
||||
// The interpretation of this input as a peg-in is indicated by
|
||||
// a bit set in the outpoint index field.
|
||||
m_is_pegin = !!(outpoint.n & COutPoint::OUTPOINT_PEGIN_FLAG);
|
||||
|
|
@ -204,13 +205,22 @@ public:
|
|||
|
||||
READWRITE(scriptSig);
|
||||
READWRITE(nSequence);
|
||||
|
||||
// ELEMENTS:
|
||||
// The asset fields are deserialized only if they are present.
|
||||
if (fHasAssetIssuance) {
|
||||
READWRITE(assetIssuance);
|
||||
} else if (ser_action.ForRead()) {
|
||||
assetIssuance.SetNull();
|
||||
}
|
||||
}
|
||||
|
||||
friend bool operator==(const CTxIn& a, const CTxIn& b)
|
||||
{
|
||||
return (a.prevout == b.prevout &&
|
||||
a.scriptSig == b.scriptSig &&
|
||||
a.nSequence == b.nSequence);
|
||||
a.nSequence == b.nSequence &&
|
||||
a.assetIssuance == b.assetIssuance);
|
||||
}
|
||||
|
||||
friend bool operator!=(const CTxIn& a, const CTxIn& b)
|
||||
|
|
@ -227,7 +237,9 @@ public:
|
|||
class CTxOut
|
||||
{
|
||||
public:
|
||||
CAmount nValue;
|
||||
CConfidentialAsset nAsset;
|
||||
CConfidentialValue nValue;
|
||||
CConfidentialNonce nNonce;
|
||||
CScript scriptPubKey;
|
||||
|
||||
CTxOut()
|
||||
|
|
@ -235,30 +247,58 @@ public:
|
|||
SetNull();
|
||||
}
|
||||
|
||||
CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn);
|
||||
CTxOut(const CConfidentialAsset& nAssetIn, const CConfidentialValue& nValueIn, CScript scriptPubKeyIn);
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(nValue);
|
||||
READWRITE(scriptPubKey);
|
||||
if (g_con_elementsmode) {
|
||||
READWRITE(nAsset);
|
||||
READWRITE(nValue);
|
||||
READWRITE(nNonce);
|
||||
READWRITE(scriptPubKey);
|
||||
} else {
|
||||
CAmount value;
|
||||
if (!ser_action.ForRead()) {
|
||||
value = nValue.GetAmount();
|
||||
}
|
||||
READWRITE(value);
|
||||
if (ser_action.ForRead()) {
|
||||
nValue.SetToAmount(value);
|
||||
}
|
||||
READWRITE(scriptPubKey);
|
||||
}
|
||||
}
|
||||
|
||||
void SetNull()
|
||||
{
|
||||
nValue = -1;
|
||||
nAsset.SetNull();
|
||||
nValue.SetNull();
|
||||
nNonce.SetNull();
|
||||
scriptPubKey.clear();
|
||||
}
|
||||
|
||||
bool IsNull() const
|
||||
{
|
||||
return (nValue == -1);
|
||||
if (!g_con_elementsmode) {
|
||||
// Ignore the asset and the nonce in compatibility mode.
|
||||
return nValue.IsNull() && scriptPubKey.empty();
|
||||
}
|
||||
|
||||
return nAsset.IsNull() && nValue.IsNull() && nNonce.IsNull() && scriptPubKey.empty();
|
||||
}
|
||||
|
||||
bool IsFee() const {
|
||||
return g_con_elementsmode && scriptPubKey == CScript()
|
||||
&& nValue.IsExplicit() && nAsset.IsExplicit();
|
||||
}
|
||||
|
||||
friend bool operator==(const CTxOut& a, const CTxOut& b)
|
||||
{
|
||||
return (a.nValue == b.nValue &&
|
||||
return (a.nAsset == b.nAsset &&
|
||||
a.nValue == b.nValue &&
|
||||
a.nNonce == b.nNonce &&
|
||||
a.scriptPubKey == b.scriptPubKey);
|
||||
}
|
||||
|
||||
|
|
@ -300,7 +340,7 @@ inline void UnserializeTransaction(TxType& tx, Stream& s) {
|
|||
|
||||
// Witness serialization is different between Elements and Core.
|
||||
// See code comments in SerializeTransaction for details about the differences.
|
||||
if (g_con_elementswitness) {
|
||||
if (g_con_elementsmode) {
|
||||
s >> flags;
|
||||
s >> tx.vin;
|
||||
s >> tx.vout;
|
||||
|
|
@ -368,7 +408,7 @@ inline void SerializeTransaction(const TxType& tx, Stream& s) {
|
|||
}
|
||||
|
||||
// Witness serialization is different between Elements and Core.
|
||||
if (g_con_elementswitness) {
|
||||
if (g_con_elementsmode) {
|
||||
// In Elements-style serialization, all normal data is serialized first and the
|
||||
// witnesses all in the end.
|
||||
s << flags;
|
||||
|
|
@ -472,7 +512,7 @@ public:
|
|||
uint256 GetWitnessOnlyHash() const;
|
||||
|
||||
// Return sum of txouts.
|
||||
CAmount GetValueOut() const;
|
||||
CAmountMap GetValueOutMap() const;
|
||||
// GetValueIn() is a method on CCoinsViewCache, because
|
||||
// inputs must be known to compute value in.
|
||||
|
||||
|
|
|
|||
|
|
@ -16,9 +16,8 @@
|
|||
uint256 CTxInWitness::GetHash() const
|
||||
{
|
||||
std::vector<uint256> leaves;
|
||||
//TODO(rebase) CA/CT
|
||||
//leaves.push_back(SerializeHash(vchIssuanceAmountRangeproof, SER_GETHASH, 0));
|
||||
//leaves.push_back(SerializeHash(vchInflationKeysRangeproof, SER_GETHASH, 0));
|
||||
leaves.push_back(SerializeHash(vchIssuanceAmountRangeproof, SER_GETHASH, 0));
|
||||
leaves.push_back(SerializeHash(vchInflationKeysRangeproof, SER_GETHASH, 0));
|
||||
leaves.push_back(SerializeHash(scriptWitness.stack, SER_GETHASH, 0));
|
||||
leaves.push_back(SerializeHash(m_pegin_witness.stack, SER_GETHASH, 0));
|
||||
return ComputeFastMerkleRoot(leaves);
|
||||
|
|
|
|||
|
|
@ -5,16 +5,15 @@
|
|||
#ifndef BITCOIN_PRIMITIVES_TXWITNESS_H
|
||||
#define BITCOIN_PRIMITIVES_TXWITNESS_H
|
||||
|
||||
#include <primitives/confidential.h>
|
||||
#include <script/script.h>
|
||||
#include <uint256.h>
|
||||
|
||||
class CTxInWitness
|
||||
{
|
||||
public:
|
||||
// TODO generalize CScriptWitness into just CWitness
|
||||
//TODO(rebase) CA/CT
|
||||
//std::vector<unsigned char> vchIssuanceAmountRangeproof;
|
||||
//std::vector<unsigned char> vchInflationKeysRangeproof;
|
||||
std::vector<unsigned char> vchIssuanceAmountRangeproof;
|
||||
std::vector<unsigned char> vchInflationKeysRangeproof;
|
||||
CScriptWitness scriptWitness;
|
||||
// Re-use script witness struct to include its own witness
|
||||
CScriptWitness m_pegin_witness;
|
||||
|
|
@ -24,8 +23,8 @@ public:
|
|||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action)
|
||||
{
|
||||
//READWRITE(vchIssuanceAmountRangeproof);
|
||||
//READWRITE(vchInflationKeysRangeproof);
|
||||
READWRITE(vchIssuanceAmountRangeproof);
|
||||
READWRITE(vchInflationKeysRangeproof);
|
||||
READWRITE(scriptWitness.stack);
|
||||
READWRITE(m_pegin_witness.stack);
|
||||
}
|
||||
|
|
@ -34,13 +33,12 @@ public:
|
|||
|
||||
bool IsNull() const
|
||||
{
|
||||
//return vchIssuanceAmountRangeproof.empty() && vchInflationKeysRangeproof.empty() && scriptWitness.IsNull() && m_pegin_witness.IsNull();
|
||||
return scriptWitness.IsNull() && m_pegin_witness.IsNull();
|
||||
return vchIssuanceAmountRangeproof.empty() && vchInflationKeysRangeproof.empty() && scriptWitness.IsNull() && m_pegin_witness.IsNull();
|
||||
}
|
||||
void SetNull()
|
||||
{
|
||||
//vchIssuanceAmountRangeproof.clear();
|
||||
//vchInflationKeysRangeproof.clear();
|
||||
vchIssuanceAmountRangeproof.clear();
|
||||
vchInflationKeysRangeproof.clear();
|
||||
scriptWitness.stack.clear();
|
||||
m_pegin_witness.stack.clear();
|
||||
}
|
||||
|
|
@ -107,15 +105,13 @@ public:
|
|||
for (size_t n = 0; n < vtxinwit.size(); n++) {
|
||||
READWRITE(vtxinwit[n]);
|
||||
}
|
||||
//TODO(rebase) CA/CT
|
||||
//for (size_t n = 0; n < vtxoutwit.size(); n++) {
|
||||
// READWRITE(vtxoutwit[n]);
|
||||
//}
|
||||
//TODO(stevenroose) re-enabled after testing
|
||||
//if (IsNull()) {
|
||||
// /* It's illegal to encode a witness when all vtxinwit and vtxoutwit entries are empty. */
|
||||
// throw std::ios_base::failure("Superfluous witness record");
|
||||
//}
|
||||
for (size_t n = 0; n < vtxoutwit.size(); n++) {
|
||||
READWRITE(vtxoutwit[n]);
|
||||
}
|
||||
if (IsNull()) {
|
||||
/* It's illegal to encode a witness when all vtxinwit and vtxoutwit entries are empty. */
|
||||
throw std::ios_base::failure("Superfluous witness record");
|
||||
}
|
||||
}
|
||||
|
||||
bool IsEmpty() const
|
||||
|
|
|
|||
|
|
@ -419,7 +419,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
|
|||
|
||||
if (amount > 0)
|
||||
{
|
||||
CTxOut txout(amount, static_cast<CScript>(std::vector<unsigned char>(24, 0)));
|
||||
CTxOut txout(::policyAsset, amount, static_cast<CScript>(std::vector<unsigned char>(24, 0)));
|
||||
txDummy.vout.push_back(txout);
|
||||
fDust |= IsDust(txout, model->node().getDustRelayFee());
|
||||
}
|
||||
|
|
@ -454,7 +454,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
|
|||
nQuantity++;
|
||||
|
||||
// Amount
|
||||
nAmount += out.txout.nValue;
|
||||
nAmount += out.txout.nValue.GetAmount();
|
||||
|
||||
// Bytes
|
||||
CTxDestination address;
|
||||
|
|
@ -468,8 +468,8 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
|
|||
else if(ExtractDestination(out.txout.scriptPubKey, address))
|
||||
{
|
||||
CPubKey pubkey;
|
||||
CKeyID *keyid = boost::get<CKeyID>(&address);
|
||||
if (keyid && model->wallet().getPubKey(*keyid, pubkey))
|
||||
PKHash *keyid = boost::get<PKHash>(&address);
|
||||
if (keyid && model->wallet().getPubKey(CKeyID(*keyid), pubkey))
|
||||
{
|
||||
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
|
||||
}
|
||||
|
|
@ -510,7 +510,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
|
|||
// Never create dust outputs; if we would, just add the dust to the fee.
|
||||
if (nChange > 0 && nChange < MIN_CHANGE)
|
||||
{
|
||||
CTxOut txout(nChange, static_cast<CScript>(std::vector<unsigned char>(24, 0)));
|
||||
CTxOut txout(::policyAsset, nChange, static_cast<CScript>(std::vector<unsigned char>(24, 0)));
|
||||
if (IsDust(txout, model->node().getDustRelayFee()))
|
||||
{
|
||||
nPayFee += nChange;
|
||||
|
|
@ -639,7 +639,7 @@ void CoinControlDialog::updateView()
|
|||
for (const auto& outpair : coins.second) {
|
||||
const COutPoint& output = std::get<0>(outpair);
|
||||
const interfaces::WalletTxOut& out = std::get<1>(outpair);
|
||||
nSum += out.txout.nValue;
|
||||
nSum += out.txout.nValue.GetAmount();
|
||||
nChildren++;
|
||||
|
||||
CCoinControlWidgetItem *itemOutput;
|
||||
|
|
@ -676,8 +676,8 @@ void CoinControlDialog::updateView()
|
|||
}
|
||||
|
||||
// amount
|
||||
itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.txout.nValue));
|
||||
itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.txout.nValue)); // padding so that sorting works correctly
|
||||
itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.txout.nValue.GetAmount()));
|
||||
itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.txout.nValue.GetAmount())); // padding so that sorting works correctly
|
||||
|
||||
// date
|
||||
itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.time));
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <qt/qvalidatedlineedit.h>
|
||||
#include <qt/walletmodel.h>
|
||||
|
||||
#include <asset.h>
|
||||
#include <base58.h>
|
||||
#include <chainparams.h>
|
||||
#include <primitives/transaction.h>
|
||||
|
|
@ -211,7 +212,7 @@ bool isDust(interfaces::Node& node, const QString& address, const CAmount& amoun
|
|||
{
|
||||
CTxDestination dest = DecodeDestination(address.toStdString());
|
||||
CScript script = GetScriptForDestination(dest);
|
||||
CTxOut txOut(amount, script);
|
||||
CTxOut txOut(CAsset(), amount, script);
|
||||
return IsDust(txOut, node.getDustRelayFee());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
#include <qt/transactiontablemodel.h>
|
||||
#include <qt/walletmodel.h>
|
||||
|
||||
#include <policy/policy.h>
|
||||
|
||||
#include <QAbstractItemDelegate>
|
||||
#include <QPainter>
|
||||
|
||||
|
|
@ -119,7 +121,7 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
|
|||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
m_balances.balance = -1;
|
||||
m_balances.balance[::policyAsset] = -1;
|
||||
|
||||
// use a SingleColorIcon for the "out of sync warning" icon
|
||||
QIcon icon = platformStyle->SingleColorIcon(":/icons/warning");
|
||||
|
|
@ -161,19 +163,19 @@ void OverviewPage::setBalance(const interfaces::WalletBalances& balances)
|
|||
{
|
||||
int unit = walletModel->getOptionsModel()->getDisplayUnit();
|
||||
m_balances = balances;
|
||||
ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balances.balance, false, BitcoinUnits::separatorAlways));
|
||||
ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_balance, false, BitcoinUnits::separatorAlways));
|
||||
ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_balance, false, BitcoinUnits::separatorAlways));
|
||||
ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, false, BitcoinUnits::separatorAlways));
|
||||
ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance, false, BitcoinUnits::separatorAlways));
|
||||
ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, balances.unconfirmed_watch_only_balance, false, BitcoinUnits::separatorAlways));
|
||||
ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
|
||||
ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, false, BitcoinUnits::separatorAlways));
|
||||
ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, valueFor(balances.balance, ::policyAsset), false, BitcoinUnits::separatorAlways));
|
||||
ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, valueFor(balances.unconfirmed_balance, ::policyAsset), false, BitcoinUnits::separatorAlways));
|
||||
ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, valueFor(balances.immature_balance, ::policyAsset), false, BitcoinUnits::separatorAlways));
|
||||
ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, valueFor(balances.balance, ::policyAsset) + valueFor(balances.unconfirmed_balance, ::policyAsset) + valueFor(balances.immature_balance, ::policyAsset), false, BitcoinUnits::separatorAlways));
|
||||
ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, valueFor(balances.watch_only_balance, ::policyAsset), false, BitcoinUnits::separatorAlways));
|
||||
ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, valueFor(balances.unconfirmed_watch_only_balance, ::policyAsset), false, BitcoinUnits::separatorAlways));
|
||||
ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, valueFor(balances.immature_watch_only_balance, ::policyAsset), false, BitcoinUnits::separatorAlways));
|
||||
ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, valueFor(balances.watch_only_balance, ::policyAsset) + valueFor(balances.unconfirmed_watch_only_balance, ::policyAsset) + valueFor(balances.immature_watch_only_balance, ::policyAsset), false, BitcoinUnits::separatorAlways));
|
||||
|
||||
// only show immature (newly mined) balance if it's non-zero, so as not to complicate things
|
||||
// for the non-mining users
|
||||
bool showImmature = balances.immature_balance != 0;
|
||||
bool showWatchOnlyImmature = balances.immature_watch_only_balance != 0;
|
||||
bool showImmature = valueFor(balances.immature_balance, ::policyAsset) != 0;
|
||||
bool showWatchOnlyImmature = valueFor(balances.immature_watch_only_balance, ::policyAsset) != 0;
|
||||
|
||||
// for symmetry reasons also show immature label when the watch-only one is shown
|
||||
ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature);
|
||||
|
|
@ -243,7 +245,7 @@ void OverviewPage::updateDisplayUnit()
|
|||
{
|
||||
if(walletModel && walletModel->getOptionsModel())
|
||||
{
|
||||
if (m_balances.balance != -1) {
|
||||
if (m_balances.balance[::policyAsset] != -1) {
|
||||
setBalance(m_balances);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -566,7 +566,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen
|
|||
}
|
||||
|
||||
// Extract and check amounts
|
||||
CTxOut txOut(sendingTo.second, sendingTo.first);
|
||||
CTxOut txOut(::policyAsset, sendingTo.second, sendingTo.first);
|
||||
if (IsDust(txOut, optionsModel->node().getDustRelayFee())) {
|
||||
Q_EMIT message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).")
|
||||
.arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)),
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <ui_interface.h>
|
||||
#include <txmempool.h>
|
||||
#include <policy/fees.h>
|
||||
#include <policy/policy.h>
|
||||
#include <wallet/fees.h>
|
||||
|
||||
#include <QFontMetrics>
|
||||
|
|
@ -526,7 +527,7 @@ void SendCoinsDialog::setBalance(const interfaces::WalletBalances& balances)
|
|||
{
|
||||
if(model && model->getOptionsModel())
|
||||
{
|
||||
ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balances.balance));
|
||||
ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), valueFor(balances.balance, ::policyAsset)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -618,7 +619,7 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
|
|||
}
|
||||
|
||||
// Calculate available amount to send.
|
||||
CAmount amount = model->wallet().getAvailableBalance(coin_control);
|
||||
CAmount amount = valueFor(model->wallet().getAvailableBalance(coin_control), ::policyAsset);
|
||||
for (int i = 0; i < ui->entries->count(); ++i) {
|
||||
SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
|
||||
if (e && !e->isHidden() && e != entry) {
|
||||
|
|
@ -784,7 +785,8 @@ void SendCoinsDialog::coinControlChangeChecked(int state)
|
|||
{
|
||||
if (state == Qt::Unchecked)
|
||||
{
|
||||
CoinControlDialog::coinControl()->destChange = CNoDestination();
|
||||
// ELEMENTS: it's a map now, should be initialized automatically
|
||||
//CoinControlDialog::coinControl()->destChange = CNoDestination();
|
||||
ui->labelCoinControlChangeLabel->clear();
|
||||
}
|
||||
else
|
||||
|
|
@ -800,7 +802,8 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
|
|||
if (model && model->getAddressTableModel())
|
||||
{
|
||||
// Default to no change address until verified
|
||||
CoinControlDialog::coinControl()->destChange = CNoDestination();
|
||||
// ELEMENTS: it's a map now, should be initialized automatically
|
||||
//CoinControlDialog::coinControl()->destChange = CNoDestination();
|
||||
ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
|
||||
|
||||
const CTxDestination dest = DecodeDestination(text.toStdString());
|
||||
|
|
@ -822,8 +825,10 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
|
|||
QMessageBox::StandardButton btnRetVal = QMessageBox::question(this, tr("Confirm custom change address"), tr("The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?"),
|
||||
QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
|
||||
|
||||
if(btnRetVal == QMessageBox::Yes)
|
||||
CoinControlDialog::coinControl()->destChange = dest;
|
||||
if(btnRetVal == QMessageBox::Yes) {
|
||||
// ELEMENTS: it's a map now
|
||||
//CoinControlDialog::coinControl()->destChange = dest;
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->lineEditCoinControlChange->setText("");
|
||||
|
|
@ -842,7 +847,8 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
|
|||
else
|
||||
ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
|
||||
|
||||
CoinControlDialog::coinControl()->destChange = dest;
|
||||
// ELEMENTS: it's a map now
|
||||
//CoinControlDialog::coinControl()->destChange = dest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
|
|||
ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
|
||||
return;
|
||||
}
|
||||
const CKeyID* keyID = boost::get<CKeyID>(&destination);
|
||||
const PKHash* keyID = boost::get<PKHash>(&destination);
|
||||
if (!keyID) {
|
||||
ui->addressIn_SM->setValid(false);
|
||||
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
|
||||
|
|
@ -137,7 +137,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
|
|||
}
|
||||
|
||||
CKey key;
|
||||
if (!model->wallet().getPrivKey(*keyID, key))
|
||||
if (!model->wallet().getPrivKey(CKeyID(*keyID), key))
|
||||
{
|
||||
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
|
||||
ui->statusLabel_SM->setText(tr("Private key for the entered address is not available."));
|
||||
|
|
@ -198,7 +198,7 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
|
|||
ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
|
||||
return;
|
||||
}
|
||||
if (!boost::get<CKeyID>(&destination)) {
|
||||
if (!boost::get<PKHash>(&destination)) {
|
||||
ui->addressIn_VM->setValid(false);
|
||||
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
|
||||
ui->statusLabel_VM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again."));
|
||||
|
|
@ -229,7 +229,7 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
|
|||
return;
|
||||
}
|
||||
|
||||
if (!(CTxDestination(pubkey.GetID()) == destination)) {
|
||||
if (!(CTxDestination(PKHash(pubkey.GetID())) == destination)) {
|
||||
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
|
||||
ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>"));
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <qt/transactionview.h>
|
||||
#include <qt/walletmodel.h>
|
||||
#include <key_io.h>
|
||||
#include <policy/policy.h>
|
||||
#include <test/test_bitcoin.h>
|
||||
#include <validation.h>
|
||||
#include <wallet/wallet.h>
|
||||
|
|
@ -164,8 +165,8 @@ void TestGUI()
|
|||
// Send two transactions, and verify they are added to transaction list.
|
||||
TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
|
||||
QCOMPARE(transactionTableModel->rowCount({}), 105);
|
||||
uint256 txid1 = SendCoins(*wallet.get(), sendCoinsDialog, CKeyID(), 5 * COIN, false /* rbf */);
|
||||
uint256 txid2 = SendCoins(*wallet.get(), sendCoinsDialog, CKeyID(), 10 * COIN, true /* rbf */);
|
||||
uint256 txid1 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 5 * COIN, false /* rbf */);
|
||||
uint256 txid2 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 10 * COIN, true /* rbf */);
|
||||
QCOMPARE(transactionTableModel->rowCount({}), 107);
|
||||
QVERIFY(FindTx(*transactionTableModel, txid1).isValid());
|
||||
QVERIFY(FindTx(*transactionTableModel, txid2).isValid());
|
||||
|
|
@ -182,7 +183,7 @@ void TestGUI()
|
|||
QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance");
|
||||
QString balanceText = balanceLabel->text();
|
||||
int unit = walletModel.getOptionsModel()->getDisplayUnit();
|
||||
CAmount balance = walletModel.wallet().getBalance();
|
||||
CAmount balance = valueFor(walletModel.wallet().getBalance(), ::policyAsset);
|
||||
QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways);
|
||||
QCOMPARE(balanceText, balanceComparison);
|
||||
|
||||
|
|
|
|||
|
|
@ -61,8 +61,8 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
|||
strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
|
||||
|
||||
int64_t nTime = wtx.time;
|
||||
CAmount nCredit = wtx.credit;
|
||||
CAmount nDebit = wtx.debit;
|
||||
CAmount nCredit = valueFor(wtx.credit, ::policyAsset);
|
||||
CAmount nDebit = valueFor(wtx.debit, ::policyAsset);
|
||||
CAmount nNet = nCredit - nDebit;
|
||||
|
||||
strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx, status, inMempool, numBlocks, adjustedTime);
|
||||
|
|
@ -134,7 +134,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
|||
//
|
||||
CAmount nUnmatured = 0;
|
||||
for (const CTxOut& txout : wtx.tx->vout)
|
||||
nUnmatured += wallet.getCredit(txout, ISMINE_ALL);
|
||||
nUnmatured += valueFor(wallet.getCredit(txout, ISMINE_ALL), ::policyAsset);
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> ";
|
||||
if (status.is_in_main_chain)
|
||||
strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", status.blocks_to_maturity) + ")";
|
||||
|
|
@ -199,21 +199,21 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
|||
}
|
||||
}
|
||||
|
||||
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -txout.nValue) + "<br>";
|
||||
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -txout.nValue.GetAmount()) + "<br>";
|
||||
if(toSelf)
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, txout.nValue) + "<br>";
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, txout.nValue.GetAmount()) + "<br>";
|
||||
}
|
||||
|
||||
if (fAllToMe)
|
||||
{
|
||||
// Payment to self
|
||||
CAmount nChange = wtx.change;
|
||||
CAmount nChange = valueFor(wtx.change, ::policyAsset);
|
||||
CAmount nValue = nCredit - nChange;
|
||||
strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nValue) + "<br>";
|
||||
strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "<br>";
|
||||
}
|
||||
|
||||
CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
|
||||
CAmount nTxFee = nDebit - valueFor(wtx.tx->GetValueOutMap(), ::policyAsset);
|
||||
if (nTxFee > 0)
|
||||
strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -nTxFee) + "<br>";
|
||||
}
|
||||
|
|
@ -225,13 +225,13 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
|||
auto mine = wtx.txin_is_mine.begin();
|
||||
for (const CTxIn& txin : wtx.tx->vin) {
|
||||
if (*(mine++)) {
|
||||
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet.getDebit(txin, ISMINE_ALL)) + "<br>";
|
||||
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -valueFor(wallet.getDebit(txin, ISMINE_ALL), ::policyAsset)) + "<br>";
|
||||
}
|
||||
}
|
||||
mine = wtx.txout_is_mine.begin();
|
||||
for (const CTxOut& txout : wtx.tx->vout) {
|
||||
if (*(mine++)) {
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet.getCredit(txout, ISMINE_ALL)) + "<br>";
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, valueFor(wallet.getCredit(txout, ISMINE_ALL), ::policyAsset)) + "<br>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -286,10 +286,10 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
|||
strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
|
||||
for (const CTxIn& txin : wtx.tx->vin)
|
||||
if(wallet.txinIsMine(txin))
|
||||
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet.getDebit(txin, ISMINE_ALL)) + "<br>";
|
||||
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, -valueFor(wallet.getDebit(txin, ISMINE_ALL), ::policyAsset)) + "<br>";
|
||||
for (const CTxOut& txout : wtx.tx->vout)
|
||||
if(wallet.txoutIsMine(txout))
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, wallet.getCredit(txout, ISMINE_ALL)) + "<br>";
|
||||
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatHtmlWithUnit(unit, valueFor(wallet.getCredit(txout, ISMINE_ALL), ::policyAsset)) + "<br>";
|
||||
|
||||
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
|
||||
strHTML += GUIUtil::HtmlEscape(wtx.tx->ToString(), true);
|
||||
|
|
@ -315,7 +315,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
|
|||
strHTML += GUIUtil::HtmlEscape(name) + " ";
|
||||
strHTML += QString::fromStdString(EncodeDestination(address));
|
||||
}
|
||||
strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue);
|
||||
strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue.GetAmount());
|
||||
strHTML = strHTML + " IsMine=" + (wallet.txoutIsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>";
|
||||
strHTML = strHTML + " IsWatchOnly=" + (wallet.txoutIsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "</li>";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <consensus/consensus.h>
|
||||
#include <interfaces/wallet.h>
|
||||
#include <key_io.h>
|
||||
#include <policy/policy.h>
|
||||
#include <timedata.h>
|
||||
#include <validation.h>
|
||||
|
||||
|
|
@ -29,8 +30,8 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
|
|||
{
|
||||
QList<TransactionRecord> parts;
|
||||
int64_t nTime = wtx.time;
|
||||
CAmount nCredit = wtx.credit;
|
||||
CAmount nDebit = wtx.debit;
|
||||
CAmount nCredit = valueFor(wtx.credit, ::policyAsset);
|
||||
CAmount nDebit = valueFor(wtx.debit, ::policyAsset);
|
||||
CAmount nNet = nCredit - nDebit;
|
||||
uint256 hash = wtx.tx->GetHash();
|
||||
std::map<std::string, std::string> mapValue = wtx.value_map;
|
||||
|
|
@ -49,7 +50,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
|
|||
TransactionRecord sub(hash, nTime);
|
||||
CTxDestination address;
|
||||
sub.idx = i; // vout index
|
||||
sub.credit = txout.nValue;
|
||||
sub.credit = txout.nValue.GetAmount();
|
||||
sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY;
|
||||
if (wtx.txout_address_is_mine[i])
|
||||
{
|
||||
|
|
@ -93,7 +94,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
|
|||
if (fAllFromMe && fAllToMe)
|
||||
{
|
||||
// Payment to self
|
||||
CAmount nChange = wtx.change;
|
||||
CAmount nChange = valueFor(wtx.change, ::policyAsset);
|
||||
|
||||
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
|
||||
-(nDebit - nChange), nCredit - nChange));
|
||||
|
|
@ -104,7 +105,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
|
|||
//
|
||||
// Debit
|
||||
//
|
||||
CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
|
||||
CAmount nTxFee = nDebit - wtx.tx->GetValueOutMap()[::policyAsset];
|
||||
|
||||
for (unsigned int nOut = 0; nOut < wtx.tx->vout.size(); nOut++)
|
||||
{
|
||||
|
|
@ -133,7 +134,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
|
|||
sub.address = mapValue["to"];
|
||||
}
|
||||
|
||||
CAmount nValue = txout.nValue;
|
||||
CAmount nValue = txout.nValue.GetAmount();
|
||||
/* Add fee to first output */
|
||||
if (nTxFee > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <interfaces/handler.h>
|
||||
#include <interfaces/node.h>
|
||||
#include <key_io.h>
|
||||
#include <policy/policy.h>
|
||||
#include <ui_interface.h>
|
||||
#include <util.h> // for GetBoolArg
|
||||
#include <wallet/coincontrol.h>
|
||||
|
|
@ -154,7 +155,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
|
|||
const unsigned char* scriptStr = (const unsigned char*)out.script().data();
|
||||
CScript scriptPubKey(scriptStr, scriptStr+out.script().size());
|
||||
CAmount nAmount = out.amount();
|
||||
CRecipient recipient = {scriptPubKey, nAmount, rcp.fSubtractFeeFromAmount};
|
||||
CRecipient recipient = {scriptPubKey, nAmount, ::policyAsset, CPubKey(), rcp.fSubtractFeeFromAmount};
|
||||
vecSend.push_back(recipient);
|
||||
}
|
||||
if (subtotal <= 0)
|
||||
|
|
@ -177,7 +178,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
|
|||
++nAddresses;
|
||||
|
||||
CScript scriptPubKey = GetScriptForDestination(DecodeDestination(rcp.address.toStdString()));
|
||||
CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount};
|
||||
CRecipient recipient = {scriptPubKey, rcp.amount, ::policyAsset, CPubKey(), rcp.fSubtractFeeFromAmount};
|
||||
vecSend.push_back(recipient);
|
||||
|
||||
total += rcp.amount;
|
||||
|
|
@ -188,7 +189,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
|
|||
return DuplicateAddress;
|
||||
}
|
||||
|
||||
CAmount nBalance = m_wallet->getAvailableBalance(coinControl);
|
||||
CAmount nBalance = m_wallet->getAvailableBalance(coinControl)[::policyAsset];
|
||||
|
||||
if(total > nBalance)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet)
|
|||
if (out.amount() <= 0) continue;
|
||||
if (i == nChangePosRet)
|
||||
i++;
|
||||
subtotal += walletTransaction->vout[i].nValue;
|
||||
subtotal += walletTransaction->vout[i].nValue.GetAmount();
|
||||
i++;
|
||||
}
|
||||
rcp.amount = subtotal;
|
||||
|
|
@ -65,7 +65,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet)
|
|||
{
|
||||
if (i == nChangePosRet)
|
||||
i++;
|
||||
rcp.amount = walletTransaction->vout[i].nValue;
|
||||
rcp.amount = walletTransaction->vout[i].nValue.GetAmount();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
25
src/random.h
25
src/random.h
|
|
@ -150,4 +150,29 @@ bool Random_SanityCheck();
|
|||
/** Initialize the RNG. */
|
||||
void RandomInit();
|
||||
|
||||
/** Shuffle function Poached from upstream: #14624 **/
|
||||
|
||||
/** More efficient than using std::shuffle on a FastRandomContext.
|
||||
*
|
||||
* This is more efficient as std::shuffle will consume entropy in groups of
|
||||
* 64 bits at the time and throw away most.
|
||||
*
|
||||
* This also works around a bug in libstdc++ std::shuffle that may cause
|
||||
* type::operator=(type&&) to be invoked on itself, which the library's
|
||||
* debug mode detects and panics on. This is a known issue, see
|
||||
* https://stackoverflow.com/questions/22915325/avoiding-self-assignment-in-stdshuffle
|
||||
*/
|
||||
template<typename I, typename R>
|
||||
void Shuffle(I first, I last, R&& rng)
|
||||
{
|
||||
while (first != last) {
|
||||
size_t j = rng.randrange(last - first);
|
||||
if (j) {
|
||||
using std::swap;
|
||||
swap(*first, *(first + j));
|
||||
}
|
||||
++first;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BITCOIN_RANDOM_H
|
||||
|
|
|
|||
|
|
@ -555,7 +555,9 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
|
|||
for (const CCoin& coin : outs) {
|
||||
UniValue utxo(UniValue::VOBJ);
|
||||
utxo.pushKV("height", (int32_t)coin.nHeight);
|
||||
utxo.pushKV("value", ValueFromAmount(coin.out.nValue));
|
||||
if (coin.out.nValue.IsExplicit()) {
|
||||
utxo.pushKV("value", ValueFromAmount(coin.out.nValue.GetAmount()));
|
||||
}
|
||||
|
||||
// include the script in a json output
|
||||
UniValue o(UniValue::VOBJ);
|
||||
|
|
|
|||
|
|
@ -889,9 +889,13 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash,
|
|||
for (const auto& output : outputs) {
|
||||
ss << VARINT(output.first + 1);
|
||||
ss << output.second.out.scriptPubKey;
|
||||
ss << VARINT(output.second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
|
||||
ss << output.second.out.nValue;
|
||||
ss << output.second.out.nAsset;
|
||||
ss << output.second.out.nNonce;
|
||||
stats.nTransactionOutputs++;
|
||||
stats.nTotalAmount += output.second.out.nValue;
|
||||
if (output.second.out.nValue.IsExplicit()) {
|
||||
stats.nTotalAmount += output.second.out.nValue.GetAmount();
|
||||
}
|
||||
stats.nBogoSize += 32 /* txid */ + 4 /* vout index */ + 4 /* height + coinbase */ + 8 /* amount */ +
|
||||
2 /* scriptPubKey len */ + output.second.out.scriptPubKey.size() /* scriptPubKey */;
|
||||
}
|
||||
|
|
@ -1098,7 +1102,20 @@ UniValue gettxout(const JSONRPCRequest& request)
|
|||
} else {
|
||||
ret.pushKV("confirmations", (int64_t)(pindex->nHeight - coin.nHeight + 1));
|
||||
}
|
||||
ret.pushKV("value", ValueFromAmount(coin.out.nValue));
|
||||
if (coin.out.nValue.IsExplicit()) {
|
||||
ret.pushKV("value", ValueFromAmount(coin.out.nValue.GetAmount()));
|
||||
} else {
|
||||
ret.pushKV("valuecommitment", coin.out.nValue.GetHex());
|
||||
}
|
||||
if (g_con_elementsmode) {
|
||||
if (coin.out.nAsset.IsExplicit()) {
|
||||
ret.pushKV("asset", coin.out.nAsset.GetAsset().GetHex());
|
||||
} else {
|
||||
ret.pushKV("assetcommitment", coin.out.nAsset.GetHex());
|
||||
}
|
||||
|
||||
ret.pushKV("commitmentnonce", coin.out.nNonce.GetHex());
|
||||
}
|
||||
UniValue o(UniValue::VOBJ);
|
||||
ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true);
|
||||
ret.pushKV("scriptPubKey", o);
|
||||
|
|
@ -1819,6 +1836,9 @@ static UniValue getblockstats(const JSONRPCRequest& request)
|
|||
}
|
||||
}
|
||||
|
||||
// ELEMENTS:
|
||||
const CAsset asset = policyAsset; // TODO Make configurable
|
||||
|
||||
const CBlock block = GetBlockChecked(pindex);
|
||||
|
||||
const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default)
|
||||
|
|
@ -1857,10 +1877,25 @@ static UniValue getblockstats(const JSONRPCRequest& request)
|
|||
outputs += tx->vout.size();
|
||||
|
||||
CAmount tx_total_out = 0;
|
||||
if (loop_outputs) {
|
||||
for (const CTxOut& out : tx->vout) {
|
||||
tx_total_out += out.nValue;
|
||||
utxo_size_inc += GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
|
||||
// ELEMENTS:
|
||||
CAmount elements_txfee = 0;
|
||||
if (g_con_elementsmode) {
|
||||
if (loop_outputs) {
|
||||
for (const CTxOut& out : tx->vout) {
|
||||
if (out.IsFee() && out.nAsset.GetAsset() == asset) {
|
||||
elements_txfee += out.nValue.GetAmount();
|
||||
}
|
||||
if (out.nValue.IsExplicit() && out.nAsset.IsExplicit() && out.nAsset.GetAsset() == asset) {
|
||||
tx_total_out += out.nValue.GetAmount();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (loop_outputs) {
|
||||
for (const CTxOut& out : tx->vout) {
|
||||
tx_total_out += out.nValue.GetAmount();
|
||||
utxo_size_inc += GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1900,7 +1935,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
|
|||
if (!g_txindex) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more of the selected stats requires -txindex enabled");
|
||||
}
|
||||
CAmount tx_total_in = 0;
|
||||
CAmount tx_total_in = 0; // Only needed for bitcoin serialization
|
||||
for (const CTxIn& in : tx->vin) {
|
||||
if (in.m_is_pegin) {
|
||||
continue;
|
||||
|
|
@ -1913,12 +1948,11 @@ static UniValue getblockstats(const JSONRPCRequest& request)
|
|||
}
|
||||
|
||||
CTxOut prevoutput = tx_in->vout[in.prevout.n];
|
||||
|
||||
tx_total_in += prevoutput.nValue;
|
||||
tx_total_in += g_con_elementsmode ? 0 : prevoutput.nValue.GetAmount();
|
||||
utxo_size_inc -= GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD;
|
||||
}
|
||||
|
||||
CAmount txfee = tx_total_in - tx_total_out;
|
||||
CAmount txfee = g_con_elementsmode ? elements_txfee : (tx_total_in - tx_total_out);
|
||||
assert(MoneyRange(txfee));
|
||||
if (do_medianfee) {
|
||||
fee_array.push_back(txfee);
|
||||
|
|
@ -2114,10 +2148,11 @@ UniValue scantxoutset(const JSONRPCRequest& request)
|
|||
" \"vout\": n, (numeric) the vout value\n"
|
||||
" \"scriptPubKey\" : \"script\", (string) the script key\n"
|
||||
" \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n"
|
||||
" \"asset\" : \"asset\", (hex) The asset ID\n"
|
||||
" \"height\" : n, (numeric) Height of the unspent transaction output\n"
|
||||
" }\n"
|
||||
" ,...], \n"
|
||||
" \"total_amount\" : x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n"
|
||||
" \"total_unblinded_bitcoin_amount\" : x.xxx, (numeric) The total amount of all found unspent unblinded outputs in " + CURRENCY_UNIT + "\n"
|
||||
"]\n"
|
||||
);
|
||||
|
||||
|
|
@ -2201,24 +2236,57 @@ UniValue scantxoutset(const JSONRPCRequest& request)
|
|||
result.pushKV("success", res);
|
||||
result.pushKV("searched_items", count);
|
||||
|
||||
for (const auto& it : coins) {
|
||||
const COutPoint& outpoint = it.first;
|
||||
const Coin& coin = it.second;
|
||||
const CTxOut& txo = coin.out;
|
||||
input_txos.push_back(txo);
|
||||
total_in += txo.nValue;
|
||||
if (!g_con_elementsmode) {
|
||||
for (const auto& it : coins) {
|
||||
const COutPoint& outpoint = it.first;
|
||||
const Coin& coin = it.second;
|
||||
const CTxOut& txo = coin.out;
|
||||
input_txos.push_back(txo);
|
||||
total_in += txo.nValue.GetAmount();
|
||||
|
||||
UniValue unspent(UniValue::VOBJ);
|
||||
unspent.pushKV("txid", outpoint.hash.GetHex());
|
||||
unspent.pushKV("vout", (int32_t)outpoint.n);
|
||||
unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey.begin(), txo.scriptPubKey.end()));
|
||||
unspent.pushKV("amount", ValueFromAmount(txo.nValue));
|
||||
unspent.pushKV("height", (int32_t)coin.nHeight);
|
||||
UniValue unspent(UniValue::VOBJ);
|
||||
unspent.pushKV("txid", outpoint.hash.GetHex());
|
||||
unspent.pushKV("vout", (int32_t)outpoint.n);
|
||||
unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey.begin(), txo.scriptPubKey.end()));
|
||||
unspent.pushKV("amount", ValueFromAmount(txo.nValue.GetAmount()));
|
||||
unspent.pushKV("height", (int32_t)coin.nHeight);
|
||||
|
||||
unspents.push_back(unspent);
|
||||
unspents.push_back(unspent);
|
||||
}
|
||||
result.pushKV("unspents", unspents);
|
||||
result.pushKV("total_amount", ValueFromAmount(total_in));
|
||||
} else {
|
||||
CAmount total_in_explicit_parent = 0;
|
||||
for (const auto& it : coins) {
|
||||
const COutPoint& outpoint = it.first;
|
||||
const Coin& coin = it.second;
|
||||
const CTxOut& txo = coin.out;
|
||||
input_txos.push_back(txo);
|
||||
if (txo.nValue.IsExplicit() && txo.nAsset.IsExplicit() && txo.nAsset.GetAsset() == Params().GetConsensus().pegged_asset) {
|
||||
total_in_explicit_parent += txo.nValue.GetAmount();
|
||||
}
|
||||
|
||||
UniValue unspent(UniValue::VOBJ);
|
||||
unspent.pushKV("txid", outpoint.hash.GetHex());
|
||||
unspent.pushKV("vout", (int32_t)outpoint.n);
|
||||
unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey.begin(), txo.scriptPubKey.end()));
|
||||
if (txo.nValue.IsExplicit()) {
|
||||
unspent.pushKV("amount", ValueFromAmount(txo.nValue.GetAmount()));
|
||||
} else {
|
||||
unspent.pushKV("amountcommitment", HexStr(txo.nValue.vchCommitment));
|
||||
}
|
||||
if (txo.nAsset.IsExplicit()) {
|
||||
unspent.pushKV("asset", txo.nAsset.GetAsset().GetHex());
|
||||
} else {
|
||||
unspent.pushKV("assetcommitment", HexStr(txo.nAsset.vchCommitment));
|
||||
}
|
||||
unspent.pushKV("height", (int32_t)coin.nHeight);
|
||||
|
||||
unspents.push_back(unspent);
|
||||
}
|
||||
result.pushKV("unspents", unspents);
|
||||
result.pushKV("total_unblinded_bitcoin_amount", ValueFromAmount(total_in_explicit_parent));
|
||||
}
|
||||
result.pushKV("unspents", unspents);
|
||||
result.pushKV("total_amount", ValueFromAmount(total_in));
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid command");
|
||||
}
|
||||
|
|
@ -2258,8 +2326,7 @@ UniValue getsidechaininfo(const JSONRPCRequest& request)
|
|||
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.pushKV("fedpegscript", HexStr(consensus.fedpegScript.begin(), consensus.fedpegScript.end()));
|
||||
//TODO(rebase) CA
|
||||
//obj.pushKV("pegged_asset", consensus.pegged_asset.GetHex());
|
||||
obj.pushKV("pegged_asset", consensus.pegged_asset.GetHex());
|
||||
obj.pushKV("min_peg_diff", consensus.parentChainPowLimit.GetHex());
|
||||
obj.pushKV("parent_blockhash", parent_blockhash.GetHex());
|
||||
obj.pushKV("parent_chain_has_pow", consensus.ParentChainHasPow());
|
||||
|
|
@ -2267,8 +2334,7 @@ UniValue getsidechaininfo(const JSONRPCRequest& request)
|
|||
if (!consensus.ParentChainHasPow()) {
|
||||
obj.pushKV("parent_chain_signblockscript_asm", ScriptToAsmStr(consensus.parent_chain_signblockscript));
|
||||
obj.pushKV("parent_chain_signblockscript_hex", HexStr(consensus.parent_chain_signblockscript));
|
||||
//TODO(stevenroose) rebase CA
|
||||
//obj.pushKV("parent_pegged_asset", HexStr(consensus.parent_pegged_asset));
|
||||
obj.pushKV("parent_pegged_asset", HexStr(consensus.parent_pegged_asset));
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "logging", 1, "exclude" },
|
||||
{ "disconnectnode", 1, "nodeid" },
|
||||
{ "addwitnessaddress", 1, "p2sh" },
|
||||
{ "initpegoutwallet", 1, "bip32_counter"},
|
||||
// Echo with conversion (For testing only)
|
||||
{ "echojson", 0, "arg0" },
|
||||
{ "echojson", 1, "arg1" },
|
||||
|
|
@ -161,12 +160,39 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "echojson", 7, "arg7" },
|
||||
{ "echojson", 8, "arg8" },
|
||||
{ "echojson", 9, "arg9" },
|
||||
{ "rescanblockchain", 0, "start_height"},
|
||||
{ "rescanblockchain", 1, "stop_height"},
|
||||
{ "createwallet", 1, "disable_private_keys"},
|
||||
{ "combineblocksigs", 1, "signatures"},
|
||||
// ELEMENTS:
|
||||
{ "rescanblockchain", 0, "start_height" },
|
||||
{ "rescanblockchain", 1, "stop_height" },
|
||||
{ "createwallet", 1, "disable_private_keys" },
|
||||
{ "combineblocksigs", 1, "signatures" },
|
||||
{ "sendtomainchain", 1, "amount" },
|
||||
{ "sendtomainchain", 2, "subtractfeefromamount" },
|
||||
{ "dumpissuanceblindingkey", 1, "vin" },
|
||||
{ "importissuanceblindingkey", 1, "vin" },
|
||||
{ "rawissueasset", 1, "issuances" },
|
||||
{ "rawreissueasset", 1, "reissuances" },
|
||||
{ "getnewblockhex", 0, "min_tx_age" },
|
||||
{ "testproposedblock", 1, "acceptnonstd" },
|
||||
{ "issueasset", 0, "assetamount" },
|
||||
{ "issueasset", 1, "tokenamount" },
|
||||
{ "issueasset", 2, "blind" },
|
||||
{ "reissueasset", 1, "assetamount" },
|
||||
{ "initpegoutwallet", 1, "bip32_counter"},
|
||||
{ "rawblindrawtransaction", 1, "inputblinder" },
|
||||
{ "rawblindrawtransaction", 2, "inputamount" },
|
||||
{ "rawblindrawtransaction", 3, "inputasset" },
|
||||
{ "rawblindrawtransaction", 4, "inputassetblinder" },
|
||||
{ "rawblindrawtransaction", 6, "ignoreblindfail" },
|
||||
{ "blindrawtransaction", 1, "ignoreblindfail" },
|
||||
{ "blindrawtransaction", 2, "asset_commitments" },
|
||||
{ "blindrawtransaction", 3, "blind_issuances" },
|
||||
{ "destroyamount", 1, "amount" },
|
||||
{ "sendmany", 8 , "output_assets" },
|
||||
{ "sendmany", 9 , "ignoreblindfail" },
|
||||
{ "sendtoaddress", 9 , "ignoreblindfail" },
|
||||
{ "importblindingkey", 2, "key_is_master"},
|
||||
{ "createrawtransaction", 4, "output_assets" },
|
||||
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
|
|
|||
|
|
@ -659,7 +659,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
|
|||
result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex());
|
||||
result.pushKV("transactions", transactions);
|
||||
result.pushKV("coinbaseaux", aux);
|
||||
result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue);
|
||||
result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue.GetAmount());
|
||||
result.pushKV("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast));
|
||||
result.pushKV("target", hashTarget.GetHex());
|
||||
result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1);
|
||||
|
|
@ -981,7 +981,7 @@ UniValue getnewblockhex(const JSONRPCRequest& request)
|
|||
{
|
||||
if (request.fHelp || request.params.size() > 1)
|
||||
throw std::runtime_error(
|
||||
"getnewblockhex\n"
|
||||
"getnewblockhex ( min_tx_age )\n"
|
||||
"\nGets hex representation of a proposed, unmined new block\n"
|
||||
"\nArguments:\n"
|
||||
"1. min_tx_age (numeric, optional, default=0) How many seconds a transaction must have been in the mempool to be inluded in the block proposal. This may help with faster block convergence among functionaries using compact blocks.\n"
|
||||
|
|
|
|||
113
src/rpc/misc.cpp
113
src/rpc/misc.cpp
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include <univalue.h>
|
||||
|
||||
#include <assetsdir.h>
|
||||
|
||||
static UniValue validateaddress(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 1)
|
||||
|
|
@ -52,6 +54,8 @@ static UniValue validateaddress(const JSONRPCRequest& request)
|
|||
" \"iswitness\" : true|false, (boolean) If the address is a witness address\n"
|
||||
" \"witness_version\" : version (numeric, optional) The version number of the witness program\n"
|
||||
" \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program\n"
|
||||
" \"confidential_key\" : \"hex\", (string) The hex value of the raw blinding public key for that address, if any. \"\" if none.\n"
|
||||
" \"unconfidential\" : \"address\", (string) The address without confidentiality key.\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
|
||||
|
|
@ -75,6 +79,8 @@ static UniValue validateaddress(const JSONRPCRequest& request)
|
|||
|
||||
UniValue detail = DescribeAddress(dest);
|
||||
ret.pushKVs(detail);
|
||||
UniValue blind_detail = DescribeBlindAddress(dest);
|
||||
ret.pushKVs(blind_detail);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -177,8 +183,8 @@ static UniValue verifymessage(const JSONRPCRequest& request)
|
|||
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
|
||||
}
|
||||
|
||||
const CKeyID *keyID = boost::get<CKeyID>(&destination);
|
||||
if (!keyID) {
|
||||
const PKHash *pkhash = boost::get<PKHash>(&destination);
|
||||
if (!pkhash) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
|
||||
}
|
||||
|
||||
|
|
@ -196,7 +202,7 @@ static UniValue verifymessage(const JSONRPCRequest& request)
|
|||
if (!pubkey.RecoverCompact(ss.GetHash(), vchSig))
|
||||
return false;
|
||||
|
||||
return (pubkey.GetID() == *keyID);
|
||||
return (pubkey.GetID() == *pkhash);
|
||||
}
|
||||
|
||||
static UniValue signmessagewithprivkey(const JSONRPCRequest& request)
|
||||
|
|
@ -470,7 +476,7 @@ UniValue tweakfedpegscript(const JSONRPCRequest& request)
|
|||
std::vector<unsigned char> scriptData = ParseHex(request.params[0].get_str());
|
||||
CScript claim_script = CScript(scriptData.begin(), scriptData.end());
|
||||
CScript tweaked_script = calculate_contract(Params().GetConsensus().fedpegScript, claim_script);
|
||||
CTxDestination parent_addr(CScriptID(GetScriptForWitness(tweaked_script)));
|
||||
CTxDestination parent_addr(ScriptHash(GetScriptForWitness(tweaked_script)));
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.pushKV("script", HexStr(tweaked_script));
|
||||
|
|
@ -542,6 +548,103 @@ UniValue calcfastmerkleroot(const JSONRPCRequest& request)
|
|||
return ret;
|
||||
}
|
||||
|
||||
UniValue dumpassetlabels(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 0)
|
||||
throw std::runtime_error(
|
||||
"dumpassetlabels\n"
|
||||
"\nLists all known asset id/label pairs in this wallet. This list can be modified with `-assetdir` configuration argument.\n"
|
||||
+ HelpExampleCli("dumpassetlabels", "" )
|
||||
+ HelpExampleRpc("dumpassetlabels", "" )
|
||||
);
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
for (const auto& as : gAssetsDir.GetKnownAssets()) {
|
||||
obj.pushKV(gAssetsDir.GetLabel(as), as.GetHex());
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
class BlindingPubkeyAdderVisitor : public boost::static_visitor<>
|
||||
{
|
||||
public:
|
||||
CPubKey blind_key;
|
||||
explicit BlindingPubkeyAdderVisitor(const CPubKey& blind_key_in) : blind_key(blind_key_in) {}
|
||||
|
||||
void operator()(const CNoDestination& dest) const {}
|
||||
|
||||
void operator()(PKHash& keyID) const
|
||||
{
|
||||
keyID.blinding_pubkey = blind_key;
|
||||
}
|
||||
|
||||
void operator()(ScriptHash& scriptID) const
|
||||
{
|
||||
scriptID.blinding_pubkey = blind_key;
|
||||
}
|
||||
|
||||
void operator()(WitnessV0KeyHash& id) const
|
||||
{
|
||||
id.blinding_pubkey = blind_key;
|
||||
}
|
||||
|
||||
void operator()(WitnessV0ScriptHash& id) const
|
||||
{
|
||||
id.blinding_pubkey = blind_key;
|
||||
}
|
||||
|
||||
void operator()(WitnessUnknown& id) const
|
||||
{
|
||||
id.blinding_pubkey = blind_key;
|
||||
}
|
||||
|
||||
void operator()(const NullData& id) const {}
|
||||
};
|
||||
|
||||
|
||||
UniValue createblindedaddress(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 2)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"createblindedaddress address blinding_key\n"
|
||||
"\nCreates a blinded address using the provided blinding key.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"address\" (string, required) The unblinded address to be blinded.\n"
|
||||
"2. \"blinding_key\" (string, required) The blinding public key. This can be obtained for a given address using `validateaddress`.\n"
|
||||
"\nResult:\n"
|
||||
"\"blinded_address\" (string) The blinded address.\n"
|
||||
"\nExamples:\n"
|
||||
"\nCreate a multisig address from 2 addresses\n"
|
||||
+ HelpExampleCli("createblindedaddress", "HEZk3iQi1jC49bxUriTtynnXgWWWdAYx16 ec09811118b6febfa5ebe68642e5091c418fbace07e655da26b4a845a691fc2d") +
|
||||
"\nAs a json rpc call\n"
|
||||
+ HelpExampleRpc("createblindedaddress", "HEZk3iQi1jC49bxUriTtynnXgWWWdAYx16, ec09811118b6febfa5ebe68642e5091c418fbace07e655da26b4a845a691fc2d")
|
||||
);
|
||||
}
|
||||
|
||||
CTxDestination address = DecodeDestination(request.params[0].get_str());
|
||||
if (!IsValidDestination(address)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
|
||||
}
|
||||
if (IsBlindDestination(address)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not an unblinded address");
|
||||
}
|
||||
|
||||
if (!IsHex(request.params[1].get_str())) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal for key");
|
||||
}
|
||||
std::vector<unsigned char> keydata = ParseHex(request.params[1].get_str());
|
||||
if (keydata.size() != 33) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal key length, must be length 66.");
|
||||
}
|
||||
|
||||
CPubKey key;
|
||||
key.Set(keydata.begin(), keydata.end());
|
||||
|
||||
// Append blinding key and return
|
||||
boost::apply_visitor(BlindingPubkeyAdderVisitor(key), address);
|
||||
return EncodeDestination(address);
|
||||
}
|
||||
|
||||
|
||||
// END ELEMENTS CALLS
|
||||
//
|
||||
|
|
@ -560,6 +663,8 @@ static const CRPCCommand commands[] =
|
|||
// ELEMENTS:
|
||||
{ "util", "getpakinfo", &getpakinfo, {}},
|
||||
{ "util", "tweakfedpegscript", &tweakfedpegscript, {"claim_script"} },
|
||||
{ "util", "createblindedaddress", &createblindedaddress, {"address", "blinding_key"}},
|
||||
{ "util", "dumpassetlabels", &dumpassetlabels, {}},
|
||||
{ "hidden", "calcfastmerkleroot", &calcfastmerkleroot, {"leaves"} },
|
||||
|
||||
/* Not shown in help */
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <asset.h>
|
||||
#include <chain.h>
|
||||
#include <coins.h>
|
||||
#include <compat/byteswap.h>
|
||||
|
|
@ -27,7 +28,11 @@
|
|||
#include <uint256.h>
|
||||
#include <utilstrencodings.h>
|
||||
#include <validation.h>
|
||||
#include <confidential_validation.h>
|
||||
#include <validationinterface.h>
|
||||
#include <blind.h>
|
||||
#include <issuance.h>
|
||||
#include <rpc/util.h>
|
||||
|
||||
#include <future>
|
||||
#include <stdint.h>
|
||||
|
|
@ -342,7 +347,7 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request)
|
|||
return res;
|
||||
}
|
||||
|
||||
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf)
|
||||
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf, const UniValue& assets_in)
|
||||
{
|
||||
if (inputs_in.isNull() || outputs_in.isNull())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
|
||||
|
|
@ -362,6 +367,11 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
|
|||
|
||||
bool rbfOptIn = rbf.isTrue();
|
||||
|
||||
UniValue assets;
|
||||
if (!assets_in.isNull()) {
|
||||
assets = assets_in.get_obj();
|
||||
}
|
||||
|
||||
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
|
||||
const UniValue& input = inputs[idx];
|
||||
const UniValue& o = input.get_obj();
|
||||
|
|
@ -416,11 +426,22 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
|
|||
}
|
||||
outputs = std::move(outputs_dict);
|
||||
}
|
||||
// Keep track of the fee output so we can add it in the very end of the transaction.
|
||||
CTxOut fee_out;
|
||||
for (const std::string& name_ : outputs.getKeys()) {
|
||||
// ELEMENTS:
|
||||
// Asset defaults to policyAsset
|
||||
CAsset asset(::policyAsset);
|
||||
if (!assets.isNull()) {
|
||||
if (!find_value(assets, name_).isNull()) {
|
||||
asset = CAsset(ParseHashO(assets, name_));
|
||||
}
|
||||
}
|
||||
|
||||
if (name_ == "data") {
|
||||
std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
|
||||
|
||||
CTxOut out(0, CScript() << OP_RETURN << data);
|
||||
CTxOut out(asset, 0, CScript() << OP_RETURN << data);
|
||||
rawTx.vout.push_back(out);
|
||||
} else if (name_ == "vdata") {
|
||||
// ELEMENTS: support multi-push OP_RETURN
|
||||
|
|
@ -431,9 +452,12 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
|
|||
datascript << data;
|
||||
}
|
||||
|
||||
//TODO(rebase) CA asset
|
||||
CTxOut out(0, datascript);
|
||||
CTxOut out(asset, 0, datascript);
|
||||
rawTx.vout.push_back(out);
|
||||
} else if (name_ == "fee") {
|
||||
// ELEMENTS: explicit fee outputs
|
||||
CAmount nAmount = AmountFromValue(outputs[name_]);
|
||||
fee_out = CTxOut(asset, nAmount, CScript());
|
||||
} else {
|
||||
CTxDestination destination = DecodeDestination(name_);
|
||||
if (!IsValidDestination(destination)) {
|
||||
|
|
@ -447,11 +471,20 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
|
|||
CScript scriptPubKey = GetScriptForDestination(destination);
|
||||
CAmount nAmount = AmountFromValue(outputs[name_]);
|
||||
|
||||
CTxOut out(nAmount, scriptPubKey);
|
||||
CTxOut out(asset, nAmount, scriptPubKey);
|
||||
if (IsBlindDestination(destination)) {
|
||||
CPubKey blind_pub = GetDestinationBlindingKey(destination);
|
||||
out.nNonce.vchCommitment = std::vector<unsigned char>(blind_pub.begin(), blind_pub.end());
|
||||
}
|
||||
rawTx.vout.push_back(out);
|
||||
}
|
||||
}
|
||||
|
||||
// Add fee output in the end.
|
||||
if (!fee_out.nValue.IsNull() && fee_out.nValue.GetAmount() > 0) {
|
||||
rawTx.vout.push_back(fee_out);
|
||||
}
|
||||
|
||||
if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(rawTx)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
|
||||
}
|
||||
|
|
@ -461,10 +494,10 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
|
|||
|
||||
static UniValue createrawtransaction(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
|
||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) {
|
||||
throw std::runtime_error(
|
||||
// clang-format off
|
||||
"createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n"
|
||||
"createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable ) ( {\"address\":\"asset\"} )\n"
|
||||
"\nCreate a transaction spending the given inputs and creating new outputs.\n"
|
||||
"Outputs can be addresses or data.\n"
|
||||
"Returns hex-encoded raw transaction.\n"
|
||||
|
|
@ -489,6 +522,9 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
|
|||
" {\n"
|
||||
" \"data\": \"hex\" , (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
|
||||
" \"vdata\": [\"hex\"] (string, optional) The key is \"vdata\", the value is an array of hex encoded data\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" \"fee\": x.xxx (numeric or string, optional) The key is \"fee\", the value the fee output you want to add.\n"
|
||||
" }\n"
|
||||
" ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
|
||||
" accepted as second parameter.\n"
|
||||
|
|
@ -496,6 +532,12 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
|
|||
"3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
|
||||
"4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
|
||||
" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
|
||||
"5. \"output_assets\" (strings, optional, default=bitcoin) A json object of assets to addresses\n"
|
||||
" {\n"
|
||||
" \"address\": \"hex\" \n"
|
||||
" \"fee\": \"hex\" \n"
|
||||
" ...\n"
|
||||
" }\n"
|
||||
"\nResult:\n"
|
||||
"\"transaction\" (string) hex string of the transaction\n"
|
||||
|
||||
|
|
@ -512,11 +554,12 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
|
|||
UniValue::VARR,
|
||||
UniValueType(), // ARR or OBJ, checked later
|
||||
UniValue::VNUM,
|
||||
UniValue::VBOOL
|
||||
UniValue::VBOOL,
|
||||
UniValue::VOBJ
|
||||
}, true
|
||||
);
|
||||
|
||||
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
|
||||
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3], request.params[4]);
|
||||
|
||||
return EncodeHexTx(rawTx);
|
||||
}
|
||||
|
|
@ -639,7 +682,7 @@ static UniValue decodescript(const JSONRPCRequest& request)
|
|||
if (type.isStr() && type.get_str() != "scripthash") {
|
||||
// P2SH cannot be wrapped in a P2SH. If this script is already a P2SH,
|
||||
// don't return the address for a P2SH of the P2SH.
|
||||
r.pushKV("p2sh", EncodeDestination(CScriptID(script)));
|
||||
r.pushKV("p2sh", EncodeDestination(ScriptHash(CScriptID(script))));
|
||||
// P2SH and witness programs cannot be wrapped in P2WSH, if this script
|
||||
// is a witness program, don't return addresses for a segwit programs.
|
||||
if (type.get_str() == "pubkey" || type.get_str() == "pubkeyhash" || type.get_str() == "multisig" || type.get_str() == "nonstandard") {
|
||||
|
|
@ -666,7 +709,7 @@ static UniValue decodescript(const JSONRPCRequest& request)
|
|||
segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script));
|
||||
}
|
||||
ScriptPubKeyToUniv(segwitScr, sr, true);
|
||||
sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr)));
|
||||
sr.pushKV("p2sh-segwit", EncodeDestination(ScriptHash(CScriptID(segwitScr))));
|
||||
r.pushKV("segwit", sr);
|
||||
}
|
||||
}
|
||||
|
|
@ -833,9 +876,13 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
|
|||
}
|
||||
Coin newcoin;
|
||||
newcoin.out.scriptPubKey = scriptPubKey;
|
||||
newcoin.out.nValue = MAX_MONEY;
|
||||
newcoin.out.nValue = CConfidentialValue(MAX_MONEY);
|
||||
if (prevOut.exists("amount")) {
|
||||
newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount"));
|
||||
newcoin.out.nValue = CConfidentialValue(AmountFromValue(find_value(prevOut, "amount")));
|
||||
} else if (prevOut.exists("amountcommitment")) {
|
||||
// Segwit sigs require the amount commitment to be sighashed
|
||||
uint256 asset_commit = uint256S(prevOut["amountcommitment"].get_str());
|
||||
newcoin.out.nValue.vchCommitment = std::vector<unsigned char>(asset_commit.begin(), asset_commit.end());
|
||||
}
|
||||
newcoin.nHeight = 1;
|
||||
view.AddCoin(out, std::move(newcoin), true);
|
||||
|
|
@ -895,9 +942,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
|
|||
}
|
||||
|
||||
const CScript& prevPubKey = txin.m_is_pegin ? GetPeginOutputFromWitness(txConst.witness.vtxinwit[i].m_pegin_witness).scriptPubKey : coin.out.scriptPubKey;
|
||||
//TODO(rebase) CT
|
||||
//const CConfidentialValue& amount = txin.m_is_pegin ? GetPeginOutputFromWitness(txConst.witness.vtxinwit[i].m_pegin_witness).nValue : coin.out.nValue;
|
||||
const CAmount& amount = txin.m_is_pegin ? GetPeginOutputFromWitness(txConst.witness.vtxinwit[i].m_pegin_witness).nValue : coin.out.nValue;
|
||||
const CConfidentialValue& amount = txin.m_is_pegin ? GetPeginOutputFromWitness(txConst.witness.vtxinwit[i].m_pegin_witness).nValue : coin.out.nValue;
|
||||
|
||||
SignatureData sigdata = DataFromTransaction(mtx, i, coin.out);
|
||||
// Only sign SIGHASH_SINGLE if there's a corresponding output:
|
||||
|
|
@ -908,7 +953,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
|
|||
UpdateTransaction(mtx, i, sigdata);
|
||||
|
||||
// amount must be specified for valid segwit signature
|
||||
if (amount == MAX_MONEY && !inWitness.scriptWitness.IsNull()) {
|
||||
if (amount.IsExplicit() && amount.GetAmount() == MAX_MONEY && !mtx.witness.vtxinwit[i].scriptWitness.IsNull()) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin.out.ToString()));
|
||||
}
|
||||
|
||||
|
|
@ -962,7 +1007,8 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
|
|||
" \"vout\":n, (numeric, required) The output number\n"
|
||||
" \"scriptPubKey\": \"hex\", (string, required) script key\n"
|
||||
" \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n"
|
||||
" \"amount\": value (numeric, required) The amount spent\n"
|
||||
" \"amount\": value (numeric, required if non-confidential segwit output) The amount spent\n"
|
||||
" \"amountcommitment\": \"hex\", (string, required if confidential segiwt output) The amount commitment spent\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
|
|
@ -1335,6 +1381,7 @@ UniValue decodepsbt(const JSONRPCRequest& request)
|
|||
UniValue tx_univ(UniValue::VOBJ);
|
||||
TxToUniv(CTransaction(*psbtx.tx), uint256(), tx_univ, false);
|
||||
result.pushKV("tx", tx_univ);
|
||||
result.pushKV("fees", AmountMapToUniv(GetFeeMap(CTransaction(*psbtx.tx)), ""));
|
||||
|
||||
// Unknown data
|
||||
UniValue unknowns(UniValue::VOBJ);
|
||||
|
|
@ -1344,8 +1391,6 @@ UniValue decodepsbt(const JSONRPCRequest& request)
|
|||
result.pushKV("unknown", unknowns);
|
||||
|
||||
// inputs
|
||||
CAmount total_in = 0;
|
||||
bool have_all_utxos = true;
|
||||
UniValue inputs(UniValue::VARR);
|
||||
for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
|
||||
const PSBTInput& input = psbtx.inputs[i];
|
||||
|
|
@ -1356,8 +1401,11 @@ UniValue decodepsbt(const JSONRPCRequest& request)
|
|||
|
||||
UniValue out(UniValue::VOBJ);
|
||||
|
||||
out.pushKV("amount", ValueFromAmount(txout.nValue));
|
||||
total_in += txout.nValue;
|
||||
if (txout.nValue.IsExplicit()) {
|
||||
out.pushKV("amount", ValueFromAmount(txout.nValue.GetAmount()));
|
||||
} else {
|
||||
out.pushKV("amountcommitment", txout.nValue.GetHex());
|
||||
}
|
||||
|
||||
UniValue o(UniValue::VOBJ);
|
||||
ScriptToUniv(txout.scriptPubKey, o, true);
|
||||
|
|
@ -1367,9 +1415,6 @@ UniValue decodepsbt(const JSONRPCRequest& request)
|
|||
UniValue non_wit(UniValue::VOBJ);
|
||||
TxToUniv(*input.non_witness_utxo, uint256(), non_wit, false);
|
||||
in.pushKV("non_witness_utxo", non_wit);
|
||||
total_in += input.non_witness_utxo->vout[psbtx.tx->vin[i].prevout.n].nValue;
|
||||
} else {
|
||||
have_all_utxos = false;
|
||||
}
|
||||
|
||||
// Partial sigs
|
||||
|
|
@ -1441,7 +1486,6 @@ UniValue decodepsbt(const JSONRPCRequest& request)
|
|||
result.pushKV("inputs", inputs);
|
||||
|
||||
// outputs
|
||||
CAmount output_value = 0;
|
||||
UniValue outputs(UniValue::VARR);
|
||||
for (unsigned int i = 0; i < psbtx.outputs.size(); ++i) {
|
||||
const PSBTOutput& output = psbtx.outputs[i];
|
||||
|
|
@ -1481,14 +1525,8 @@ UniValue decodepsbt(const JSONRPCRequest& request)
|
|||
}
|
||||
|
||||
outputs.push_back(out);
|
||||
|
||||
// Fee calculation
|
||||
output_value += psbtx.tx->vout[i].nValue;
|
||||
}
|
||||
result.pushKV("outputs", outputs);
|
||||
if (have_all_utxos) {
|
||||
result.pushKV("fee", ValueFromAmount(total_in - output_value));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1641,6 +1679,12 @@ UniValue createpsbt(const JSONRPCRequest& request)
|
|||
"3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
|
||||
"4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
|
||||
" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
|
||||
"5. \"output_assets\" (strings, optional, default=bitcoin) A json object of assets to addresses\n"
|
||||
" {\n"
|
||||
" \"address\": \"hex\" \n"
|
||||
" \"fee\": \"hex\" \n"
|
||||
" ...\n"
|
||||
" }\n"
|
||||
"\nResult:\n"
|
||||
" \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
|
||||
"\nExamples:\n"
|
||||
|
|
@ -1656,7 +1700,7 @@ UniValue createpsbt(const JSONRPCRequest& request)
|
|||
}, true
|
||||
);
|
||||
|
||||
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]);
|
||||
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3], request.params[4]);
|
||||
|
||||
// Make a blank psbt
|
||||
PartiallySignedTransaction psbtx;
|
||||
|
|
@ -1744,12 +1788,483 @@ UniValue converttopsbt(const JSONRPCRequest& request)
|
|||
return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size());
|
||||
}
|
||||
|
||||
UniValue rawblindrawtransaction(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || (request.params.size() < 5 || request.params.size() > 7))
|
||||
throw std::runtime_error(
|
||||
"rawblindrawtransaction \"hexstring\" [\"inputblinder\",...] [\"inputamount\",...] [\"inputasset\",...] [\"inputassetblinder\",...] ( totalblinder, ignoreblindfail )\n"
|
||||
"\nConvert one or more outputs of a raw transaction into confidential ones.\n"
|
||||
"Returns the hex-encoded raw transaction.\n"
|
||||
"The input raw transaction cannot have already-blinded outputs.\n"
|
||||
"The output keys used can be specified by using a confidential address in createrawtransaction.\n"
|
||||
"If an additional blinded output is required to make a balanced blinding, a 0-value unspendable output will be added. Since there is no access to the wallet the blinding pubkey from the last output with blinding key will be repeated.\n"
|
||||
"You can not blind issuances with this call.\n"
|
||||
|
||||
"\nArguments:\n"
|
||||
"1. \"hexstring\", (string, required) A hex-encoded raw transaction.\n"
|
||||
"2. [ (array, required) An array with one entry per transaction input.\n"
|
||||
" \"inputamountblinder\" (string, required) A hex-encoded blinding factor, one for each input.\n"
|
||||
" Blinding factors can be found in the \"blinder\" output of listunspent.\n"
|
||||
" ],\n"
|
||||
"3. [ (array, required) An array with one entry per transaction input.\n"
|
||||
" \"inputamount\" (numeric, required) An amount for each input.\n"
|
||||
" ],\n"
|
||||
"4. [ (array, required) An array with one entry per transaction input.\n"
|
||||
" \"inputasset\" (string, required) A hex-encoded asset id, one for each input.\n"
|
||||
" ],\n"
|
||||
"5. [ (array, required) An array with one entry per transaction input.\n"
|
||||
" \"inputassetblinder\" (string, required) A hex-encoded asset blinding factor, one for each input.\n"
|
||||
" ],\n"
|
||||
"6. \"totalblinder\" (string, optional) Ignored for now.\n"
|
||||
"7. \"ignoreblindfail\"\" (bool, optional, default=true) Return a transaction even when a blinding attempt fails due to number of blinded inputs/outputs.\n"
|
||||
|
||||
"\nResult:\n"
|
||||
"\"transaction\" (string) hex string of the transaction\n"
|
||||
);
|
||||
|
||||
std::vector<unsigned char> txData(ParseHexV(request.params[0], "argument 1"));
|
||||
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
||||
CMutableTransaction tx;
|
||||
try {
|
||||
ssData >> tx;
|
||||
} catch (const std::exception &) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
}
|
||||
|
||||
UniValue inputBlinds = request.params[1].get_array();
|
||||
UniValue inputAmounts = request.params[2].get_array();
|
||||
UniValue inputAssets = request.params[3].get_array();
|
||||
UniValue inputAssetBlinds = request.params[4].get_array();
|
||||
|
||||
bool fIgnoreBlindFail = true;
|
||||
if (!request.params[6].isNull()) {
|
||||
fIgnoreBlindFail = request.params[6].get_bool();
|
||||
}
|
||||
|
||||
int n_blinded_ins = 0;
|
||||
|
||||
if (inputBlinds.size() != tx.vin.size()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER,
|
||||
"Invalid parameter: one (potentially empty) input blind for each input must be provided");
|
||||
}
|
||||
if (inputAmounts.size() != tx.vin.size()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER,
|
||||
"Invalid parameter: one (potentially empty) input blind for each input must be provided");
|
||||
}
|
||||
if (inputAssets.size() != tx.vin.size()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER,
|
||||
"Invalid parameter: one (potentially empty) input asset id for each input must be provided");
|
||||
}
|
||||
if (inputAssetBlinds.size() != tx.vin.size()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER,
|
||||
"Invalid parameter: one (potentially empty) input asset blind for each input must be provided");
|
||||
}
|
||||
|
||||
std::vector<CAmount> input_amounts;
|
||||
std::vector<uint256> input_blinds;
|
||||
std::vector<uint256> input_asset_blinds;
|
||||
std::vector<CAsset> input_assets;
|
||||
std::vector<uint256> output_value_blinds;
|
||||
std::vector<uint256> output_asset_blinds;
|
||||
std::vector<CAsset> output_assets;
|
||||
std::vector<CPubKey> output_pubkeys;
|
||||
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
|
||||
if (!inputBlinds[nIn].isStr())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "input blinds must be an array of hex strings");
|
||||
if (!inputAssetBlinds[nIn].isStr())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "input asset blinds must be an array of hex strings");
|
||||
if (!inputAssets[nIn].isStr())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "input asset ids must be an array of hex strings");
|
||||
|
||||
std::string blind(inputBlinds[nIn].get_str());
|
||||
std::string assetblind(inputAssetBlinds[nIn].get_str());
|
||||
std::string asset(inputAssets[nIn].get_str());
|
||||
if (!IsHex(blind) || blind.length() != 32*2)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "input blinds must be an array of 32-byte hex-encoded strings");
|
||||
if (!IsHex(assetblind) || assetblind.length() != 32*2)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "input asset blinds must be an array of 32-byte hex-encoded strings");
|
||||
if (!IsHex(asset) || asset.length() != 32*2)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "input asset blinds must be an array of 32-byte hex-encoded strings");
|
||||
|
||||
input_blinds.push_back(uint256S(blind));
|
||||
input_asset_blinds.push_back(uint256S(assetblind));
|
||||
input_assets.push_back(CAsset(uint256S(asset)));
|
||||
input_amounts.push_back(AmountFromValue(inputAmounts[nIn]));
|
||||
|
||||
if (!input_blinds.back().IsNull()) {
|
||||
n_blinded_ins++;
|
||||
}
|
||||
}
|
||||
|
||||
RawFillBlinds(tx, output_value_blinds, output_asset_blinds, output_pubkeys);
|
||||
|
||||
// How many are we trying to blind?
|
||||
int num_pubkeys = 0;
|
||||
unsigned int keyIndex = -1;
|
||||
for (unsigned int i = 0; i < output_pubkeys.size(); i++) {
|
||||
const CPubKey& key = output_pubkeys[i];
|
||||
if (key.IsValid()) {
|
||||
num_pubkeys++;
|
||||
keyIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_pubkeys == 0 && n_blinded_ins == 0) {
|
||||
// Vacuous, just return the transaction
|
||||
return EncodeHexTx(tx);
|
||||
} else if (n_blinded_ins > 0 && num_pubkeys == 0) {
|
||||
// No notion of wallet, cannot complete this blinding without passed-in pubkey
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Unable to blind transaction: Add another output to blind in order to complete the blinding.");
|
||||
} else if (n_blinded_ins == 0 && num_pubkeys == 1) {
|
||||
if (fIgnoreBlindFail) {
|
||||
// Just get rid of the ECDH key in the nonce field and return
|
||||
tx.vout[keyIndex].nNonce.SetNull();
|
||||
return EncodeHexTx(tx);
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Unable to blind transaction: Add another output to blind in order to complete the blinding.");
|
||||
}
|
||||
}
|
||||
|
||||
int ret = BlindTransaction(input_blinds, input_asset_blinds, input_assets, input_amounts, output_value_blinds, output_asset_blinds, output_pubkeys, std::vector<CKey>(), std::vector<CKey>(), tx);
|
||||
if (ret != num_pubkeys) {
|
||||
// TODO Have more rich return values, communicating to user what has been blinded
|
||||
// User may be ok not blinding something that for instance has no corresponding type on input
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Unable to blind transaction: Are you sure each asset type to blind is represented in the inputs?");
|
||||
}
|
||||
|
||||
return EncodeHexTx(tx);
|
||||
}
|
||||
|
||||
struct RawIssuanceDetails
|
||||
{
|
||||
int input_index;
|
||||
uint256 entropy;
|
||||
CAsset asset;
|
||||
CAsset token;
|
||||
};
|
||||
|
||||
// Appends a single issuance to the first input that doesn't have one, and includes
|
||||
// a single output per asset type in shuffled positions.
|
||||
void issueasset_base(CMutableTransaction& mtx, RawIssuanceDetails& issuance_details, const CAmount asset_amount, const CAmount token_amount, const std::string& asset_address_str, const std::string& token_address_str, const bool blind_issuance, const uint256& contract_hash)
|
||||
{
|
||||
|
||||
CTxDestination asset_address(DecodeDestination(asset_address_str));
|
||||
CTxDestination token_address(DecodeDestination(token_address_str));
|
||||
CScript asset_destination = GetScriptForDestination(asset_address);
|
||||
CScript token_destination = GetScriptForDestination(token_address);
|
||||
|
||||
// Find an input with no issuance field
|
||||
size_t issuance_input_index = 0;
|
||||
for (; issuance_input_index < mtx.vin.size(); issuance_input_index++) {
|
||||
if (mtx.vin[issuance_input_index].assetIssuance.IsNull()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Can't add another one, exit
|
||||
if (issuance_input_index == mtx.vin.size()) {
|
||||
issuance_details.input_index = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
uint256 entropy;
|
||||
CAsset asset;
|
||||
CAsset token;
|
||||
GenerateAssetEntropy(entropy, mtx.vin[issuance_input_index].prevout, contract_hash);
|
||||
CalculateAsset(asset, entropy);
|
||||
CalculateReissuanceToken(token, entropy, blind_issuance);
|
||||
|
||||
issuance_details.input_index = issuance_input_index;
|
||||
issuance_details.entropy = entropy;
|
||||
issuance_details.asset = asset;
|
||||
issuance_details.token = token;
|
||||
|
||||
mtx.vin[issuance_input_index].assetIssuance.assetEntropy = contract_hash;
|
||||
|
||||
// Place assets into randomly placed output slots, just insert in place
|
||||
// -1 due to fee output being at the end no matter what.
|
||||
int asset_place = GetRandInt(mtx.vout.size()-1);
|
||||
int token_place = GetRandInt(mtx.vout.size()); // Don't bias insertion
|
||||
|
||||
CTxOut asset_out(asset, asset_amount, asset_destination);
|
||||
// If blinded address, insert the pubkey into the nonce field for later substitution by blinding
|
||||
if (IsBlindDestination(asset_address)) {
|
||||
CPubKey asset_blind = GetDestinationBlindingKey(asset_address);
|
||||
asset_out.nNonce.vchCommitment = std::vector<unsigned char>(asset_blind.begin(), asset_blind.end());
|
||||
}
|
||||
// Explicit 0 is represented by a null value, don't set to non-null in that case
|
||||
if (blind_issuance || asset_amount != 0) {
|
||||
mtx.vin[issuance_input_index].assetIssuance.nAmount = asset_amount;
|
||||
}
|
||||
// Don't make zero value output(impossible by consensus)
|
||||
if (asset_amount > 0) {
|
||||
mtx.vout.insert(mtx.vout.begin()+asset_place, asset_out);
|
||||
}
|
||||
|
||||
CTxOut token_out(token, token_amount, token_destination);
|
||||
// If blinded address, insert the pubkey into the nonce field for later substitution by blinding
|
||||
if (IsBlindDestination(token_address)) {
|
||||
CPubKey token_blind = GetDestinationBlindingKey(token_address);
|
||||
token_out.nNonce.vchCommitment = std::vector<unsigned char>(token_blind.begin(), token_blind.end());
|
||||
}
|
||||
// Explicit 0 is represented by a null value, don't set to non-null in that case
|
||||
if (blind_issuance || token_amount != 0) {
|
||||
mtx.vin[issuance_input_index].assetIssuance.nInflationKeys = token_amount;
|
||||
}
|
||||
// Don't make zero value output(impossible by consensus)
|
||||
if (token_amount > 0) {
|
||||
mtx.vout.insert(mtx.vout.begin()+token_place, token_out);
|
||||
}
|
||||
}
|
||||
|
||||
// Appends a single reissuance to the specified input if none exists,
|
||||
// and the corresponding output in a shuffled position. Errors otherwise.
|
||||
void reissueasset_base(CMutableTransaction& mtx, int& issuance_input_index, const CAmount asset_amount, const std::string& asset_address_str, const uint256& asset_blinder, const uint256& entropy)
|
||||
{
|
||||
|
||||
CTxDestination asset_address(DecodeDestination(asset_address_str));
|
||||
CScript asset_destination = GetScriptForDestination(asset_address);
|
||||
|
||||
// Check if issuance already exists, error if already exists
|
||||
if ((size_t)issuance_input_index >= mtx.vin.size() || !mtx.vin[issuance_input_index].assetIssuance.IsNull()) {
|
||||
issuance_input_index = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
CAsset asset;
|
||||
CalculateAsset(asset, entropy);
|
||||
|
||||
mtx.vin[issuance_input_index].assetIssuance.assetEntropy = entropy;
|
||||
mtx.vin[issuance_input_index].assetIssuance.assetBlindingNonce = asset_blinder;
|
||||
mtx.vin[issuance_input_index].assetIssuance.nAmount = asset_amount;
|
||||
|
||||
// Place assets into randomly placed output slots, before change output, inserted in place
|
||||
assert(mtx.vout.size() >= 1);
|
||||
int asset_place = GetRandInt(mtx.vout.size()-1);
|
||||
|
||||
CTxOut asset_out(asset, asset_amount, asset_destination);
|
||||
// If blinded address, insert the pubkey into the nonce field for later substitution by blinding
|
||||
if (IsBlindDestination(asset_address)) {
|
||||
CPubKey asset_blind = GetDestinationBlindingKey(asset_address);
|
||||
asset_out.nNonce.vchCommitment = std::vector<unsigned char>(asset_blind.begin(), asset_blind.end());
|
||||
}
|
||||
assert(asset_amount > 0);
|
||||
mtx.vout.insert(mtx.vout.begin()+asset_place, asset_out);
|
||||
mtx.vin[issuance_input_index].assetIssuance.nAmount = asset_amount;
|
||||
}
|
||||
|
||||
UniValue rawissueasset(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 2)
|
||||
throw std::runtime_error(
|
||||
"rawissueasset transaction [{\"asset_amount\":x.xxx, \"asset_address\":\"address\", \"token_amount\":x.xxx, \"token_address\":\"address\", \"blind\":bool, ( \"contract_hash\":\"hash\" )}, ...]\n"
|
||||
"\nCreate an asset by attaching issuances to transaction inputs. Returns the transaction hex. There must be as many inputs as issuances requested. The final transaction hex is the final version of the transaction appended to the last object in the array.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"transaction\" (string, required) Transaction in hex in which to include an issuance input.\n"
|
||||
"2. \"issuances\" (list, required) List of issuances to create. Each issuance must have one non-zero amount. \n"
|
||||
"[\n"
|
||||
" {\n"
|
||||
" \"asset_amount\":x.xxx (numeric or string, optional) Amount of asset to generate, if any.\n"
|
||||
" \"asset_address\":addr (string, optional) Destination address of generated asset. Required if `asset_amount` given.\n"
|
||||
" \"token_amount\":x.xxx (numeric or string, optional) Amount of reissuance token to generate, if any.\n"
|
||||
" \"token_address\":addr (string, optional) Destination address of generated reissuance tokens. Required if `token_amount` given.\n"
|
||||
" \"blind\":bool (bool, optional, default=true) Whether to mark the issuance input for blinding or not. Only affects issuances with re-issuance tokens."
|
||||
" \"contract_hash\":str (string, optional, default=00..00) Contract hash that is put into issuance definition. Must be 32 bytes worth in hex string form. This will affect the asset id."
|
||||
" }\n"
|
||||
" ...\n"
|
||||
"]\n"
|
||||
"\nResult:\n"
|
||||
"[ (json array) Results of issuances, in the order of `issuances` argument\n"
|
||||
" { (json object)\n"
|
||||
" \"hex\":<hex>, (string) The transaction with issuances appended. Only appended to final index in returned array.\n"
|
||||
" \"vin\":\"n\", (numeric) The input position of the issuance in the transaction.\n"
|
||||
" \"entropy\":\"<entropy>\" (string) Entropy of the asset type.\n"
|
||||
" \"asset\":\"<asset>\", (string) Asset type for issuance if known.\n"
|
||||
" \"token\":\"<token>\", (string) Token type for issuance.\n"
|
||||
" },\n"
|
||||
" ...\n"
|
||||
"]"
|
||||
);
|
||||
|
||||
CMutableTransaction mtx;
|
||||
|
||||
if (!DecodeHexTx(mtx, request.params[0].get_str()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
|
||||
UniValue issuances = request.params[1].get_array();
|
||||
|
||||
std::string asset_address_str = "";
|
||||
std::string token_address_str = "";
|
||||
|
||||
UniValue ret(UniValue::VARR);
|
||||
|
||||
// Count issuances, only append hex to final one
|
||||
unsigned int issuances_til_now = 0;
|
||||
|
||||
for (unsigned int idx = 0; idx < issuances.size(); idx++) {
|
||||
const UniValue& issuance = issuances[idx];
|
||||
const UniValue& issuance_o = issuance.get_obj();
|
||||
|
||||
CAmount asset_amount = 0;
|
||||
const UniValue& asset_amount_uni = issuance_o["asset_amount"];
|
||||
if (asset_amount_uni.isNum()) {
|
||||
asset_amount = AmountFromValue(asset_amount_uni);
|
||||
if (asset_amount <= 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, asset_amount must be positive");
|
||||
}
|
||||
const UniValue& asset_address_uni = issuance_o["asset_address"];
|
||||
if (!asset_address_uni.isStr()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing corresponding asset_address");
|
||||
}
|
||||
asset_address_str = asset_address_uni.get_str();
|
||||
}
|
||||
|
||||
CAmount token_amount = 0;
|
||||
const UniValue& token_amount_uni = issuance_o["token_amount"];
|
||||
if (token_amount_uni.isNum()) {
|
||||
token_amount = AmountFromValue(token_amount_uni);
|
||||
if (token_amount <= 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, token_amount must be positive");
|
||||
}
|
||||
const UniValue& token_address_uni = issuance_o["token_address"];
|
||||
if (!token_address_uni.isStr()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing corresponding token_address");
|
||||
}
|
||||
token_address_str = token_address_uni.get_str();
|
||||
}
|
||||
if (asset_amount == 0 && token_amount == 0) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Issuance must have one non-zero component");
|
||||
}
|
||||
|
||||
// If we have issuances, check if reissuance tokens will be generated via blinding path
|
||||
const UniValue blind_uni = issuance_o["blind"];
|
||||
const bool blind_issuance = !blind_uni.isBool() || blind_uni.get_bool();
|
||||
|
||||
// Check for optional contract to hash into definition
|
||||
uint256 contract_hash;
|
||||
if (!issuance_o["contract_hash"].isNull()) {
|
||||
contract_hash = ParseHashV(issuance_o["contract_hash"], "contract_hash");
|
||||
}
|
||||
|
||||
RawIssuanceDetails details;
|
||||
|
||||
issueasset_base(mtx, details, asset_amount, token_amount, asset_address_str, token_address_str, blind_issuance, contract_hash);
|
||||
if (details.input_index == -1) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to find enough blank inputs for listed issuances.");
|
||||
}
|
||||
|
||||
issuances_til_now++;
|
||||
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
if (issuances_til_now == issuances.size()) {
|
||||
obj.pushKV("hex", EncodeHexTx(mtx, RPCSerializationFlags()));
|
||||
}
|
||||
obj.pushKV("vin", details.input_index);
|
||||
obj.pushKV("entropy", details.entropy.GetHex());
|
||||
obj.pushKV("asset", details.asset.GetHex());
|
||||
obj.pushKV("token", details.token.GetHex());
|
||||
|
||||
ret.push_back(obj);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
UniValue rawreissueasset(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 2)
|
||||
throw std::runtime_error(
|
||||
"rawreissueasset transaction {\"vin\":\"n\", \"asset_amount\":x.xxx, \"asset_address\":\"address\", \"asset_blinder\":<hex>, \"entropy\":<hex>, ( \"contract_hash\":<hex> )}\n"
|
||||
"\nRe-issue an asset by attaching pseudo-inputs to transaction inputs, revealing the underlying reissuance token of the input. Returns the transaction hex.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"transaction\" (string, required) Transaction in hex in which to include an issuance input.\n"
|
||||
"2. \"reissuances\" (list, required) List of re-issuances to create. Each issuance must have one non-zero amount.\n"
|
||||
"[\n"
|
||||
" {\n"
|
||||
" \"input_index\":\"n\", (numeric, required) The input position of the reissuance in the transaction.\n"
|
||||
" \"asset_amount\":x.xxx, (numeric or string, required) Amount of asset to generate, if any.\n"
|
||||
" \"asset_address\":addr, (string, required) Destination address of generated asset. Required if `asset_amount` given.\n"
|
||||
" \"asset_blinder\":<hex>, (string, required) The blinding factor of the reissuance token output being spent.\n"
|
||||
" \"entropy\":<hex>, (string, required) The `entropy` returned during initial issuance for the asset being reissued."
|
||||
" }\n"
|
||||
"\nResult:\n"
|
||||
"{ (json object)\n"
|
||||
" \"hex\":<hex>, (string) The transaction with reissuances appended.\n"
|
||||
"}\n"
|
||||
);
|
||||
|
||||
CMutableTransaction mtx;
|
||||
|
||||
if (!DecodeHexTx(mtx, request.params[0].get_str()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
|
||||
if (mtx.vout.empty()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction must have at least one output.");
|
||||
}
|
||||
|
||||
UniValue issuances = request.params[1].get_array();
|
||||
|
||||
unsigned int num_issuances = 0;
|
||||
|
||||
for (unsigned int idx = 0; idx < issuances.size(); idx++) {
|
||||
const UniValue& issuance = issuances[idx];
|
||||
const UniValue& issuance_o = issuance.get_obj();
|
||||
|
||||
CAmount asset_amount = 0;
|
||||
const UniValue& asset_amount_uni = issuance_o["asset_amount"];
|
||||
if (asset_amount_uni.isNum()) {
|
||||
asset_amount = AmountFromValue(asset_amount_uni);
|
||||
if (asset_amount <= 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, asset_amount must be positive");
|
||||
}
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Asset amount must be given for each reissuance.");
|
||||
}
|
||||
|
||||
const UniValue& asset_address_uni = issuance_o["asset_address"];
|
||||
if (!asset_address_uni.isStr()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Reissuance missing asset_address");
|
||||
}
|
||||
std::string asset_address_str = asset_address_uni.get_str();
|
||||
|
||||
int input_index = -1;
|
||||
const UniValue& input_index_o = issuance_o["input_index"];
|
||||
if (input_index_o.isNum()) {
|
||||
input_index = input_index_o.get_int();
|
||||
if (input_index < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Input index must be non-negative.");
|
||||
}
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Input indexes for all reissuances are required.");
|
||||
}
|
||||
|
||||
uint256 asset_blinder = ParseHashV(issuance_o["asset_blinder"], "asset_blinder");
|
||||
|
||||
uint256 entropy = ParseHashV(issuance_o["entropy"], "entropy");
|
||||
|
||||
reissueasset_base(mtx, input_index, asset_amount, asset_address_str, asset_blinder, entropy);
|
||||
if (input_index == -1) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Selected transaction input already has issuance data.");
|
||||
}
|
||||
|
||||
num_issuances++;
|
||||
}
|
||||
|
||||
if (num_issuances != issuances.size()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to find enough blank inputs for listed issuances.");
|
||||
}
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.pushKV("hex", EncodeHexTx(mtx, RPCSerializationFlags()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// clang-format off
|
||||
static const CRPCCommand commands[] =
|
||||
{ // category name actor (function) argNames
|
||||
// --------------------- ------------------------ ----------------------- ----------
|
||||
{ "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} },
|
||||
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
|
||||
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable", "output_assets"} },
|
||||
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
|
||||
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
|
||||
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} },
|
||||
|
|
@ -1762,9 +2277,12 @@ static const CRPCCommand commands[] =
|
|||
{ "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} },
|
||||
{ "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime","replaceable"} },
|
||||
{ "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata","iswitness"} },
|
||||
{ "rawtransactions", "rawblindrawtransaction", &rawblindrawtransaction, {"hexstring", "inputblinder", "inputamount", "inputasset", "inputassetblinder", "totalblinder", "ignoreblindfail"} },
|
||||
|
||||
{ "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
|
||||
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
|
||||
{ "rawtransactions", "rawissueasset", &rawissueasset, {"transaction", "issuances"}},
|
||||
{ "rawtransactions", "rawreissueasset", &rawreissueasset, {"transaction", "reissuances"}},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,6 @@ class UniValue;
|
|||
UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxs, CBasicKeyStore *keystore, bool tempKeystore, const UniValue& hashType);
|
||||
|
||||
/** Create a transaction from univalue parameters */
|
||||
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf);
|
||||
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf, const UniValue& assets_in);
|
||||
|
||||
#endif // BITCOIN_RPC_RAWTRANSACTION_H
|
||||
|
|
|
|||
176
src/rpc/util.cpp
176
src/rpc/util.cpp
|
|
@ -8,6 +8,9 @@
|
|||
#include <rpc/util.h>
|
||||
#include <tinyformat.h>
|
||||
#include <utilstrencodings.h>
|
||||
#include <policy/policy.h>
|
||||
#include <assetsdir.h>
|
||||
#include <core_io.h>
|
||||
|
||||
// Converts a hex string to a public key if possible
|
||||
CPubKey HexToPubKey(const std::string& hex_in)
|
||||
|
|
@ -76,7 +79,7 @@ public:
|
|||
return UniValue(UniValue::VOBJ);
|
||||
}
|
||||
|
||||
UniValue operator()(const CKeyID& keyID) const
|
||||
UniValue operator()(const PKHash& keyID) const
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.pushKV("isscript", false);
|
||||
|
|
@ -84,7 +87,7 @@ public:
|
|||
return obj;
|
||||
}
|
||||
|
||||
UniValue operator()(const CScriptID& scriptID) const
|
||||
UniValue operator()(const ScriptHash& scriptID) const
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.pushKV("isscript", true);
|
||||
|
|
@ -134,3 +137,172 @@ UniValue DescribeAddress(const CTxDestination& dest)
|
|||
{
|
||||
return boost::apply_visitor(DescribeAddressVisitor(), dest);
|
||||
}
|
||||
|
||||
// ELEMENTS
|
||||
|
||||
class BlindingPubkeyVisitor : public boost::static_visitor<CPubKey>
|
||||
{
|
||||
public:
|
||||
explicit BlindingPubkeyVisitor() {}
|
||||
|
||||
CPubKey operator()(const CNoDestination& dest) const
|
||||
{
|
||||
return CPubKey();
|
||||
}
|
||||
|
||||
CPubKey operator()(const PKHash& keyID) const
|
||||
{
|
||||
return keyID.blinding_pubkey;
|
||||
}
|
||||
|
||||
CPubKey operator()(const ScriptHash& scriptID) const
|
||||
{
|
||||
return scriptID.blinding_pubkey;
|
||||
}
|
||||
|
||||
CPubKey operator()(const WitnessV0KeyHash& id) const
|
||||
{
|
||||
return id.blinding_pubkey;
|
||||
}
|
||||
|
||||
CPubKey operator()(const WitnessV0ScriptHash& id) const
|
||||
{
|
||||
return id.blinding_pubkey;
|
||||
}
|
||||
|
||||
CPubKey operator()(const WitnessUnknown& id) const
|
||||
{
|
||||
return id.blinding_pubkey;
|
||||
}
|
||||
|
||||
CPubKey operator()(const NullData& id) const
|
||||
{
|
||||
return CPubKey();
|
||||
}
|
||||
};
|
||||
|
||||
CPubKey GetDestinationBlindingKey(const CTxDestination& dest) {
|
||||
return boost::apply_visitor(BlindingPubkeyVisitor(), dest);
|
||||
}
|
||||
|
||||
bool IsBlindDestination(const CTxDestination& dest) {
|
||||
return GetDestinationBlindingKey(dest).IsFullyValid();
|
||||
}
|
||||
|
||||
class DescribeBlindAddressVisitor : public boost::static_visitor<UniValue>
|
||||
{
|
||||
public:
|
||||
|
||||
explicit DescribeBlindAddressVisitor() {}
|
||||
|
||||
UniValue operator()(const CNoDestination& dest) const { return UniValue(UniValue::VOBJ); }
|
||||
|
||||
UniValue operator()(const PKHash& pkhash) const
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
const CPubKey& blind_pub = pkhash.blinding_pubkey;
|
||||
if (IsBlindDestination(pkhash)) {
|
||||
obj.pushKV("confidential_key", HexStr(blind_pub.begin(), blind_pub.end()));
|
||||
PKHash unblinded(pkhash);
|
||||
unblinded.blinding_pubkey = CPubKey();
|
||||
obj.pushKV("unconfidential", EncodeDestination(unblinded));
|
||||
} else {
|
||||
obj.pushKV("confidential_key", "");
|
||||
obj.pushKV("unconfidential", EncodeDestination(pkhash));
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue operator()(const ScriptHash& scripthash) const
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
const CPubKey& blind_pub = scripthash.blinding_pubkey;
|
||||
if (IsBlindDestination(scripthash)) {
|
||||
obj.pushKV("confidential_key", HexStr(blind_pub.begin(), blind_pub.end()));
|
||||
ScriptHash unblinded(scripthash);
|
||||
unblinded.blinding_pubkey = CPubKey();
|
||||
obj.pushKV("unconfidential", EncodeDestination(unblinded));
|
||||
} else {
|
||||
obj.pushKV("confidential_key", "");
|
||||
obj.pushKV("unconfidential", EncodeDestination(scripthash));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue operator()(const WitnessV0KeyHash& id) const
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
const CPubKey& blind_pub = id.blinding_pubkey;
|
||||
if (IsBlindDestination(id)) {
|
||||
obj.pushKV("confidential_key", HexStr(blind_pub.begin(), blind_pub.end()));
|
||||
WitnessV0KeyHash unblinded(id);
|
||||
unblinded.blinding_pubkey = CPubKey();
|
||||
obj.pushKV("unconfidential", EncodeDestination(unblinded));
|
||||
} else {
|
||||
obj.pushKV("confidential_key", "");
|
||||
obj.pushKV("unconfidential", EncodeDestination(id));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue operator()(const WitnessV0ScriptHash& id) const
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
const CPubKey& blind_pub = id.blinding_pubkey;
|
||||
if (IsBlindDestination(id)) {
|
||||
obj.pushKV("confidential_key", HexStr(blind_pub.begin(), blind_pub.end()));
|
||||
WitnessV0ScriptHash unblinded(id);
|
||||
unblinded.blinding_pubkey = CPubKey();
|
||||
obj.pushKV("unconfidential", EncodeDestination(unblinded));
|
||||
} else {
|
||||
obj.pushKV("confidential_key", "");
|
||||
obj.pushKV("unconfidential", EncodeDestination(id));
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
|
||||
UniValue operator()(const NullData& id) const { return NullUniValue; }
|
||||
};
|
||||
|
||||
UniValue DescribeBlindAddress(const CTxDestination& dest)
|
||||
{
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.pushKVs(boost::apply_visitor(DescribeBlindAddressVisitor(), dest));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Attaches labeled balance reports to UniValue obj with asset filter
|
||||
// "" displays *all* assets as VOBJ pairs, while named assets must have
|
||||
// been entered via -assetdir configuration argument and are returned as VNUM.
|
||||
UniValue AmountMapToUniv(const CAmountMap& balanceOrig, std::string strasset)
|
||||
{
|
||||
// Make sure the policyAsset is always present in the balance map.
|
||||
CAmountMap balance = balanceOrig;
|
||||
balance[::policyAsset] += 0;
|
||||
|
||||
// If we don't do assets or a specific asset is given, we filter out once asset.
|
||||
if (!g_con_elementsmode || strasset != "") {
|
||||
if (g_con_elementsmode) {
|
||||
return ValueFromAmount(balance[GetAssetFromString(strasset)]);
|
||||
} else {
|
||||
return ValueFromAmount(balance[::policyAsset]);
|
||||
}
|
||||
}
|
||||
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
for(std::map<CAsset, CAmount>::const_iterator it = balance.begin(); it != balance.end(); ++it) {
|
||||
// Unknown assets
|
||||
if (it->first.IsNull())
|
||||
continue;
|
||||
UniValue pair(UniValue::VOBJ);
|
||||
std::string label = gAssetsDir.GetLabel(it->first);
|
||||
if (label == "") {
|
||||
label = it->first.GetHex();
|
||||
}
|
||||
obj.pushKV(label, ValueFromAmount(it->second));
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,5 +23,10 @@ CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in);
|
|||
CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys);
|
||||
|
||||
UniValue DescribeAddress(const CTxDestination& dest);
|
||||
UniValue DescribeBlindAddress(const CTxDestination& dest);
|
||||
CPubKey GetDestinationBlindingKey(const CTxDestination& dest);
|
||||
bool IsBlindDestination(const CTxDestination& dest);
|
||||
|
||||
UniValue AmountMapToUniv(const CAmountMap& balanceOrig, std::string strasset);
|
||||
|
||||
#endif // BITCOIN_RPC_UTIL_H
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ static bool verify_flags(unsigned int flags)
|
|||
return (flags & ~(bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL)) == 0;
|
||||
}
|
||||
|
||||
static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CAmount amount,
|
||||
static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CConfidentialValue amount,
|
||||
const unsigned char *txTo , unsigned int txToLen,
|
||||
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
|
||||
{
|
||||
|
|
@ -102,12 +102,20 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP
|
|||
}
|
||||
}
|
||||
|
||||
int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount,
|
||||
int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
|
||||
const unsigned char *amount, unsigned int amountLen,
|
||||
const unsigned char *txTo , unsigned int txToLen,
|
||||
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
|
||||
{
|
||||
CAmount am(amount);
|
||||
return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err);
|
||||
try {
|
||||
TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, amount, amountLen);
|
||||
CConfidentialValue am;
|
||||
stream >> am;
|
||||
|
||||
return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err);
|
||||
} catch (const std::exception&) {
|
||||
return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -119,7 +127,7 @@ int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned i
|
|||
return set_error(err, bitcoinconsensus_ERR_AMOUNT_REQUIRED);
|
||||
}
|
||||
|
||||
CAmount am(0);
|
||||
CConfidentialValue am(0);
|
||||
return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,8 @@ EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *scriptPubK
|
|||
const unsigned char *txTo , unsigned int txToLen,
|
||||
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err);
|
||||
|
||||
EXPORT_SYMBOL int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount,
|
||||
EXPORT_SYMBOL int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
|
||||
const unsigned char *amount, unsigned int amountLen,
|
||||
const unsigned char *txTo , unsigned int txToLen,
|
||||
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err);
|
||||
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
CScript P2PKHGetScript(const CPubKey& pubkey) { return GetScriptForDestination(pubkey.GetID()); }
|
||||
CScript P2PKHGetScript(const CPubKey& pubkey) { return GetScriptForDestination(PKHash(pubkey)); }
|
||||
CScript P2PKGetScript(const CPubKey& pubkey) { return GetScriptForRawPubKey(pubkey); }
|
||||
CScript P2WPKHGetScript(const CPubKey& pubkey) { return GetScriptForDestination(WitnessV0KeyHash(pubkey.GetID())); }
|
||||
|
||||
|
|
@ -320,7 +320,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
CScript ConvertP2SH(const CScript& script) { return GetScriptForDestination(CScriptID(script)); }
|
||||
CScript ConvertP2SH(const CScript& script) { return GetScriptForDestination(ScriptHash(script)); }
|
||||
CScript ConvertP2WSH(const CScript& script) { return GetScriptForDestination(WitnessV0ScriptHash(script)); }
|
||||
|
||||
/** A parsed combo(P) descriptor. */
|
||||
|
|
@ -347,14 +347,14 @@ public:
|
|||
CKeyID keyid = key.GetID();
|
||||
{
|
||||
CScript p2pk = GetScriptForRawPubKey(key);
|
||||
CScript p2pkh = GetScriptForDestination(keyid);
|
||||
CScript p2pkh = GetScriptForDestination(PKHash(keyid));
|
||||
output_scripts = std::vector<CScript>{std::move(p2pk), std::move(p2pkh)};
|
||||
out.pubkeys.emplace(keyid, key);
|
||||
}
|
||||
if (key.IsCompressed()) {
|
||||
CScript p2wpkh = GetScriptForDestination(WitnessV0KeyHash(keyid));
|
||||
CScriptID p2wpkh_id(p2wpkh);
|
||||
CScript p2sh_p2wpkh = GetScriptForDestination(p2wpkh_id);
|
||||
CScript p2sh_p2wpkh = GetScriptForDestination(ScriptHash(p2wpkh_id));
|
||||
out.scripts.emplace(p2wpkh_id, p2wpkh);
|
||||
output_scripts.push_back(std::move(p2wpkh));
|
||||
output_scripts.push_back(std::move(p2sh_p2wpkh));
|
||||
|
|
|
|||
|
|
@ -1519,6 +1519,11 @@ public:
|
|||
::Serialize(s, (int)0);
|
||||
else
|
||||
::Serialize(s, txTo.vin[nInput].nSequence);
|
||||
// Serialize the asset issuance object
|
||||
if (!txTo.vin[nInput].assetIssuance.IsNull()) {
|
||||
assert(g_con_elementsmode);
|
||||
::Serialize(s, txTo.vin[nInput].assetIssuance);
|
||||
}
|
||||
}
|
||||
|
||||
/** Serialize an output of txTo */
|
||||
|
|
@ -1571,6 +1576,19 @@ uint256 GetSequenceHash(const T& txTo)
|
|||
return ss.GetHash();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uint256 GetIssuanceHash(const T& txTo)
|
||||
{
|
||||
CHashWriter ss(SER_GETHASH, 0);
|
||||
for (const auto& txin : txTo.vin) {
|
||||
if (txin.assetIssuance.IsNull())
|
||||
ss << (unsigned char)0;
|
||||
else
|
||||
ss << txin.assetIssuance;
|
||||
}
|
||||
return ss.GetHash();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uint256 GetOutputsHash(const T& txTo)
|
||||
{
|
||||
|
|
@ -1590,6 +1608,7 @@ PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
|
|||
if (txTo.HasWitness()) {
|
||||
hashPrevouts = GetPrevoutHash(txTo);
|
||||
hashSequence = GetSequenceHash(txTo);
|
||||
hashIssuance = GetIssuanceHash(txTo);
|
||||
hashOutputs = GetOutputsHash(txTo);
|
||||
ready = true;
|
||||
}
|
||||
|
|
@ -1600,13 +1619,14 @@ template PrecomputedTransactionData::PrecomputedTransactionData(const CTransacti
|
|||
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
|
||||
|
||||
template <class T>
|
||||
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
|
||||
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
|
||||
{
|
||||
assert(nIn < txTo.vin.size());
|
||||
|
||||
if (sigversion == SigVersion::WITNESS_V0) {
|
||||
uint256 hashPrevouts;
|
||||
uint256 hashSequence;
|
||||
uint256 hashIssuance;
|
||||
uint256 hashOutputs;
|
||||
const bool cacheready = cache && cache->ready;
|
||||
|
||||
|
|
@ -1618,6 +1638,9 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
|
|||
hashSequence = cacheready ? cache->hashSequence : GetSequenceHash(txTo);
|
||||
}
|
||||
|
||||
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
|
||||
hashIssuance = cacheready ? cache->hashIssuance : GetIssuanceHash(txTo);
|
||||
}
|
||||
|
||||
if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
|
||||
hashOutputs = cacheready ? cache->hashOutputs : GetOutputsHash(txTo);
|
||||
|
|
@ -1633,13 +1656,24 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
|
|||
// Input prevouts/nSequence (none/all, depending on flags)
|
||||
ss << hashPrevouts;
|
||||
ss << hashSequence;
|
||||
if (g_con_elementsmode) {
|
||||
ss << hashIssuance;
|
||||
}
|
||||
// The input being signed (replacing the scriptSig with scriptCode + amount)
|
||||
// The prevout may already be contained in hashPrevout, and the nSequence
|
||||
// may already be contain in hashSequence.
|
||||
ss << txTo.vin[nIn].prevout;
|
||||
ss << scriptCode;
|
||||
ss << amount;
|
||||
if (g_con_elementsmode) {
|
||||
ss << amount;
|
||||
} else {
|
||||
ss << amount.GetAmount();
|
||||
}
|
||||
ss << txTo.vin[nIn].nSequence;
|
||||
if (!txTo.vin[nIn].assetIssuance.IsNull()) {
|
||||
assert(g_con_elementsmode);
|
||||
ss << txTo.vin[nIn].assetIssuance;
|
||||
}
|
||||
// Outputs (none/one/all, depending on flags)
|
||||
ss << hashOutputs;
|
||||
// Locktime
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
|
|||
|
||||
struct PrecomputedTransactionData
|
||||
{
|
||||
uint256 hashPrevouts, hashSequence, hashOutputs;
|
||||
uint256 hashPrevouts, hashSequence, hashOutputs, hashIssuance;
|
||||
bool ready = false;
|
||||
|
||||
template <class T>
|
||||
|
|
@ -143,7 +143,7 @@ static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32;
|
|||
static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20;
|
||||
|
||||
template <class T>
|
||||
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
|
||||
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CConfidentialValue& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
|
||||
|
||||
class BaseSignatureChecker
|
||||
{
|
||||
|
|
@ -172,15 +172,15 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker
|
|||
private:
|
||||
const T* txTo;
|
||||
unsigned int nIn;
|
||||
const CAmount amount;
|
||||
const CConfidentialValue amount;
|
||||
const PrecomputedTransactionData* txdata;
|
||||
|
||||
protected:
|
||||
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
|
||||
|
||||
public:
|
||||
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
|
||||
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
|
||||
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
|
||||
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
|
||||
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
|
||||
bool CheckLockTime(const CScriptNum& nLockTime) const override;
|
||||
bool CheckSequence(const CScriptNum& nSequence) const override;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey,
|
|||
case TX_NONSTANDARD:
|
||||
case TX_NULL_DATA:
|
||||
case TX_WITNESS_UNKNOWN:
|
||||
case TX_FEE:
|
||||
break;
|
||||
case TX_PUBKEY:
|
||||
keyID = CPubKey(vSolutions[0]).GetID();
|
||||
|
|
@ -91,7 +92,7 @@ IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey,
|
|||
// This also applies to the P2WSH case.
|
||||
break;
|
||||
}
|
||||
ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0));
|
||||
ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(PKHash(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0));
|
||||
break;
|
||||
}
|
||||
case TX_PUBKEYHASH:
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@
|
|||
#include <uint256.h>
|
||||
#include <vector>
|
||||
|
||||
// IsUnspendable() compatibility
|
||||
extern bool g_con_elementsmode;
|
||||
|
||||
// Maximum number of bytes pushable to the stack
|
||||
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520;
|
||||
|
||||
|
|
@ -570,7 +573,8 @@ public:
|
|||
*/
|
||||
bool IsUnspendable() const
|
||||
{
|
||||
return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE);
|
||||
return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE) ||
|
||||
(g_con_elementsmode && size() == 0 /* Elements rule for fee outputs */);
|
||||
}
|
||||
|
||||
void clear()
|
||||
|
|
|
|||
|
|
@ -68,7 +68,11 @@ typedef enum ScriptError_t
|
|||
SCRIPT_ERR_OP_CODESEPARATOR,
|
||||
SCRIPT_ERR_SIG_FINDANDDELETE,
|
||||
|
||||
SCRIPT_ERR_ERROR_COUNT
|
||||
SCRIPT_ERR_ERROR_COUNT,
|
||||
|
||||
// ELEMENTS:
|
||||
SCRIPT_ERR_RANGEPROOF,
|
||||
SCRIPT_ERR_PEDERSEN_TALLY
|
||||
} ScriptError;
|
||||
|
||||
#define SCRIPT_ERR_LAST SCRIPT_ERR_ERROR_COUNT
|
||||
|
|
|
|||
|
|
@ -41,6 +41,14 @@ public:
|
|||
CSHA256().Write(nonce.begin(), 32).Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
|
||||
}
|
||||
|
||||
// ELEMENTS:
|
||||
void ComputeEntry(uint256& entry, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment) {
|
||||
CSHA256().Write(nonce.begin(), nonce.size()).Write(proof.data(), proof.size()).Write(commitment.data(), commitment.size()).Finalize(entry.begin());
|
||||
}
|
||||
void ComputeEntry(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& proof, const std::vector<unsigned char>& commitment) {
|
||||
CSHA256().Write(nonce.begin(), nonce.size()).Write(hash.begin(), 32).Write(proof.data(), proof.size()).Write(commitment.data(), commitment.size()).Finalize(entry.begin());
|
||||
}
|
||||
|
||||
bool
|
||||
Get(const uint256& entry, const bool erase)
|
||||
{
|
||||
|
|
@ -66,6 +74,11 @@ public:
|
|||
* signatureCache could be made local to VerifySignature.
|
||||
*/
|
||||
static CSignatureCache signatureCache;
|
||||
|
||||
// ELEMENTS:
|
||||
static CSignatureCache rangeProofCache;
|
||||
static CSignatureCache surjectionProofCache;
|
||||
|
||||
} // namespace
|
||||
|
||||
// To be called once in AppInitMain/BasicTestingSetup to initialize the
|
||||
|
|
@ -92,3 +105,102 @@ bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsig
|
|||
signatureCache.Set(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// ELEMENTS CACHES
|
||||
|
||||
// To be called once in AppInit2/TestingSetup to initialize the rangeproof cache
|
||||
void InitRangeproofCache()
|
||||
{
|
||||
// nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
|
||||
// setup_bytes creates the minimum possible cache (2 elements).
|
||||
size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE)), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
|
||||
size_t nElems = rangeProofCache.setup_bytes(nMaxCacheSize);
|
||||
LogPrintf("Using %zu MiB out of %zu requested for rangeproof cache, able to store %zu elements\n",
|
||||
(nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems);
|
||||
}
|
||||
|
||||
// To be called once in AppInit2/TestingSetup to initialize the surjectionrproof cache
|
||||
void InitSurjectionproofCache()
|
||||
{
|
||||
// nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
|
||||
// setup_bytes creates the minimum possible cache (2 elements).
|
||||
size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE)), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
|
||||
size_t nElems = surjectionProofCache.setup_bytes(nMaxCacheSize);
|
||||
LogPrintf("Using %zu MiB out of %zu requested for surjectionproof cache, able to store %zu elements\n",
|
||||
(nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems);
|
||||
}
|
||||
|
||||
bool CachingRangeProofChecker::VerifyRangeProof(const std::vector<unsigned char>& vchRangeProof, const std::vector<unsigned char>& vchValueCommitment, const std::vector<unsigned char>& vchAssetCommitment, const CScript& scriptPubKey, const secp256k1_context* secp256k1_ctx_verify_amounts) const
|
||||
{
|
||||
uint256 entry;
|
||||
rangeProofCache.ComputeEntry(entry, vchRangeProof, vchValueCommitment);
|
||||
|
||||
if (rangeProofCache.Get(entry, !store)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (vchRangeProof.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t min_value, max_value;
|
||||
secp256k1_pedersen_commitment commit;
|
||||
if (secp256k1_pedersen_commitment_parse(secp256k1_ctx_verify_amounts, &commit, &vchValueCommitment[0]) != 1)
|
||||
return false;
|
||||
|
||||
secp256k1_generator tag;
|
||||
if (secp256k1_generator_parse(secp256k1_ctx_verify_amounts, &tag, &vchAssetCommitment[0]) != 1)
|
||||
return false;
|
||||
|
||||
if (!secp256k1_rangeproof_verify(secp256k1_ctx_verify_amounts, &min_value, &max_value, &commit, vchRangeProof.data(), vchRangeProof.size(), scriptPubKey.size() ? &scriptPubKey.front() : NULL, scriptPubKey.size(), &tag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// An rangeproof is not valid if the output is spendable but the minimum number
|
||||
// is 0. This is to prevent people passing 0-value tokens around, or conjuring
|
||||
// reissuance tokens from nothing then attempting to reissue an asset.
|
||||
// ie reissuance doesn't require revealing value of reissuance output
|
||||
// Issuances proofs are always "unspendable" as they commit to an empty script.
|
||||
if (min_value == 0 && !scriptPubKey.IsUnspendable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (store) {
|
||||
rangeProofCache.Set(entry);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CachingSurjectionProofChecker::VerifySurjectionProof(secp256k1_surjectionproof& proof, std::vector<secp256k1_generator>& vTags, secp256k1_generator& gen, const secp256k1_context* secp256k1_ctx_verify_amounts, const uint256& wtxid) const
|
||||
{
|
||||
|
||||
// Serialize proof
|
||||
std::vector<unsigned char> vchproof;
|
||||
size_t proof_len = secp256k1_surjectionproof_serialized_size(secp256k1_ctx_verify_amounts, &proof);
|
||||
vchproof.resize(proof_len);
|
||||
assert(secp256k1_surjectionproof_serialize(secp256k1_ctx_verify_amounts, vchproof.data(), &proof_len, &proof) == 1);
|
||||
|
||||
// wtxid commits to all data including surj targets
|
||||
// we need to specify the proof and output asset point to be unique
|
||||
uint256 entry;
|
||||
surjectionProofCache.ComputeEntry(entry, wtxid, vchproof, std::vector<unsigned char>(std::begin(gen.data), std::end(gen.data)));
|
||||
|
||||
if (surjectionProofCache.Get(entry, !store)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (secp256k1_surjectionproof_verify(secp256k1_ctx_verify_amounts, &proof, vTags.data(), vTags.size(), &gen) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (store) {
|
||||
surjectionProofCache.Set(entry);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// END ELEMENTS
|
||||
//
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
#include <script/interpreter.h>
|
||||
|
||||
#include <secp256k1.h>
|
||||
#include <secp256k1_rangeproof.h>
|
||||
#include <secp256k1_surjectionproof.h>
|
||||
#include <vector>
|
||||
|
||||
// DoS prevention: limit cache size to 32MB (over 1000000 entries on 64-bit
|
||||
|
|
@ -46,11 +49,46 @@ private:
|
|||
bool store;
|
||||
|
||||
public:
|
||||
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn), store(storeIn) {}
|
||||
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn), store(storeIn) {}
|
||||
|
||||
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
|
||||
};
|
||||
|
||||
void InitSignatureCache();
|
||||
|
||||
//
|
||||
// ELEMENTS:
|
||||
|
||||
class CachingRangeProofChecker
|
||||
{
|
||||
private:
|
||||
bool store;
|
||||
public:
|
||||
CachingRangeProofChecker(bool storeIn){
|
||||
store = storeIn;
|
||||
};
|
||||
|
||||
bool VerifyRangeProof(const std::vector<unsigned char>& vchRangeProof, const std::vector<unsigned char>& vchValueCommitment, const std::vector<unsigned char>& vchAssetCommitment, const CScript& scriptPubKey, const secp256k1_context* ctx) const;
|
||||
|
||||
};
|
||||
|
||||
class CachingSurjectionProofChecker
|
||||
{
|
||||
private:
|
||||
bool store;
|
||||
public:
|
||||
CachingSurjectionProofChecker(bool storeIn){
|
||||
store = storeIn;
|
||||
};
|
||||
|
||||
bool VerifySurjectionProof(secp256k1_surjectionproof& proof, std::vector<secp256k1_generator>& vTags, secp256k1_generator& gen, const secp256k1_context* ctx, const uint256& wtxid) const;
|
||||
|
||||
};
|
||||
|
||||
void InitRangeproofCache();
|
||||
void InitSurjectionproofCache();
|
||||
|
||||
// END ELEMENTS
|
||||
//
|
||||
|
||||
#endif // BITCOIN_SCRIPT_SIGCACHE_H
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
typedef std::vector<unsigned char> valtype;
|
||||
|
||||
MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
|
||||
MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
|
||||
|
||||
bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
|
||||
{
|
||||
|
|
@ -419,7 +419,7 @@ void SignatureData::MergeSignatureData(SignatureData sigdata)
|
|||
signatures.insert(std::make_move_iterator(sigdata.signatures.begin()), std::make_move_iterator(sigdata.signatures.end()));
|
||||
}
|
||||
|
||||
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType)
|
||||
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CConfidentialValue& amount, int nHashType)
|
||||
{
|
||||
assert(nIn < txTo.vin.size());
|
||||
txTo.witness.vtxinwit.resize(txTo.vin.size());
|
||||
|
|
|
|||
|
|
@ -82,11 +82,11 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator {
|
|||
const CMutableTransaction* txTo;
|
||||
unsigned int nIn;
|
||||
int nHashType;
|
||||
CAmount amount;
|
||||
CConfidentialValue amount;
|
||||
const MutableTransactionSignatureChecker checker;
|
||||
|
||||
public:
|
||||
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn = SIGHASH_ALL);
|
||||
MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CConfidentialValue& amountIn, int nHashTypeIn = SIGHASH_ALL);
|
||||
const BaseSignatureChecker& Checker() const override { return checker; }
|
||||
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
|
||||
};
|
||||
|
|
@ -703,7 +703,7 @@ struct PartiallySignedTransaction
|
|||
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata, unsigned int additional_flags=0);
|
||||
|
||||
/** Produce a script signature for a transaction. */
|
||||
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType);
|
||||
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CConfidentialValue& amount, int nHashType);
|
||||
bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType);
|
||||
|
||||
/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
|
||||
|
|
|
|||
|
|
@ -21,11 +21,26 @@ unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY;
|
|||
|
||||
CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
|
||||
|
||||
ScriptHash::ScriptHash(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
|
||||
ScriptHash::ScriptHash(const CScript& in, const CPubKey& blinding_pubkey_in) : uint160(Hash160(in.begin(), in.end())), blinding_pubkey(blinding_pubkey_in) {}
|
||||
ScriptHash::ScriptHash(const uint160& hash, const CPubKey& blinding_pubkey_in) : uint160(hash), blinding_pubkey(blinding_pubkey_in) {}
|
||||
|
||||
PKHash::PKHash(const CPubKey& pubkey) : uint160(pubkey.GetID()) {}
|
||||
PKHash::PKHash(const CPubKey& pubkey, const CPubKey& blinding_pubkey_in) : uint160(pubkey.GetID()), blinding_pubkey(blinding_pubkey_in) {}
|
||||
PKHash::PKHash(const uint160& hash, const CPubKey& blinding_pubkey_in) : uint160(hash), blinding_pubkey(blinding_pubkey_in) {}
|
||||
|
||||
WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in)
|
||||
{
|
||||
CSHA256().Write(in.data(), in.size()).Finalize(begin());
|
||||
}
|
||||
|
||||
WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in, const CPubKey& blinding_pubkey_in)
|
||||
{
|
||||
CSHA256().Write(in.data(), in.size()).Finalize(begin());
|
||||
blinding_pubkey = blinding_pubkey_in;
|
||||
}
|
||||
|
||||
|
||||
const char* GetTxnOutputType(txnouttype t)
|
||||
{
|
||||
switch (t)
|
||||
|
|
@ -40,6 +55,7 @@ const char* GetTxnOutputType(txnouttype t)
|
|||
case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
|
||||
case TX_WITNESS_UNKNOWN: return "witness_unknown";
|
||||
case TX_TRUE: return "true";
|
||||
case TX_FEE: return "fee";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -98,6 +114,11 @@ txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned
|
|||
return TX_TRUE;
|
||||
}
|
||||
|
||||
// Fee outputs are for elements-style transactions only
|
||||
if (g_con_elementsmode && scriptPubKey == CScript()) {
|
||||
return TX_FEE;
|
||||
}
|
||||
|
||||
// Shortcut for pay-to-script-hash, which are more constrained than the other types:
|
||||
// it is always OP_HASH160 20 [20 byte hash] OP_EQUAL
|
||||
if (scriptPubKey.IsPayToScriptHash())
|
||||
|
|
@ -174,17 +195,17 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
|
|||
if (!pubKey.IsValid())
|
||||
return false;
|
||||
|
||||
addressRet = pubKey.GetID();
|
||||
addressRet = PKHash(pubKey);
|
||||
return true;
|
||||
}
|
||||
else if (whichType == TX_PUBKEYHASH)
|
||||
{
|
||||
addressRet = CKeyID(uint160(vSolutions[0]));
|
||||
addressRet = PKHash(uint160(vSolutions[0]));
|
||||
return true;
|
||||
}
|
||||
else if (whichType == TX_SCRIPTHASH)
|
||||
{
|
||||
addressRet = CScriptID(uint160(vSolutions[0]));
|
||||
addressRet = ScriptHash(uint160(vSolutions[0]));
|
||||
return true;
|
||||
} else if (whichType == TX_WITNESS_V0_KEYHASH) {
|
||||
WitnessV0KeyHash hash;
|
||||
|
|
@ -229,7 +250,7 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::
|
|||
if (!pubKey.IsValid())
|
||||
continue;
|
||||
|
||||
CTxDestination address = pubKey.GetID();
|
||||
CTxDestination address = PKHash(pubKey);
|
||||
addressRet.push_back(address);
|
||||
}
|
||||
|
||||
|
|
@ -262,13 +283,13 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
bool operator()(const CKeyID &keyID) const {
|
||||
bool operator()(const PKHash &keyID) const {
|
||||
script->clear();
|
||||
*script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator()(const CScriptID &scriptID) const {
|
||||
bool operator()(const ScriptHash &scriptID) const {
|
||||
script->clear();
|
||||
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <pubkey.h> // blinding_pubkey
|
||||
|
||||
static const bool DEFAULT_ACCEPT_DATACARRIER = true;
|
||||
|
||||
class CKeyID;
|
||||
|
|
@ -66,6 +68,8 @@ enum txnouttype
|
|||
TX_WITNESS_V0_KEYHASH,
|
||||
TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
|
||||
TX_TRUE, // For testing purposes only
|
||||
// ELEMENTS:
|
||||
TX_FEE,
|
||||
};
|
||||
|
||||
class CNoDestination {
|
||||
|
|
@ -74,19 +78,45 @@ public:
|
|||
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
|
||||
};
|
||||
|
||||
struct PKHash : public uint160
|
||||
{
|
||||
PKHash() : uint160() {}
|
||||
explicit PKHash(const uint160& hash) : uint160(hash) {}
|
||||
explicit PKHash(const CPubKey& pubkey);
|
||||
explicit PKHash(const CPubKey& pubkey, const CPubKey& blinding_pubkey);
|
||||
explicit PKHash(const uint160& hash, const CPubKey& blinding_pubkey);
|
||||
using uint160::uint160;
|
||||
CPubKey blinding_pubkey;
|
||||
};
|
||||
|
||||
struct ScriptHash : public uint160
|
||||
{
|
||||
ScriptHash() : uint160() {}
|
||||
explicit ScriptHash(const uint160& hash) : uint160(hash) {}
|
||||
explicit ScriptHash(const CScript& script);
|
||||
explicit ScriptHash(const CScript& script, const CPubKey& blinding_pubkey);
|
||||
explicit ScriptHash(const uint160& hash, const CPubKey& blinding_pubkey);
|
||||
using uint160::uint160;
|
||||
CPubKey blinding_pubkey;
|
||||
};
|
||||
|
||||
struct WitnessV0ScriptHash : public uint256
|
||||
{
|
||||
WitnessV0ScriptHash() : uint256() {}
|
||||
explicit WitnessV0ScriptHash(const uint256& hash) : uint256(hash) {}
|
||||
explicit WitnessV0ScriptHash(const CScript& script);
|
||||
explicit WitnessV0ScriptHash(const CScript& script, const CPubKey& blinding_pubkey);
|
||||
using uint256::uint256;
|
||||
CPubKey blinding_pubkey;
|
||||
};
|
||||
|
||||
struct WitnessV0KeyHash : public uint160
|
||||
{
|
||||
WitnessV0KeyHash() : uint160() {}
|
||||
explicit WitnessV0KeyHash(const uint160& hash) : uint160(hash) {}
|
||||
explicit WitnessV0KeyHash(const uint160& hash, const CPubKey& blinding_pubkey_in) : uint160(hash), blinding_pubkey(blinding_pubkey_in) {}
|
||||
using uint160::uint160;
|
||||
CPubKey blinding_pubkey;
|
||||
};
|
||||
|
||||
//! CTxDestination subtype to encode any future Witness version
|
||||
|
|
@ -95,6 +125,7 @@ struct WitnessUnknown
|
|||
unsigned int version;
|
||||
unsigned int length;
|
||||
unsigned char program[40];
|
||||
CPubKey blinding_pubkey;
|
||||
|
||||
friend bool operator==(const WitnessUnknown& w1, const WitnessUnknown& w2) {
|
||||
if (w1.version != w2.version) return false;
|
||||
|
|
@ -129,15 +160,15 @@ public:
|
|||
/**
|
||||
* A txout script template with a specific destination. It is either:
|
||||
* * CNoDestination: no destination set
|
||||
* * CKeyID: TX_PUBKEYHASH destination (P2PKH)
|
||||
* * CScriptID: TX_SCRIPTHASH destination (P2SH)
|
||||
* * PKHash: TX_PUBKEYHASH destination (P2PKH)
|
||||
* * ScriptHash: TX_SCRIPTHASH destination (P2SH)
|
||||
* * WitnessV0ScriptHash: TX_WITNESS_V0_SCRIPTHASH destination (P2WSH)
|
||||
* * WitnessV0KeyHash: TX_WITNESS_V0_KEYHASH destination (P2WPKH)
|
||||
* * WitnessUnknown: TX_WITNESS_UNKNOWN destination (P2W???)
|
||||
* * NullData: TX_NULL_DATA destination (OP_RETURN)
|
||||
* A CTxDestination is the internal data type encoded in a bitcoin address
|
||||
*/
|
||||
typedef boost::variant<CNoDestination, CKeyID, CScriptID, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown, NullData> CTxDestination;
|
||||
typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown, NullData> CTxDestination;
|
||||
|
||||
/** Check whether a CTxDestination is a CNoDestination. */
|
||||
bool IsValidDestination(const CTxDestination& dest);
|
||||
|
|
|
|||
370
src/test/blind_tests.cpp
Normal file
370
src/test/blind_tests.cpp
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
// Copyright (c) 2013-2019 The Elements Core developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <arith_uint256.h>
|
||||
#include <blind.h>
|
||||
#include <coins.h>
|
||||
#include <uint256.h>
|
||||
#include <validation.h>
|
||||
|
||||
#include <test/test_bitcoin.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <secp256k1.h>
|
||||
|
||||
// For elements serialization rules
|
||||
struct ElementsSetup : public TestingSetup {
|
||||
ElementsSetup() : TestingSetup("custom") {}
|
||||
};
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(blind_tests, ElementsSetup)
|
||||
|
||||
// TODO: Make deterministic blinding wrapper function, test caching more exactly
|
||||
|
||||
BOOST_AUTO_TEST_CASE(naive_blinding_test)
|
||||
{
|
||||
CKey key1;
|
||||
CKey key2;
|
||||
CKey keyDummy;
|
||||
|
||||
// Any asset id will do
|
||||
CAsset bitcoinID(GetRandHash());
|
||||
CAsset otherID(GetRandHash());
|
||||
CAsset unblinded_id;
|
||||
uint256 asset_blind;
|
||||
CScript op_true(OP_TRUE);
|
||||
std::vector<CKey> vDummy;
|
||||
|
||||
unsigned char k1[32] = {1,2,3};
|
||||
unsigned char k2[32] = {22,33,44};
|
||||
unsigned char kDummy[32] = {133,144,155};
|
||||
key1.Set(&k1[0], &k1[32], true);
|
||||
key2.Set(&k2[0], &k2[32], true);
|
||||
keyDummy.Set(&kDummy[0], &kDummy[32], true);
|
||||
CPubKey pubkey1 = key1.GetPubKey();
|
||||
CPubKey pubkey2 = key2.GetPubKey();
|
||||
CPubKey pubkeyDummy = keyDummy.GetPubKey();
|
||||
|
||||
uint256 blind3, blind4, blindDummy;
|
||||
|
||||
std::vector<CTxOut> inputs;
|
||||
CTxOut btc_oo(bitcoinID, 11, CScript());
|
||||
CTxOut btc_ooo(bitcoinID, 111, CScript());
|
||||
CTxOut other_fzz(otherID, 500, CScript());
|
||||
CTxOut blind_ozz; // Will be computed later
|
||||
|
||||
{
|
||||
inputs.clear();
|
||||
inputs.push_back(btc_oo);
|
||||
inputs.push_back(btc_ooo);
|
||||
|
||||
// Build a transaction that spends 2 unblinded coins (11, 111), and produces a single blinded one (100) and fee (22).
|
||||
CMutableTransaction tx3;
|
||||
tx3.vin.resize(2);
|
||||
tx3.vin[0].prevout.hash = ArithToUint256(1);
|
||||
|
||||
tx3.vin[0].prevout.n = 0;
|
||||
tx3.vin[1].prevout.hash = ArithToUint256(2);
|
||||
tx3.vin[1].prevout.n = 0;
|
||||
tx3.vout.resize(0);
|
||||
tx3.vout.push_back(CTxOut(bitcoinID, 100, CScript() << OP_TRUE));
|
||||
// Fee outputs are blank scriptpubkeys, and unblinded value/asset
|
||||
tx3.vout.push_back(CTxOut(bitcoinID, 22, CScript()));
|
||||
BOOST_CHECK(VerifyAmounts(inputs, tx3, nullptr, false));
|
||||
|
||||
// Malleate the output and check for correct handling of bad commitments
|
||||
// These will fail IsValid checks
|
||||
std::vector<unsigned char> asset_copy(tx3.vout[0].nAsset.vchCommitment);
|
||||
std::vector<unsigned char> value_copy(tx3.vout[0].nValue.vchCommitment);
|
||||
tx3.vout[0].nAsset.vchCommitment[0] = 122;
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, tx3, nullptr, false));
|
||||
tx3.vout[0].nAsset.vchCommitment = asset_copy;
|
||||
tx3.vout[0].nValue.vchCommitment[0] = 122;
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, tx3, nullptr, false));
|
||||
tx3.vout[0].nValue.vchCommitment = value_copy;
|
||||
|
||||
// Make sure null values are handled correctly
|
||||
tx3.vout[0].nAsset.SetNull();
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, tx3, nullptr, false));
|
||||
tx3.vout[0].nAsset.vchCommitment = asset_copy;
|
||||
tx3.vout[0].nValue.SetNull();
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, tx3, nullptr, false));
|
||||
tx3.vout[0].nValue.vchCommitment = value_copy;
|
||||
|
||||
// Bad nonce values will result in failure to deserialize
|
||||
tx3.vout[0].nNonce.SetNull();
|
||||
BOOST_CHECK(VerifyAmounts(inputs, tx3, nullptr, false));
|
||||
tx3.vout[0].nNonce.vchCommitment = tx3.vout[0].nValue.vchCommitment;
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, tx3, nullptr, false));
|
||||
|
||||
// Try to blind with a single non-fee output, which fails as its blinding factor ends up being zero.
|
||||
std::vector<uint256> input_blinds;
|
||||
std::vector<uint256> input_asset_blinds;
|
||||
std::vector<CAsset> input_assets;
|
||||
std::vector<CAmount> input_amounts;
|
||||
std::vector<uint256> output_blinds;
|
||||
std::vector<uint256> output_asset_blinds;
|
||||
std::vector<CPubKey> output_pubkeys;
|
||||
input_blinds.push_back(uint256());
|
||||
input_blinds.push_back(uint256());
|
||||
input_asset_blinds.push_back(uint256());
|
||||
input_asset_blinds.push_back(uint256());
|
||||
input_assets.push_back(bitcoinID);
|
||||
input_assets.push_back(bitcoinID);
|
||||
input_amounts.push_back(11);
|
||||
input_amounts.push_back(111);
|
||||
output_pubkeys.push_back(pubkey1);
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
BOOST_CHECK(BlindTransaction(input_blinds, input_asset_blinds, input_assets, input_amounts, output_blinds, output_asset_blinds, output_pubkeys, vDummy, vDummy, tx3) == 0);
|
||||
|
||||
// Add a dummy output. Must be unspendable since it's 0-valued.
|
||||
tx3.vout.push_back(CTxOut(bitcoinID, 0, CScript() << OP_RETURN));
|
||||
output_pubkeys.push_back(pubkeyDummy);
|
||||
BOOST_CHECK(BlindTransaction(input_blinds, input_asset_blinds, input_assets, input_amounts, output_blinds, output_asset_blinds, output_pubkeys, vDummy, vDummy, tx3) == 2);
|
||||
BOOST_CHECK(!tx3.vout[0].nValue.IsExplicit());
|
||||
BOOST_CHECK(!tx3.vout[2].nValue.IsExplicit());
|
||||
BOOST_CHECK(VerifyAmounts(inputs, tx3, nullptr, false));
|
||||
|
||||
CAmount unblinded_amount;
|
||||
BOOST_CHECK(UnblindConfidentialPair(key2, tx3.vout[0].nValue, tx3.vout[0].nAsset, tx3.vout[0].nNonce, op_true, tx3.witness.vtxoutwit[0].vchRangeproof, unblinded_amount, blind3, unblinded_id, asset_blind) == 0);
|
||||
// Saving unblinded_id and asset_blind for later since we need for input
|
||||
BOOST_CHECK(UnblindConfidentialPair(key1, tx3.vout[0].nValue, tx3.vout[0].nAsset, tx3.vout[0].nNonce, op_true, tx3.witness.vtxoutwit[0].vchRangeproof, unblinded_amount, blind3, unblinded_id, asset_blind) == 1);
|
||||
BOOST_CHECK(unblinded_amount == 100);
|
||||
BOOST_CHECK(unblinded_id == bitcoinID);
|
||||
CAsset temp_asset;
|
||||
uint256 temp_asset_blinder;
|
||||
BOOST_CHECK(UnblindConfidentialPair(keyDummy, tx3.vout[2].nValue, tx3.vout[2].nAsset, tx3.vout[2].nNonce, CScript() << OP_RETURN, tx3.witness.vtxoutwit[2].vchRangeproof, unblinded_amount, blindDummy, temp_asset, temp_asset_blinder) == 1);
|
||||
BOOST_CHECK(unblinded_amount == 0);
|
||||
|
||||
// Storing for next section
|
||||
BOOST_CHECK(tx3.vout[0].nValue.IsCommitment());
|
||||
BOOST_CHECK(tx3.vout[0].nAsset.IsCommitment());
|
||||
blind_ozz = tx3.vout[0];
|
||||
|
||||
tx3.vout[1].nValue = CConfidentialValue(tx3.vout[1].nValue.GetAmount() - 1);
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, tx3, nullptr, false));
|
||||
}
|
||||
|
||||
{
|
||||
inputs.clear();
|
||||
inputs.push_back(btc_ooo);
|
||||
inputs.push_back(blind_ozz);
|
||||
|
||||
// Build a transactions that spends an unblinded (111) and blinded (100) coin, and produces only unblinded coins (impossible)
|
||||
CMutableTransaction tx4;
|
||||
tx4.vin.resize(2);
|
||||
tx4.vin[0].prevout.hash = ArithToUint256(2);
|
||||
tx4.vin[0].prevout.n = 0;
|
||||
tx4.vin[1].prevout.hash = ArithToUint256(3);
|
||||
tx4.vin[1].prevout.n = 0;
|
||||
tx4.vout.push_back(CTxOut(bitcoinID, 30, CScript() << OP_TRUE));
|
||||
tx4.vout.push_back(CTxOut(bitcoinID, 40, CScript() << OP_TRUE));
|
||||
tx4.vout.push_back(CTxOut(bitcoinID, 111+100-30-40, CScript()));
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, tx4, nullptr, false)); // Spends a blinded coin with no blinded outputs to compensate.
|
||||
|
||||
std::vector<uint256> input_blinds;
|
||||
std::vector<uint256> input_asset_blinds;
|
||||
std::vector<CAsset> input_assets;
|
||||
std::vector<CAmount> input_amounts;
|
||||
std::vector<uint256> output_blinds;
|
||||
std::vector<uint256> output_asset_blinds;
|
||||
std::vector<CPubKey> output_pubkeys;
|
||||
input_blinds.push_back(uint256());
|
||||
input_blinds.push_back(blind3);
|
||||
input_asset_blinds.push_back(uint256());
|
||||
input_asset_blinds.push_back(asset_blind);
|
||||
input_amounts.push_back(111);
|
||||
input_amounts.push_back(100);
|
||||
input_assets.push_back(unblinded_id);
|
||||
input_assets.push_back(unblinded_id);
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
BOOST_CHECK(BlindTransaction(input_blinds, input_asset_blinds, input_assets, input_amounts, output_blinds, output_asset_blinds, output_pubkeys, vDummy, vDummy, tx4) == 0); // Blinds nothing
|
||||
}
|
||||
|
||||
{
|
||||
inputs.clear();
|
||||
inputs.push_back(btc_ooo);
|
||||
inputs.push_back(blind_ozz);
|
||||
|
||||
// Build a transactions that spends an unblinded (111) and blinded (100) coin, and produces a blinded (30), unblinded (40), and blinded (50) coin and fee (91)
|
||||
CMutableTransaction tx4;
|
||||
tx4.vin.resize(2);
|
||||
tx4.vin[0].prevout.hash = ArithToUint256(2);
|
||||
tx4.vin[0].prevout.n = 0;
|
||||
tx4.vin[1].prevout.hash = ArithToUint256(3);
|
||||
tx4.vin[1].prevout.n = 0;
|
||||
tx4.vout.push_back(CTxOut(bitcoinID, 30, CScript() << OP_TRUE));
|
||||
tx4.vout.push_back(CTxOut(bitcoinID, 40, CScript() << OP_TRUE));
|
||||
tx4.vout.push_back(CTxOut(bitcoinID, 50, CScript() << OP_TRUE));
|
||||
// Fee
|
||||
tx4.vout.push_back(CTxOut(bitcoinID, 111+100-30-40-50, CScript()));
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, tx4, nullptr, false)); // Spends a blinded coin with no blinded outputs to compensate.
|
||||
|
||||
std::vector<uint256> input_blinds;
|
||||
std::vector<uint256> input_asset_blinds;
|
||||
std::vector<CAsset> input_assets;
|
||||
std::vector<CAmount> input_amounts;
|
||||
std::vector<uint256> output_blinds;
|
||||
std::vector<uint256> output_asset_blinds;
|
||||
std::vector<CPubKey> output_pubkeys;
|
||||
|
||||
input_blinds.push_back(uint256());
|
||||
input_blinds.push_back(blind3);
|
||||
input_asset_blinds.push_back(uint256());
|
||||
input_asset_blinds.push_back(asset_blind);
|
||||
input_amounts.push_back(111);
|
||||
input_amounts.push_back(100);
|
||||
input_assets.push_back(unblinded_id);
|
||||
input_assets.push_back(unblinded_id);
|
||||
|
||||
output_pubkeys.push_back(pubkey2);
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
output_pubkeys.push_back(pubkey2);
|
||||
output_pubkeys.push_back(CPubKey());
|
||||
|
||||
BOOST_CHECK(BlindTransaction(input_blinds, input_asset_blinds, input_assets, input_amounts, output_blinds, output_asset_blinds, output_pubkeys, vDummy, vDummy, tx4) == 2);
|
||||
BOOST_CHECK(!tx4.vout[0].nValue.IsExplicit());
|
||||
BOOST_CHECK(tx4.vout[1].nValue.IsExplicit());
|
||||
BOOST_CHECK(!tx4.vout[2].nValue.IsExplicit());
|
||||
// This one broken
|
||||
BOOST_CHECK(VerifyAmounts(inputs, tx4, nullptr, false));
|
||||
|
||||
CAmount unblinded_amount;
|
||||
CAsset asset_out;
|
||||
uint256 asset_blinder_out;
|
||||
BOOST_CHECK(UnblindConfidentialPair(key1, tx4.vout[0].nValue, tx4.vout[0].nAsset, tx4.vout[0].nNonce, op_true, tx4.witness.vtxoutwit[0].vchRangeproof, unblinded_amount, blind4, asset_out, asset_blinder_out) == 0);
|
||||
BOOST_CHECK(UnblindConfidentialPair(key2, tx4.vout[0].nValue, tx4.vout[0].nAsset, tx4.vout[0].nNonce, op_true, tx4.witness.vtxoutwit[0].vchRangeproof, unblinded_amount, blind4, asset_out, asset_blinder_out) == 1);
|
||||
BOOST_CHECK(unblinded_amount == 30);
|
||||
BOOST_CHECK(asset_out == unblinded_id);
|
||||
BOOST_CHECK(UnblindConfidentialPair(key2, tx4.vout[2].nValue, tx4.vout[2].nAsset, tx4.vout[2].nNonce, op_true, tx4.witness.vtxoutwit[2].vchRangeproof, unblinded_amount, blind4, asset_out, asset_blinder_out) == 1);
|
||||
BOOST_CHECK(asset_out == unblinded_id);
|
||||
BOOST_CHECK(unblinded_amount == 50);
|
||||
|
||||
// Commit to the wrong script in the rangeproof
|
||||
BOOST_CHECK(UnblindConfidentialPair(key2, tx4.vout[2].nValue, tx4.vout[2].nAsset, tx4.vout[2].nNonce, CScript() << OP_FALSE, tx4.witness.vtxoutwit[2].vchRangeproof, unblinded_amount, blind4, asset_out, asset_blinder_out) == 0);
|
||||
|
||||
// Make invalid public keys in nonce commitment, first of right size
|
||||
tx4.vout[2].nNonce.vchCommitment = std::vector<unsigned char>(33, 0);
|
||||
tx4.vout[2].nNonce.vchCommitment[0] = 0x03;
|
||||
BOOST_CHECK(UnblindConfidentialPair(key2, tx4.vout[2].nValue, tx4.vout[2].nAsset, tx4.vout[2].nNonce, op_true, tx4.witness.vtxoutwit[2].vchRangeproof, unblinded_amount, blind4, asset_out, asset_blinder_out) == 0);
|
||||
|
||||
// Next, leading byte claiming to be 33 bytes in size
|
||||
tx4.vout[2].nNonce.vchCommitment.resize(1);
|
||||
BOOST_CHECK(UnblindConfidentialPair(key2, tx4.vout[2].nValue, tx4.vout[2].nAsset, tx4.vout[2].nNonce, op_true, tx4.witness.vtxoutwit[2].vchRangeproof, unblinded_amount, blind4, asset_out, asset_blinder_out) == 0);
|
||||
|
||||
// Last, blank nonce commitment
|
||||
tx4.vout[2].nNonce.vchCommitment.clear();
|
||||
BOOST_CHECK(UnblindConfidentialPair(key2, tx4.vout[2].nValue, tx4.vout[2].nAsset, tx4.vout[2].nNonce, op_true, tx4.witness.vtxoutwit[2].vchRangeproof, unblinded_amount, blind4, asset_out, asset_blinder_out) == 0);
|
||||
|
||||
tx4.vout[3].nValue = CConfidentialValue(tx4.vout[3].nValue.GetAmount() - 1);
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, tx4, nullptr, false));
|
||||
|
||||
// Check wallet borromean-based rangeproof results against expected args
|
||||
size_t proof_size = DEFAULT_RANGEPROOF_SIZE;
|
||||
BOOST_CHECK(tx4.witness.vtxoutwit[2].vchRangeproof.size() == proof_size);
|
||||
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
|
||||
int exp = 0;
|
||||
int mantissa = 0;
|
||||
uint64_t min_value = 0;
|
||||
uint64_t max_value = 0;
|
||||
BOOST_CHECK(secp256k1_rangeproof_info(ctx, &exp, &mantissa, &min_value, &max_value, tx4.witness.vtxoutwit[2].vchRangeproof.data(), proof_size) == 1);
|
||||
BOOST_CHECK(exp == 0);
|
||||
BOOST_CHECK(mantissa == 36); // 36 bit default
|
||||
BOOST_CHECK(min_value == 1);
|
||||
BOOST_CHECK(max_value == 68719476736);
|
||||
}
|
||||
{
|
||||
inputs.clear();
|
||||
inputs.push_back(blind_ozz);
|
||||
inputs.push_back(other_fzz);
|
||||
|
||||
// Spends 100 blinded bitcoin, 500 of unblinded "other"
|
||||
CMutableTransaction tx5;
|
||||
tx5.vin.resize(0);
|
||||
tx5.vout.resize(0);
|
||||
tx5.vin.push_back(CTxIn(COutPoint(ArithToUint256(3), 0)));
|
||||
tx5.vin.push_back(CTxIn(COutPoint(ArithToUint256(5), 0)));
|
||||
tx5.vout.push_back(CTxOut(bitcoinID, 29, CScript() << OP_TRUE));
|
||||
tx5.vout.push_back(CTxOut(bitcoinID, 70, CScript() << OP_TRUE));
|
||||
tx5.vout.push_back(CTxOut(otherID, 250, CScript() << OP_TRUE));
|
||||
tx5.vout.push_back(CTxOut(otherID, 249, CScript() << OP_TRUE));
|
||||
// Fees
|
||||
tx5.vout.push_back(CTxOut(bitcoinID, 1, CScript()));
|
||||
tx5.vout.push_back(CTxOut(otherID, 1, CScript()));
|
||||
|
||||
// Blinds don't balance
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, tx5, nullptr, false));
|
||||
|
||||
// Blinding setup stuff
|
||||
std::vector<uint256> input_blinds;
|
||||
std::vector<uint256> input_asset_blinds;
|
||||
std::vector<CAsset> input_assets;
|
||||
std::vector<CAmount> input_amounts;
|
||||
std::vector<uint256> output_blinds;
|
||||
std::vector<uint256> output_asset_blinds;
|
||||
std::vector<CPubKey> output_pubkeys;
|
||||
input_blinds.push_back(blind3);
|
||||
input_blinds.push_back(uint256()); //
|
||||
input_asset_blinds.push_back(asset_blind);
|
||||
input_asset_blinds.push_back(uint256());
|
||||
input_amounts.push_back(100);
|
||||
input_amounts.push_back(500);
|
||||
input_assets.push_back(bitcoinID);
|
||||
input_assets.push_back(otherID);
|
||||
for (unsigned int i = 0; i < 6; i++) {
|
||||
output_pubkeys.push_back(pubkey2);
|
||||
}
|
||||
|
||||
CMutableTransaction txtemp(tx5);
|
||||
|
||||
// No blinding keys for fees, bails out blinding nothing, still invalid due to imbalance
|
||||
BOOST_CHECK(BlindTransaction(input_blinds, input_asset_blinds, input_assets, input_amounts, output_blinds, output_asset_blinds, output_pubkeys, vDummy, vDummy, txtemp) == -1);
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, txtemp, nullptr, false));
|
||||
// Last will be implied blank keys
|
||||
output_pubkeys.resize(4);
|
||||
|
||||
// Blind transaction, verify amounts
|
||||
txtemp = tx5;
|
||||
BOOST_CHECK(BlindTransaction(input_blinds, input_asset_blinds, input_assets, input_amounts, output_blinds, output_asset_blinds, output_pubkeys, vDummy, vDummy, txtemp) == 4);
|
||||
BOOST_CHECK(VerifyAmounts(inputs, txtemp, nullptr, false));
|
||||
|
||||
// Transaction may not have spendable 0-value output
|
||||
txtemp.vout.push_back(CTxOut(CAsset(), 0, CScript() << OP_TRUE));
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, txtemp, nullptr, false));
|
||||
|
||||
// Create imbalance by removing fees, should still be able to blind
|
||||
txtemp = tx5;
|
||||
txtemp.vout.resize(5);
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, txtemp, nullptr, false));
|
||||
txtemp.vout.resize(4);
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, txtemp, nullptr, false));
|
||||
BOOST_CHECK(BlindTransaction(input_blinds, input_asset_blinds, input_assets, input_amounts, output_blinds, output_asset_blinds, output_pubkeys, vDummy, vDummy, txtemp) == 4);
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, txtemp, nullptr, false));
|
||||
|
||||
txtemp = tx5;
|
||||
// Remove other input, make surjection proof impossible for 2 "otherID" outputs
|
||||
std::vector<uint256> t_input_blinds;
|
||||
std::vector<uint256> t_input_asset_blinds;
|
||||
std::vector<CAsset> t_input_assets;
|
||||
std::vector<CAmount> t_input_amounts;
|
||||
|
||||
t_input_blinds = input_blinds;
|
||||
t_input_asset_blinds = input_asset_blinds;
|
||||
t_input_assets = input_assets;
|
||||
t_input_amounts = input_amounts;
|
||||
txtemp.vin.resize(1);
|
||||
inputs.resize(1);
|
||||
t_input_blinds.resize(1);
|
||||
t_input_asset_blinds.resize(1);
|
||||
t_input_assets.resize(1);
|
||||
t_input_amounts.resize(1);
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, txtemp, nullptr, false));
|
||||
BOOST_CHECK(BlindTransaction(t_input_blinds, t_input_asset_blinds, t_input_assets, t_input_amounts, output_blinds, output_asset_blinds, output_pubkeys, vDummy, vDummy, txtemp) == 2);
|
||||
BOOST_CHECK(!VerifyAmounts(inputs, txtemp, nullptr, false));
|
||||
}
|
||||
}
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
#include <test/data/blockfilters.json.h>
|
||||
#include <test/test_bitcoin.h>
|
||||
|
||||
#include <asset.h>
|
||||
#include <blockfilter.h>
|
||||
#include <core_io.h>
|
||||
#include <serialize.h>
|
||||
|
|
@ -61,13 +62,13 @@ BOOST_AUTO_TEST_CASE(blockfilter_basic_test)
|
|||
excluded_scripts[1] << std::vector<unsigned char>(5, 33) << OP_CHECKSIG;
|
||||
|
||||
CMutableTransaction tx_1;
|
||||
tx_1.vout.emplace_back(100, included_scripts[0]);
|
||||
tx_1.vout.emplace_back(200, included_scripts[1]);
|
||||
tx_1.vout.emplace_back(CTxOut(CAsset(), 100, included_scripts[0]));
|
||||
tx_1.vout.emplace_back(CTxOut(CAsset(), 200, included_scripts[1]));
|
||||
|
||||
CMutableTransaction tx_2;
|
||||
tx_2.vout.emplace_back(300, included_scripts[2]);
|
||||
tx_2.vout.emplace_back(0, excluded_scripts[0]);
|
||||
tx_2.vout.emplace_back(400, excluded_scripts[2]); // Script is empty
|
||||
tx_2.vout.emplace_back(CTxOut(CAsset(), 300, included_scripts[2]));
|
||||
tx_2.vout.emplace_back(CTxOut(CAsset(), 0, excluded_scripts[0]));
|
||||
tx_2.vout.emplace_back(CTxOut(CAsset(), 400, excluded_scripts[2])); // Script is empty
|
||||
|
||||
CBlock block;
|
||||
block.vtx.push_back(MakeTransactionRef(tx_1));
|
||||
|
|
@ -75,9 +76,9 @@ BOOST_AUTO_TEST_CASE(blockfilter_basic_test)
|
|||
|
||||
CBlockUndo block_undo;
|
||||
block_undo.vtxundo.emplace_back();
|
||||
block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(500, included_scripts[3]), 1000, true);
|
||||
block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(600, included_scripts[4]), 10000, false);
|
||||
block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(700, excluded_scripts[2]), 100000, false);
|
||||
block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(CAsset(), 500, included_scripts[3]), 1000, true);
|
||||
block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(CAsset(), 600, included_scripts[4]), 10000, false);
|
||||
block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(CAsset(), 700, excluded_scripts[2]), 100000, false);
|
||||
|
||||
BlockFilter block_filter(BlockFilterType::BASIC, block, block_undo);
|
||||
const GCSFilter& filter = block_filter.GetFilter();
|
||||
|
|
@ -125,7 +126,7 @@ BOOST_AUTO_TEST_CASE(blockfilters_json_test)
|
|||
const UniValue& prev_scripts = test[pos++].get_array();
|
||||
for (unsigned int ii = 0; ii < prev_scripts.size(); ii++) {
|
||||
std::vector<unsigned char> raw_script = ParseHex(prev_scripts[ii].get_str());
|
||||
CTxOut txout(0, CScript(raw_script.begin(), raw_script.end()));
|
||||
CTxOut txout(CAsset(), 0, CScript(raw_script.begin(), raw_script.end()));
|
||||
tx_undo.vprevout.emplace_back(txout, 0, false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ static void Correct_Queue_range(std::vector<size_t> range)
|
|||
tg.create_thread([&]{small_queue->Thread();});
|
||||
}
|
||||
// Make vChecks here to save on malloc (this test can be slow...)
|
||||
std::vector<FakeCheckCheckCompletion> vChecks;
|
||||
std::vector<FakeCheckCheckCompletion*> vChecks;
|
||||
for (const size_t i : range) {
|
||||
size_t total = i;
|
||||
FakeCheckCheckCompletion::n_calls = 0;
|
||||
|
|
@ -226,10 +226,10 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure)
|
|||
while (remaining) {
|
||||
size_t r = InsecureRandRange(10);
|
||||
|
||||
std::vector<FailingCheck> vChecks;
|
||||
vChecks.reserve(r);
|
||||
for (size_t k = 0; k < r && remaining; k++, remaining--)
|
||||
vChecks.emplace_back(remaining == 1);
|
||||
std::vector<FailingCheck*> vChecks;
|
||||
for (size_t k = 0; k < r && remaining; k++, remaining--) {
|
||||
vChecks.push_back(new FailingCheck(remaining == 1));
|
||||
}
|
||||
control.Add(vChecks);
|
||||
}
|
||||
bool success = control.Wait();
|
||||
|
|
@ -256,9 +256,11 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure)
|
|||
for (const bool end_fails : {true, false}) {
|
||||
CCheckQueueControl<FailingCheck> control(fail_queue.get());
|
||||
{
|
||||
std::vector<FailingCheck> vChecks;
|
||||
vChecks.resize(100, false);
|
||||
vChecks[99] = end_fails;
|
||||
std::vector<FailingCheck*> vChecks;
|
||||
for (size_t i = 0; i < 100; i++) {
|
||||
vChecks.push_back(new FailingCheck(false));
|
||||
}
|
||||
vChecks[99] = new FailingCheck(end_fails);
|
||||
control.Add(vChecks);
|
||||
}
|
||||
bool r =control.Wait();
|
||||
|
|
@ -287,9 +289,10 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck)
|
|||
CCheckQueueControl<UniqueCheck> control(queue.get());
|
||||
while (total) {
|
||||
size_t r = InsecureRandRange(10);
|
||||
std::vector<UniqueCheck> vChecks;
|
||||
for (size_t k = 0; k < r && total; k++)
|
||||
vChecks.emplace_back(--total);
|
||||
std::vector<UniqueCheck*> vChecks;
|
||||
for (size_t k = 0; k < r && total; k++) {
|
||||
vChecks.emplace_back(new UniqueCheck(--total));
|
||||
}
|
||||
control.Add(vChecks);
|
||||
}
|
||||
}
|
||||
|
|
@ -321,12 +324,12 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory)
|
|||
CCheckQueueControl<MemoryCheck> control(queue.get());
|
||||
while (total) {
|
||||
size_t r = InsecureRandRange(10);
|
||||
std::vector<MemoryCheck> vChecks;
|
||||
std::vector<MemoryCheck*> vChecks;
|
||||
for (size_t k = 0; k < r && total; k++) {
|
||||
total--;
|
||||
// Each iteration leaves data at the front, back, and middle
|
||||
// to catch any sort of deallocation failure
|
||||
vChecks.emplace_back(total == 0 || total == i || total == i/2);
|
||||
vChecks.emplace_back(new MemoryCheck(total == 0 || total == i || total == i/2));
|
||||
}
|
||||
control.Add(vChecks);
|
||||
}
|
||||
|
|
@ -349,11 +352,12 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup)
|
|||
}
|
||||
std::thread t0([&]() {
|
||||
CCheckQueueControl<FrozenCleanupCheck> control(queue.get());
|
||||
std::vector<FrozenCleanupCheck> vChecks(1);
|
||||
std::vector<FrozenCleanupCheck*> vChecks;
|
||||
// Freezing can't be the default initialized behavior given how the queue
|
||||
// swaps in default initialized Checks (otherwise freezing destructor
|
||||
// would get called twice).
|
||||
vChecks[0].should_freeze = true;
|
||||
vChecks.push_back(new FrozenCleanupCheck());
|
||||
vChecks[0]->should_freeze = true;
|
||||
control.Add(vChecks);
|
||||
control.Wait(); // Hangs here
|
||||
});
|
||||
|
|
|
|||
|
|
@ -481,8 +481,8 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
|
|||
ss1 >> cc1;
|
||||
BOOST_CHECK_EQUAL(cc1.fCoinBase, false);
|
||||
BOOST_CHECK_EQUAL(cc1.nHeight, 203998U);
|
||||
BOOST_CHECK_EQUAL(cc1.out.nValue, CAmount{60000000000});
|
||||
BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35"))))));
|
||||
BOOST_CHECK_EQUAL(cc1.out.nValue.GetAmount(), 60000000000);
|
||||
BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35"))))));
|
||||
|
||||
// Good example
|
||||
CDataStream ss2(ParseHex("8ddf77bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4"), SER_DISK, CLIENT_VERSION);
|
||||
|
|
@ -490,8 +490,8 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
|
|||
ss2 >> cc2;
|
||||
BOOST_CHECK_EQUAL(cc2.fCoinBase, true);
|
||||
BOOST_CHECK_EQUAL(cc2.nHeight, 120891U);
|
||||
BOOST_CHECK_EQUAL(cc2.out.nValue, 110397);
|
||||
BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"))))));
|
||||
BOOST_CHECK_EQUAL(cc2.out.nValue.GetAmount(), 110397);
|
||||
BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"))))));
|
||||
|
||||
// Smallest possible example
|
||||
CDataStream ss3(ParseHex("000006"), SER_DISK, CLIENT_VERSION);
|
||||
|
|
@ -499,7 +499,7 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
|
|||
ss3 >> cc3;
|
||||
BOOST_CHECK_EQUAL(cc3.fCoinBase, false);
|
||||
BOOST_CHECK_EQUAL(cc3.nHeight, 0U);
|
||||
BOOST_CHECK_EQUAL(cc3.out.nValue, 0);
|
||||
BOOST_CHECK_EQUAL(cc3.out.nValue.GetAmount(), 0);
|
||||
BOOST_CHECK_EQUAL(cc3.out.scriptPubKey.size(), 0U);
|
||||
|
||||
// scriptPubKey that ends beyond the end of the stream
|
||||
|
|
@ -577,7 +577,7 @@ void GetCoinsMapEntry(const CCoinsMap& map, CAmount& value, char& flags)
|
|||
if (it->second.coin.IsSpent()) {
|
||||
value = PRUNED;
|
||||
} else {
|
||||
value = it->second.coin.out.nValue;
|
||||
value = it->second.coin.out.nValue.GetAmount();
|
||||
}
|
||||
flags = it->second.flags;
|
||||
assert(flags != NO_ENTRY);
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
|||
tx.vin[0].scriptSig << OP_1;
|
||||
tx.vout.resize(1);
|
||||
tx.vout[0].nValue = 1*CENT;
|
||||
tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
|
||||
tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
|
||||
|
||||
AddOrphanTx(MakeTransactionRef(tx), i);
|
||||
}
|
||||
|
|
@ -365,7 +365,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
|||
tx.vin[0].prevout.hash = txPrev->GetHash();
|
||||
tx.vout.resize(1);
|
||||
tx.vout[0].nValue = 1*CENT;
|
||||
tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
|
||||
tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
|
||||
SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL);
|
||||
|
||||
AddOrphanTx(MakeTransactionRef(tx), i);
|
||||
|
|
@ -379,7 +379,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
|||
CMutableTransaction tx;
|
||||
tx.vout.resize(1);
|
||||
tx.vout[0].nValue = 1*CENT;
|
||||
tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
|
||||
tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
|
||||
tx.vin.resize(2777);
|
||||
for (unsigned int j = 0; j < tx.vin.size(); j++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -68,10 +68,10 @@ BOOST_AUTO_TEST_CASE(key_test1)
|
|||
BOOST_CHECK(!key2C.VerifyPubKey(pubkey2));
|
||||
BOOST_CHECK(key2C.VerifyPubKey(pubkey2C));
|
||||
|
||||
BOOST_CHECK(DecodeDestination(addr1) == CTxDestination(pubkey1.GetID()));
|
||||
BOOST_CHECK(DecodeDestination(addr2) == CTxDestination(pubkey2.GetID()));
|
||||
BOOST_CHECK(DecodeDestination(addr1C) == CTxDestination(pubkey1C.GetID()));
|
||||
BOOST_CHECK(DecodeDestination(addr2C) == CTxDestination(pubkey2C.GetID()));
|
||||
BOOST_CHECK(DecodeDestination(addr1) == CTxDestination(PKHash(pubkey1)));
|
||||
BOOST_CHECK(DecodeDestination(addr2) == CTxDestination(PKHash(pubkey2)));
|
||||
BOOST_CHECK(DecodeDestination(addr1C) == CTxDestination(PKHash(pubkey1C)));
|
||||
BOOST_CHECK(DecodeDestination(addr2C) == CTxDestination(PKHash(pubkey2C)));
|
||||
|
||||
for (int n=0; n<16; n++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
|
|||
// of the transactions is below the min relay fee
|
||||
// Remove the low fee transaction and replace with a higher fee transaction
|
||||
mempool.removeRecursive(tx);
|
||||
tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
|
||||
tx.vout[0].nValue = tx.vout[0].nValue.GetAmount() - 2; // Now we should be just over the min relay fee
|
||||
hashLowFeeTx = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
|
||||
pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
|
||||
|
|
@ -273,7 +273,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
tx.vout[0].nValue = BLOCKSUBSIDY;
|
||||
for (unsigned int i = 0; i < 1001; ++i)
|
||||
{
|
||||
tx.vout[0].nValue -= LOWFEE;
|
||||
tx.vout[0].nValue = tx.vout[0].nValue.GetAmount() - LOWFEE;
|
||||
hash = tx.GetHash();
|
||||
bool spendsCoinbase = i == 0; // only first tx spends coinbase
|
||||
// If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails
|
||||
|
|
@ -288,7 +288,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
tx.vout[0].nValue = BLOCKSUBSIDY;
|
||||
for (unsigned int i = 0; i < 1001; ++i)
|
||||
{
|
||||
tx.vout[0].nValue -= LOWFEE;
|
||||
tx.vout[0].nValue = tx.vout[0].nValue.GetAmount() - LOWFEE;
|
||||
hash = tx.GetHash();
|
||||
bool spendsCoinbase = i == 0; // only first tx spends coinbase
|
||||
// If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes
|
||||
|
|
@ -309,7 +309,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
tx.vout[0].nValue = BLOCKSUBSIDY;
|
||||
for (unsigned int i = 0; i < 128; ++i)
|
||||
{
|
||||
tx.vout[0].nValue -= LOWFEE;
|
||||
tx.vout[0].nValue = tx.vout[0].nValue.GetAmount() - LOWFEE;
|
||||
hash = tx.GetHash();
|
||||
bool spendsCoinbase = i == 0; // only first tx spends coinbase
|
||||
mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
|
||||
|
|
@ -335,7 +335,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
tx.vin[1].scriptSig = CScript() << OP_1;
|
||||
tx.vin[1].prevout.hash = txFirst[0]->GetHash();
|
||||
tx.vin[1].prevout.n = 0;
|
||||
tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee
|
||||
tx.vout[0].nValue = tx.vout[0].nValue.GetAmount() + BLOCKSUBSIDY - HIGHERFEE; //First txn output + fresh coinbase - new txn fee
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
|
||||
|
|
@ -399,12 +399,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
tx.vin[0].scriptSig = CScript() << OP_1;
|
||||
tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE;
|
||||
script = CScript() << OP_0;
|
||||
tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
|
||||
tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script));
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
tx.vin[0].prevout.hash = hash;
|
||||
tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
|
||||
tx.vout[0].nValue -= LOWFEE;
|
||||
tx.vout[0].nValue = tx.vout[0].nValue.GetAmount() - LOWFEE;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||
// Should throw block-validation-failed
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue