From 20de5f9e2cb94c21bd368a53e71a9ecd44226b39 Mon Sep 17 00:00:00 2001 From: levoncrypto Date: Fri, 20 Mar 2026 12:31:55 +0400 Subject: [PATCH 01/12] =?UTF-8?q?Lelantus=20strip=20=E2=80=93=20resolved?= =?UTF-8?q?=20conflicts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- contrib/flathub/org.firo.firo-qt.metainfo.xml | 2 +- qa/pull-tester/rpc-tests.py | 4 - qa/rpc-tests/bip47-sendreceive.py | 60 - qa/rpc-tests/lelantus_mint.py | 80 - qa/rpc-tests/lelantus_mintspend.py | 137 -- .../lelantus_setmintstatus_validation.py | 66 - qa/rpc-tests/lelantus_spend_gettransaction.py | 67 - qa/rpc-tests/llmq-is-spark.py | 4 +- qa/rpc-tests/wallet-dump.py | 8 +- src/CMakeLists.txt | 17 - src/batchproof_container.cpp | 220 -- src/batchproof_container.h | 39 - src/bip47/account.cpp | 26 +- src/bip47/secretpoint.cpp | 2 - src/bloom.cpp | 1 - src/chain.h | 2 +- src/chainparams.cpp | 32 +- src/coin_containers.cpp | 4 +- src/coin_containers.h | 7 +- src/evo/specialtx.cpp | 9 +- src/evo/spork.cpp | 31 +- src/evo/spork.h | 2 - src/hdmint/hdmint.cpp | 52 - src/hdmint/hdmint.h | 72 - src/hdmint/mintpool.cpp | 62 - src/hdmint/mintpool.h | 35 - src/hdmint/test/lelantus_tests.cpp | 174 -- src/hdmint/tracker.cpp | 765 ------- src/hdmint/tracker.h | 54 - src/hdmint/wallet.cpp | 858 -------- src/hdmint/wallet.h | 64 - src/init.cpp | 25 +- src/init.h | 1 - src/lelantus.cpp | 1770 ----------------- src/lelantus.h | 311 --- src/liblelantus/challenge_generator.h | 21 - src/liblelantus/challenge_generator_impl.h | 79 - src/liblelantus/coin.cpp | 182 -- src/liblelantus/coin.h | 183 -- src/liblelantus/innerproduct_proof.h | 36 - .../innerproduct_proof_generator.cpp | 193 -- .../innerproduct_proof_generator.h | 78 - .../innerproduct_proof_verifier.cpp | 128 -- src/liblelantus/innerproduct_proof_verifier.h | 43 - src/liblelantus/joinsplit.cpp | 235 --- src/liblelantus/joinsplit.h | 145 -- src/liblelantus/lelantus_primitives.cpp | 274 --- src/liblelantus/lelantus_primitives.h | 123 -- src/liblelantus/lelantus_proof.h | 37 - src/liblelantus/lelantus_prover.cpp | 282 --- src/liblelantus/lelantus_prover.h | 51 - src/liblelantus/lelantus_verifier.cpp | 294 --- src/liblelantus/lelantus_verifier.h | 80 - src/liblelantus/openssl_context.h | 38 - src/liblelantus/params.cpp | 133 -- src/liblelantus/params.h | 53 - src/liblelantus/range_proof.h | 54 - src/liblelantus/range_prover.cpp | 199 -- src/liblelantus/range_prover.h | 40 - src/liblelantus/range_verifier.cpp | 301 --- src/liblelantus/range_verifier.h | 39 - src/liblelantus/schnorr_proof.h | 29 - src/liblelantus/schnorr_prover.cpp | 69 - src/liblelantus/schnorr_prover.h | 25 - src/liblelantus/schnorr_verifier.cpp | 80 - src/liblelantus/schnorr_verifier.h | 25 - src/liblelantus/sigmaextended_proof.h | 59 - src/liblelantus/sigmaextended_prover.cpp | 212 -- src/liblelantus/sigmaextended_prover.h | 52 - src/liblelantus/sigmaextended_verifier.cpp | 438 ---- src/liblelantus/sigmaextended_verifier.h | 86 - src/liblelantus/spend_metadata.cpp | 16 - src/liblelantus/spend_metadata.h | 32 - .../test/challenge_generator_tests.cpp | 64 - src/liblelantus/test/coin_tests.cpp | 168 -- src/liblelantus/test/inner_product_test.cpp | 168 -- src/liblelantus/test/joinsplit_tests.cpp | 107 - .../test/lelantus_primitives_tests.cpp | 275 --- src/liblelantus/test/lelantus_test.cpp | 235 --- .../test/lelantus_test_fixture.cpp | 30 - src/liblelantus/test/lelantus_test_fixture.h | 60 - src/liblelantus/test/range_proof_test.cpp | 346 ---- src/liblelantus/test/schnorr_test.cpp | 100 - src/liblelantus/test/serialize_test.cpp | 85 - src/liblelantus/test/sigma_extended_test.cpp | 238 --- src/liblelantus/threadpool.h | 139 -- src/libspark/coin.h | 131 ++ src/libspark/grootle.h | 4 +- src/libspark/spend_transaction.h | 5 + src/llmq/quorums_instantsend.cpp | 55 +- src/miner.cpp | 39 - src/primitives/mint_spend.cpp | 20 - src/primitives/mint_spend.h | 80 +- src/qt/bitcoin.qrc | 1 - src/qt/bitcoingui.cpp | 12 - src/qt/bitcoingui.h | 1 - src/qt/coincontroldialog.cpp | 4 +- src/qt/forms/optionsdialog.ui | 40 - src/qt/locale/bitcoin_zh_CN.ts | 26 +- src/qt/optionsdialog.cpp | 25 +- src/qt/optionsmodel.cpp | 36 - src/qt/optionsmodel.h | 5 - src/qt/overviewpage.cpp | 248 +-- src/qt/overviewpage.h | 20 +- src/qt/res/css/firo.css | 36 - src/qt/sendcoinsdialog.cpp | 13 +- src/qt/sparkmodel.cpp | 1 - src/qt/transactiondesc.cpp | 24 +- src/qt/transactionrecord.cpp | 15 +- src/qt/walletmodel.cpp | 25 - src/qt/walletmodel.h | 4 - src/qt/walletmodeltransaction.cpp | 10 - src/qt/walletmodeltransaction.h | 10 +- src/rpc/client.cpp | 14 +- src/rpc/misc.cpp | 253 +-- src/rpc/rawtransaction.cpp | 17 - src/rpc/rpcevo.cpp | 2 - src/spark/sparkwallet.cpp | 72 +- src/spark/state.cpp | 71 +- src/spark/state.h | 2 +- src/test/CMakeLists.txt | 16 - src/test/fixtures.cpp | 102 - src/test/fixtures.h | 31 - src/test/lelantus_mintspend_test.cpp | 164 -- src/test/lelantus_state_tests.cpp | 591 ------ src/test/lelantus_tests.cpp | 962 --------- src/test/test_bitcoin.cpp | 5 - src/txmempool.cpp | 34 - src/txmempool.h | 6 +- src/validation.cpp | 407 +--- src/validation.h | 6 +- src/wallet/CMakeLists.txt | 6 - src/wallet/crypter.cpp | 7 - src/wallet/lelantusjoinsplitbuilder.cpp | 491 ----- src/wallet/lelantusjoinsplitbuilder.h | 47 - src/wallet/rpcdump.cpp | 5 +- src/wallet/rpcwallet.cpp | 762 +------ src/wallet/test/CMakeLists.txt | 1 - src/wallet/test/lelantus_tests.cpp | 298 --- src/wallet/txbuilder.cpp | 10 +- src/wallet/wallet.cpp | 1393 +------------ src/wallet/wallet.h | 78 +- src/wallet/walletdb.cpp | 232 --- src/wallet/walletdb.h | 19 - 145 files changed, 439 insertions(+), 18081 deletions(-) delete mode 100755 qa/rpc-tests/bip47-sendreceive.py delete mode 100755 qa/rpc-tests/lelantus_mint.py delete mode 100755 qa/rpc-tests/lelantus_mintspend.py delete mode 100755 qa/rpc-tests/lelantus_setmintstatus_validation.py delete mode 100755 qa/rpc-tests/lelantus_spend_gettransaction.py delete mode 100644 src/hdmint/hdmint.cpp delete mode 100644 src/hdmint/hdmint.h delete mode 100644 src/hdmint/mintpool.cpp delete mode 100644 src/hdmint/mintpool.h delete mode 100644 src/hdmint/test/lelantus_tests.cpp delete mode 100644 src/hdmint/tracker.cpp delete mode 100644 src/hdmint/tracker.h delete mode 100644 src/hdmint/wallet.cpp delete mode 100644 src/hdmint/wallet.h delete mode 100644 src/lelantus.cpp delete mode 100644 src/lelantus.h delete mode 100644 src/liblelantus/challenge_generator.h delete mode 100644 src/liblelantus/challenge_generator_impl.h delete mode 100644 src/liblelantus/coin.cpp delete mode 100644 src/liblelantus/coin.h delete mode 100755 src/liblelantus/innerproduct_proof.h delete mode 100755 src/liblelantus/innerproduct_proof_generator.cpp delete mode 100755 src/liblelantus/innerproduct_proof_generator.h delete mode 100755 src/liblelantus/innerproduct_proof_verifier.cpp delete mode 100755 src/liblelantus/innerproduct_proof_verifier.h delete mode 100644 src/liblelantus/joinsplit.cpp delete mode 100644 src/liblelantus/joinsplit.h delete mode 100644 src/liblelantus/lelantus_primitives.cpp delete mode 100644 src/liblelantus/lelantus_primitives.h delete mode 100644 src/liblelantus/lelantus_proof.h delete mode 100644 src/liblelantus/lelantus_prover.cpp delete mode 100644 src/liblelantus/lelantus_prover.h delete mode 100644 src/liblelantus/lelantus_verifier.cpp delete mode 100644 src/liblelantus/lelantus_verifier.h delete mode 100644 src/liblelantus/openssl_context.h delete mode 100644 src/liblelantus/params.cpp delete mode 100644 src/liblelantus/params.h delete mode 100644 src/liblelantus/range_proof.h delete mode 100644 src/liblelantus/range_prover.cpp delete mode 100644 src/liblelantus/range_prover.h delete mode 100644 src/liblelantus/range_verifier.cpp delete mode 100644 src/liblelantus/range_verifier.h delete mode 100644 src/liblelantus/schnorr_proof.h delete mode 100644 src/liblelantus/schnorr_prover.cpp delete mode 100644 src/liblelantus/schnorr_prover.h delete mode 100644 src/liblelantus/schnorr_verifier.cpp delete mode 100644 src/liblelantus/schnorr_verifier.h delete mode 100644 src/liblelantus/sigmaextended_proof.h delete mode 100644 src/liblelantus/sigmaextended_prover.cpp delete mode 100644 src/liblelantus/sigmaextended_prover.h delete mode 100644 src/liblelantus/sigmaextended_verifier.cpp delete mode 100644 src/liblelantus/sigmaextended_verifier.h delete mode 100644 src/liblelantus/spend_metadata.cpp delete mode 100644 src/liblelantus/spend_metadata.h delete mode 100644 src/liblelantus/test/challenge_generator_tests.cpp delete mode 100644 src/liblelantus/test/coin_tests.cpp delete mode 100755 src/liblelantus/test/inner_product_test.cpp delete mode 100644 src/liblelantus/test/joinsplit_tests.cpp delete mode 100644 src/liblelantus/test/lelantus_primitives_tests.cpp delete mode 100644 src/liblelantus/test/lelantus_test.cpp delete mode 100644 src/liblelantus/test/lelantus_test_fixture.cpp delete mode 100644 src/liblelantus/test/lelantus_test_fixture.h delete mode 100644 src/liblelantus/test/range_proof_test.cpp delete mode 100644 src/liblelantus/test/schnorr_test.cpp delete mode 100644 src/liblelantus/test/serialize_test.cpp delete mode 100644 src/liblelantus/test/sigma_extended_test.cpp delete mode 100644 src/liblelantus/threadpool.h delete mode 100644 src/test/lelantus_mintspend_test.cpp delete mode 100644 src/test/lelantus_state_tests.cpp delete mode 100644 src/test/lelantus_tests.cpp delete mode 100644 src/wallet/lelantusjoinsplitbuilder.cpp delete mode 100644 src/wallet/lelantusjoinsplitbuilder.h delete mode 100644 src/wallet/test/lelantus_tests.cpp diff --git a/README.md b/README.md index 2f4d6b185b..51c5228b69 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,7 @@ [![GitHub last-commit](https://img.shields.io/github/last-commit/firoorg/firo)](https://github.com/firoorg/firo/commits/master) ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/firoorg/firo?utm_source=oss&utm_medium=github&utm_campaign=firoorg%2Ffiro&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews) -[Firo](https://firo.org) formerly known as Zcoin, is a privacy focused cryptocurrency that utilizes the [Lelantus Spark protocol](https://eprint.iacr.org/2021/1173) which supports high anonymity sets without requiring trusted setup and relying on standard cryptographic assumptions. - -The Lelantus Spark cryptographic library and implementation was audited by [HashCloak](https://firo.org/about/research/papers/lelantus_spark_code_audit_report.pdf). The Lelantus Spark cryptography paper has undergone two separate audits by [HashCloak](https://firo.org/about/research/papers/Lelantus_Spark_Audit_Report.pdf) and [Daniel (Linfeng) Zhao](https://firo.org/about/research/papers/LinfengSparkAudit.pdf). +[Firo](https://firo.org) formerly known as Zcoin, is a privacy focused cryptocurrency that utilizes the [Spark protocol](https://eprint.iacr.org/2021/1173) which supports high anonymity sets without requiring trusted setup and relying on standard cryptographic assumptions. Firo also utilises [Dandelion++](https://arxiv.org/abs/1805.11060) to obscure the originating IP of transactions without relying on any external services such as Tor/i2P. diff --git a/contrib/flathub/org.firo.firo-qt.metainfo.xml b/contrib/flathub/org.firo.firo-qt.metainfo.xml index d277784c0a..0a789f823c 100644 --- a/contrib/flathub/org.firo.firo-qt.metainfo.xml +++ b/contrib/flathub/org.firo.firo-qt.metainfo.xml @@ -24,7 +24,7 @@ Bitcoin was originally created as an answer to this by ensuring you can be self sovereign over your money and to serve as uncensorable and unseizable money that isn't controlled by any one entity.

Bitcoin's lack of privacy however has now made it much easier to seize or blacklist funds and due to ossification of the protocol, is unlikely to take serious steps to address this. - Firo has dedicated itself to being a privacy preserving cryptocurrency and have designed and built trustless privacy protocols such as Lelantus and Lelantus Spark that have inspired and shaped the designs of other privacy protocols (for e.g. Triptych, Seraphis, Lelantus-MW).

+ Firo has dedicated itself to being a privacy preserving cryptocurrency and have designed and built trustless privacy protocols such as Spark that have inspired and shaped the designs of other privacy protocols (for e.g. Triptych, Seraphis, Lelantus-MW).

diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index abe9a539d5..f6cb7cd3c9 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -105,10 +105,6 @@ 'spark_mintspend.py', 'spark_spend_gettransaction.py', 'spark_setmintstatus_validation.py', - 'lelantus_mint.py', - 'lelantus_setmintstatus_validation.py', - 'lelantus_mintspend.py', - 'lelantus_spend_gettransaction.py', 'mempool_doublesend_oneblock.py', 'mempool_reorg.py', 'mempool_spendcoinbase.py', diff --git a/qa/rpc-tests/bip47-sendreceive.py b/qa/rpc-tests/bip47-sendreceive.py deleted file mode 100755 index f3af936454..0000000000 --- a/qa/rpc-tests/bip47-sendreceive.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Firo Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""dip47 sending receiving RPCs QA test. -""" - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * - -class Bip47SendReceive(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = True - self.num_nodes = 3 - - def setup_network(self, split=False): - self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) - connect_nodes_bi(self.nodes,0,1) - connect_nodes_bi(self.nodes,1,2) - connect_nodes_bi(self.nodes,0,2) - self.is_network_split=False - self.sync_all() - - def run_test(self): - - self.nodes[1].generate(210) - node0_pcode = self.nodes[0].createrapaddress("node0-pcode0") - - try: - self.nodes[1].setupchannel(node0_pcode) - raise AssertionError('Lelantus balance should be zero') - except JSONRPCException as e: - assert(e.error['code']==-6) - - self.nodes[1].mintlelantus(1) - self.nodes[1].mintlelantus(1) - self.nodes[1].generate(10) - self.nodes[1].setupchannel(node0_pcode) - self.nodes[1].generate(1) - sync_blocks(self.nodes) - self.nodes[1].sendtorapaddress(node0_pcode, 10) - - self.nodes[1].generate(1) - self.sync_all() - - assert_equal(self.nodes[0].getbalance(), Decimal("10.0001")) - - self.nodes[0].sendtoaddress(self.nodes[2].getaccountaddress(""), 9.99) - - self.sync_all() - self.nodes[1].generate(1) - sync_blocks(self.nodes) - - assert_equal(self.nodes[2].getbalance(), Decimal("9.99")) - - -if __name__ == '__main__': - Bip47SendReceive().main() diff --git a/qa/rpc-tests/lelantus_mint.py b/qa/rpc-tests/lelantus_mint.py deleted file mode 100755 index e52c12f07f..0000000000 --- a/qa/rpc-tests/lelantus_mint.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python3 -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_raises_message, JSONRPCException - -class LelantusMintTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 1 - self.setup_clean_chain = False - - def run_test(self): - # generate coins - amounts = [1, 1.1, 2, 10] - - # 10 confirmations - self.nodes[0].mintlelantus(amounts[0]) - self.nodes[0].mintlelantus(amounts[1]) - self.nodes[0].generate(5) - - # 5 confirmations - self.nodes[0].mintlelantus(amounts[2]) - self.nodes[0].mintlelantus(amounts[3]) - self.nodes[0].generate(5) - - # get all mints and utxos - mints = self.verify_listlelantusmints(amounts) - self.verify_listunspentlelantusmints(amounts) - self.verify_listunspentlelantusmints([], 1000) # [1000, 9999999] - self.verify_listunspentlelantusmints([2, 10], 1, 5) # [1, 5] - self.verify_listunspentlelantusmints([1, 1.1], 6, 10) - self.verify_listunspentlelantusmints([1, 1.1, 2, 10], 5, 10) - assert_equal([False, False, False, False], list(map(lambda m : m["isUsed"], mints))) - - # state modification test - # mark two coins as used - self.nodes[0].setlelantusmintstatus(mints[2]["serialNumber"], True) - self.nodes[0].setlelantusmintstatus(mints[3]["serialNumber"], True) - - mints = self.verify_listlelantusmints(amounts) - self.verify_listunspentlelantusmints([1, 1.1]) - self.verify_listunspentlelantusmints([], 1000) - self.verify_listunspentlelantusmints([], 1, 5) - self.verify_listunspentlelantusmints([1, 1.1], 6, 10) - self.verify_listunspentlelantusmints([1, 1.1], 5, 10) - assert_equal([False, False, True, True], list(map(lambda m : m["isUsed"], mints))) - - # set a coin as unused - self.nodes[0].setlelantusmintstatus(mints[3]["serialNumber"], False) - mints = self.verify_listlelantusmints(amounts) - self.verify_listunspentlelantusmints([1, 1.1, 10]) - assert_equal([False, False, True, False], list(map(lambda m : m["isUsed"], mints))) - - # reset coins state - self.nodes[0].resetlelantusmint() - mints = self.verify_listlelantusmints(amounts) - self.verify_listunspentlelantusmints(amounts) - assert_equal([False, False, False, False], list(map(lambda m : m["isUsed"], mints))) - - def verify_listlelantusmints(self, expected_amounts, *args): - mints = self.nodes[0].listlelantusmints(*args) - mints = sorted(mints, key = lambda u: u['amount']) - - assert_equal( - sorted(expected_amounts), - list(map(lambda u: u['amount'] / 1e8, mints))) - - return mints - - def verify_listunspentlelantusmints(self, expected_amounts, *args): - utxos = self.nodes[0].listunspentlelantusmints(*args) - utxos = sorted(utxos, key = lambda u: float(u['amount'])) - - assert_equal( - sorted(expected_amounts), - list(map(lambda u: float(u['amount']), utxos))) - - return utxos - -if __name__ == '__main__': - LelantusMintTest().main() \ No newline at end of file diff --git a/qa/rpc-tests/lelantus_mintspend.py b/qa/rpc-tests/lelantus_mintspend.py deleted file mode 100755 index 689c93cdc9..0000000000 --- a/qa/rpc-tests/lelantus_mintspend.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env python3 -from decimal import * - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * - - -class LelantusMintSpendTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False - - def run_test(self): - # Decimal formating: 6 digits for balance will be enought 000.000 - getcontext().prec = 6 - self.nodes[0].generate(11) - self.sync_all() - - start_bal = self.nodes[0].getbalance() - - mint_trans = list() - mint_trans.append(self.nodes[0].mintlelantus(1)) - mint_trans.append(self.nodes[0].mintlelantus(2)) - - # Get fees from both transactions (they may differ) - info1 = self.nodes[0].gettransaction(mint_trans[0][0]) - info2 = self.nodes[0].gettransaction(mint_trans[1][0]) - - # fee in transaction is negative, sum both actual fees - fee = -(info1['fee'] + info2['fee']) - cur_bal = self.nodes[0].getbalance() - start_bal = float(start_bal) - float(fee) - 3 - start_bal = Decimal(format(start_bal, '.8f')) - - assert start_bal == cur_bal, \ - 'Unexpected current balance: {}, should be minus two mints and two fee, ' \ - 'but start was {}'.format(cur_bal, start_bal) - - for tr in mint_trans: - info = self.nodes[0].gettransaction(tr[0]) - confrms = info['confirmations'] - assert confrms == 0, \ - 'Confirmations should be {}, ' \ - 'due to {} blocks was generated after transaction was created,' \ - 'but was {}'.format(0, 0, confrms) - - tr_type = info['details'][0]['category'] - assert tr_type == 'mint', 'Unexpected transaction type: {}'.format(tr_type) - - res = False - args = {'THAYjKnnCsN5xspnEcb1Ztvw4mSPBuwxzU': 1} - try: - res = self.nodes[0].joinsplit(args) - except JSONRPCException as ex: - assert ex.error['message'] == 'Insufficient funds' - - assert not res, 'Did not raise spend exception, but should be.' - - self.nodes[0].generate(1) - self.sync_all() - - # generate last confirmation block - now all transactions should be confimed - self.nodes[0].generate(1) - self.sync_all() - - for tr in mint_trans: - info = self.nodes[0].gettransaction(tr[0]) - confrms = info['confirmations'] - assert confrms == 2, \ - 'Confirmations should be 2, ' \ - 'due to 2 blocks was generated after transaction was created,' \ - 'but was {}.'.format(confrms) - tr_type = info['details'][0]['category'] - assert tr_type == 'mint', 'Unexpected transaction type' - - spend_trans = list() - spend_total = Decimal(0) - - self.sync_all() - - start_bal = self.nodes[0].getbalance() - print(start_bal) - total_spend_fee = 0 - - myaddr = self.nodes[0].listreceivedbyaddress(0, True)[0]['address'] - print(1) - args = {myaddr: 1} - - spend_trans.append(self.nodes[0].joinsplit(args)) - - info = self.nodes[0].gettransaction(spend_trans[-1]) - confrms = info['confirmations'] - tr_type = info['details'][0]['category'] - total_spend_fee += -info['fee'] - print(info['fee']) - print(self.nodes[0].getbalance()) - spend_total = float(spend_total) + 1 - assert confrms == 0, \ - 'Confirmations should be 0, ' \ - 'due to 0 blocks was generated after transaction was created,' \ - 'but was {}.'.format(confrms) - assert tr_type == 'spend', 'Unexpected transaction type' - print(self.nodes[0].getbalance()) - - before_new = self.nodes[0].getbalance() - self.nodes[0].generate(2) - after_new = self.nodes[0].getbalance() - delta = after_new - before_new - self.sync_all() - - # # Start balance increase on generated blocks to confirm - start_bal += delta - cur_bal = Decimal(format(self.nodes[0].getbalance(), '.1f')) - spend_total = Decimal(format(spend_total, '.8f')) - - #TODO check why currently was not minused from total sum - start_bal = start_bal + spend_total - - assert start_bal == cur_bal, \ - 'Unexpected current balance: {}, should increase on {}, ' \ - 'but start was {}'.format(cur_bal, spend_total, start_bal) - - for tr in spend_trans: - info = self.nodes[0].gettransaction(tr) - - confrms = info['confirmations'] - tr_type = info['details'][0]['category'] - assert confrms >= 1, \ - 'Confirmations should be 1 or more, ' \ - 'due to 1 blocks was generated after transaction was created,' \ - 'but was {}.'.format(confrms) - assert tr_type == 'spend', 'Unexpected transaction type' - - -if __name__ == '__main__': - LelantusMintSpendTest().main() diff --git a/qa/rpc-tests/lelantus_setmintstatus_validation.py b/qa/rpc-tests/lelantus_setmintstatus_validation.py deleted file mode 100755 index 61b4d6257e..0000000000 --- a/qa/rpc-tests/lelantus_setmintstatus_validation.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * - -class SetLelantusMintSatusValidationWithFundsTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False - - def setup_nodes(self): - # This test requires mocktime - enable_mocktime() - return start_nodes(self.num_nodes, self.options.tmpdir) - - def run_test(self): - self.nodes[0].generate(100) - self.sync_all() - - txid = self.nodes[0].mintlelantus(10) - - lelantus_mint = self.nodes[0].listlelantusmints() - - assert len(lelantus_mint) == len(txid), 'Should be same number.' - - mint_info = lelantus_mint[0] - - assert not mint_info['isUsed'], \ - 'This mint with txid: {} should not be Used.'.format(txid) - - print('Set mint status from False to True.') - - self.nodes[0].setlelantusmintstatus(mint_info['serialNumber'], True) - - lelantus_mint = self.nodes[0].listlelantusmints() - - assert len(lelantus_mint) == len(txid), 'Should be same number.' - - mint_info = lelantus_mint[0] - - assert mint_info['isUsed'], \ - 'This mint with txid: {} should be Used.'.format(txid) - - print('Set mint status from True to False back.') - - self.nodes[0].setlelantusmintstatus(mint_info['serialNumber'], False) - - lelantus_mint = self.nodes[0].listlelantusmints() - - assert len(lelantus_mint) == len(txid), 'Should be same number.' - - mint_info = lelantus_mint[0] - - assert not mint_info['isUsed'], \ - 'This mint with txid: {} should not be Used.'.format(txid) - - - assert_raises(JSONRPCException, self.nodes[0].setlelantusmintstatus, [(mint_info['serialNumber'], "sometext")]) - assert_raises(JSONRPCException, self.nodes[0].setlelantusmintstatus, [mint_info['serialNumber']]) - assert_raises(JSONRPCException, self.nodes[0].setlelantusmintstatus, []) - assert_raises(JSONRPCException, self.nodes[0].setlelantusmintstatus, ["sometext"]) - assert_raises(JSONRPCException, self.nodes[0].setlelantusmintstatus, [123]) - - -if __name__ == '__main__': - SetLelantusMintSatusValidationWithFundsTest().main() \ No newline at end of file diff --git a/qa/rpc-tests/lelantus_spend_gettransaction.py b/qa/rpc-tests/lelantus_spend_gettransaction.py deleted file mode 100755 index 73599a2890..0000000000 --- a/qa/rpc-tests/lelantus_spend_gettransaction.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python3 -from decimal import * - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * - -class JoinSplitGettransactionTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = True - - def setup_nodes(self): - # This test requires mocktime - enable_mocktime() - return start_nodes(self.num_nodes, self.options.tmpdir) - - def run_test(self): - self.nodes[0].generate(101) - self.sync_all() - - # get a watch only address - watchonly_address = self.nodes[3].getnewaddress() - watchonly_pubkey = self.nodes[3].validateaddress(watchonly_address)["pubkey"] - self.nodes[0].importpubkey(watchonly_pubkey, "", True) - - valid_address = self.nodes[0].getnewaddress() - - for _ in range(10): - self.nodes[0].mintlelantus(1) - - self.nodes[0].generate(10) - self.sync_all() - - # case 1: Spend many with watchonly address - spendto_wo_id = self.nodes[0].joinsplit({watchonly_address: 1}) - spendto_wo_tx = self.nodes[0].gettransaction(spendto_wo_id) - - assert spendto_wo_tx['amount'] == Decimal('-1') - assert spendto_wo_tx['fee'] < Decimal('0') - assert isinstance(spendto_wo_tx['details'], list) - assert len(spendto_wo_tx['details']) == 1 - assert spendto_wo_tx['details'][0]['involvesWatchonly'] - - # case 2: Spend many with watchonly address and valid address - spendto_wo_and_valid_id = self.nodes[0].joinsplit({watchonly_address: 1, valid_address: 2}) - spendto_wo_and_valid_tx = self.nodes[0].gettransaction(spendto_wo_and_valid_id) - - assert spendto_wo_and_valid_tx['amount'] == Decimal('-1') - assert spendto_wo_and_valid_tx['fee'] < Decimal('0') - assert isinstance(spendto_wo_and_valid_tx['details'], list) - assert len(spendto_wo_and_valid_tx['details']) == 3 - - involves_watch_only_count = 0 - for detial in spendto_wo_and_valid_tx['details']: - if 'involvesWatchonly' in detial and bool(detial['involvesWatchonly']): - involves_watch_only_count += 1 - - assert involves_watch_only_count == 1 - - # case 3: spend many with watchonly address and invalid address - assert_raises(JSONRPCException, self.nodes[0].joinsplit, [{watchonly_address: 1, 'invalidaddress': 2}]) - -if __name__ == '__main__': - JoinSplitGettransactionTest().main() - - diff --git a/qa/rpc-tests/llmq-is-spark.py b/qa/rpc-tests/llmq-is-spark.py index 2c70c5e81a..d59ac343c5 100755 --- a/qa/rpc-tests/llmq-is-spark.py +++ b/qa/rpc-tests/llmq-is-spark.py @@ -18,7 +18,7 @@ Testing Instantsend for Spark transactions ''' -class LLMQ_IS_Lelantus(EvoZnodeTestFramework): +class LLMQ_IS_Spark(EvoZnodeTestFramework): def __init__(self): super().__init__(6, 5, extra_args=[['-debug=instantsend']] * 6 ) self.sporkprivkey = "cW2YM2xaeCaebfpKguBahUAgEzLXgSserWRuD29kSyKHq1TTgwRQ" @@ -55,4 +55,4 @@ def run_test(self): assert(self.wait_for_instantlock(spendTxid, self.nodes[0])) if __name__ == '__main__': - LLMQ_IS_Lelantus().main() + LLMQ_IS_Spark().main() diff --git a/qa/rpc-tests/wallet-dump.py b/qa/rpc-tests/wallet-dump.py index c1fee55d1c..d495330d8f 100755 --- a/qa/rpc-tests/wallet-dump.py +++ b/qa/rpc-tests/wallet-dump.py @@ -81,8 +81,8 @@ def setup_network(self, split=False): def run_test (self): tmpdir = self.options.tmpdir - # 21 hdmint keys generated initially (0-20) - hdmint_key_count = 21 + # hdmint/sigma mint keys no longer generated (Lelantus stripped) + hdmint_key_count = 0 # generate 20 addresses to compare against the dump test_addr_count = 20 @@ -132,8 +132,8 @@ def run_test (self): assert_equal(found_addr, test_addr_count) assert_equal(found_addr_chg, 50) # 50 block were mined - # Wallet encryption doesn't change master key anymore, therefore we just verify hdmint_key_count is the same as before. - assert_equal(found_addr_sigma, hdmint_key_count) # hdmint keys + # Wallet encryption doesn't change master key anymore; sigma key count unchanged after Lelantus strip (0). + assert_equal(found_addr_sigma, hdmint_key_count) assert_equal(found_addr_rsv, 90 + 1) # keypool size (TODO: fix off-by-one) if __name__ == '__main__': diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bf9e78a2d8..aa80161a0d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -200,7 +200,6 @@ add_library(bitcoin_common STATIC EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/compressor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core_read.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core_write.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/hdmint/hdmint.cpp ${CMAKE_CURRENT_SOURCE_DIR}/init.cpp ${CMAKE_CURRENT_SOURCE_DIR}/key.cpp ${CMAKE_CURRENT_SOURCE_DIR}/keystore.cpp @@ -270,22 +269,6 @@ add_library(firo_node STATIC EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/httprpc.cpp ${CMAKE_CURRENT_SOURCE_DIR}/httpserver.cpp ${CMAKE_CURRENT_SOURCE_DIR}/init.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/lelantus.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/coin.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/innerproduct_proof_generator.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/innerproduct_proof_verifier.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/joinsplit.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/lelantus_primitives.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/lelantus_prover.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/lelantus_verifier.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/params.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/range_prover.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/range_verifier.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/schnorr_prover.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/schnorr_verifier.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/sigmaextended_prover.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/sigmaextended_verifier.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/liblelantus/spend_metadata.cpp ${CMAKE_CURRENT_SOURCE_DIR}/libspark/aead.cpp ${CMAKE_CURRENT_SOURCE_DIR}/libspark/bech32.cpp ${CMAKE_CURRENT_SOURCE_DIR}/libspark/bpplus.cpp diff --git a/src/batchproof_container.cpp b/src/batchproof_container.cpp index 98b760d89d..e2a2fe040e 100644 --- a/src/batchproof_container.cpp +++ b/src/batchproof_container.cpp @@ -1,8 +1,4 @@ #include "batchproof_container.h" -#include "liblelantus/sigmaextended_verifier.h" -#include "liblelantus/threadpool.h" -#include "liblelantus/range_verifier.h" -#include "lelantus.h" #include "ui_interface.h" #include "spark/state.h" @@ -18,20 +14,11 @@ BatchProofContainer* BatchProofContainer::get_instance() { } void BatchProofContainer::init() { - tempRangeProofs.clear(); tempSparkTransactions.clear(); } void BatchProofContainer::finalize() { if (fCollectProofs) { - for (const auto& itr : tempLelantusSigmaProofs) { - lelantusSigmaProofs[itr.first].insert(lelantusSigmaProofs[itr.first].begin(), itr.second.begin(), itr.second.end()); - } - - for (const auto& itr : tempRangeProofs) { - rangeProofs[itr.first].insert(rangeProofs[itr.first].begin(), itr.second.begin(), itr.second.end()); - } - sparkTransactions.insert(sparkTransactions.end(), tempSparkTransactions.begin(), tempSparkTransactions.end()); } fCollectProofs = false; @@ -39,218 +26,11 @@ void BatchProofContainer::finalize() { void BatchProofContainer::verify() { if (!fCollectProofs) { - batch_lelantus(); - batch_rangeProofs(); batch_spark(); } fCollectProofs = false; } -void BatchProofContainer::add(lelantus::JoinSplit* joinSplit, - const std::map& setSizes, - const Scalar& challenge, - bool fStartLelantusBlacklist) { - const std::vector& sigma_proofs = joinSplit->getLelantusProof().sigma_proofs; - const std::vector& serials = joinSplit->getCoinSerialNumbers(); - const std::vector& groupIds = joinSplit->getCoinGroupIds(); - if (joinSplit->isSigmaToLelantus()) - return; - - for (size_t i = 0; i < sigma_proofs.size(); i++) { - // pair(pair(set id, fAfterFixes), isSigmaToLelantus) - std::pair idAndFlag = std::make_pair(groupIds[i], fStartLelantusBlacklist); - tempLelantusSigmaProofs[idAndFlag].push_back(LelantusSigmaProofData(sigma_proofs[i], serials[i], challenge, setSizes.at(groupIds[i]))); - } -} - - -void BatchProofContainer::add(lelantus::JoinSplit* joinSplit, const std::vector& Cout) { - tempRangeProofs[joinSplit->getVersion()].push_back(std::make_pair(joinSplit->getLelantusProof().bulletproofs, Cout)); -} - -void BatchProofContainer::removeLelantus(std::unordered_map spentSerials) { - for (auto& spendSerial : spentSerials) { - - int id = spendSerial.second; - - // afterFixes bool with the pair of set id is considered separate set identifiers, so try to find in one set, if not found try also in another - std::pair key1 = std::make_pair(id, false); - std::pair key2 = std::make_pair(id, true); - std::vector* vProofs; - if (lelantusSigmaProofs.count(key1) > 0) { - vProofs = &lelantusSigmaProofs[key1]; - erase(vProofs, spendSerial.first); - } - - if (lelantusSigmaProofs.count(key2) > 0) { - vProofs = &lelantusSigmaProofs[key2]; - erase(vProofs, spendSerial.first); - } - } -} - -void BatchProofContainer::remove(const std::vector& rangeProofsToRemove) { - for (auto& itrRemove : rangeProofsToRemove) { - for (auto itrVersions = rangeProofs.begin(); itrVersions != rangeProofs.end(); ++itrVersions) { - bool found = false; - for (auto itr = itrVersions->second.begin(); itr != itrVersions->second.end(); ++itr) { - if (itr->first.T_x1 == itrRemove.T_x1 && itr->first.T_x2 == itrRemove.T_x2 && itr->first.u == itrRemove.u) { - itrVersions->second.erase(itr); - found = true; - break; - } - } - if (itrVersions->second.empty()) { - rangeProofs.erase(itrVersions); - itrVersions--; - } - if (found) - break; - } - } -} - -void BatchProofContainer::erase(std::vector* vProofs, const Scalar& serial) { - vProofs->erase(std::remove_if(vProofs->begin(), - vProofs->end(), - [serial](LelantusSigmaProofData& proof){return proof.serialNumber == serial;}), - vProofs->end()); - -} - -void BatchProofContainer::batch_lelantus() { - if (!lelantusSigmaProofs.empty()){ - LogPrintf("Lelantus batch verification started.\n"); - uiInterface.UpdateProgressBarLabel("Batch verifying Lelantus..."); - } - else - return; - - auto params = lelantus::Params::get_default(); - - DoNotDisturb dnd; - std::size_t threadsMaxCount = std::min((unsigned int)lelantusSigmaProofs.size(), boost::thread::hardware_concurrency()); - std::vector> parallelTasks; - parallelTasks.reserve(threadsMaxCount); - ParallelOpThreadPool threadPool(threadsMaxCount); - auto itr = lelantusSigmaProofs.begin(); - - lelantus::SigmaExtendedVerifier sigmaVerifier(params->get_g(), params->get_sigma_h(), params->get_sigma_n(), - params->get_sigma_m()); - for (std::size_t j = 0; j < lelantusSigmaProofs.size(); j += threadsMaxCount) { - for (std::size_t i = j; i < j + threadsMaxCount; ++i) { - if (i < lelantusSigmaProofs.size()) { - std::vector anonymity_set; - lelantus::CLelantusState* state = lelantus::CLelantusState::GetState(); - std::vector coins; - state->GetAnonymitySet( - itr->first.first, - itr->first.second, - coins); - anonymity_set.reserve(coins.size()); - for (auto& coin : coins) - anonymity_set.emplace_back(coin.getValue()); - - size_t m = itr->second.size(); - std::vector serials; - serials.reserve(m); - std::vector setSizes; - setSizes.reserve(m); - std::vector proofs; - proofs.reserve(m); - std::vector challenges; - challenges.reserve(m); - - for (auto& proofData : itr->second) { - serials.emplace_back(proofData.serialNumber); - setSizes.emplace_back(proofData.anonymitySetSize); - proofs.emplace_back(proofData.lelantusSigmaProof); - challenges.emplace_back(proofData.challenge); - } - - - - parallelTasks.emplace_back(threadPool.PostTask([=]() { - try { - if (!sigmaVerifier.batchverify(anonymity_set, challenges, serials, setSizes, proofs)) - return false; - } catch (const std::exception &) { - return false; - } - return true; - })); - - ++itr; - } - } - bool isFail = false; - for (auto& th : parallelTasks) { - if (!th.get()) - isFail = true; - } - - if (isFail) { - LogPrintf("Lelantus batch verification failed."); - throw std::invalid_argument("Lelantus batch verification failed, please run Firo with -reindex -batching=0"); - } - - parallelTasks.clear(); - } - if (!lelantusSigmaProofs.empty()) - LogPrintf("Lelantus batch verification finished successfully.\n"); - lelantusSigmaProofs.clear(); -} - -void BatchProofContainer::batch_rangeProofs() { - if (!rangeProofs.empty()){ - LogPrintf("RangeProof batch verification started.\n"); - uiInterface.UpdateProgressBarLabel("Batch verifying Range Proofs..."); - } - - auto params = lelantus::Params::get_default(); - for (const auto& itr : rangeProofs) { - lelantus::RangeVerifier rangeVerifier(params->get_h1(), params->get_h0(), params->get_g(), params->get_bulletproofs_g(), params->get_bulletproofs_h(), params->get_bulletproofs_n(), itr.first); - std::vector> V; - std::vector> commitments; - size_t proofSize = itr.second.size(); - V.resize(proofSize); //size of batch - commitments.resize(proofSize); // size of batch - std::vector proofs; - proofs.reserve(proofSize); // size of batch - for (size_t i = 0; i < proofSize; ++i) { - size_t coutSize = itr.second[i].second.size(); - std::size_t m = coutSize * 2; - - while (m & (m - 1)) - m++; - proofs.emplace_back(itr.second[i].first); - V[i].reserve(m); // aggregation size - commitments[i].reserve(2 * coutSize); - commitments[i].resize(coutSize); // prepend zero elements, to match the prover's behavior - auto& Cout = itr.second[i].second; - for (std::size_t j = 0; j < coutSize; ++j) { - V[i].push_back(Cout[j].getValue()); - V[i].push_back(Cout[j].getValue() + params->get_h1_limit_range()); - commitments[i].emplace_back(Cout[j].getValue()); - } - - // Pad with zero elements - for (std::size_t t = coutSize * 2; t < m; ++t) - V[i].push_back(GroupElement()); - } - - if (!rangeVerifier.verify(V, commitments, proofs)) { - LogPrintf("RangeProof batch verification failed.\n"); - throw std::invalid_argument("RangeProof batch verification failed, please run Firo with -reindex -batching=0"); - } - } - - if (!rangeProofs.empty()) - LogPrintf("RangeProof batch verification finished successfully.\n"); - - rangeProofs.clear(); -} - void BatchProofContainer::add(const spark::SpendTransaction& tx) { tempSparkTransactions.push_back(tx); } diff --git a/src/batchproof_container.h b/src/batchproof_container.h index 519c84074c..dc87dcfce2 100644 --- a/src/batchproof_container.h +++ b/src/batchproof_container.h @@ -3,7 +3,6 @@ #include #include "chain.h" -#include "liblelantus/joinsplit.h" #include "libspark/spend_transaction.h" extern CChain chainActive; @@ -12,42 +11,12 @@ class BatchProofContainer { public: static BatchProofContainer* get_instance(); - struct LelantusSigmaProofData { - LelantusSigmaProofData(const lelantus::SigmaExtendedProof& lelantusSigmaProof_, - const Scalar& serialNumber_, - const Scalar& challenge_, - size_t anonymitySetSize_) - : lelantusSigmaProof(lelantusSigmaProof_), - serialNumber(serialNumber_), - challenge(challenge_), - anonymitySetSize(anonymitySetSize_) {} - - lelantus::SigmaExtendedProof lelantusSigmaProof; - Scalar serialNumber; - Scalar challenge; - size_t anonymitySetSize; - }; - void init(); void finalize(); void verify(); - void add(lelantus::JoinSplit* joinSplit, - const std::map& setSizes, - const Scalar& challenge, - bool fStartLelantusBlacklist); - - void add(lelantus::JoinSplit* joinSplit, const std::vector& Cout); - - void removeLelantus(std::unordered_map spentSerials); - void remove(const std::vector& rangeProofsToRemove); - void erase(std::vector* vProofs, const Scalar& serial); - - void batch_lelantus(); - void batch_rangeProofs(); - void add(const spark::SpendTransaction& tx); void remove(const spark::SpendTransaction& tx); void batch_spark(); @@ -56,17 +25,9 @@ class BatchProofContainer { private: static std::unique_ptr instance; - // temp containers, to forget in case block connection fails - // map ((id, afterFixes), fIsSigmaToLelantus) to (sigma proof, serial, set size, challenge) - std::map, std::vector> tempLelantusSigmaProofs; - // map (version to (Range proof, Pubcoins)) - std::map>>> tempRangeProofs; // temp spark transaction proofs std::vector tempSparkTransactions; - // containers to keep proofs for batching - std::map, std::vector> lelantusSigmaProofs; - std::map>>> rangeProofs; // spark transaction proofs std::vector sparkTransactions; }; diff --git a/src/bip47/account.cpp b/src/bip47/account.cpp index 6a0c1f9538..1d75f63c9b 100644 --- a/src/bip47/account.cpp +++ b/src/bip47/account.cpp @@ -6,7 +6,6 @@ #include "util.h" #include "bip47/bip47utils.h" #include "wallet/wallet.h" -#include "lelantus.h" namespace bip47 { @@ -251,27 +250,10 @@ bool CAccountReceiver::acceptMaskedPayload(std::vector const & ma bool CAccountReceiver::acceptMaskedPayload(std::vector const & maskedPayload, CTransaction const & tx) { - std::unique_ptr jsplit; - try { - jsplit = lelantus::ParseLelantusJoinSplit(tx); - }catch (const std::exception &) { - return false; - } - if (!jsplit) - return false; - std::unique_ptr pcode; - CExtKey pcodePrivkey = utils::Derive(privkey, {0}); - try { - CDataStream ds(SER_NETWORK, 0); - ds << jsplit->getCoinSerialNumbers()[0]; - pcode = bip47::utils::PcodeFromMaskedPayload(maskedPayload, (unsigned char const *)ds.vch.data(), ds.vch.size(), pcodePrivkey.key, jsplit->GetEcdsaPubkeys()[0]); - if (!pcode) - return false; - } catch (std::runtime_error const &) { - return false; - } - acceptPcode(*pcode); - return true; + // Lelantus was removed; BIP47 masked payload from Lelantus JoinSplit is no longer parsed. + (void)maskedPayload; + (void)tx; + return false; } CPaymentCode const & CAccountReceiver::lastPcode() const diff --git a/src/bip47/secretpoint.cpp b/src/bip47/secretpoint.cpp index 1c7b97ae9d..3f55039400 100644 --- a/src/bip47/secretpoint.cpp +++ b/src/bip47/secretpoint.cpp @@ -1,6 +1,4 @@ -#include "liblelantus/coin.h" #include "bip47/secretpoint.h" -#include "liblelantus/openssl_context.h" #include "bip47/bip47utils.h" #include "utilstrencodings.h" diff --git a/src/bloom.cpp b/src/bloom.cpp index 9b18b2468f..bf1c315c9d 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -224,7 +224,6 @@ bool CBloomFilter::CheckSpecialTransactionMatchesAndUpdate(const CTransaction &t case(TRANSACTION_COINBASE): case(TRANSACTION_QUORUM_COMMITMENT): case (TRANSACTION_SPORK): - case (TRANSACTION_LELANTUS): // No aditional checks for this transaction types return false; } diff --git a/src/chain.h b/src/chain.h index f4629b72c8..1c043b36d9 100644 --- a/src/chain.h +++ b/src/chain.h @@ -14,7 +14,6 @@ #include "bitcoin_bignum/bignum.h" #include #include -#include "liblelantus/coin.h" #include "libspark/coin.h" #include "evo/spork.h" #include "firo_params.h" @@ -23,6 +22,7 @@ #include "coin_containers.h" #include "streams.h" #include "sparkname.h" +#include "libspark/coin.h" #include #include diff --git a/src/chainparams.cpp b/src/chainparams.cpp index d22e882c0e..fac4265e39 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -744,12 +744,11 @@ class CTestNetParams : public CChainParams { consensus.nRestartSigmaWithBlacklistCheck = INT_MAX; consensus.nOldSigmaBanBlock = 1; - consensus.nLelantusStartBlock = ZC_LELANTUS_TESTNET_STARTING_BLOCK; - consensus.nLelantusFixesStartBlock = ZC_LELANTUS_TESTNET_FIXES_START_BLOCK; - + consensus.nLelantusStartBlock = 1; + consensus.nLelantusFixesStartBlock = 1; consensus.nSparkStartBlock = SPARK_TESTNET_START_BLOCK; - consensus.nLelantusGracefulPeriod = LELANTUS_TESTNET_GRACEFUL_PERIOD; - consensus.nSigmaEndBlock = ZC_SIGMA_TESTNET_END_BLOCK; + consensus.nLelantusGracefulPeriod = 0; + consensus.nSigmaEndBlock = 1; consensus.nZerocoinV2MintMempoolGracefulPeriod = ZC_V2_MINT_TESTNET_GRACEFUL_MEMPOOL_PERIOD; consensus.nZerocoinV2MintGracefulPeriod = ZC_V2_MINT_TESTNET_GRACEFUL_PERIOD; consensus.nZerocoinV2SpendMempoolGracefulPeriod = ZC_V2_SPEND_TESTNET_GRACEFUL_MEMPOOL_PERIOD; @@ -802,9 +801,9 @@ class CTestNetParams : public CChainParams { // Bip39 consensus.nMnemonicBlock = 1; - // moving lelantus data to v3 payload - consensus.nLelantusV3PayloadStartBlock = 35000; - + // Lelantus strip + consensus.nLelantusV3PayloadStartBlock = 1; + // ProgPow consensus.nPPSwitchTime = 1630069200; // August 27 2021, 13:00 UTC consensus.nPPBlockNumber = 37305; @@ -1021,7 +1020,7 @@ class CDevNetParams : public CChainParams { consensus.nLelantusFixesStartBlock = 1; consensus.nSparkStartBlock = 1500; - consensus.nLelantusGracefulPeriod = 6000; + consensus.nLelantusGracefulPeriod = 0; consensus.nSigmaEndBlock = 3600; consensus.nMaxSigmaInputPerBlock = ZC_SIGMA_INPUT_LIMIT_PER_BLOCK; consensus.nMaxValueSigmaSpendPerBlock = ZC_SIGMA_VALUE_SPEND_LIMIT_PER_BLOCK; @@ -1271,11 +1270,12 @@ class CRegTestParams : public CChainParams { consensus.nStartSigmaBlacklist = INT_MAX; consensus.nRestartSigmaWithBlacklistCheck = INT_MAX; consensus.nOldSigmaBanBlock = 1; - consensus.nLelantusStartBlock = 100; - consensus.nLelantusFixesStartBlock = 100; - consensus.nSparkStartBlock = 400; + // Lelantus stripped (like Sigma): both "start" at 1, only Spark active (Sigma end=1, Lelantus grace=0) + consensus.nLelantusStartBlock = 1; + consensus.nLelantusFixesStartBlock = 1; + consensus.nSparkStartBlock = 100; consensus.nExchangeAddressStartBlock = 1000; - consensus.nLelantusGracefulPeriod = 600; + consensus.nLelantusGracefulPeriod = 0; consensus.nSigmaEndBlock = 1; consensus.nZerocoinV2MintMempoolGracefulPeriod = 1; consensus.nZerocoinV2MintGracefulPeriod = 1; @@ -1317,9 +1317,9 @@ class CRegTestParams : public CChainParams { // Bip39 consensus.nMnemonicBlock = 0; - // moving lelantus data to v3 payload - consensus.nLelantusV3PayloadStartBlock = 800; - + // Lelantus strip: no v3 payload boundary (same as testnet/devnet) + consensus.nLelantusV3PayloadStartBlock = 1; + // ProgPow // this can be overridden with either -ppswitchtime or -ppswitchtimefromnow flags consensus.nPPSwitchTime = INT_MAX; diff --git a/src/coin_containers.cpp b/src/coin_containers.cpp index d755ee5a49..2069ca4f05 100644 --- a/src/coin_containers.cpp +++ b/src/coin_containers.cpp @@ -4,7 +4,7 @@ #include namespace lelantus { -std::size_t CScalarHash::operator ()(const Scalar& bn) const noexcept { +std::size_t CScalarHash::operator ()(const secp_primitives::Scalar& bn) const noexcept { std::vector bnData(bn.memoryRequired()); bn.serialize(&bnData[0]); @@ -18,7 +18,7 @@ std::size_t CScalarHash::operator ()(const Scalar& bn) const noexcept { } std::size_t CPublicCoinHash::operator ()(const lelantus::PublicCoin& coin) const noexcept { - uint256 hash = coin.getValueHash(); + ::uint256 hash = coin.getValueHash(); std::size_t result; std::memcpy(&result, hash.begin(), sizeof(std::size_t)); diff --git a/src/coin_containers.h b/src/coin_containers.h index 0e162c65b2..4f05d9bb0f 100644 --- a/src/coin_containers.h +++ b/src/coin_containers.h @@ -2,9 +2,14 @@ #define COIN_CONTAINERS_H #include -#include "liblelantus/coin.h" +#include "uint256.h" +#include "serialize.h" #include +#include + +// lelantus::PublicCoin and secp_primitives types +#include "libspark/coin.h" namespace lelantus { diff --git a/src/evo/specialtx.cpp b/src/evo/specialtx.cpp index dc9b261887..949a756d4e 100644 --- a/src/evo/specialtx.cpp +++ b/src/evo/specialtx.cpp @@ -42,11 +42,12 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali return llmq::CheckLLMQCommitment(tx, pindexPrev, state); case TRANSACTION_SPORK: return CheckSporkTx(tx, pindexPrev, state); + case TRANSACTION_LELANTUS: + return true; case TRANSACTION_SPARK: // spark transaction checks are done in other places return true; - case TRANSACTION_LELANTUS: - // lelantus transaction checks are done in other places + case TRANSACTION_ALIAS: return true; } @@ -75,6 +76,8 @@ bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValida return true; case TRANSACTION_SPARK: return true; + case TRANSACTION_ALIAS: + return true; } return state.DoS(100, false, REJECT_INVALID, "bad-tx-type-proc"); @@ -102,6 +105,8 @@ bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex) return true; case TRANSACTION_SPARK: return true; + case TRANSACTION_ALIAS: + return true; } return false; diff --git a/src/evo/spork.cpp b/src/evo/spork.cpp index 9a0ccfb400..77e5f2080f 100644 --- a/src/evo/spork.cpp +++ b/src/evo/spork.cpp @@ -53,19 +53,7 @@ bool CheckSporkTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValida static bool IsTransactionAllowed(const CTransaction &tx, const ActiveSporkMap &sporkMap, CValidationState &state) { - if (tx.IsLelantusTransaction()) { - if (sporkMap.count(CSporkAction::featureLelantus) > 0) - return state.DoS(100, false, REJECT_CONFLICT, "txn-lelantus-disabled", false, "Lelantus transactions are disabled at the moment"); - - if (tx.IsLelantusJoinSplit()) { - const auto &limitSpork = sporkMap.find(CSporkAction::featureLelantusTransparentLimit); - if (limitSpork != sporkMap.cend()) { - if (lelantus::GetSpendTransparentAmount(tx) > (CAmount)limitSpork->second.second) - return state.DoS(100, false, REJECT_CONFLICT, "txn-lelantus-disabled", false, "Lelantus transaction is over the transparent limit"); - } - } - } - else if (tx.IsSparkTransaction()) { + if (tx.IsSparkTransaction()) { if (sporkMap.count(CSporkAction::featureSpark) > 0) return state.DoS(100, false, REJECT_CONFLICT, "txn-spark-disabled", false, "Spark transactions are disabled at the moment"); @@ -176,24 +164,7 @@ bool CSporkManager::IsTransactionAllowed(const CTransaction &tx, const ActiveSpo } bool CSporkManager::IsBlockAllowed(const CBlock &block, const CBlockIndex *pindex, CValidationState &state) { - if (pindex->activeDisablingSporks.count(CSporkAction::featureLelantusTransparentLimit) > 0) { - // limit total transparent output of lelantus joinsplit - int64_t limit = pindex->activeDisablingSporks.at(CSporkAction::featureLelantusTransparentLimit).second; - CAmount totalTransparentOutput = 0; - - for (const auto &tx: block.vtx) { - if (!tx->IsLelantusJoinSplit()) - continue; - - totalTransparentOutput += lelantus::GetSpendTransparentAmount(*tx); - } - - if (totalTransparentOutput > CAmount(limit)) - return state.DoS(100, false, REJECT_CONFLICT, "txn-lelantus-disabled", false, "Block is over the transparent output limit because of existing spork"); - } - if (pindex->activeDisablingSporks.count(CSporkAction::featureSparkTransparentLimit) > 0) { - // limit total transparent output of lelantus joinsplit int64_t limit = pindex->activeDisablingSporks.at(CSporkAction::featureSparkTransparentLimit).second; CAmount totalTransparentOutput = 0; diff --git a/src/evo/spork.h b/src/evo/spork.h index 4b4d67fc67..a750c46ebb 100644 --- a/src/evo/spork.h +++ b/src/evo/spork.h @@ -13,8 +13,6 @@ typedef std::map> ActiveSporkMap; // one action to perform. Spork transaction can have multiple actions struct CSporkAction { - static constexpr const char *featureLelantus = "lelantus"; - static constexpr const char *featureLelantusTransparentLimit = "lelantustransparentlimit"; static constexpr const char *featureChainlocks = "chainlocks"; static constexpr const char *featureInstantSend = "instantsend"; static constexpr const char *featureSpark = "spark"; diff --git a/src/hdmint/hdmint.cpp b/src/hdmint/hdmint.cpp deleted file mode 100644 index 1a43351a28..0000000000 --- a/src/hdmint/hdmint.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2019 The Firo Core Developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include "hdmint.h" - -/** - * CHDMint empty constructor - */ -CHDMint::CHDMint() -{ - SetNull(); -} - -/** - * CHDMint constructor from given values - */ -CHDMint::CHDMint(const int32_t& nCount, const CKeyID& seedId, const uint256& hashSerial, const GroupElement& pubCoinValue) -{ - SetNull(); - this->nCount = nCount; - this->seedId = seedId; - this->hashSerial = hashSerial; - this->pubCoinValue = pubCoinValue; -} - -/** - * Set HDMint object null - */ -void CHDMint::SetNull() -{ - nCount = 0; - seedId.SetNull(); - hashSerial.SetNull(); - txid.SetNull(); - nHeight = -1; - nId = -1; - amount = 0; - isUsed = false; -} - -/** - * Convert CHDMint object to string - * - * @return CHDMint object as string - */ -std::string CHDMint::ToString() const -{ - return strprintf(" HDMint:\n count=%d\n seedId=%s\n hashSerial=%s\n hashPubCoinValue=%s\n txid=%s\n height=%d\n id=%d\n amount=%d\n isUsed=%d\n", - nCount, seedId.ToString(), hashSerial.GetHex(), GetPubCoinHash().GetHex(), txid.GetHex(), nHeight, nId, amount, isUsed); -} diff --git a/src/hdmint/hdmint.h b/src/hdmint/hdmint.h deleted file mode 100644 index 1831f198c9..0000000000 --- a/src/hdmint/hdmint.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2019 The Firo Core Developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef FIRO_HDMINT_H -#define FIRO_HDMINT_H - -#include "primitives/mint_spend.h" - -/** - * CHDMint object - * - * struct that is safe to store essential mint data, without holding any information that allows for actual spending - * (ie. serial, randomness, private key) - */ -class CHDMint -{ -private: - int32_t nCount; - CKeyID seedId; - uint256 hashSerial; - GroupElement pubCoinValue; - uint256 txid; - int nHeight; - int nId; - int64_t amount; - bool isUsed; - -public: - CHDMint(); - CHDMint(const int32_t& nCount, const CKeyID& seedId, const uint256& hashSerial, const GroupElement& pubCoinValue); - - int64_t GetAmount() const { - return amount; - } - int32_t GetCount() const { return nCount; } - int GetHeight() const { return nHeight; } - int GetId() const { return nId; } - CKeyID GetSeedId() const { return seedId; } - uint256 GetSerialHash() const { return hashSerial; } - GroupElement GetPubcoinValue() const { return pubCoinValue; } - uint256 GetPubCoinHash() const { return primitives::GetPubCoinValueHash(pubCoinValue); } - uint256 GetTxHash() const { return txid; } - bool IsUsed() const { return isUsed; } - void SetAmount(int64_t amount) { this->amount = amount; } - void SetHeight(int nHeight) { this->nHeight = nHeight; } - void SetId(int nId) { this->nId = nId; } - void SetNull(); - void SetTxHash(const uint256& txid) { this->txid = txid; } - void SetUsed(const bool isUsed) { this->isUsed = isUsed; } - void SetPubcoinValue(const GroupElement pubCoinValue) { this->pubCoinValue = pubCoinValue; } - std::string ToString() const; - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) - { - READWRITE(nCount); - READWRITE(seedId); - READWRITE(hashSerial); - READWRITE(pubCoinValue); - READWRITE(txid); - READWRITE(nHeight); - READWRITE(nId); - READWRITE(amount); - READWRITE(isUsed); - }; -}; - -#endif //FIRO_HDMINT_H - diff --git a/src/hdmint/mintpool.cpp b/src/hdmint/mintpool.cpp deleted file mode 100644 index 1debd918a1..0000000000 --- a/src/hdmint/mintpool.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2019 The Firo Core Developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "hdmint/mintpool.h" -#include "lelantus.h" -CMintPool::CMintPool(){} - -/** - * Add a mintpool entry - */ -void CMintPool::Add(std::pair pMint, bool fVerbose) -{ - insert(pMint); - - if (fVerbose) - LogPrintf("%s : add %s count %d to mint pool\n", __func__, pMint.first.GetHex().substr(0, 6), std::get<2>(pMint.second)); -} - -/** - * Sort mintpool entries in terms of the mint count. - * - * @return success - */ -bool SortSmallest(const std::pair& a, const std::pair& b) -{ - return std::get<2>(a.second) < std::get<2>(b.second); -} - -/** - * place the mintpool in listMints. - */ -void CMintPool::List(std::list>& listMints) -{ - for (auto pMint : *(this)) { - listMints.emplace_back(pMint); - } - - listMints.sort(SortSmallest); -} - -/** - * clear the current mintpool - */ -void CMintPool::Reset() -{ - clear(); -} - -bool CMintPool::Get(int32_t nCount, uint160 hashSeedMaster, std::pair& result){ - for (auto pMint : *(this)) { - if(std::get<0>(pMint.second)==hashSeedMaster && std::get<2>(pMint.second)==nCount){ - result = pMint; - return true; - } - } - - return false; - -} - - diff --git a/src/hdmint/mintpool.h b/src/hdmint/mintpool.h deleted file mode 100644 index ca11b546ce..0000000000 --- a/src/hdmint/mintpool.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2019 The Firo Core Developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef FIRO_MINTPOOL_H -#define FIRO_MINTPOOL_H - -#include -#include - -#include "primitives/mint_spend.h" -#include "uint256.h" - -typedef std::tuple MintPoolEntry; - -/** - * The MintPool only contains mint seed values that have not been added to the blockchain yet. - * When a mint is generated by the wallet, or found while syncing, the mint will be removed - * from the MintPool. - * - * The MintPool provides a convenient way to check whether mints in the blockchain belong to a - * wallet's deterministic seed. - */ -class CMintPool : public std::map //hashPubcoin mapped to (hashSeedMaster, seedId, count) -{ - -public: - CMintPool(); - void Add(std::pair pMint, bool fVerbose = false); - void List(std::list>& listMints); - void Reset(); - bool Get(int32_t nCount, uint160 hashSeedMaster, std::pair& result); -}; - -#endif // FIRO_MINTPOOL_H diff --git a/src/hdmint/test/lelantus_tests.cpp b/src/hdmint/test/lelantus_tests.cpp deleted file mode 100644 index 573c43f611..0000000000 --- a/src/hdmint/test/lelantus_tests.cpp +++ /dev/null @@ -1,174 +0,0 @@ -#include "../../test/fixtures.h" -#include "../wallet.h" - -#include - -class HDMintLelantusTests : public ZerocoinTestingSetup200 -{ -public: - HDMintLelantusTests() : - params(lelantus::Params::get_default()) { - } - -public: - MintPoolEntry FrontMintPool() const { - LOCK(pwalletMain->cs_wallet); - - auto nCountNextUse = pwalletMain->zwallet->GetCount(); - auto mints = CWalletDB(pwalletMain->strWalletFile).ListMintPool(); - - for (auto &m : mints) { - if (std::get<2>(m.second) == nCountNextUse) { - return m.second; - } - } - - throw std::runtime_error("next count is not available"); - } - -public: - lelantus::Params const *params; -}; - -BOOST_FIXTURE_TEST_SUITE(hdmint_lelantus_tests, HDMintLelantusTests) - -BOOST_AUTO_TEST_CASE(deterministic_coin_generation_from_seed) -{ - std::array rawSeed1, rawSeed2; - std::fill(rawSeed1.begin(), rawSeed1.end(), 0); - std::fill(rawSeed2.begin(), rawSeed2.end(), 0); - rawSeed2.back() = 1; - - uint512 seed1(rawSeed1), seed2(rawSeed2); - - lelantus::PrivateCoin - coin1(params, 1), - coin2(params, 1), - coin3(params, 1); - - BOOST_CHECK(pwalletMain->zwallet->SeedToLelantusMint(seed1, coin1)); - BOOST_CHECK(pwalletMain->zwallet->SeedToLelantusMint(seed2, coin2)); - BOOST_CHECK(pwalletMain->zwallet->SeedToLelantusMint(seed1, coin3)); - - // TODO: compare private coin directly instead of comparing via mint - auto pubCoin1 = coin1.getPublicCoin(); - auto pubCoin2 = coin2.getPublicCoin(); - auto pubCoin3 = coin3.getPublicCoin(); - - BOOST_CHECK(pubCoin1 == pubCoin3); - BOOST_CHECK(pubCoin1 != pubCoin2); -} - -BOOST_AUTO_TEST_CASE(lelantus_mint_generation) -{ - lelantus::PrivateCoin - coin1(params, 1), - coin2(params, 1); - - CHDMint mint1, mint2; - - // coin should be valid - CWalletDB walletdb(pwalletMain->strWalletFile); - uint160 seedID; - BOOST_CHECK(pwalletMain->zwallet->GenerateLelantusMint(walletdb, coin1, mint1, seedID)); - BOOST_CHECK(pwalletMain->zwallet->GenerateLelantusMint(walletdb, coin2, mint2, seedID)); - - auto entry = FrontMintPool(); - - BOOST_CHECK_EQUAL(2, std::get<2>(entry)); - - auto pubCoin1 = coin1.getPublicCoin(); - auto pubCoin2 = coin2.getPublicCoin(); - - // verify - BOOST_CHECK(pubCoin1 != pubCoin2); - BOOST_CHECK_EQUAL(0, mint1.GetCount()); - BOOST_CHECK_EQUAL(1, mint2.GetCount()); - - // chain state - BOOST_CHECK_EQUAL(-1, mint1.GetHeight()); - BOOST_CHECK_EQUAL(-1, mint1.GetId()); - BOOST_CHECK(mint1.GetTxHash().IsNull()); - BOOST_CHECK(!mint1.IsUsed()); - - // value should be unique - BOOST_CHECK(mint1.GetSeedId() != mint2.GetSeedId()); - BOOST_CHECK(mint1.GetSerialHash() != mint2.GetSerialHash()); - BOOST_CHECK(mint1.GetPubcoinValue() != mint2.GetPubcoinValue()); - BOOST_CHECK(mint1.GetPubCoinHash() != mint2.GetPubCoinHash()); -} - -BOOST_AUTO_TEST_CASE(lelantus_mint_regeneration) -{ - lelantus::PrivateCoin - coin1(params, 1), - coin2(params, 1); - - CHDMint mint1, mint2; - auto entry1 = FrontMintPool(); - - CWalletDB walletdb(pwalletMain->strWalletFile); - uint160 seedID; - BOOST_CHECK(pwalletMain->zwallet->GenerateLelantusMint(walletdb, coin1, mint1, seedID)); - - // re-generate - BOOST_CHECK(pwalletMain->zwallet->GenerateLelantusMint(walletdb, coin2, mint2, seedID, entry1)); - - auto pubCoin1 = coin1.getPublicCoin(); - auto pubCoin2 = coin2.getPublicCoin(); - - // verify - BOOST_CHECK(pubCoin1 == pubCoin2); - BOOST_CHECK_EQUAL(0, mint1.GetCount()); - BOOST_CHECK_EQUAL(0, mint2.GetCount()); - - // chain state - BOOST_CHECK_EQUAL(-1, mint2.GetHeight()); - BOOST_CHECK_EQUAL(-1, mint2.GetId()); - BOOST_CHECK(mint2.GetTxHash().IsNull()); - BOOST_CHECK(!mint2.IsUsed()); - - // value should be the same - BOOST_CHECK(mint1.GetSeedId() == mint2.GetSeedId()); - BOOST_CHECK(mint1.GetSerialHash() == mint2.GetSerialHash()); - BOOST_CHECK(mint1.GetPubcoinValue() == mint2.GetPubcoinValue()); - BOOST_CHECK(mint1.GetPubCoinHash() == mint2.GetPubCoinHash()); -} - -BOOST_AUTO_TEST_CASE(regenerate_mint) -{ - lelantus::PrivateCoin coin(params, 1); - - CHDMint mint; - CWalletDB walletdb(pwalletMain->strWalletFile); - uint160 seedID; - BOOST_CHECK(pwalletMain->zwallet->GenerateLelantusMint(walletdb, coin, mint, seedID)); - - // Should be generated deterministically - - CLelantusEntry entry1, entry2; - pwalletMain->zwallet->RegenerateMint(walletdb, mint, entry1); - pwalletMain->zwallet->RegenerateMint(walletdb, mint, entry2); - - // verify - BOOST_CHECK(entry1.value == entry2.value); - BOOST_CHECK(entry1.randomness == entry2.randomness); - BOOST_CHECK(entry1.serialNumber == entry2.serialNumber); - BOOST_CHECK(entry1.ecdsaSecretKey == entry2.ecdsaSecretKey); - BOOST_CHECK_EQUAL(false, entry1.IsUsed); - BOOST_CHECK_EQUAL(-1, entry1.nHeight); - BOOST_CHECK_EQUAL(-1, entry1.id); - BOOST_CHECK_EQUAL(1, entry1.amount); - - auto key = coin.getEcdsaSeckey(); // 32 bytes - - BOOST_CHECK(coin.getPublicCoin() == entry1.value); - BOOST_CHECK(coin.getRandomness() == entry1.randomness); - BOOST_CHECK(coin.getSerialNumber() == entry1.serialNumber); - BOOST_CHECK_EQUAL(coin.getV(), entry1.amount); - BOOST_CHECK_EQUAL_COLLECTIONS( - key, key + 32, - entry1.ecdsaSecretKey.begin(), entry1.ecdsaSecretKey.end()); -} - -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/src/hdmint/tracker.cpp b/src/hdmint/tracker.cpp deleted file mode 100644 index 272724de6d..0000000000 --- a/src/hdmint/tracker.cpp +++ /dev/null @@ -1,765 +0,0 @@ -// Copyright (c) 2019 The Firo Core Developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include -#include "hdmint/tracker.h" -#include "util.h" -#include "sync.h" -#include "txdb.h" -#include "wallet/wallet.h" -#include "hdmint/wallet.h" -#include "validation.h" -#include "lelantus.h" -#include "txmempool.h" - -using namespace sigma; - -/** - * CHDMintTracker constructor. - * - * Sets the wallet file string and clears the in-memory map of serial hashes -> CMintMeta objects - * and the map of serial hashes -> pending spend txids. - * - * @param strWalletFile wallet file string - */ -CHDMintTracker::CHDMintTracker(std::string strWalletFile) -{ - this->strWalletFile = strWalletFile; - mapLelantusSerialHashes.clear(); - mapPendingSpends.clear(); - fInitialized = false; -} - -/** - * Destroy the CHDMintTracker object. - * - * clears the in-memory map of serial hashes -> CMintMeta objects and the map of - * serial hashes -> pending spend txids. - * - */ -CHDMintTracker::~CHDMintTracker() -{ - mapLelantusSerialHashes.clear(); - mapPendingSpends.clear(); -} - -/** - * Initialize the CHDMintTracker object. - * - * Calls ListMints, which loads all CSigmaEntries and CHDMints from the database. - */ -void CHDMintTracker::Init() -{ - if (!fInitialized) { - ListLelantusMints(false, false, false, true); - fInitialized = true; - } -} - -/** - * Archive a mint. - * - * Ensures the mint exists in the database and then adds it to the archive. - * - * @param meta mint meta object - * @return success - */ - -bool CHDMintTracker::Archive(CLelantusMintMeta& meta) -{ - uint256 hashPubcoin = meta.GetPubCoinValueHash(); - - if (HasLelantusSerialHash(meta.hashSerial)) - mapLelantusSerialHashes.at(meta.hashSerial).isArchived = true; - - CWalletDB walletdb(strWalletFile); - CHDMint dMint; - if (!walletdb.ReadHDMint(hashPubcoin, true, dMint)) - return error("%s: could not find pubcoinhash %s in db", __func__, hashPubcoin.GetHex()); - if (!walletdb.ArchiveDeterministicOrphan(dMint)) - return error("%s: failed to archive deterministic ophaned mint", __func__); - - LogPrintf("%s: archived pubcoinhash %s\n", __func__, hashPubcoin.GetHex()); - return true; -} - -/** - * Unarchives a mint. - * - * @param hashPubcoin reference to mint pubcoin hash - * @return success - */ -bool CHDMintTracker::UnArchive(const uint256& hashPubcoin, bool isDeterministic) -{ - CWalletDB walletdb(strWalletFile); - if (isDeterministic) { - CHDMint dMint; - if (!walletdb.UnarchiveHDMint(hashPubcoin, false, dMint)) - return error("%s: failed to unarchive deterministic mint", __func__); - AddLelantus(walletdb, dMint, false); - } - - LogPrintf("%s: unarchived %s\n", __func__, hashPubcoin.GetHex()); - return true; -} - -/** - * Get a CMintMeta object from memory using mint serial hash - * - * @param hashSerial mint serial hash used to retrieve object - * @param mMeta reference to CMintMeta object - * @return success - */ -bool CHDMintTracker::GetMetaFromSerial(const uint256 &hashSerial, CLelantusMintMeta& mMeta) -{ - auto it = mapLelantusSerialHashes.find(hashSerial); - if(it == mapLelantusSerialHashes.end()) - return false; - - mMeta = mapLelantusSerialHashes.at(hashSerial); - return true; -} - -/** - * Get a CMintMeta object from memory using mint pubcoin hash - * - * @param hashPubcoin mint pubcoin hash used to retrieve object - * @param mMeta reference to CMintMeta object - * @return success - */ -bool CHDMintTracker::GetLelantusMetaFromPubcoin(const uint256& hashPubcoin, CLelantusMintMeta& mMeta) -{ - for (auto it : mapLelantusSerialHashes) { - if (it.second.GetPubCoinValueHash() == hashPubcoin){ - mMeta = it.second; - return true; - } - } - - return false; -} - -/** - * Get the list of non-archiveed, in-memory mint serial hashes. - * - * @return vHashes vector of serial hashes - */ -std::vector CHDMintTracker::GetSerialHashes() -{ - std::vector vHashes; - for (auto it : mapLelantusSerialHashes) { - if (it.second.isArchived) - continue; - - vHashes.emplace_back(it.first); - } - - - return vHashes; -} - -/** - * Does this mint pubcoin hash exist in a CMintMeta object in memory - * - * @param hashPubcoin mint pubcoin hash - * @return success - */ -bool CHDMintTracker::HasPubcoinHash(const uint256& hashPubcoin, CWalletDB& walletdb) const -{ - for (auto const & it : mapLelantusSerialHashes) { - CLelantusMintMeta meta = it.second; - uint256 reducedHash; - walletdb.ReadPubcoinHashes(meta.GetPubCoinValueHash(), reducedHash); - if (reducedHash == hashPubcoin) - return true; - } - - return false; -} - -/** - * Does this mint serial hash map to a CMintMeta object in memory - * - * @param hashSerial mint serial hash - * @return success - */ - -bool CHDMintTracker::HasLelantusSerialHash(const uint256& hashSerial) const -{ - auto it = mapLelantusSerialHashes.find(hashSerial); - return it != mapLelantusSerialHashes.end(); -} - -/** - * Update the tracker state - * - * From the CMintMeta object passed, update the state (both memory and database) accordingly. - * If a CHDMint object does not exist for this mint, fail. - * - * @param meta the CMintMeta object used to update - * @return success - */ - -bool CHDMintTracker::UpdateState(const CLelantusMintMeta& meta) -{ - uint256 hashPubcoin = meta.GetPubCoinValueHash(); - CWalletDB walletdb(strWalletFile); - - CHDMint dMint; - if (!walletdb.ReadHDMint(hashPubcoin, true, dMint)) { - // Check archive just in case - if (!meta.isArchived) - return error("%s: failed to read Lelantus mint from database", __func__); - - // Unarchive this mint since it is being requested and updated - if (!walletdb.UnarchiveHDMint(hashPubcoin, true, dMint)) - return error("%s: failed to unarchive Lelantus mint from database", __func__); - } - - // get coin id & height - int height, id; - if(meta.nHeight<0 || meta.nId <= 0){ - std::tie(height, id) = lelantus::CLelantusState::GetState()->GetMintedCoinHeightAndId(lelantus::PublicCoin(dMint.GetPubcoinValue())); - } - else{ - height = meta.nHeight; - id = meta.nId; - } - - dMint.SetHeight(height); - dMint.SetId(id); - dMint.SetUsed(meta.isUsed); - dMint.SetAmount(meta.amount); - - if (!walletdb.WriteHDMint(meta.GetPubCoinValueHash(), dMint, true)) - return error("%s: failed to update Lelantus mint when writing to db", __func__); - - auto pubcoin = dMint.GetPubcoinValue() + lelantus::Params::get_default()->get_h1() * Scalar(meta.amount).negate(); - walletdb.WritePubcoinHashes(hashPubcoin, primitives::GetPubCoinValueHash(pubcoin)); - - - pwalletMain->NotifyZerocoinChanged( - pwalletMain, - dMint.GetPubcoinValue().GetHex(), - std::string("Update (") + std::to_string((double)dMint.GetAmount() / COIN) + "mint)", - CT_UPDATED); - - mapLelantusSerialHashes[meta.hashSerial] = meta; - - return true; -} - -/** - * Add a mint object to memory. - * - * If this is a new mint, also write the CHDMint object to database. - * Also notifies Qt that a Sigma mint has been added so as to update the balance display correctly. - * This is used to populate memory on startup. - * - * @param dMint CHDMint object to add - * @param isNew set to true if this mint has just been created, also adds mint to database - * @param isArchived set to true if this mint is archived, used to set meta object correctly - */ -void CHDMintTracker::AddLelantus(CWalletDB& walletdb, const CHDMint& dMint, bool isNew, bool isArchived) -{ - CLelantusMintMeta meta; - meta.SetPubCoinValue(dMint.GetPubcoinValue()); - meta.nHeight = dMint.GetHeight(); - meta.nId = dMint.GetId(); - meta.txid = dMint.GetTxHash(); - meta.isUsed = dMint.IsUsed(); - meta.hashSerial = dMint.GetSerialHash(); - meta.amount = dMint.GetAmount(); - meta.isArchived = isArchived; - meta.isSeedCorrect = true; - mapLelantusSerialHashes[meta.hashSerial] = meta; - - pwalletMain->NotifyZerocoinChanged( - pwalletMain, - dMint.GetPubcoinValue().GetHex(), - std::string("Update (") + std::to_string((double)dMint.GetAmount() / COIN) + "mint)", - CT_UPDATED); - - if (isNew) { - walletdb.WriteHDMint(meta.GetPubCoinValueHash(), dMint, true); - auto pubcoin = dMint.GetPubcoinValue() + lelantus::Params::get_default()->get_h1() * Scalar(meta.amount).negate(); - walletdb.WritePubcoinHashes(meta.GetPubCoinValueHash(), primitives::GetPubCoinValueHash(pubcoin)); - } -} - -/** - * Sets a mint as used via it's pubcoin hash. - * - * @param hashPubcoin mint pubcoin hash. Used to retrieve meta object - * @param txid transaction ID of mint - */ -void CHDMintTracker::SetLelantusPubcoinUsed(const uint256& hashPubcoin, const uint256& txid) -{ - CLelantusMintMeta meta; - if(!GetLelantusMetaFromPubcoin(hashPubcoin, meta)) - return; - meta.isUsed = true; - mapPendingSpends.insert(std::make_pair(meta.hashSerial, txid)); - UpdateState(meta); -} - - -/** - * Sets a mint as not used via it's pubcoin hash. - * - * @param hashPubcoin mint pubcoin hash. Used to retrieve meta object - */ - -void CHDMintTracker::SetLelantusPubcoinNotUsed(const uint256& hashPubcoin) -{ - CLelantusMintMeta meta; - if(!GetLelantusMetaFromPubcoin(hashPubcoin, meta)) - return; - meta.isUsed = false; - - if (mapPendingSpends.count(meta.hashSerial)) - mapPendingSpends.erase(meta.hashSerial); - - UpdateState(meta); -} - - -/** - * Check mempool for the spend associated with the mint serial hash passed - * - * @param setMempool the set of txid hashes in the mempool - * @param hashSerial the mint serial hash to check for - * @return success - */ -bool CHDMintTracker::IsMempoolSpendOurs(const std::set& setMempool, const uint256& hashSerial){ - for(auto& mempoolTxid : setMempool){ - CTransactionRef ptx = txpools.get(mempoolTxid); - if(!ptx) { - continue; - } - - const CTransaction &tx = *ptx; - for (const CTxIn& txin : tx.vin) { - if (txin.IsLelantusJoinSplit()) { - std::unique_ptr joinsplit; - try { - joinsplit = lelantus::ParseLelantusJoinSplit(tx); - } catch (const std::exception &) { - return false; - } - - const std::vector& serials = joinsplit->getCoinSerialNumbers(); - for(const auto& serial: serials) { - uint256 mempoolHashSerial = primitives::GetSerialHash(serial); - if(mempoolHashSerial==hashSerial){ - return true; - } - } - } - } - } - - return false; -} - -/** - * Update the in-memory CMintMeta object for the current mempool - * - * @param setMempool the set of txid hashes in the mempool - * @param mint the CMintMeta object to check for - * @param fSpend if this mint object is being updated as a result of a spend transaction - * @return success - */ -bool CHDMintTracker::UpdateLelantusMetaStatus(const std::set& setMempool, CLelantusMintMeta& mint, bool fSpend) -{ - uint256 hashPubcoin = mint.GetPubCoinValueHash(); - //! Check whether this mint has been spent and is considered 'pending' or 'confirmed' - // If there is not a record of the block height, then look it up and assign it - COutPoint outPoint; - lelantus::PublicCoin pubCoin(mint.GetPubCoinValue()); - bool isMintInChain = GetOutPoint(outPoint, pubCoin); - LogPrintf("UpdateLelantusMetaStatus : isMintInChain: %d\n", isMintInChain); - const uint256& txidMint = outPoint.hash; - - //See if there is internal record of spending this mint (note this is memory only, would reset on restart - next function checks this) - bool isPendingSpend = static_cast(mapPendingSpends.count(mint.hashSerial)); - - // Mempool might hold pending spend - if(!isPendingSpend && fSpend) - isPendingSpend = IsMempoolSpendOurs(setMempool, mint.hashSerial); - - LogPrintf("UpdateLelantusMetaStatus : isPendingSpend: %d\n", isPendingSpend); - - // See if there is a blockchain record of spending this mint - lelantus::CLelantusState *lelantusState = lelantus::CLelantusState::GetState(); - Scalar bnSerial; - bool isConfirmedSpend = lelantusState->IsUsedCoinSerialHash(bnSerial, mint.hashSerial); - LogPrintf("UpdateLelantusMetaStatus : isConfirmedSpend: %d\n", isConfirmedSpend); - - bool isUsed = isPendingSpend || isConfirmedSpend; - - if ((mint.nHeight==-1) || (mint.nId==-1) || !isMintInChain || isUsed != mint.isUsed) { - CTransactionRef tx; - uint256 hashBlock; - - // Txid will be marked 0 if there is no knowledge of the final tx hash yet - if (mint.txid.IsNull()) { - if (!isMintInChain) { - if(mint.nHeight>-1) mint.nHeight = -1; - if(mint.nId>-1) mint.nId = -1; - // still want to update this mint later if syncing. else ignore - if(IsInitialBlockDownload()){ - return true; - } - return false; - } - mint.txid = txidMint; - } - - LogPrintf("UpdateLelantusMetaStatus : mint.txid = %d\n", mint.txid.GetHex()); - - if (setMempool.count(mint.txid)) { - if(mint.nHeight>-1) mint.nHeight = -1; - if(mint.nId>-1) mint.nId = -1; - return true; - } - - // Check the transaction associated with this mint - if (!GetTransaction(mint.txid, tx, ::Params().GetConsensus(), hashBlock, true)) { - LogPrintf("%s : Failed to find tx for mint txid=%s\n", __func__, mint.txid.GetHex()); - mint.isArchived = true; - Archive(mint); - return true; - } - - bool isUpdated = false; - - // An orphan tx if hashblock is in mapBlockIndex but not in chain active - if (mapBlockIndex.count(hashBlock)) { - if(!chainActive.Contains(mapBlockIndex.at(hashBlock))) { - LogPrintf("%s : Found orphaned mint txid=%s\n", __func__, mint.txid.GetHex()); - mint.isUsed = false; - mint.nHeight = 0; - - return true; - } else if((mint.nHeight==-1) || (mint.nId<=0)) { // assign nHeight if not present - lelantus::PublicCoin pubcoin(mint.GetPubCoinValue()); - auto MintedCoinHeightAndId = lelantusState->GetMintedCoinHeightAndId(pubcoin); - mint.nHeight = MintedCoinHeightAndId.first; - mint.nId = MintedCoinHeightAndId.second; - LogPrintf("%s : Set mint %s nHeight to %d\n", __func__, hashPubcoin.GetHex(), mint.nHeight); - LogPrintf("%s : Set mint %s nId to %d\n", __func__, hashPubcoin.GetHex(), mint.nId); - isUpdated = true; - } - } - - // Check that the mint has correct used status - if (mint.isUsed != isUsed) { - LogPrintf("%s : Set mint %s isUsed to %d\n", __func__, hashPubcoin.GetHex(), isUsed); - mint.isUsed = isUsed; - isUpdated = true; - } - - if(isUpdated) return true; - } - - return false; -} - -/** - * Update mints found on-chain. - * - * @param mintPoolEntries the set of mint pool entries to update - * @param updatedMeta the CMintMeta objects to update - */ - -void CHDMintTracker::UpdateFromBlock(const std::list>& mintPoolEntries, const std::vector& updatedMeta){ - if (mintPoolEntries.size() > 0) { - pwalletMain->zwallet->SyncWithChain(false, mintPoolEntries); - } - - //overwrite any updates - for (CLelantusMintMeta meta : updatedMeta) - UpdateState(meta); -} - -/** - * Update the state if mint transactions found on-chain exist in the wallet. - * - * We attempt to read a mintpool object from the on-chain data found. If so, update state. - * - * @param mints the set of public coin objects to check for. - */ -void CHDMintTracker::UpdateMintStateFromBlock(const std::vector>>& mints) { - CWalletDB walletdb(strWalletFile); - std::vector updatedMeta; - std::list> mintPoolEntries; - uint160 hashSeedMasterEntry; - CKeyID seedId; - int32_t nCount; - std::set setMempool = GetMempoolTxids(); - for (auto& mint : mints) { - uint256 reducedHash; - if(!walletdb.ReadPubcoinHashes(primitives::GetPubCoinValueHash(mint.first.getValue()), reducedHash)) { - uint64_t amount = mint.second.first; - auto pubcoin = mint.first.getValue() + lelantus::Params::get_default()->get_h1() * Scalar(amount).negate(); - reducedHash = primitives::GetPubCoinValueHash(pubcoin); - } - CLelantusMintMeta meta; - // Check reducedHash in db - if(walletdb.ReadMintPoolPair(reducedHash, hashSeedMasterEntry, seedId, nCount)) { - // If found in db but not in memory - this is likely a resync - if(!GetLelantusMetaFromPubcoin(primitives::GetPubCoinValueHash(mint.first.getValue()), meta)){ - MintPoolEntry mintPoolEntry(hashSeedMasterEntry, seedId, nCount); - mintPoolEntries.push_back(std::make_pair(reducedHash, mintPoolEntry)); - continue; - } - if(UpdateLelantusMetaStatus(setMempool, meta)){ - updatedMeta.emplace_back(meta); - } - } - } - - UpdateFromBlock(mintPoolEntries, updatedMeta); -} - -/** - * Update the state if spend transactions found on-chain exist in the wallet. - * - * We attempt to read a mintpool object from the on-chain data found. If so, update state. - * - * @param spentSerials the set of spent serial objects to check for. - */ -void CHDMintTracker::UpdateSpendStateFromBlock(const std::unordered_map& spentSerials){ - CWalletDB walletdb(strWalletFile); - std::vector updatedMeta; - std::list> mintPoolEntries; - uint160 hashSeedMasterEntry; - CKeyID seedId; - int32_t nCount; - std::set setMempool = GetMempoolTxids(); - for(auto& spentSerial : spentSerials){ - uint256 spentSerialHash = primitives::GetSerialHash(spentSerial.first); - CLelantusMintMeta meta; - GroupElement pubcoin; - // Check serialHash in db - if(walletdb.ReadPubcoin(spentSerialHash, pubcoin)) { - // If found in db but not in memory - this is likely a resync - if(!GetMetaFromSerial(spentSerialHash, meta)){ - uint256 hashPubcoin = primitives::GetPubCoinValueHash(pubcoin); - if(!walletdb.ReadMintPoolPair(hashPubcoin, hashSeedMasterEntry, seedId, nCount)) { - continue; - } - MintPoolEntry mintPoolEntry(hashSeedMasterEntry, seedId, nCount); - mintPoolEntries.push_back(std::make_pair(hashPubcoin, mintPoolEntry)); - continue; - } - if(UpdateLelantusMetaStatus(setMempool, meta, true)){ - updatedMeta.emplace_back(meta); - } - } - } - - UpdateFromBlock(mintPoolEntries, updatedMeta); -} - -/** - * Update the state if mint transactions found in the mempool exist in the wallet. - * - * We attempt to read a mintpool object from the mempool data found. If so, update state. - * - * @param pubCoins the set of public coin objects to check for. - */ - -void CHDMintTracker::UpdateLelantusMintStateFromMempool(const std::vector& pubCoins, const std::vector& amounts) { - CWalletDB walletdb(strWalletFile); - std::vector updatedLelantusMeta; - std::list> mintPoolEntries; - uint160 hashSeedMasterEntry; - CKeyID seedId; - int32_t nCount; - std::set setMempool = GetMempoolTxids(); - int i = 0; - for (auto& pubcoin : pubCoins) { - uint256 reducedHash; - if(!walletdb.ReadPubcoinHashes(primitives::GetPubCoinValueHash(pubcoin), reducedHash)) { - auto pub = pubcoin + lelantus::Params::get_default()->get_h1() * Scalar(amounts[i]).negate(); - reducedHash = primitives::GetPubCoinValueHash(pub); - } - - - LogPrintf("UpdateMintStateFromMempool: hashPubcoin=%d\n", reducedHash.GetHex()); - // Check reducedHash in db - if(walletdb.ReadMintPoolPair(reducedHash, hashSeedMasterEntry, seedId, nCount)){ - // If found in db but not in memory - this is likely a resync - CLelantusMintMeta metaLelantus; - bool skip = !GetLelantusMetaFromPubcoin(primitives::GetPubCoinValueHash(pubcoin), metaLelantus); - - if(skip) { - MintPoolEntry mintPoolEntry(hashSeedMasterEntry, seedId, nCount); - mintPoolEntries.push_back(std::make_pair(reducedHash, mintPoolEntry)); - i++; - continue; - } - - if(UpdateLelantusMetaStatus(setMempool, metaLelantus)){ - updatedLelantusMeta.emplace_back(metaLelantus); - } - - } - i++; - } - - UpdateFromBlock(mintPoolEntries, updatedLelantusMeta); -} - -/** - * Update the state if spend transactions found in the mempool exist in the wallet. - * - * We attempt to read a mintpool object from the mempool data found. If so, update state. - * - * @param spentSerials the set of spent serial objects to check for. - */ - -void CHDMintTracker::UpdateJoinSplitStateFromMempool(const std::vector& spentSerials) { - CWalletDB walletdb(strWalletFile); - std::vector updatedMeta; - std::list> mintPoolEntries; - uint160 hashSeedMasterEntry; - CKeyID seedId; - int32_t nCount; - std::set setMempool = GetMempoolTxids(); - for(auto& spentSerial : spentSerials){ - uint256 spentSerialHash = primitives::GetSerialHash(spentSerial); - CLelantusMintMeta meta; - GroupElement pubcoin; - // Check serialHash in db - if(walletdb.ReadPubcoin(spentSerialHash, pubcoin)) { - // If found in db but not in memory - this is likely a resync - if(!GetMetaFromSerial(spentSerialHash, meta)){ - uint256 hashPubcoin = primitives::GetPubCoinValueHash(pubcoin); - if(!walletdb.ReadMintPoolPair(hashPubcoin, hashSeedMasterEntry, seedId, nCount)) { - continue; - } - MintPoolEntry mintPoolEntry(hashSeedMasterEntry, seedId, nCount); - mintPoolEntries.push_back(std::make_pair(hashPubcoin, mintPoolEntry)); - continue; - } - if(UpdateLelantusMetaStatus(setMempool, meta, true)){ - updatedMeta.emplace_back(meta); - } - } - } - - UpdateFromBlock(mintPoolEntries, updatedMeta); -} - -/** - * Returns the in memory mint objects as CSigmaEntry objects (ie. mints containing private data) - * - * @param fUnusedOnly convert unused mints only - * @param fMatureOnly convert mature (ie. spendable due to sufficient confirmations) mints only - * @return list of CSigmaEntry objects - */ -std::list CHDMintTracker::MintsAsLelantusEntries(bool fUnusedOnly, bool fMatureOnly){ - std::list listCoin; - CWalletDB walletdb(strWalletFile); - std::vector vecMists = ListLelantusMints(fUnusedOnly, fMatureOnly, false); - std::list listMints(vecMists.begin(), vecMists.end()); - for (const CLelantusMintMeta& mint : listMints) { - CLelantusEntry entry; - pwalletMain->GetMint(mint.hashSerial, entry); - listCoin.push_back(entry); - } - return listCoin; -} - -/** - * Sets up the in memory mint objects. - * - * @param fUnusedOnly process mature (ie. spendable due to sufficient confirmations) mints only - * @param fUpdateStatus If the mints should be updated - * @param fLoad If the mints should be loaded from database - * @param fWrongSeed If mints without correct seed should be added - * @return vector of CMintMeta objects - */ -std::vector CHDMintTracker::ListLelantusMints(bool fUnusedOnly, bool fMatureOnly, bool fUpdateStatus, bool fLoad, bool fWrongSeed) -{ - std::vector setMints; - if (fLoad) { - LOCK2(cs_main, pwalletMain->cs_wallet); - CWalletDB walletdb(strWalletFile); - - std::list listDeterministicDB = walletdb.ListHDMints(true); - for (auto& dMint : listDeterministicDB) { - AddLelantus(walletdb, dMint, false, false); - } - LogPrint("zero", "%s: added %d lelantus hdmint from DB\n", __func__, listDeterministicDB.size()); - } - - std::vector vOverWrite; - std::set setMempool = GetMempoolTxids(); - - for (auto& it : mapLelantusSerialHashes) { - CLelantusMintMeta mint = it.second; - - //This is only intended for unarchived coins - if (mint.isArchived) - continue; - - // Update the metadata of the mints if requested - if (fUpdateStatus){ - if(UpdateLelantusMetaStatus(setMempool, mint)) { - if (mint.isArchived) - continue; - - // Mint was updated, queue for overwrite - vOverWrite.emplace_back(mint); - } - } - - if (fUnusedOnly && mint.isUsed) - continue; - - if (fMatureOnly) { - // Not confirmed - if (!mint.nHeight || !(mint.nHeight + (ZC_MINT_CONFIRMATIONS-1) <= chainActive.Height())) - continue; - } - - if (!fWrongSeed && !mint.isSeedCorrect) - continue; - - setMints.push_back(mint); - } - - //overwrite any updates - for (CLelantusMintMeta& meta : vOverWrite) - UpdateState(meta); - - return setMints; -} - -/** - * Get txids of all mempool entries. - * - * @return set of mempool txids - */ -std::set CHDMintTracker::GetMempoolTxids(){ - std::set setMempool; - setMempool.clear(); - { - LOCK(mempool.cs); - txpools.getTransactions(setMempool); - } - return setMempool; -} - -/** - * map of serial hashes -> CMintMeta objects - */ -void CHDMintTracker::Clear() -{ - mapLelantusSerialHashes.clear(); -} diff --git a/src/hdmint/tracker.h b/src/hdmint/tracker.h deleted file mode 100644 index addb3296d6..0000000000 --- a/src/hdmint/tracker.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2019 The Firo Core Developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef FIRO_HDMINTTRACKER_H -#define FIRO_HDMINTTRACKER_H - -#include "primitives/mint_spend.h" -#include "hdmint/mintpool.h" -#include "wallet/walletdb.h" -#include - -class CHDMint; -class CHDMintWallet; - -class CHDMintTracker -{ -private: - bool fInitialized; - std::string strWalletFile; - std::map mapLelantusSerialHashes; - std::map mapPendingSpends; //serialhash, txid of spend - bool IsMempoolSpendOurs(const std::set& setMempool, const uint256& hashSerial); - bool UpdateLelantusMetaStatus(const std::set& setMempool, CLelantusMintMeta& mint, bool fSpend=false); - - std::set GetMempoolTxids(); -public: - CHDMintTracker(std::string strWalletFile); - ~CHDMintTracker(); - void AddLelantus(CWalletDB& walletdb, const CHDMint& dMint, bool isNew = false, bool isArchived = false); - bool Archive(CLelantusMintMeta& meta); - bool HasPubcoinHash(const uint256& hashPubcoin, CWalletDB& walletdb) const; - bool HasLelantusSerialHash(const uint256& hashSerial) const; - bool IsEmpty() const { return mapLelantusSerialHashes.empty(); } - void Init(); - bool GetMetaFromSerial(const uint256& hashSerial, CLelantusMintMeta& mMeta); - bool GetLelantusMetaFromPubcoin(const uint256& hashPubcoin, CLelantusMintMeta& mMeta); - - std::vector GetSerialHashes(); - void UpdateFromBlock(const std::list>& mintPoolEntries, const std::vector& updatedMeta); - void UpdateMintStateFromBlock(const std::vector>>& mints); - void UpdateSpendStateFromBlock(const std::unordered_map& spentSerials); - void UpdateLelantusMintStateFromMempool(const std::vector& pubCoins, const std::vector& amounts); - void UpdateJoinSplitStateFromMempool(const std::vector& spentSerials); - std::list MintsAsLelantusEntries(bool fUnusedOnly = true, bool fMatureOnly = true); - std::vector ListLelantusMints(bool fUnusedOnly = true, bool fMatureOnly = true, bool fUpdateStatus = true, bool fLoad = false, bool fWrongSeed = false); - void SetLelantusPubcoinUsed(const uint256& hashPubcoin, const uint256& txid); - void SetLelantusPubcoinNotUsed(const uint256& hashPubcoin); - bool UnArchive(const uint256& hashPubcoin, bool isDeterministic); - bool UpdateState(const CLelantusMintMeta& meta); - void Clear(); -}; - -#endif //FIRO_HDMINTTRACKER_H diff --git a/src/hdmint/wallet.cpp b/src/hdmint/wallet.cpp deleted file mode 100644 index 2c720644bc..0000000000 --- a/src/hdmint/wallet.cpp +++ /dev/null @@ -1,858 +0,0 @@ -// Copyright (c) 2019 The Firo Core Developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "hdmint/wallet.h" -#include "validation.h" -#include "txdb.h" -#include "init.h" -#include "hdmint/hdmint.h" -#include "wallet/walletdb.h" -#include "wallet/wallet.h" -#include "lelantus.h" -#include "crypto/hmac_sha256.h" -#include "crypto/hmac_sha512.h" -#include "keystore.h" -#include -#include "masternode-sync.h" -#include "ui_interface.h" - -/** - * Constructor for CHDMintWallet object. - * - * Sets database values: the wallet file, mintpool, masterseed hash and count values. - * Doesn't set encrypted values if the wallet is locked. - * - * @param strWalletFile wallet file string - */ -CHDMintWallet::CHDMintWallet(const std::string& strWalletFile, bool resetCount) : strWalletFile(strWalletFile), tracker(strWalletFile) -{ - this->mintPool = CMintPool(); - - //Don't try to do anything else if the wallet is locked. - if (pwalletMain->IsLocked()) { - return; - } - - // Use MasterKeyId from HDChain as index for mintpool - uint160 hashSeedMaster = pwalletMain->GetHDChain().masterKeyID; - - if (!SetupWallet(hashSeedMaster, resetCount)) { - LogPrintf("%s: failed to save deterministic seed\n", __func__); - return; - } -} - -/** - * Constructor helper function. - * - * @param hashSeedMaster hash master seed - * @param fResetCount if true, set DB counts to 0. - * @return bool - */ -bool CHDMintWallet::SetupWallet(const uint160& hashSeedMaster, bool fResetCount) -{ - CWalletDB walletdb(strWalletFile); - if (pwalletMain->IsLocked()) - return false; - - if (hashSeedMaster.IsNull()) { - return error("%s: failed to set master seed.", __func__); - } - - this->hashSeedMaster = hashSeedMaster; - - nCountNextUse = COUNT_DEFAULT; - nCountNextGenerate = COUNT_DEFAULT; - - if (fResetCount){ - walletdb.WriteMintCount(nCountNextUse); - walletdb.WriteMintSeedCount(nCountNextGenerate); - }else{ - if (!walletdb.ReadMintCount(nCountNextUse)) - nCountNextUse = COUNT_DEFAULT; - if (!walletdb.ReadMintSeedCount(nCountNextGenerate)) - nCountNextGenerate = COUNT_DEFAULT; - } - - return true; -} - -/** - * Regenerate a MintPoolEntry value from given values. - * - * Doesn't do anything if the wallet is encrypted+locked. - * Attempts to recreate mint seed (512-bit value used for mint generate) from inputs. - * Then recreates mint from the seed, and stores walletdb values appropriately. - * - * @param mintHashSeedMaster hash master seed for this mint - * @param seedId seed ID for the key used to generate mint - * @param nCount count for this mint in the HD chain - * @RETURN pair of for this mint - */ -std::pair CHDMintWallet::RegenerateMintPoolEntry(CWalletDB& walletdb, const uint160& mintHashSeedMaster, CKeyID& seedId, const int32_t& nCount) -{ - // hashPubcoin, hashSerial - std::pair nIndexes; - - //Is locked - if (pwalletMain->IsLocked()) - throw std::runtime_error("Error: Please enter the wallet passphrase with walletpassphrase first."); - - uint512 mintSeed; - if(!CreateMintSeed(walletdb, mintSeed, nCount, seedId)) - throw std::runtime_error("Unable to create seed for mint regeneration."); - - GroupElement commitmentValue; - lelantus::PrivateCoin coin(lelantus::Params::get_default(), 0); - if(!SeedToMint(mintSeed, commitmentValue, coin)) //for lelantus put just part of commit, for checking we will need to reduce h1^v from lelantus mint - throw std::runtime_error("Unable to create sigmamint from seed in mint regeneration."); - - uint256 hashPubcoin = primitives::GetPubCoinValueHash(commitmentValue); - uint256 hashSerial = primitives::GetSerialHash(coin.getSerialNumber()); - - MintPoolEntry mintPoolEntry(mintHashSeedMaster, seedId, nCount); - mintPool.Add(std::make_pair(hashPubcoin, mintPoolEntry)); - walletdb.WritePubcoin(hashSerial, commitmentValue); - walletdb.WriteMintPoolPair(hashPubcoin, mintPoolEntry); - - nIndexes.first = hashPubcoin; - nIndexes.second = hashSerial; - - return nIndexes; - -} - -/** - * Generate the mintpool for the current master seed. - * - * only runs if the current mintpool is exhausted and we need new mints (ie. the next mint to - * generate is the same as the one last used) - * Generates 20 mints at a time. - * Makes the appropriate database entries. - * - * @param nIndex The number of mints to generate. Defaults to 20 if no param passed. - */ -void CHDMintWallet::GenerateMintPool(CWalletDB& walletdb, bool forceGenerate, int32_t nIndex) -{ - //Is locked - if (pwalletMain->IsLocked()) - return; - - // Only generate new values (ie. if last generated less than or the same, proceed) - if(nCountNextGenerate > nCountNextUse && !forceGenerate){ - return; - } - - LOCK(pwalletMain->cs_wallet); - - unsigned int mintpoolsize = std::min((unsigned int)GetArg("-mintpoolsize", DEFAULT_MINTPOOL_SIZE), MAX_MINTPOOL_SIZE); - - int32_t nLastCount = nCountNextGenerate; - int32_t nStop = nLastCount + mintpoolsize; - if(nIndex > 0 && nIndex >= nLastCount) - nStop = nIndex + mintpoolsize; - LogPrintf("%s : nLastCount=%d nStop=%d\n", __func__, nLastCount, nStop - 1); - for (; nLastCount <= nStop; ++nLastCount) { - if (ShutdownRequested()) - return; - - CKeyID seedId; - uint512 mintSeed; - if(!CreateMintSeed(walletdb, mintSeed, nLastCount, seedId, false)) - continue; - - GroupElement commitmentValue; - lelantus::PrivateCoin coin(lelantus::Params::get_default(), 0); - if(!SeedToMint(mintSeed, commitmentValue, coin)) //for lelantus put just part of commit, for checking we will need to reduce h1^v from lelantus mint - continue; - - uint256 hashPubcoin = primitives::GetPubCoinValueHash(commitmentValue); - - MintPoolEntry mintPoolEntry(hashSeedMaster, seedId, nLastCount); - mintPool.Add(std::make_pair(hashPubcoin, mintPoolEntry)); - walletdb.WritePubcoin(primitives::GetSerialHash(coin.getSerialNumber()), commitmentValue); - walletdb.WriteMintPoolPair(hashPubcoin, mintPoolEntry); - } - - // write hdchain back to database - if (!walletdb.WriteHDChain(pwalletMain->GetHDChain())) - throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); - - // Update local + DB entries for count last generated - nCountNextGenerate = nLastCount; - walletdb.WriteMintSeedCount(nCountNextGenerate); - -} - -/** - * Load mintpool values from the database into memory. - * - */ -bool CHDMintWallet::LoadMintPoolFromDB() -{ - CWalletDB walletdb(strWalletFile); - std::vector> listMintPool = walletdb.ListMintPool(); - - for (auto& mintPoolPair : listMintPool){ - mintPool.Add(mintPoolPair); - } - - return true; -} - -/** - * Get the mint serial hash for the mint pubcoin hash given. - * - * We only have a map of serial hashes to pubcoins (from db), so need to traverse the other way in a loop. - * - * @param serialPubcoinPairs a vector of mint hash serials to pubcoin objects. - * @param hashPubcoin mint pubcoin hash - * @param hashSerial reference to a mint serial hash. Is set if found - * @return success - */ -bool CHDMintWallet::GetSerialForPubcoin(const std::vector>& serialPubcoinPairs, const uint256& hashPubcoin, uint256& hashSerial) -{ - bool fFound = false; - for(const auto& serialPubcoinPair : serialPubcoinPairs){ - if(hashPubcoin == primitives::GetPubCoinValueHash(serialPubcoinPair.second)){ - hashSerial = serialPubcoinPair.first; - fFound = true; - break; - } - } - - return fFound; -} - -void CHDMintWallet::SetWalletTransactionBlock(CWalletTx &wtx, const CBlockIndex *blockIndex, const CBlock &block) { - size_t posInBlock = INT_MAX; - uint256 txHash = wtx.tx->GetHash(); - for (size_t i=0; iGetHash() == txHash) - posInBlock = i; - assert(posInBlock < INT_MAX); - wtx.SetMerkleBranch(blockIndex, (int)posInBlock); -} - -/** - * Catch the mint counter up with the chain. - * - * Mints are created deterministically so we can completely regenerate all mints and transaction data for them from chain data. - * Rather than a single pass of listMints, we wrap each pass in an outer while loop, that continues until no updates are found. - * The reason for this is to allow the mint counter in the wallet to update and regenerate more of the mint pool should it need to. - * - * @param fGenerateMintPool whether or not to call GenerateMintPool. defaults to true - * @param listMints An optional value. If passed, only sync the mints in this list. Else get all mints in the mintpool - */ -void CHDMintWallet::SyncWithChain(bool fGenerateMintPool, boost::optional>> listMints) -{ - CWalletDB walletdb(strWalletFile); - bool found = true; - - std::set setAddedTx; - std::set setChecked; - int mintsFound = 1; - bool firstIteration = true; - do { - found = false; - mintsFound = 0; - if (fGenerateMintPool) - GenerateMintPool(walletdb, !firstIteration); - LogPrintf("%s: Mintpool size=%d\n", __func__, mintPool.size()); - - firstIteration = false; - - if(listMints==boost::none){ - listMints = std::list>(); - mintPool.List(listMints.get()); - } - for (std::pair& pMint : listMints.get()) { - if (setChecked.count(pMint.first)) - continue; - setChecked.insert(pMint.first); - uiInterface.UpdateProgressBarLabel("Synchronizing mints..."); - - if (ShutdownRequested()) - return; - uint160& mintHashSeedMaster = std::get<0>(pMint.second); - int32_t& mintCount = std::get<2>(pMint.second); - - // halt processing if mint already in tracker - if (tracker.HasPubcoinHash(pMint.first, walletdb)) - continue; - - uint160 seedId = std::get<1>(pMint.second); - CDataStream ss(SER_GETHASH, 0); - ss << pMint.first; - ss << seedId; - uint256 mintTag = Hash(ss.begin(), ss.end()); - - COutPoint outPoint; - if (!pwalletMain->IsLocked() && lelantus::GetOutPointFromMintTag(outPoint, mintTag)) { - const uint256& txHash = outPoint.hash; - //this mint has already occurred on the chain, increment counter's state to reflect this - LogPrintf("%s : Found wallet coin mint=%s count=%d tx=%s\n", __func__, pMint.first.GetHex(), mintCount, txHash.GetHex()); - found = true; - - uint256 hashBlock; - CTransactionRef tx; - if (!GetTransaction(txHash, tx, Params().GetConsensus(), hashBlock, true)) { - LogPrintf("%s : failed to get transaction for mint %s!\n", __func__, pMint.first.GetHex()); - found = false; - continue; - } - - uint64_t amount = 0; - bool fFoundMint = false; - for (const CTxOut& out : tx->vout) { - if (!out.scriptPubKey.IsLelantusMint() && !out.scriptPubKey.IsLelantusJMint()) - continue; - secp_primitives::GroupElement pubcoin; - try { - if (out.scriptPubKey.IsLelantusMint()) { - amount = out.nValue; - lelantus::ParseLelantusMintScript(out.scriptPubKey, pubcoin); - } else { - std::vector encryptedValue; - lelantus::ParseLelantusJMintScript(out.scriptPubKey, pubcoin, encryptedValue); - if(!pwalletMain->DecryptMintAmount(encryptedValue, pubcoin, amount)) - continue; - } - } catch (std::invalid_argument&) { - continue; - } - if(amount != 0) - pubcoin += lelantus::Params::get_default()->get_h1() * Scalar(amount).negate(); - // See if this is the mint that we are looking for - uint256 hashPubcoin = primitives::GetPubCoinValueHash(pubcoin); - if (pMint.first == hashPubcoin) { - fFoundMint = true; - break; - } - } - - if (!fFoundMint) { - LogPrintf("%s : failed to get mint %s from tx %s!\n", __func__, pMint.first.GetHex(), tx->GetHash().GetHex()); - found = false; - break; - } - - CBlockIndex* pindex = nullptr; - if (mapBlockIndex.count(hashBlock)) - pindex = mapBlockIndex.at(hashBlock); - - if (!setAddedTx.count(txHash)) { - CBlock block; - CWalletTx wtx(pwalletMain, tx); - if (pindex && ReadBlockFromDisk(block, pindex, Params().GetConsensus())) - SetWalletTransactionBlock(wtx, pindex, block); - - //Fill out wtx so that a transaction record can be created - wtx.nTimeReceived = pindex->GetBlockTime(); - pwalletMain->AddToWallet(wtx, false); - setAddedTx.insert(txHash); - } - - if(!SetLelantusMintSeedSeen(walletdb, pMint, pindex->nHeight, txHash, amount)) - continue; - - if (tx->IsLelantusJoinSplit()) { - std::vector serials = lelantus::GetLelantusJoinSplitSerialNumbers(*tx, tx->vin[0]); - for (auto& serial : serials) { - CLelantusMintMeta mMeta; - if (!tracker.GetMetaFromSerial(primitives::GetSerialHash(serial), mMeta)) - continue; - - if (mMeta.isUsed) - continue; - - tracker.SetLelantusPubcoinUsed(mMeta.GetPubCoinValueHash(), tx->GetHash()); - - // add CLelantusSpendEntry - CLelantusSpendEntry spend; - spend.coinSerial = serial; - spend.hashTx = tx->GetHash(); - spend.pubCoin = mMeta.GetPubCoinValue(); - spend.id = mMeta.nId; - spend.amount = mMeta.amount; - if (!walletdb.WriteLelantusSpendSerialEntry(spend)) { - throw std::runtime_error(_("Failed to write coin serial number into wallet")); - } - } - } - - // Only update if the current hashSeedMaster matches the mints' - if(hashSeedMaster == mintHashSeedMaster && mintCount >= GetCount()){ - SetCount(++mintCount); - UpdateCountDB(walletdb); - LogPrint("zero", "%s: updated count to %d\n", __func__, nCountNextUse); - } - } - if (found) - mintsFound++; - } - uiInterface.UpdateProgressBarLabel(""); - // Clear listMints to allow it to be repopulated by the mintPool on the next iteration - if(found) - listMints = boost::none; - if (!fGenerateMintPool) - mintsFound = 0; - } while (found || mintsFound > 0); -} - -/** - * Add the mint from the chain to the mint tracker. - * - * Gets the mint from known values and creates a CHDMint object. Stores it in the tracker. - * If the wallet is not locked, the mint is regenerated from the known values. If regeneration fails, return false. - * If the wallet is locked, we use unencrypted db values to regenerate the object. - * - * @param mintPoolEntryPair pair of pubcoin hash to MintPoolEntry object - * @param nHeight mint txid height - * @param txid mint txid height - * @param denom mint denomination - */ - -bool CHDMintWallet::SetLelantusMintSeedSeen(CWalletDB& walletdb, std::pair mintPoolEntryPair, int nHeight, const uint256& txid, uint64_t amount) -{ - // Regenerate the mint - uint256 hashPubcoin = mintPoolEntryPair.first; - CKeyID seedId = std::get<1>(mintPoolEntryPair.second); - int32_t mintCount = std::get<2>(mintPoolEntryPair.second); - - auto params = lelantus::Params::get_default(); - - GroupElement bnValue; - uint256 hashSerial; - // Can regenerate if unlocked (cheaper) - Scalar serial; - if(!pwalletMain->IsLocked()) { - LogPrintf("%s: Wallet not locked, creating mind seed..\n", __func__); - uint512 mintSeed; - CreateMintSeed(walletdb, mintSeed, mintCount, seedId); - lelantus::PrivateCoin coin(params, amount); - if(!SeedToLelantusMint(mintSeed, coin)) - return false; - hashSerial = primitives::GetSerialHash(coin.getSerialNumber()); - bnValue = coin.getPublicCoin().getValue(); - serial = coin.getSerialNumber(); - } else { - LogPrintf("%s: Wallet locked, retrieving mind seed..\n", __func__); - // Get serial and pubcoin data from the db - std::vector> serialPubcoinPairs = walletdb.ListSerialPubcoinPairs(); - bool fFound = false; - for(auto serialPubcoinPair : serialPubcoinPairs){ - GroupElement pubcoin = serialPubcoinPair.second; - uint256 reducedHash; - walletdb.ReadPubcoinHashes(primitives::GetPubCoinValueHash(pubcoin), reducedHash); - if(hashPubcoin == reducedHash){ - LogPrintf("%s: Found pubcoin and serial hash\n", __func__); - bnValue = pubcoin; - hashSerial = serialPubcoinPair.first; - fFound = true; - break; - } - } - // Not found in DB - if(!fFound){ - LogPrintf("%s: Pubcoin not found in DB. \n", __func__); - return false; - } - } - - LogPrintf("%s: Creating mint object.. \n", __func__); - int height, id; - std::tie(height, id) = lelantus::CLelantusState::GetState()->GetMintedCoinHeightAndId(bnValue); - - // Create mint object - CHDMint dMint(mintCount, seedId, hashSerial, bnValue); - dMint.SetAmount(amount); - dMint.SetHeight(nHeight); - dMint.SetId(id); - - // Check if this is also already spent - int nHeightTx; - uint256 txidSpend; - CTransactionRef txSpend; - bool used = false; - if (IsLelantusSerialInBlockchain(hashSerial, nHeightTx, txidSpend, txSpend)) { - //Find transaction details and make a wallettx and add to wallet - LogPrintf("%s: Mint object is spent. Setting used..\n", __func__); - dMint.SetUsed(true); - CWalletTx wtx(pwalletMain, txSpend); - CBlockIndex* pindex = chainActive[nHeightTx]; - CBlock block; - if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) - SetWalletTransactionBlock(wtx, pindex, block); - - wtx.nTimeReceived = pindex->nTime; - pwalletMain->AddToWallet(wtx, false); - used = true; - } else { - lelantus::CLelantusState *lelantusState = lelantus::CLelantusState::GetState(); - // this is for some edge cases, when mint is used but the serial is not at map - Scalar s; - if (lelantusState->IsUsedCoinSerialHash(s, hashSerial)) { - dMint.SetUsed(true); - serial = s; - used = true; - } - } - - // Adding spend entry into db, - if(used && (serial != uint64_t(0))) { - CLelantusSpendEntry spend; - spend.coinSerial = serial; - spend.pubCoin = bnValue; - spend.id = id; - spend.amount = amount; - - if (!walletdb.WriteLelantusSpendSerialEntry(spend)) { - throw std::runtime_error(_("Failed to write coin serial number into wallet")); - } - } - - LogPrintf("%s: Adding mint to tracker.. \n", __func__); - // Add to tracker which also adds to database - tracker.AddLelantus(walletdb, dMint, true); - - return true; -} - -bool CHDMintWallet::SeedToMint(const uint512& mintSeed, GroupElement& commit, lelantus::PrivateCoin& coin) -{ - //convert state seed into a seed for the private key - uint256 nSeedPrivKey = mintSeed.trim256(); - nSeedPrivKey = Hash(nSeedPrivKey.begin(), nSeedPrivKey.end()); - coin.setEcdsaSeckey(nSeedPrivKey); - - // Create a key pair - secp256k1_pubkey pubkey; - if (!secp256k1_ec_pubkey_create(OpenSSLContext::get_context(), &pubkey, coin.getEcdsaSeckey())){ - return false; - } - // Hash the public key in the group to obtain a serial number - Scalar serialNumber = coin.serialNumberFromSerializedPublicKey(OpenSSLContext::get_context(), &pubkey); - coin.setSerialNumber(serialNumber); - - //hash randomness seed with Bottom 256 bits of mintSeed - Scalar randomness; - uint256 nSeedRandomness = ArithToUint512(UintToArith512(mintSeed) >> 256).trim256(); - randomness.memberFromSeed(nSeedRandomness.begin()); - coin.setRandomness(randomness); - - // Generate a Pedersen commitment to the serial number - commit = coin.getParams()->get_g() * coin.getSerialNumber() + coin.getParams()->get_h0() * coin.getRandomness(); - - return true; -} - -/** - * Convert a 512-bit mint seed into a mint. - * - * See https://github.com/firoorg/firo/pull/392 for specification on mint generation. - * - * @param mintSeed uint512 object of seed for mint - * @param coin reference to private coin. Is set in this function - * @return success - */ -bool CHDMintWallet::SeedToLelantusMint(const uint512& mintSeed, lelantus::PrivateCoin& coin) -{ - //convert state seed into a seed for the private key - uint256 nSeedPrivKey = mintSeed.trim256(); - nSeedPrivKey = Hash(nSeedPrivKey.begin(), nSeedPrivKey.end()); - - // Create a key pair - secp256k1_pubkey pubkey; - if (!secp256k1_ec_pubkey_create(OpenSSLContext::get_context(), &pubkey, nSeedPrivKey.begin())) { - return false; - } - - // Hash the public key in the group to obtain a serial number - Scalar serialNumber = coin.serialNumberFromSerializedPublicKey(OpenSSLContext::get_context(), &pubkey); - - // hash randomness seed with Bottom 256 bits of mintSeed - Scalar randomness; - uint256 nSeedRandomness = ArithToUint512(UintToArith512(mintSeed) >> 256).trim256(); - randomness.memberFromSeed(nSeedRandomness.begin()); - - std::vector seckey(nSeedPrivKey.begin(), nSeedPrivKey.end()); - //generating coin - coin = lelantus::PrivateCoin( - coin.getParams(), - serialNumber, - coin.getV(), - randomness, - seckey, - LELANTUS_TX_VERSION_4); - - return true; -} - -/** - * Get seed ID for the key used in mint generation. - * - * See https://github.com/firoorg/firo/pull/392 for specification on mint generation. - * Looks to the mintpool first - if mint doesn't exist, generates new mints in the mintpool. - * - * @param nCount count in the HD Chain of the mint to use. - * @return the seed ID - */ -CKeyID CHDMintWallet::GetMintSeedID(CWalletDB& walletdb, int32_t nCount){ - // Get CKeyID for n from mintpool - uint256 hashPubcoin; - std::pair mintPoolEntryPair; - - if(!mintPool.Get(nCount, hashSeedMaster, mintPoolEntryPair)){ - // Add up to mintPool index + 20 - GenerateMintPool(walletdb, nCount); - if(!mintPool.Get(nCount, hashSeedMaster, mintPoolEntryPair)){ - ResetCount(walletdb); - throw std::runtime_error("Unable to retrieve mint seed ID"); - } - } - - return std::get<1>(mintPoolEntryPair.second); -} - -/** - * Create the mint seed for the count passed. - * - * See https://github.com/firoorg/firo/pull/392 for specification on mint generation. - * We check if the key for the count passed exists. if so retrieve it's seed ID. if not, generate a new key. - * If seedId is passed, use that seedId and ignore key generation section. - * Following that, get the key, and use it to generate the mint seed according to the specification. - * - * @param mintSeed mint seed - * @param nCount (optional) count in the HD Chain of the key to use for mint generation. - * @param seedId (optional) seedId of the key to use for mint generation. - * @return success - */ -bool CHDMintWallet::CreateMintSeed(CWalletDB& walletdb, uint512& mintSeed, const int32_t& nCount, CKeyID& seedId, bool fWriteChain) -{ - LOCK(pwalletMain->cs_wallet); - CKey key; - - if(seedId.IsNull()){ - CPubKey pubKey; - int32_t chainIndex = pwalletMain->GetHDChain().nExternalChainCounters[BIP44_MINT_INDEX]; - if(nCount==chainIndex){ - // If chainIndex is the same as n (ie. we are generating next available key), generate a new key. - pubKey = pwalletMain->GenerateNewKey(BIP44_MINT_INDEX, fWriteChain); - } - else if(nCountGetKeyFromKeypath(BIP44_MINT_INDEX, nCount, secret); - } - else{ - throw std::runtime_error("Unable to retrieve mint seed ID (internal index greater than HDChain index). \n" - "We recommend restarting with -zapwalletmints."); - } - seedId = pubKey.GetID(); - } - - if (!pwalletMain->CCryptoKeyStore::GetKey(seedId, key)){ - ResetCount(walletdb); - throw std::runtime_error("Unable to retrieve generated key for mint seed. Is the wallet locked?"); - } - - // HMAC-SHA512(SHA256(count),key) - unsigned char countHash[CSHA256::OUTPUT_SIZE]; - std::vector result(CSHA512::OUTPUT_SIZE); - - std::string nCountStr = std::to_string(nCount); - CSHA256().Write(reinterpret_cast(nCountStr.c_str()), nCountStr.size()).Finalize(countHash); - - CHMAC_SHA512(countHash, CSHA256().OUTPUT_SIZE).Write(key.begin(), key.size()).Finalize(&result[0]); - - mintSeed = uint512(result); - - return true; -} - -/** - * Get in-memory count of the next mint to use. - * - * @return the count - */ -int32_t CHDMintWallet::GetCount() -{ - return nCountNextUse; -} - -/** - * Reset in-memory count to that of the database value. - * Necessary during transaction creation when fee calcuation causes the creation to reset. - */ -void CHDMintWallet::ResetCount(CWalletDB& walletdb) -{ - walletdb.ReadMintCount(nCountNextUse); -} - -/** - * Set in-memory count to parameter passed - * - * @param nCount count to be set - */ -void CHDMintWallet::SetCount(int32_t nCount) -{ - nCountNextUse = nCount; -} - -/** - * Increment in-memory count of the next mint to use. - */ -void CHDMintWallet::UpdateCountLocal() -{ - nCountNextUse++; - LogPrintf("CHDMintWallet : Updating count local to %s\n",nCountNextUse); -} - -/** - * Increment database count of the next mint to use. - * calls GenerateMintPool, which will run if we have exhausted the mintpool. - */ -void CHDMintWallet::UpdateCountDB(CWalletDB& walletdb) -{ - LogPrintf("CHDMintWallet : Updating count in DB to %s\n",nCountNextUse); - walletdb.WriteMintCount(nCountNextUse); - GenerateMintPool(walletdb); -} - -/** - * Gets a CHDMint object from a mintpool entry. - * - * @param coin reference to private coin object,should keep the value of coin - * @param dMint reference to CHDMint object - * @param mintPoolEntry mintpool data - * @return success - */ -bool CHDMintWallet::GetLelantusHDMintFromMintPoolEntry(CWalletDB& walletdb, lelantus::PrivateCoin& coin, CHDMint& dMint, MintPoolEntry& mintPoolEntry){ - uint512 mintSeed; - CreateMintSeed(walletdb, mintSeed, std::get<2>(mintPoolEntry), std::get<1>(mintPoolEntry)); - - if(!SeedToLelantusMint(mintSeed, coin)){ - return false; - } - - uint256 hashSerial = primitives::GetSerialHash(coin.getSerialNumber()); - dMint = CHDMint(std::get<2>(mintPoolEntry), std::get<1>(mintPoolEntry), hashSerial, coin.getPublicCoin().getValue()); - return true; -} - -/** - * Generate a CHDMint object, taking care of surrounding conditions. - * - * If the chain is not synced, do not proceed, unless fAllowUnsynced is set. - * If passed the mintpool entry, we directly call GetHDMintFromMintPoolEntry and return. - * If not, we assume that this is a new mint being created. - * Following creation, verify the mint does not already exist, in-memory or on-chain. This is to prevent sync issues with the - * mint counter between copies of the same wallet. If it does, increment the count and repeat creation. Continue until an available - * mint is found. - * - * @param coin reference to private coin object, should keep the value of coin - * @param dMint reference to CHDMint object - * @param mintPoolEntry mintpool data - * @param fAllowUnsynced allow mint creation if chain is not synced (for tests) - * @return success - */ -bool CHDMintWallet::GenerateLelantusMint(CWalletDB& walletdb, lelantus::PrivateCoin& coin, CHDMint& dMint, uint160& seedIdOut, boost::optional mintPoolEntry, bool fAllowUnsynced) -{ - if(!masternodeSync.IsBlockchainSynced() && !fAllowUnsynced && !(Params().NetworkIDString() == CBaseChainParams::REGTEST)) - throw std::runtime_error("Unable to generate mint: Blockchain not yet synced."); - - if(mintPoolEntry!=boost::none) - return GetLelantusHDMintFromMintPoolEntry(walletdb, coin, dMint, mintPoolEntry.get()); - - lelantus::CLelantusState *lelantusState = lelantus::CLelantusState::GetState(); - while(true) { - if(hashSeedMaster.IsNull()) - throw std::runtime_error("Unable to generate mint: HashSeedMaster not set"); - CKeyID seedId = GetMintSeedID(walletdb, nCountNextUse); - seedIdOut = seedId; - mintPoolEntry = MintPoolEntry(hashSeedMaster, seedId, nCountNextUse); - // Empty mintPoolEntry implies this is a new mint being created, so update nCountNextUse - UpdateCountLocal(); - - if(!GetLelantusHDMintFromMintPoolEntry(walletdb, coin, dMint, mintPoolEntry.get())) - return false; - - // New HDMint exists, try new count - if(walletdb.HasHDMint(dMint.GetPubcoinValue()) - || lelantusState->HasCoin(coin.getPublicCoin())) { - LogPrintf("%s: Coin detected used, trying next. count: %d\n", __func__, std::get<2>(mintPoolEntry.get())); - }else{ - LogPrintf("%s: Found unused coin, count: %d\n", __func__, std::get<2>(mintPoolEntry.get())); - break; - } - } - - dMint.SetAmount(coin.getV()); - return true; -} - - -bool CHDMintWallet::RegenerateMint(CWalletDB& walletdb, const CHDMint& dMint, CLelantusEntry& lelantusEntry, bool forEstimation) -{ - //Generate the coin - lelantus::PrivateCoin coin(lelantus::Params::get_default(), dMint.GetAmount()); - CHDMint dMintDummy; - CKeyID seedId = dMint.GetSeedId(); - int32_t nCount = dMint.GetCount(); - MintPoolEntry mintPoolEntry(hashSeedMaster, seedId, nCount); - uint160 dummySeedId; - if(!forEstimation) - GenerateLelantusMint(walletdb, coin, dMintDummy, dummySeedId, mintPoolEntry, true); - - //Fill in the lelantus object's details - GroupElement bnValue = coin.getPublicCoin().getValue(); - if (primitives::GetPubCoinValueHash(bnValue) != dMint.GetPubCoinHash() && !forEstimation) - return error("%s: failed to correctly generate lelantus mint, pubcoin hash mismatch", __func__); - if(forEstimation) - lelantusEntry.value = dMint.GetPubcoinValue(); - else - lelantusEntry.value = bnValue; - - Scalar bnSerial = coin.getSerialNumber(); - if (primitives::GetSerialHash(bnSerial) != dMint.GetSerialHash() && !forEstimation) - return error("%s: failed to correctly generate lelantus mint, serial hash mismatch", __func__); - - lelantusEntry.amount = dMint.GetAmount(); - lelantusEntry.randomness = coin.getRandomness(); - lelantusEntry.serialNumber = bnSerial; - lelantusEntry.IsUsed = dMint.IsUsed(); - lelantusEntry.nHeight = dMint.GetHeight(); - lelantusEntry.id = dMint.GetId(); - lelantusEntry.ecdsaSecretKey = std::vector(&coin.getEcdsaSeckey()[0],&coin.getEcdsaSeckey()[32]); - - return true; -} - -/** - * Checks to see if serial passed is on-chain (ie. a check on whether the mint for the serial is spent) - * - * @param hashSerial mint serial hash - * @param nHeightTx transaction height on-chain - * @param txidSpend transaction hash - * @param tx full transaction object - * @return success - */ - -bool CHDMintWallet::IsLelantusSerialInBlockchain(const uint256& hashSerial, int& nHeightTx, uint256& txidSpend, CTransactionRef & tx) -{ - txidSpend.SetNull(); - CLelantusMintMeta mMeta; - Scalar bnSerial; - - if (!lelantus::CLelantusState::GetState()->IsUsedCoinSerialHash(bnSerial, hashSerial)) - return false; - - if(!tracker.GetMetaFromSerial(hashSerial, mMeta)) - return false; - - txidSpend = mMeta.txid; - - return IsTransactionInChain(txidSpend, nHeightTx, tx); -} diff --git a/src/hdmint/wallet.h b/src/hdmint/wallet.h deleted file mode 100644 index ddee0cb973..0000000000 --- a/src/hdmint/wallet.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2019 The Firo Core Developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef FIRO_HDMINTWALLET_H -#define FIRO_HDMINTWALLET_H - -#include -#include "hdmint/mintpool.h" -#include "uint256.h" -#include "primitives/mint_spend.h" -#include "wallet/wallet.h" -#include "tracker.h" - -class CHDMint; - -static const unsigned int DEFAULT_MINTPOOL_SIZE = 20; -static const unsigned int MAX_MINTPOOL_SIZE = 200; - -class CHDMintWallet -{ -private: - int32_t nCountNextUse; - int32_t nCountNextGenerate; - const std::string& strWalletFile; - CMintPool mintPool; - CHDMintTracker tracker; - uint160 hashSeedMaster; - -public: - int static const COUNT_DEFAULT = 0; - - CHDMintWallet(const std::string& strWalletFile, bool resetCount=false); - - bool SetupWallet(const uint160& hashSeedMaster, bool fResetCount=false); - void SyncWithChain(bool fGenerateMintPool = true, boost::optional>> listMints = boost::none); - bool GetLelantusHDMintFromMintPoolEntry(CWalletDB& walletdb, lelantus::PrivateCoin& coin, CHDMint& dMint, MintPoolEntry& mintPoolEntry); - bool GenerateLelantusMint(CWalletDB& walletdb, lelantus::PrivateCoin& coin, CHDMint& dMint, uint160& seedIdOut, boost::optional mintPoolEntry = boost::none, bool fAllowUnsynced=false); - bool LoadMintPoolFromDB(); - bool RegenerateMint(CWalletDB& walletdb, const CHDMint& dMint, CLelantusEntry& sigma, bool forEstimation = false); - bool GetSerialForPubcoin(const std::vector>& serialPubcoinPairs, const uint256& hashPubcoin, uint256& hashSerial); - bool IsSerialInBlockchain(const uint256& hashSerial, int& nHeightTx, uint256& txidSpend, CTransactionRef & tx); - bool IsLelantusSerialInBlockchain(const uint256& hashSerial, int& nHeightTx, uint256& txidSpend, CTransactionRef & tx); - std::pair RegenerateMintPoolEntry(CWalletDB& walletdb, const uint160& mintHashSeedMaster, CKeyID& seedId, const int32_t& nCount); - void GenerateMintPool(CWalletDB& walletdb, bool forceGenerate = false, int32_t nIndex = 0); - bool SetLelantusMintSeedSeen(CWalletDB& walletdb, std::pair mintPoolEntryPair, int nHeight, const uint256& txid, uint64_t amount); - bool SeedToMint(const uint512& mintSeed, GroupElement& bnValue, lelantus::PrivateCoin& coin); - bool SeedToLelantusMint(const uint512& mintSeed, lelantus::PrivateCoin& coin); - - // Count updating functions - int32_t GetCount(); - CHDMintTracker& GetTracker() { return tracker; } - void ResetCount(CWalletDB& walletdb); - void SetCount(int32_t nCount); - void UpdateCountLocal(); - void UpdateCountDB(CWalletDB& walletdb); - void SetWalletTransactionBlock(CWalletTx &wtx, const CBlockIndex *blockIndex, const CBlock &block); - -private: - CKeyID GetMintSeedID(CWalletDB& walletdb, int32_t nCount); - bool CreateMintSeed(CWalletDB& walletdb, uint512& mintSeed, const int32_t& n, CKeyID& seedId, bool nWriteChain = true); -}; - -#endif //FIRO_HDMINTWALLET_H diff --git a/src/init.cpp b/src/init.cpp index 78bb0ccc7a..98e9272724 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -711,12 +711,7 @@ void CleanupBlockRevFiles() void ThreadImport(std::vector vImportFiles) { #ifdef ENABLE_WALLET - if (!GetBoolArg("-disablewallet", false) && pwalletMain->zwallet) { - //Load zerocoin mint hashes to memory - LogPrintf("Loading mints to wallet..\n"); - pwalletMain->zwallet->GetTracker().Init(); - pwalletMain->zwallet->LoadMintPoolFromDB(); - } + // Lelantus wallet (zwallet) was removed; mint loading skipped #endif const CChainParams &chainparams = Params(); @@ -806,15 +801,6 @@ void ThreadImport(std::vector vImportFiles) { LoadMempool(); } -#ifdef ENABLE_WALLET - if (!GetBoolArg("-disablewallet", false) && pwalletMain->zwallet) { - pwalletMain->zwallet->SyncWithChain(); - } - // Need this to restore Sigma spend state - if (GetBoolArg("-rescan", false) && !GetBoolArg("-disablewallet", false) && pwalletMain->zwallet) { - pwalletMain->zwallet->GetTracker().ListLelantusMints(); - } -#endif fDumpMempoolLater = !fRequestShutdown; } @@ -1830,11 +1816,10 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) CBlockIndex *tip = chainActive.Tip(); if (tip) { int lastBlockIndexVersion = pblocktree->GetBlockIndexVersion(*tip->phashBlock); - if ((tip->nHeight >= chainparams.GetConsensus().nLelantusStartBlock && - lastBlockIndexVersion < LELANTUS_PROTOCOL_ENABLEMENT_VERSION) || - (tip->nHeight >= chainparams.GetConsensus().nEvoSporkStartBlock && - lastBlockIndexVersion < EVOSPORK_MIN_VERSION)) - { + bool lelantusReindex = false; + bool evoReindex = (tip->nHeight >= chainparams.GetConsensus().nEvoSporkStartBlock && + lastBlockIndexVersion < EVOSPORK_MIN_VERSION); + if (lelantusReindex || evoReindex) { strLoadError = _( "Block index is outdated, reindex required\n"); break; diff --git a/src/init.h b/src/init.h index 47d80ab45a..8222794374 100644 --- a/src/init.h +++ b/src/init.h @@ -10,7 +10,6 @@ class CScheduler; class CWallet; -class CHDMintWallet; namespace boost { diff --git a/src/lelantus.cpp b/src/lelantus.cpp deleted file mode 100644 index f23d35f098..0000000000 --- a/src/lelantus.cpp +++ /dev/null @@ -1,1770 +0,0 @@ -#include "validation.h" -#include "lelantus.h" -#include "timedata.h" -#include "chainparams.h" -#include "util.h" -#include "base58.h" -#include "definition.h" -#include "txmempool.h" -#ifdef ENABLE_WALLET -#include "wallet/wallet.h" -#include "wallet/walletdb.h" -#endif // ENABLE_WALLET -#include "crypto/sha256.h" -#include "liblelantus/coin.h" -#include "liblelantus/schnorr_prover.h" -#include "liblelantus/schnorr_verifier.h" -#include "primitives/mint_spend.h" -#include "liblelantus/challenge_generator_impl.h" -#include "policy/policy.h" -#include "coins.h" -#include "batchproof_container.h" - -#include -#include -#include - -#include -#include - -#include - -int64_t nMinimumInputValue = DUST_HARD_LIMIT; - -namespace lelantus { - -static CLelantusState lelantusState; - -static bool CheckLelantusSpendSerial( - CValidationState &state, - CLelantusTxInfo *lelantusTxInfo, - const Scalar &serial, - int nHeight, - bool fConnectTip) { - // check for Lelantus transaction in this block as well - if (lelantusTxInfo && - !lelantusTxInfo->fInfoIsComplete && - lelantusTxInfo->spentSerials.find(serial) != lelantusTxInfo->spentSerials.end()) - return state.DoS(0, error("CTransaction::CheckTransaction() : two or more joinsplits with same serial in the same block")); - - // check for used serials in lelantusState - if (lelantusState.IsUsedCoinSerial(serial)) { - // Proceed with checks ONLY if we're accepting tx into the memory pool or connecting block to the existing blockchain - if (nHeight == INT_MAX || fConnectTip) { - return state.DoS(0, error("CTransaction::CheckTransaction() : The lelantus JoinSplit serial has been used")); - } - } - return true; -} - -/* - * Util funtions - */ -size_t CountCoinInBlock(CBlockIndex *index, int id) { - return index->lelantusMintedPubCoins.count(id) > 0 - ? index->lelantusMintedPubCoins[id].size() : 0; -} - -std::vector GetAnonymitySetHash(CBlockIndex *index, int group_id, bool generation = false) { - std::vector out_hash; - - CLelantusState::LelantusCoinGroupInfo coinGroup; - if (!lelantusState.GetCoinGroupInfo(group_id, coinGroup)) - return out_hash; - - if ((coinGroup.firstBlock == coinGroup.lastBlock && generation) || (coinGroup.nCoins == 0)) - return out_hash; - - while (index != coinGroup.firstBlock) { - if (index->anonymitySetHash.count(group_id) > 0) { - out_hash = index->anonymitySetHash[group_id]; - break; - } - index = index->pprev; - } - return out_hash; -} - -bool IsLelantusAllowed() -{ - LOCK(cs_main); - return IsLelantusAllowed(chainActive.Height()); -} - -bool IsLelantusAllowed(int height) -{ - return height >= ::Params().GetConsensus().nLelantusStartBlock && height < ::Params().GetConsensus().nSparkStartBlock; -} - -bool IsLelantusGraceFulPeriod() -{ - LOCK(cs_main); - int height = chainActive.Height(); - return height >= ::Params().GetConsensus().nLelantusStartBlock && height < ::Params().GetConsensus().nLelantusGracefulPeriod; -} - -bool IsAvailableToMint(const CAmount& amount) -{ - return amount <= ::Params().GetConsensus().nMaxValueLelantusMint; -} - -void GenerateMintSchnorrProof(const lelantus::PrivateCoin& coin, CDataStream& serializedSchnorrProof) -{ - auto params = lelantus::Params::get_default(); - - LOCK(cs_main); - SchnorrProof schnorrProof; - - // after nLelantusFixesStartBlock block start to pass whole data to transcript - bool afterFixes = chainActive.Height() >= ::Params().GetConsensus().nLelantusFixesStartBlock; - SchnorrProver schnorrProver(params->get_g(), params->get_h0(), afterFixes); - Scalar v = coin.getVScalar(); - secp_primitives::GroupElement commit = coin.getPublicCoin().getValue(); - secp_primitives::GroupElement comm = commit + (params->get_h1() * v.negate()); - - std::unique_ptr challengeGenerator; - if (afterFixes) { - // start to use CHash256 which is more secure - challengeGenerator = std::make_unique>(1); - } else { - challengeGenerator = std::make_unique>(0); - } - - // commit (G^s*H1^v*H2^r), comm (G^s*H2^r), and H1^v are used in challenge generation if nLelantusFixesStartBlock is passed - schnorrProver.proof(coin.getSerialNumber(), coin.getRandomness(), comm, commit, (params->get_h1() * v), challengeGenerator, schnorrProof); - - serializedSchnorrProof << schnorrProof; -} - -bool VerifyMintSchnorrProof(const uint64_t& v, const secp_primitives::GroupElement& commit, const SchnorrProof& schnorrProof) -{ - auto params = lelantus::Params::get_default(); - - LOCK(cs_main); - // after nLelantusFixesStartBlock block start to pass whole data to transcript - bool afterFixes = chainActive.Height() >= ::Params().GetConsensus().nLelantusFixesStartBlock; - secp_primitives::GroupElement comm = commit + (params->get_h1() * Scalar(v).negate()); - SchnorrVerifier verifier(params->get_g(), params->get_h0(), afterFixes); - std::unique_ptr challengeGenerator; - if (afterFixes) { - // start to use CHash256 which is more secure - challengeGenerator = std::make_unique>(1); - } else { - challengeGenerator = std::make_unique>(0); - } - - // commit (G^s*H1^v*H2^r), comm (G^s*H2^r), and H1^v are used in challenge generation if nLelantusFixesStartBlock is passed - return verifier.verify(comm, commit, (params->get_h1() * Scalar(v)), schnorrProof, challengeGenerator); -} - -void ParseLelantusMintScript(const CScript& script, secp_primitives::GroupElement& pubcoin, SchnorrProof& schnorrProof, uint256& mintTag) -{ - if (script.size() < 1) { - throw std::invalid_argument("Script is not a valid Lelantus mint"); - } - - std::vector serialized(script.begin() + 1, script.end()); - if (serialized.size() < (pubcoin.memoryRequired() + schnorrProof.memoryRequired())) { - throw std::invalid_argument("Script is not a valid Lelantus mint"); - } - - bool skipTag = serialized.size() == (pubcoin.memoryRequired() + schnorrProof.memoryRequired()); - - pubcoin.deserialize(serialized.data()); - - CDataStream stream( - std::vector(serialized.begin() + pubcoin.memoryRequired(), serialized.end()), - SER_NETWORK, - PROTOCOL_VERSION - ); - - stream >> schnorrProof; - if(!skipTag) - stream >> mintTag; -} - -void ParseLelantusJMintScript(const CScript& script, secp_primitives::GroupElement& pubcoin, std::vector& encryptedValue) -{ - uint256 mintTag; - ParseLelantusJMintScript(script, pubcoin, encryptedValue, mintTag); -} - -void ParseLelantusJMintScript(const CScript& script, secp_primitives::GroupElement& pubcoin, std::vector& encryptedValue, uint256& mintTag) -{ - if (script.size() < 1) { - throw std::invalid_argument("Script is not a valid Lelantus jMint"); - } - - std::vector serialized(script.begin() + 1, script.end()); - // 16 is the size of encrypted mint value, 32 is size of mintTag - if (serialized.size() < (pubcoin.memoryRequired() + 16)) { - throw std::invalid_argument("Script is not a valid Lelantus jMint"); - } - - bool skipTag = serialized.size() == (pubcoin.memoryRequired() + 16); - - pubcoin.deserialize(serialized.data()); - encryptedValue.insert(encryptedValue.begin(), serialized.begin() + pubcoin.memoryRequired(), serialized.end()); - CDataStream stream( - std::vector(serialized.begin() + pubcoin.memoryRequired() + 16, serialized.end()), - SER_NETWORK, - PROTOCOL_VERSION - ); - if(!skipTag) - stream >> mintTag; -} - - -void ParseLelantusMintScript(const CScript& script, secp_primitives::GroupElement& pubcoin) -{ - uint256 mintTag; - if(script.IsLelantusMint()) { - SchnorrProof schnorrProof; - ParseLelantusMintScript(script, pubcoin, schnorrProof, mintTag); - } else if (script.IsLelantusJMint()) { - std::vector encryptedValue; - ParseLelantusJMintScript(script, pubcoin, encryptedValue, mintTag); - } -} - -std::unique_ptr ParseLelantusJoinSplit(const CTransaction &tx) -{ - if (tx.vin.size() != 1 || tx.vin[0].scriptSig.size() < 1) { - throw CBadTxIn(); - } - - CDataStream serialized(SER_NETWORK, PROTOCOL_VERSION); - - if (tx.vin[0].scriptSig[0] == OP_LELANTUSJOINSPLIT) { - serialized.write((const char *)tx.vin[0].scriptSig.data()+1, tx.vin[0].scriptSig.size()-1); - } - else if (tx.vin[0].scriptSig[0] == OP_LELANTUSJOINSPLITPAYLOAD && tx.nVersion >= 3 && tx.nType == TRANSACTION_LELANTUS) { - serialized.write((const char *)tx.vExtraPayload.data(), tx.vExtraPayload.size()); - } - else - throw CBadTxIn(); - - return std::make_unique(lelantus::Params::get_default(), serialized); -} - -bool CheckLelantusBlock(CValidationState &state, const CBlock& block) { - auto& consensus = ::Params().GetConsensus(); - - size_t blockSpendsAmount = 0; - CAmount blockSpendsValue(0); - - for (const auto& tx : block.vtx) { - auto txSpendsValue = GetSpendTransparentAmount(*tx); - size_t txSpendNumber = GetSpendInputs(*tx); - - if (txSpendNumber > consensus.nMaxLelantusInputPerTransaction) { - return state.DoS(100, false, REJECT_INVALID, - "bad-txns-lelantus-spend-invalid"); - } - - if (txSpendsValue > consensus.nMaxValueLelantusSpendPerTransaction) { - return state.DoS(100, false, REJECT_INVALID, - "bad-txns-lelantus-spend-invalid"); - } - - blockSpendsAmount += txSpendNumber; - blockSpendsValue += txSpendsValue; - } - - if (blockSpendsAmount > consensus.nMaxLelantusInputPerBlock) { - return state.DoS(100, false, REJECT_INVALID, - "bad-txns-lelantus-spend-invalid"); - } - - if (blockSpendsValue > consensus.nMaxValueLelantusSpendPerBlock) { - return state.DoS(100, false, REJECT_INVALID, - "bad-txns-lelantus-spend-invalid"); - } - - return true; -} - -bool CheckLelantusJMintTransaction( - const CTxOut &txout, - CValidationState &state, - uint256 hashTx, - bool fStatefulSigmaCheck, - std::vector& Cout, - CLelantusTxInfo* lelantusTxInfo) { - - LogPrintf("CheckLelantusJMintTransaction txHash = %s\n", txout.GetHash().ToString()); - - secp_primitives::GroupElement pubCoinValue; - uint256 mintTag; - std::vector encryptedValue; - try { - ParseLelantusJMintScript(txout.scriptPubKey, pubCoinValue, encryptedValue, mintTag); - } catch (std::invalid_argument&) { - return state.DoS(100, - false, - PUBCOIN_NOT_VALIDATE, - "CTransaction::CheckTransaction() : Mint parsing failure."); - } - - lelantus::PublicCoin pubCoin(pubCoinValue); - - //checking whether commitment is valid - if(!pubCoin.validate()) - return state.DoS(100, - false, - PUBCOIN_NOT_VALIDATE, - "CheckLelantusMintTransaction : PubCoin validation failed"); - - bool hasCoin = lelantusState.HasCoin(pubCoin); - - if (!hasCoin && lelantusTxInfo && !lelantusTxInfo->fInfoIsComplete) { - BOOST_FOREACH(const auto& mint, lelantusTxInfo->mints) { - if (mint.first == pubCoin) { - hasCoin = true; - break; - } - } - } - - if (hasCoin && fStatefulSigmaCheck) { - LogPrintf("CheckLelantusMintTransaction: double mint, tx=%s\n", - txout.GetHash().ToString()); - return state.DoS(100, - false, - PUBCOIN_NOT_VALIDATE, - "CheckLelantusMintTransaction: double mint"); - } - - uint64_t amount = 0; -#ifdef ENABLE_WALLET - if (!GetBoolArg("-disablewallet", false)) { - if (!pwalletMain->DecryptMintAmount(encryptedValue, pubCoinValue, amount)) - amount = 0; - } -#endif - if (lelantusTxInfo != NULL && !lelantusTxInfo->fInfoIsComplete) { - - // Update public coin list in the info - lelantusTxInfo->mints.push_back(std::make_pair(pubCoin, std::make_pair(amount, mintTag))); - lelantusTxInfo->encryptedJmintValues.insert(std::make_pair(pubCoin, encryptedValue)); - lelantusTxInfo->zcTransactions.insert(hashTx); - } - - Cout.push_back(pubCoin); - - return true; -} - -bool CheckLelantusJoinSplitTransaction( - const CTransaction &tx, - CValidationState &state, - uint256 hashTx, - bool isVerifyDB, - int nHeight, - int realHeight, - bool isCheckWallet, - bool fStatefulSigmaCheck, - CLelantusTxInfo* lelantusTxInfo) { - std::unordered_set txSerials; - - Consensus::Params const & params = ::Params().GetConsensus(); - - if(tx.vin.size() != 1 || !tx.vin[0].scriptSig.IsLelantusJoinSplit()) { - // mixing lelantus spend input with non-lelantus inputs is prohibited - return state.DoS(100, false, - REJECT_MALFORMED, - "CheckLelantusJoinSplitTransaction: can't mix lelantus spend input with other tx types or have more than one spend"); - } - - int height = nHeight == INT_MAX ? chainActive.Height()+1 : nHeight; - if (!isVerifyDB) { - if (height >= params.nLelantusV3PayloadStartBlock) { - // data should be moved to v3 payload - if (tx.nVersion < 3 || tx.nType != TRANSACTION_LELANTUS) - return state.DoS(100, false, NSEQUENCE_INCORRECT, - "CheckLelantusJoinSplitTransaction: lelantus data should reside in transaction payload"); - } - else { - if (tx.nVersion >= 3 && tx.nType != TRANSACTION_NORMAL) - return state.DoS(100, false, NSEQUENCE_INCORRECT, - "CheckLelantusJoinSplitTransaction: network hasn't yet switched over to lelantus payload data"); - } - } - FIRO_UNUSED const CTxIn &txin = tx.vin[0]; - std::unique_ptr joinsplit; - - try { - joinsplit = ParseLelantusJoinSplit(tx); - } - catch (CBadTxIn&) { - return state.DoS(100, - false, - REJECT_MALFORMED, - "CheckLelantusJoinSplitTransaction: invalid joinsplit transaction"); - } - catch (const std::exception &) { - return state.DoS(100, - false, - REJECT_MALFORMED, - "CheckLelantusJoinSplitTransaction: failed to deserialize joinsplit"); - } - - int jSplitVersion = joinsplit->getVersion(); - - if (jSplitVersion < LELANTUS_TX_VERSION_4 || - (!isVerifyDB && - ((height >= params.nLelantusFixesStartBlock && height < params.nLelantusV3PayloadStartBlock && jSplitVersion != LELANTUS_TX_VERSION_4_5 && jSplitVersion != SIGMA_TO_LELANTUS_JOINSPLIT_FIXED) || - (height >= params.nLelantusV3PayloadStartBlock && jSplitVersion != LELANTUS_TX_TPAYLOAD && jSplitVersion != SIGMA_TO_LELANTUS_TX_TPAYLOAD)))) { - return state.DoS(100, - false, - NSEQUENCE_INCORRECT, - "CTransaction::CheckLelantusJoinSplitTransaction() : Error: incorrect joinsplit transaction verion"); - } - - if (joinsplit->isSigmaToLelantus() && height >= params.nSigmaEndBlock) { - return state.DoS(100, - false, - NSEQUENCE_INCORRECT, - "CTransaction::CheckLelantusJoinSplitTransaction() : Sigma pool already closed."); - } - - uint256 txHashForMetadata; - - // Obtain the hash of the transaction sans the zerocoin part - CMutableTransaction txTemp = tx; - txTemp.vin[0].scriptSig.clear(); - txTemp.vExtraPayload.clear(); - - txHashForMetadata = txTemp.GetHash(); - - LogPrintf("CheckLelantusJoinSplitTransaction: tx version=%d, tx metadata hash=%s\n", - jSplitVersion, txHashForMetadata.ToString()); - - if (!fStatefulSigmaCheck) { - return true; - } - - bool passVerify = false; - std::map> anonymity_sets; - std::vector Cout; - uint64_t Vout = 0; - - for (const CTxOut &txout : tx.vout) { - if (!txout.scriptPubKey.empty() && txout.scriptPubKey.IsLelantusJMint()) { - try { - if (!CheckLelantusJMintTransaction(txout, state, hashTx, fStatefulSigmaCheck, Cout, lelantusTxInfo)) - return false; - } - catch (const std::exception &x) { - return state.Error(x.what()); - } - } else if(txout.scriptPubKey.IsLelantusMint()) { - return false; //putting regular mints at JoinSplit transactions is not allowed - } else { - Vout += txout.nValue; - } - } - - std::vector> anonymity_set_hashes; - const std::vector& ids = joinsplit->getCoinGroupIds(); - - if (joinsplit->isSigmaToLelantus()) { - passVerify = true; - goto skipSigmaToLelantus; - } - - for (auto& idAndHash : joinsplit->getIdAndBlockHashes()) { - auto& anonymity_set = anonymity_sets[idAndHash.first]; - { - CLelantusState::LelantusCoinGroupInfo coinGroup; - if (!lelantusState.GetCoinGroupInfo(idAndHash.first, coinGroup)) - return state.DoS(100, false, NO_MINT_ZEROCOIN, - "CheckLelantusJoinSplitTransaction: Error: no coins were minted with such parameters"); - - CBlockIndex *index = coinGroup.lastBlock; - - - // find index for block with hash of accumulatorBlockHash or set index to the coinGroup.firstBlock if not found - while (index != coinGroup.firstBlock && index->GetBlockHash() != idAndHash.second) - index = index->pprev; - - // take the hash from last block of anonymity set, it is used at challenge generation if nLelantusFixesStartBlock is passed - if (nHeight >= params.nLelantusFixesStartBlock) { - std::vector set_hash = GetAnonymitySetHash(index, idAndHash.first); - if (!set_hash.empty()) - anonymity_set_hashes.push_back(set_hash); - } - // Build a vector with all the public coins with given id before - // the block on which the spend occured. - // This list of public coins is required by function "Verify" of JoinSplit. - - while (true) { - int id = 0; - if (CountCoinInBlock(index, idAndHash.first)) { - id = idAndHash.first; - } else if (CountCoinInBlock(index, idAndHash.first - 1)) { - id = idAndHash.first - 1; - } - if (id) { - if(index->lelantusMintedPubCoins.count(id) > 0) { - BOOST_FOREACH( - const auto& pubCoinValue, - index->lelantusMintedPubCoins[id]) { - // skip mints from blacklist if nLelantusFixesStartBlock is passed - if (chainActive.Height() >= ::Params().GetConsensus().nLelantusFixesStartBlock) { - if (::Params().GetConsensus().lelantusBlacklist.count(pubCoinValue.first.getValue()) > 0) { - continue; - } - } - anonymity_set.push_back(pubCoinValue.first); - } - } - } - if (index == coinGroup.firstBlock) - break; - index = index->pprev; - } - } - anonymity_sets[idAndHash.first] = anonymity_set; - } - - { - for (const auto& id: ids) { - if (!anonymity_sets.count(id)) - return state.DoS(100, - error("CheckLelantusJoinSplitTransaction: No anonymity set found.")); - } - - BatchProofContainer* batchProofContainer = BatchProofContainer::get_instance(); - bool useBatching = batchProofContainer->fCollectProofs && !isVerifyDB && !isCheckWallet && lelantusTxInfo && !lelantusTxInfo->fInfoIsComplete; - - Scalar challenge; - // if we are collecting proofs, skip verification and collect proofs - passVerify = joinsplit->Verify(anonymity_sets, anonymity_set_hashes, Cout, Vout, txHashForMetadata, challenge, useBatching); - - // add proofs into container - if(useBatching) { - std::map idAndSizes; - - for(auto itr : anonymity_sets) - idAndSizes[itr.first] = itr.second.size(); - - batchProofContainer->add(joinsplit.get(), Cout); - } - } - -skipSigmaToLelantus: - - if (passVerify) { - const std::vector& serials = joinsplit->getCoinSerialNumbers(); - if (serials.size() != ids.size()) { - return state.DoS(100, - error("CheckLelantusJoinSplitTransaction: sized of serials and group ids don't match.")); - } - - // do not check for duplicates in case we've seen exact copy of this tx in this block before - if (!(lelantusTxInfo && lelantusTxInfo->zcTransactions.count(hashTx) > 0)) { - for (size_t i = 0; i < serials.size(); ++i) { - if (!CheckLelantusSpendSerial( - state, lelantusTxInfo, serials[i], nHeight, false)) { - LogPrintf("CheckLelantusJoinSplitTransaction: serial check failed, serial=%s\n", serials[i].GetHex()); - return false; - } - } - } - - // check duplicated serials in same transaction. - for (const auto &serial : serials) { - if (!txSerials.insert(serial).second) { - return state.DoS(100, - error("CheckLelantusJoinSplitTransaction: two or more spends with same serial in the same transaction")); - } - } - - if (!isVerifyDB && !isCheckWallet) { - // add spend information to the index - if (lelantusTxInfo && !lelantusTxInfo->fInfoIsComplete) { - for (size_t i = 0; i < serials.size(); i++) { - lelantusTxInfo->spentSerials.insert(std::make_pair(serials[i], ids[i])); - } - } - } - } - else { - LogPrintf("CheckLelantusJoinSplitTransaction: verification failed at block %d\n", nHeight); - return false; - } - - if(!isVerifyDB && !isCheckWallet) { - if (lelantusTxInfo && !lelantusTxInfo->fInfoIsComplete) { - lelantusTxInfo->zcTransactions.insert(hashTx); - } - } - - return true; -} - -bool CheckLelantusMintTransaction( - const CTxOut &txout, - CValidationState &state, - uint256 hashTx, - bool fStatefulSigmaCheck, - CLelantusTxInfo* lelantusTxInfo) { - secp_primitives::GroupElement pubCoinValue; - uint256 mintTag; - SchnorrProof schnorrProof; - - LogPrintf("CheckLelantusMintTransaction txHash = %s\n", txout.GetHash().ToString()); - LogPrintf("nValue = %d\n", txout.nValue); - if(txout.nValue > ::Params().GetConsensus().nMaxValueLelantusMint) - return state.DoS(100, - false, - REJECT_INVALID, - "CTransaction::CheckTransaction() : Mint is out of limit."); - - try { - ParseLelantusMintScript(txout.scriptPubKey, pubCoinValue, schnorrProof, mintTag); - } catch (std::invalid_argument&) { - return state.DoS(100, - false, - PUBCOIN_NOT_VALIDATE, - "CTransaction::CheckTransaction() : Mint parsing failure."); - } - - lelantus::PublicCoin pubCoin(pubCoinValue); - - //checking whether commitment is valid - if(!VerifyMintSchnorrProof(txout.nValue, pubCoinValue, schnorrProof) || !pubCoin.validate()) - return state.DoS(100, - false, - PUBCOIN_NOT_VALIDATE, - "CheckLelantusMintTransaction : PubCoin validation failed"); - - - bool hasCoin = lelantusState.HasCoin(pubCoin); - - if (!hasCoin && lelantusTxInfo && !lelantusTxInfo->fInfoIsComplete) { - BOOST_FOREACH(const auto& mint, lelantusTxInfo->mints) { - if (mint.first == pubCoin) { - hasCoin = true; - break; - } - } - } - - if (hasCoin && fStatefulSigmaCheck) { - LogPrintf("CheckLelantusMintTransaction: double mint, tx=%s\n", - txout.GetHash().ToString()); - return state.DoS(100, - false, - PUBCOIN_NOT_VALIDATE, - "CheckLelantusMintTransaction: double mint"); - } - - if (lelantusTxInfo != NULL && !lelantusTxInfo->fInfoIsComplete) { - // Update public coin list in the info - lelantusTxInfo->mints.push_back(std::make_pair(pubCoin, std::make_pair(txout.nValue, mintTag))); - lelantusTxInfo->zcTransactions.insert(hashTx); - } - - return true; -} - -bool CheckLelantusTransaction( - const CTransaction &tx, - CValidationState &state, - uint256 hashTx, - bool isVerifyDB, - int nHeight, - bool isCheckWallet, - bool fStatefulSigmaCheck, - CLelantusTxInfo* lelantusTxInfo) -{ - Consensus::Params const & consensus = ::Params().GetConsensus(); - - int realHeight = nHeight; - - if (realHeight == INT_MAX) { - LOCK(cs_main); - realHeight = chainActive.Height(); - } - - // accept Lelantus mint tx into 5 more blocks, to allow mempool cleared - if (!isVerifyDB && realHeight >= (::Params().GetConsensus().nSparkStartBlock + 5)) { - if (tx.IsLelantusMint() && !tx.IsLelantusJoinSplit()) - return state.DoS(100, false, - REJECT_INVALID, - "Lelantus already is not available, start using Spark."); - } - - // accept lelantus spends until nLelantusGracefulPeriod passed, to allow migration of funds from lelantus to spark - if (!isVerifyDB && realHeight >= (::Params().GetConsensus().nLelantusGracefulPeriod)) { - if (tx.IsLelantusJoinSplit()) - return state.DoS(100, false, - REJECT_INVALID, - "Lelantus is fully disabled."); - } - - bool const allowLelantus = (realHeight >= consensus.nLelantusStartBlock); - - if (!isVerifyDB && !isCheckWallet) { - if (allowLelantus && lelantusState.IsSurgeConditionDetected()) { - return state.DoS(100, false, - REJECT_INVALID, - "Lelantus surge protection is ON."); - } - } - - // Check Mint Lelantus Transaction - if (allowLelantus && !isVerifyDB) { - for (const CTxOut &txout : tx.vout) { - if (!txout.scriptPubKey.empty() && txout.scriptPubKey.IsLelantusMint()) { - try { - if (!CheckLelantusMintTransaction(txout, state, hashTx, fStatefulSigmaCheck, lelantusTxInfo)) - return false; - } - catch (const std::exception &x) { - return state.Error(x.what()); - } - } - } - } - - // Check Lelantus JoinSplit Transaction - if(tx.IsLelantusJoinSplit()) { - // First check number of inputs does not exceed transaction limit - if (GetSpendInputs(tx) > consensus.nMaxLelantusInputPerTransaction) { - return state.DoS(100, false, - REJECT_INVALID, - "bad-txns-spend-invalid"); - } - - if (GetSpendTransparentAmount(tx) > consensus.nMaxValueLelantusSpendPerTransaction) { - return state.DoS(100, false, - REJECT_INVALID, - "bad-txns-spend-invalid"); - } - - if (!isVerifyDB) { - try { - if (!CheckLelantusJoinSplitTransaction( - tx, state, hashTx, isVerifyDB, nHeight, realHeight, - isCheckWallet, fStatefulSigmaCheck, lelantusTxInfo)) { - return false; - } - } - catch (const std::exception &x) { - return state.Error(x.what()); - } - } - } - - return true; -} - -void RemoveLelantusJoinSplitReferencingBlock(CTxMemPool& pool, CBlockIndex* blockIndex) { - LOCK2(cs_main, pool.cs); - std::vector txn_to_remove; - for (CTxMemPool::txiter mi = pool.mapTx.begin(); mi != pool.mapTx.end(); ++mi) { - const CTransaction& tx = mi->GetTx(); - if (tx.IsLelantusJoinSplit()) { - // Run over all the inputs, check if their CoinGroup block hash is equal to - // block removed. If any one is equal, remove txn from mempool. - for (const CTxIn& txin : tx.vin) { - if (txin.IsLelantusJoinSplit()) { - std::unique_ptr joinsplit; - - try { - joinsplit = ParseLelantusJoinSplit(tx); - } - catch (const std::exception &) { - txn_to_remove.push_back(tx); - break; - } - - const std::vector>& coinGroupIdAndBlockHash = joinsplit->getIdAndBlockHashes(); - for(const auto& idAndHash : coinGroupIdAndBlockHash) { - if (idAndHash.second == blockIndex->GetBlockHash()) { - // Do not remove transaction immediately, that will invalidate iterator mi. - txn_to_remove.push_back(tx); - break; - } - } - } - } - } - } - for (const CTransaction& tx: txn_to_remove) { - // Remove txn from mempool. - pool.removeRecursive(tx); - LogPrintf("DisconnectTipLelantus: removed lelantus joinsplit which referenced a removed blockchain tip."); - } -} - -void DisconnectTipLelantus(CBlock& block, CBlockIndex *pindexDelete) { - lelantusState.RemoveBlock(pindexDelete); - - // Also remove from mempool lelantus joinsplits that reference given block hash. - RemoveLelantusJoinSplitReferencingBlock(mempool, pindexDelete); - RemoveLelantusJoinSplitReferencingBlock(txpools.getStemTxPool(), pindexDelete); -} - -std::vector GetLelantusJoinSplitSerialNumbers(const CTransaction &tx, const CTxIn &txin) { - if (!tx.IsLelantusJoinSplit()) - return std::vector(); - - try { - return ParseLelantusJoinSplit(tx)->getCoinSerialNumbers(); - } - catch (const std::exception &) { - return std::vector(); - } -} - -std::vector GetLelantusJoinSplitIds(const CTransaction &tx, const CTxIn &txin) { - if (!tx.IsLelantusJoinSplit()) - return std::vector(); - - try { - return ParseLelantusJoinSplit(tx)->getCoinGroupIds(); - } - catch (const std::exception &) { - return std::vector(); - } -} - -size_t GetSpendInputs(const CTransaction &tx, const CTxIn& in) { - return in.IsLelantusJoinSplit() ? - GetLelantusJoinSplitSerialNumbers(tx, in).size() : 0; -} - -size_t GetSpendInputs(const CTransaction &tx) { - size_t sum = 0; - for (const auto& vin : tx.vin) { - sum += GetSpendInputs(tx, vin); - } - return sum; -} - -CAmount GetSpendTransparentAmount(const CTransaction& tx) { - CAmount result = 0; - if(!tx.IsLelantusJoinSplit()) - return 0; - - for (const CTxOut &txout : tx.vout) - result += txout.nValue; - return result; -} - -/** - * Connect a new ZCblock to chainActive. pblock is either NULL or a pointer to a CBlock - * corresponding to pindexNew, to bypass loading it again from disk. - */ -bool ConnectBlockLelantus( - CValidationState &state, - const CChainParams &chainparams, - CBlockIndex *pindexNew, - const CBlock *pblock, - bool fJustCheck) { - // Add lelantus transaction information to index - if (pblock && pblock->lelantusTxInfo) { - if (!fJustCheck) { - pindexNew->lelantusMintedPubCoins.clear(); - pindexNew->lelantusSpentSerials.clear(); - pindexNew->anonymitySetHash.clear(); - } - - if (!CheckLelantusBlock(state, *pblock)) { - return false; - } - - BOOST_FOREACH(auto& serial, pblock->lelantusTxInfo->spentSerials) { - if (!CheckLelantusSpendSerial( - state, - pblock->lelantusTxInfo.get(), - serial.first, - pindexNew->nHeight, - true /* fConnectTip */ - )) { - return false; - } - } - - if (!fJustCheck) { - BOOST_FOREACH(auto& serial, pblock->lelantusTxInfo->spentSerials) { - pindexNew->lelantusSpentSerials.insert(serial); - lelantusState.AddSpend(serial.first, serial.second); - } - } - else { - return true; - } - - const auto& params = ::Params().GetConsensus(); - CHash256 hash; - std::vector data(GroupElement::serialize_size); - bool updateHash = false; - - // create first anonymity set hash with whole existing set, at HF block - if (pindexNew->nHeight == params.nLelantusFixesStartBlock) { - updateHash = true; - std::vector coins; - lelantusState.GetAnonymitySet(1, false, coins); - for (auto &coin : coins) { - coin.getValue().serialize(data.data()); - hash.Write(data.data(), data.size()); - } - } - - if (!pblock->lelantusTxInfo->mints.empty()) { - lelantusState.AddMintsToStateAndBlockIndex(pindexNew, pblock); - int latestCoinId = lelantusState.GetLatestCoinID(); - // add coins into hasher, for generating set hash - // if this is HF block just add mint from this block too, - // else hasher is supposed to be empty, so add previous hash first, then coins - if (pindexNew->nHeight >= params.nLelantusFixesStartBlock) { - updateHash = true; - - if (pindexNew->nHeight > params.nLelantusFixesStartBlock) { - // get previous hash of the set, if there is no such, don't write anything - std::vector prev_hash = GetAnonymitySetHash(pindexNew->pprev, latestCoinId, true); - if (!prev_hash.empty()) - hash.Write(prev_hash.data(), 32); - else { - if(latestCoinId > 1) { - prev_hash = GetAnonymitySetHash(pindexNew->pprev, latestCoinId - 1, true); - hash.Write(prev_hash.data(), 32); - } - } - } - - for (auto &coin : pindexNew->lelantusMintedPubCoins[latestCoinId]) { - coin.first.getValue().serialize(data.data()); - hash.Write(data.data(), data.size()); - } - } - } - - // generate hash if we need it - if (updateHash) { - unsigned char hash_result[CSHA256::OUTPUT_SIZE]; - hash.Finalize(hash_result); - auto &out_hash = pindexNew->anonymitySetHash[lelantusState.GetLatestCoinID()]; - out_hash.clear(); - out_hash.insert(out_hash.begin(), std::begin(hash_result), std::end(hash_result)); - } - } - else if (!fJustCheck) { - lelantusState.AddBlock(pindexNew); - } - return true; -} - -bool GetOutPointFromBlock(COutPoint& outPoint, const GroupElement &pubCoinValue, const CBlock &block) { - secp_primitives::GroupElement txPubCoinValue; - // cycle transaction hashes, looking for this pubcoin. - BOOST_FOREACH(CTransactionRef tx, block.vtx){ - uint32_t nIndex = 0; - for (const CTxOut &txout: tx->vout) { - if (txout.scriptPubKey.IsLelantusMint() || txout.scriptPubKey.IsLelantusJMint()) { - try { - ParseLelantusMintScript(txout.scriptPubKey, txPubCoinValue); - } - catch (const std::exception &) { - continue; - } - if(pubCoinValue==txPubCoinValue){ - outPoint = COutPoint(tx->GetHash(), nIndex); - return true; - } - } - nIndex++; - } - } - - return false; -} - -uint256 GetTxHashFromPubcoin(const lelantus::PublicCoin& pubCoin) { - COutPoint outPoint; - GetOutPoint(outPoint, pubCoin.getValue()); - return outPoint.hash; -} - -bool GetOutPoint(COutPoint& outPoint, const lelantus::PublicCoin &pubCoin) { - - lelantus::CLelantusState *lelantusState = lelantus::CLelantusState::GetState(); - auto mintedCoinHeightAndId = lelantusState->GetMintedCoinHeightAndId(pubCoin); - int mintHeight = mintedCoinHeightAndId.first; - int coinId = mintedCoinHeightAndId.second; - - if(mintHeight==-1 && coinId==-1) - return false; - - // get block containing mint - CBlockIndex *mintBlock = chainActive[mintHeight]; - CBlock block; - if(!ReadBlockFromDisk(block, mintBlock, ::Params().GetConsensus())) - LogPrintf("can't read block from disk.\n"); - - return GetOutPointFromBlock(outPoint, pubCoin.getValue(), block); -} - -bool GetOutPoint(COutPoint& outPoint, const GroupElement &pubCoinValue) { - lelantus::PublicCoin pubCoin(pubCoinValue); - - return GetOutPoint(outPoint, pubCoin); -} - -bool GetOutPoint(COutPoint& outPoint, const uint256 &pubCoinValueHash) { - GroupElement pubCoinValue; - lelantus::CLelantusState *lelantusState = lelantus::CLelantusState::GetState(); - if(!lelantusState->HasCoinHash(pubCoinValue, pubCoinValueHash)){ - return false; - } - - return GetOutPoint(outPoint, pubCoinValue); -} - -bool GetOutPointFromMintTag(COutPoint& outPoint, const uint256 &pubCoinTag) { - GroupElement pubCoinValue; - lelantus::CLelantusState *lelantusState = lelantus::CLelantusState::GetState(); - if(!lelantusState->HasCoinTag(pubCoinValue, pubCoinTag)){ - return false; - } - - return GetOutPoint(outPoint, pubCoinValue); -} - -bool BuildLelantusStateFromIndex(CChain *chain) { - for (CBlockIndex *blockIndex = chain->Genesis(); blockIndex; blockIndex=chain->Next(blockIndex)) - { - lelantusState.AddBlock(blockIndex); - } - // DEBUG - LogPrintf( - "Latest ID for Lelantus coin group %d\n", - lelantusState.GetLatestCoinID()); - return true; -} - -// CLelantusTxInfo -void CLelantusTxInfo::Complete() { - // We need to sort mints lexicographically by serialized value of pubCoin. That's the way old code - // works, we need to stick to it. - sort(mints.begin(), mints.end(), - [](decltype(mints)::const_reference m1, decltype(mints)::const_reference m2)->bool { - CDataStream ds1(SER_DISK, CLIENT_VERSION), ds2(SER_DISK, CLIENT_VERSION); - ds1 << m1; - ds2 << m2; - return ds1.str() < ds2.str(); - }); - - // Mark this info as complete - fInfoIsComplete = true; -} - -/******************************************************************************/ -// CLelantusState::Containers -/******************************************************************************/ - -CLelantusState::Containers::Containers(std::atomic & surgeCondition) -: surgeCondition(surgeCondition) -{} - -void CLelantusState::Containers::AddMint(lelantus::PublicCoin const & pubCoin, CMintedCoinInfo const & coinInfo, const uint256& tag) { - mintedPubCoins.insert(std::make_pair(pubCoin, coinInfo)); - tagToPublicCoin.insert(std::make_pair(tag, pubCoin)); - mintMetaInfo[coinInfo.coinGroupId] += 1; - CheckSurgeCondition(); -} - -void CLelantusState::Containers::RemoveMint(lelantus::PublicCoin const & pubCoin) { - mint_info_container::const_iterator iter = mintedPubCoins.find(pubCoin); - if (iter != mintedPubCoins.end()) { - for(auto hashPair = tagToPublicCoin.begin(); hashPair != tagToPublicCoin.end(); hashPair++) - if(hashPair->second == pubCoin) { - tagToPublicCoin.erase(hashPair); - break; - } - - mintMetaInfo[iter->second.coinGroupId] -= 1; - mintedPubCoins.erase(iter); - CheckSurgeCondition(); - } -} - -void CLelantusState::Containers::AddSpend(Scalar const & serial, int coinGroupId) { - if (mintMetaInfo.count(coinGroupId) > 0) { - usedCoinSerials[serial] = coinGroupId; - spendMetaInfo[coinGroupId] += 1; - CheckSurgeCondition(); - } -} - -void CLelantusState::Containers::RemoveSpend(Scalar const & serial) { - auto iter = usedCoinSerials.find(serial); - if (iter != usedCoinSerials.end()) { - spendMetaInfo[iter->second] -= 1; - usedCoinSerials.erase(iter); - CheckSurgeCondition(); - } -} - -void CLelantusState::Containers::AddExtendedMints(int group, size_t mints) { - extendedMintMetaInfo[group] = mints; - CheckSurgeCondition(); -} - -void CLelantusState::Containers::RemoveExtendedMints(int group) { - extendedMintMetaInfo.erase(group); - CheckSurgeCondition(); -} - -mint_info_container const & CLelantusState::Containers::GetMints() const { - return mintedPubCoins; -} - -std::unordered_map& CLelantusState::Containers::GetTagToPublicCoin() { - return tagToPublicCoin; -} - -std::unordered_map const & CLelantusState::Containers::GetSpends() const { - return usedCoinSerials; -} - -bool CLelantusState::Containers::IsSurgeCondition() const { - return surgeCondition; -} - -void CLelantusState::Containers::Reset() { - mintedPubCoins.clear(); - usedCoinSerials.clear(); - mintMetaInfo.clear(); - spendMetaInfo.clear(); - tagToPublicCoin.clear(); - surgeCondition = false; -} - -void CLelantusState::Containers::CheckSurgeCondition() { - bool result = false; - - // find a range of groups that sum of serials larger than sum of mints - size_t serials = 0; - size_t mints = 0; - int start = 0; - - for (auto it = mintMetaInfo.begin(); it != mintMetaInfo.end(); it++) { - auto id = it->first; - - // include serials and mints to accumulators - serials += spendMetaInfo.count(id) ? spendMetaInfo[id] : 0; - mints += it->second; - - // serials exceed mints then trigger - if (serials > mints) { - result = true; - - std::ostringstream ostr; - ostr << "Turning Lelantus surge protection ON: in group range: " << start << " - " << id << '\n'; - error(ostr.str().c_str()); - - break; - } - - auto extendedMints = extendedMintMetaInfo.count(id + 1) ? - extendedMintMetaInfo[id + 1] : 0; - - if (serials <= mints - extendedMints) { - start = id + 1; - serials = 0; - mints = extendedMints; - } - } - - surgeCondition = result; -} - -/******************************************************************************/ -// CLelantusState -/******************************************************************************/ - -CLelantusState::CLelantusState( - size_t maxCoinInGroup, - size_t startGroupSize) - : - maxCoinInGroup(maxCoinInGroup), - startGroupSize(startGroupSize), - containers(surgeCondition) -{ - Reset(); -} - -void CLelantusState::AddMintsToStateAndBlockIndex( - CBlockIndex *index, - const CBlock* pblock) { - - std::vector> blockMints; - std::unordered_map lelantusMintData; - - for (const auto& mint : pblock->lelantusTxInfo->mints) { - if (GetBoolArg("-mobile", false)) { - lelantus::MintValueData mintdata; - mintdata.amount = mint.second.first; - if (pblock->lelantusTxInfo->encryptedJmintValues.count(mint.first) > 0) { - mintdata.isJMint = true; - mintdata.encryptedValue = pblock->lelantusTxInfo->encryptedJmintValues[mint.first]; - } - - COutPoint outPoint; - GetOutPointFromBlock(outPoint, mint.first.getValue(), *pblock); - mintdata.txHash = outPoint.hash; - lelantusMintData[mint.first.getValue()] = mintdata; - } - blockMints.push_back(std::make_pair(mint.first, mint.second.second)); - } - - latestCoinId = std::max(1, latestCoinId); - - auto &coinGroup = coinGroups[latestCoinId]; - - if (coinGroup.nCoins + blockMints.size() <= maxCoinInGroup) { - if (coinGroup.nCoins == 0) { - // first group of coins - assert(coinGroup.firstBlock == nullptr); - assert(coinGroup.lastBlock == nullptr); - - coinGroup.firstBlock = coinGroup.lastBlock = index; - } else { - assert(coinGroup.firstBlock != nullptr); - assert(coinGroup.lastBlock != nullptr); - assert(coinGroup.lastBlock->nHeight <= index->nHeight); - - coinGroup.lastBlock = index; - } - coinGroup.nCoins += blockMints.size(); - } else { - auto& newCoinGroup = coinGroups[++latestCoinId]; - - CBlockIndex *first; - auto coins = CountLastNCoins(latestCoinId - 1, startGroupSize, first); - newCoinGroup.firstBlock = first ? first : index; - newCoinGroup.lastBlock = index; - newCoinGroup.nCoins = coins + blockMints.size(); - - containers.AddExtendedMints(latestCoinId, coins); - } - - for (const auto& mint : blockMints) { - containers.AddMint(mint.first, CMintedCoinInfo::make(latestCoinId, index->nHeight), mint.second); - - LogPrintf("AddMintsToStateAndBlockIndex: Lelantus mint added id=%d\n", latestCoinId); - index->lelantusMintedPubCoins[latestCoinId].push_back(mint); - - if (GetBoolArg("-mobile", false)) { - index->lelantusMintData[mint.first.getValue()] = lelantusMintData[mint.first.getValue()]; - } - } -} - -void CLelantusState::AddSpend(const Scalar &serial, int coinGroupId) { - containers.AddSpend(serial, coinGroupId); -} - -void CLelantusState::AddBlock(CBlockIndex *index) { - for (auto const &pubCoins : index->lelantusMintedPubCoins) { - - if (pubCoins.second.empty()) - continue; - - auto &coinGroup = coinGroups[pubCoins.first]; - - if (coinGroup.firstBlock == nullptr) { - coinGroup.firstBlock = index; - - if (pubCoins.first > 1) { - CBlockIndex *first; - coinGroup.nCoins = CountLastNCoins(pubCoins.first - 1, startGroupSize, first); - coinGroup.firstBlock = first ? first : index; - - containers.AddExtendedMints(pubCoins.first, coinGroup.nCoins); - } - } - coinGroup.lastBlock = index; - coinGroup.nCoins += pubCoins.second.size(); - - latestCoinId = pubCoins.first; - for (auto const &coin : pubCoins.second) { - containers.AddMint(coin.first, CMintedCoinInfo::make(pubCoins.first, index->nHeight), coin.second); - } - } - - for (auto const &serial : index->lelantusSpentSerials) { - AddSpend(serial.first, serial.second); - } -} - -void CLelantusState::RemoveBlock(CBlockIndex *index) { - // roll back coin group updates - for (auto &coins : index->lelantusMintedPubCoins) - { - if (coinGroups.count(coins.first) == 0) - continue; - - LelantusCoinGroupInfo& coinGroup = coinGroups[coins.first]; - auto nMintsToForget = coins.second.size(); - - if (nMintsToForget == 0) - continue; - - assert(cmp::greater_equal(coinGroup.nCoins, nMintsToForget)); - auto isExtended = coins.first > 1; - coinGroup.nCoins -= nMintsToForget; - - // if `index` is edged block we need to erase group - auto isEdgedBlock = false; - if (isExtended) { - auto prevBlockContainMints = index; - size_t prevGroupCount = 0; - - // find block that contain some Lelantus mints - do { - prevBlockContainMints = prevBlockContainMints->pprev; - } while (prevBlockContainMints - && CountCoinInBlock(prevBlockContainMints, coins.first) == 0 - && (prevGroupCount = CountCoinInBlock(prevBlockContainMints, coins.first - 1)) == 0); - - isEdgedBlock = prevGroupCount > 0 && (coinGroup.nCoins - prevGroupCount) < startGroupSize; - } - - if ((!isExtended && coinGroup.nCoins == 0) || (isExtended && isEdgedBlock)) { - // all the coins of this group have been erased, remove the group altogether - coinGroups.erase(coins.first); - // decrease pubcoin id - latestCoinId--; - // erase from containers - containers.RemoveExtendedMints(coins.first); - } else { - // roll back lastBlock to previous position - assert(coinGroup.lastBlock == index); - - do { - assert(coinGroup.lastBlock != coinGroup.firstBlock); - coinGroup.lastBlock = coinGroup.lastBlock->pprev; - } while (coinGroup.lastBlock->lelantusMintedPubCoins.count(coins.first) == 0); - } - } - - // roll back mints - for (auto const &pubCoins : index->lelantusMintedPubCoins) { - for (auto const &coin : pubCoins.second) { - auto coins = containers.GetMints().equal_range(coin.first); - auto coinIt = find_if( - coins.first, coins.second, - [&pubCoins](const mint_info_container::value_type &v) { - return v.second.coinGroupId == pubCoins.first; - }); - assert(coinIt != coins.second); - containers.RemoveMint(coinIt->first); - } - } - - // roll back spends - for (auto const &serial : index->lelantusSpentSerials) { - containers.RemoveSpend(serial.first); - } -} - -bool CLelantusState::GetCoinGroupInfo( - int group_id, - LelantusCoinGroupInfo& result) { - if (coinGroups.count(group_id) == 0) - return false; - - result = coinGroups[group_id]; - return true; -} - -bool CLelantusState::IsUsedCoinSerial(const Scalar &coinSerial) { - return containers.GetSpends().count(coinSerial) != 0; -} - -bool CLelantusState::IsUsedCoinSerialHash(Scalar &coinSerial, const uint256 &coinSerialHash) { - for ( auto it = GetSpends().begin(); it != GetSpends().end(); ++it ){ - if(primitives::GetSerialHash(it->first)==coinSerialHash){ - coinSerial = it->first; - return true; - } - } - return false; -} - -bool CLelantusState::HasCoin(const lelantus::PublicCoin& pubCoin) { - return containers.GetMints().find(pubCoin) != containers.GetMints().end(); -} - -bool CLelantusState::HasCoinHash(GroupElement &pubCoinValue, const uint256 &pubCoinValueHash) { - for ( auto it = GetMints().begin(); it != GetMints().end(); ++it ){ - const lelantus::PublicCoin & pubCoin = (*it).first; - if(pubCoin.getValueHash()==pubCoinValueHash){ - pubCoinValue = pubCoin.getValue(); - return true; - } - } - return false; -} - -bool CLelantusState::HasCoinTag(GroupElement& pubCoinValue, const uint256& pubCoinTag) { - auto const& mints = containers.GetTagToPublicCoin(); - if(mints.count(pubCoinTag) > 0) { - pubCoinValue = mints.at(pubCoinTag).getValue(); - return true; - } - return false; -} - -int CLelantusState::GetCoinSetForSpend( - CChain *chain, - int maxHeight, - int coinGroupID, - uint256& blockHash_out, - std::vector& coins_out, - std::vector& setHash_out, - std::string start_block_hash) { - - coins_out.clear(); - - if (coinGroups.count(coinGroupID) == 0) { - return 0; - } - - LelantusCoinGroupInfo &coinGroup = coinGroups[coinGroupID]; - - int numberOfCoins = 0; - for (CBlockIndex *block = coinGroup.lastBlock;; block = block->pprev) { - - // ignore block heigher than max height - if (block->nHeight > maxHeight) { - continue; - } - - if (block->GetBlockHash().GetHex() == start_block_hash) { - break ; - } - - // check coins in group coinGroupID - 1 in the case that using coins from prev group. - int id = 0; - if (CountCoinInBlock(block, coinGroupID)) { - id = coinGroupID; - } else if (CountCoinInBlock(block, coinGroupID - 1)) { - id = coinGroupID - 1; - } - - if (id) { - if (numberOfCoins == 0) { - // latest block satisfying given conditions - // remember block hash and set hash - blockHash_out = block->GetBlockHash(); - setHash_out = GetAnonymitySetHash(block, id); - } - numberOfCoins += block->lelantusMintedPubCoins[id].size(); - if (block->lelantusMintedPubCoins.count(id) > 0) { - for (const auto &coin : block->lelantusMintedPubCoins[id]) { - LOCK(cs_main); - // skip mints from blacklist if nLelantusFixesStartBlock is passed - if (chainActive.Height() >= ::Params().GetConsensus().nLelantusFixesStartBlock) { - if (::Params().GetConsensus().lelantusBlacklist.count(coin.first.getValue()) > 0) { - continue; - } - } - coins_out.push_back(coin.first); - } - } - } - - if (block == coinGroup.firstBlock) { - break ; - } - } - - return numberOfCoins; -} - -void CLelantusState::GetCoinsForRecovery( - CChain *chain, - int maxHeight, - int coinGroupID, - std::string start_block_hash, - uint256& blockHash_out, - std::vector>>& coins, - std::vector& setHash_out) { - - coins.clear(); - if (coinGroups.count(coinGroupID) == 0) { - return; - } - - LelantusCoinGroupInfo &coinGroup = coinGroups[coinGroupID]; - - int numberOfCoins = 0; - for (CBlockIndex *block = coinGroup.lastBlock;; block = block->pprev) { - // ignore block heigher than max height - if (block->nHeight > maxHeight) { - continue; - } - - if (block->GetBlockHash().GetHex() == start_block_hash) { - break; - } - - // check coins in group coinGroupID - 1 in the case that using coins from prev group. - int id = 0; - if (CountCoinInBlock(block, coinGroupID)) { - id = coinGroupID; - } else if (CountCoinInBlock(block, coinGroupID - 1)) { - id = coinGroupID - 1; - } - - if (id) { - if (numberOfCoins == 0) { - // latest block satisfying given conditions - // remember block hash and set hash - blockHash_out = block->GetBlockHash(); - setHash_out = GetAnonymitySetHash(block, id); - } - - numberOfCoins += block->lelantusMintedPubCoins[id].size(); - if (block->lelantusMintedPubCoins.count(id) > 0) { - for (const auto &coin : block->lelantusMintedPubCoins[id]) { - LOCK(cs_main); - // skip mints from blacklist if nLelantusFixesStartBlock is passed - if (chainActive.Height() >= ::Params().GetConsensus().nLelantusFixesStartBlock) { - if (::Params().GetConsensus().lelantusBlacklist.count(coin.first.getValue()) > 0) { - continue; - } - } - - lelantus::MintValueData lelantusMintData; - if (block->lelantusMintData.count(coin.first.getValue())) - lelantusMintData = block->lelantusMintData[coin.first.getValue()]; - coins.push_back(std::make_pair(coin.first, std::make_pair(lelantusMintData, coin.second))); - - } - } - } - - if (block == coinGroup.firstBlock) { - break ; - } - } - -} - -void CLelantusState::GetAnonymitySet( - int coinGroupID, - bool fStartLelantusBlacklist, - std::vector& coins_out) { - - coins_out.clear(); - - if (coinGroups.count(coinGroupID) == 0) { - return; - } - - LelantusCoinGroupInfo &coinGroup = coinGroups[coinGroupID]; - const auto ¶ms = ::Params().GetConsensus(); - LOCK(cs_main); - int maxHeight = fStartLelantusBlacklist ? (chainActive.Height() - (ZC_MINT_CONFIRMATIONS - 1)) : (params.nLelantusFixesStartBlock - 1); - - for (CBlockIndex *block = coinGroup.lastBlock;; block = block->pprev) { - - // ignore block heigher than max height - if (block->nHeight > maxHeight) { - continue; - } - - // check coins in group coinGroupID - 1 in the case that using coins from prev group. - int id = 0; - if (CountCoinInBlock(block, coinGroupID)) { - id = coinGroupID; - } else if (CountCoinInBlock(block, coinGroupID - 1)) { - id = coinGroupID - 1; - } - - if (id) { - if(block->lelantusMintedPubCoins.count(id) > 0) { - for (const auto &coin : block->lelantusMintedPubCoins[id]) { - if (fStartLelantusBlacklist && - chainActive.Height() >= ::Params().GetConsensus().nLelantusFixesStartBlock) { - std::vector vch = coin.first.getValue().getvch(); - if (::Params().GetConsensus().lelantusBlacklist.count(coin.first.getValue()) > 0) { - continue; - } - } - coins_out.push_back(coin.first); - } - } - } - - if (block == coinGroup.firstBlock) { - break ; - } - } -} - -std::pair CLelantusState::GetMintedCoinHeightAndId( - const lelantus::PublicCoin& pubCoin) { - auto coinIt = containers.GetMints().find(pubCoin); - - if (coinIt != containers.GetMints().end()) { - return std::make_pair(coinIt->second.nHeight, coinIt->second.coinGroupId); - } - return std::make_pair(-1, -1); -} - -bool CLelantusState::AddSpendToMempool(const std::vector &coinSerials, uint256 txHash) { - LOCK(mempool.cs); - BOOST_FOREACH(const Scalar& coinSerial, coinSerials){ - if (IsUsedCoinSerial(coinSerial) || mempool.lelantusState.HasCoinSerial(coinSerial)) - return false; - - mempool.lelantusState.AddSpendToMempool(coinSerial, txHash); - } - - return true; -} - -void CLelantusState::RemoveSpendFromMempool(const std::vector &coinSerials) { - LOCK(mempool.cs); - BOOST_FOREACH(const Scalar& coinSerial, coinSerials) { - mempool.lelantusState.RemoveSpendFromMempool(coinSerial); - } -} - -void CLelantusState::AddMintsToMempool(const std::vector& pubCoins) { - LOCK(mempool.cs); - BOOST_FOREACH(const GroupElement& pubCoin, pubCoins) { - mempool.lelantusState.AddMintToMempool(pubCoin); - } -} - -void CLelantusState::RemoveMintFromMempool(const GroupElement& pubCoin) { - LOCK(mempool.cs); - mempool.lelantusState.RemoveMintFromMempool(pubCoin); -} - -uint256 CLelantusState::GetMempoolConflictingTxHash(const Scalar& coinSerial) { - LOCK(mempool.cs); - return mempool.lelantusState.GetMempoolConflictingTxHash(coinSerial); -} - -bool CLelantusState::CanAddSpendToMempool(const Scalar& coinSerial) { - LOCK(mempool.cs); - return !IsUsedCoinSerial(coinSerial) && !mempool.lelantusState.HasCoinSerial(coinSerial); -} - -bool CLelantusState::CanAddMintToMempool(const GroupElement& pubCoin){ - LOCK(mempool.cs); - return !HasCoin(pubCoin) && !mempool.lelantusState.HasMint(pubCoin); -} - -void CLelantusState::Reset() { - coinGroups.clear(); - latestCoinId = 0; - containers.Reset(); -} - -CLelantusState* CLelantusState::GetState() { - return &lelantusState; -} - -int CLelantusState::GetLatestCoinID() const { - return latestCoinId; -} - -bool CLelantusState::IsSurgeConditionDetected() const { - return surgeCondition; -} - -mint_info_container const & CLelantusState::GetMints() const { - return containers.GetMints(); -} - -std::unordered_map const & CLelantusState::GetSpends() const { - return containers.GetSpends(); -} - -std::unordered_map const & CLelantusState::GetCoinGroups() const { - return coinGroups; -} - -std::unordered_map const & CLelantusState::GetMempoolCoinSerials() const { - LOCK(mempool.cs); - return mempool.lelantusState.GetMempoolCoinSerials(); -} - -// private -size_t CLelantusState::CountLastNCoins(int groupId, size_t required, CBlockIndex* &first) { - first = nullptr; - size_t coins = 0; - - if (coinGroups.count(groupId)) { - auto &group = coinGroups[groupId]; - - for (auto block = group.lastBlock - ; coins < required && block - ; block = block->pprev) { - - size_t inBlock; - if (block->lelantusMintedPubCoins.count(groupId) - && (inBlock = block->lelantusMintedPubCoins[groupId].size())) { - - coins += inBlock; - first = block; - } - } - } - - return coins; -} - -// CLelantusMempoolState - -bool CLelantusMempoolState::HasCoinSerial(const Scalar& coinSerial) { - return mempoolCoinSerials.count(coinSerial) > 0; -} - -bool CLelantusMempoolState::HasMint(const GroupElement& pubCoin) { - return mempoolMints.count(pubCoin) > 0; -} - -bool CLelantusMempoolState::AddSpendToMempool(const Scalar &coinSerial, uint256 txHash) { - return mempoolCoinSerials.insert({coinSerial, txHash}).second; -} - -void CLelantusMempoolState::AddMintToMempool(const GroupElement& pubCoin) { - mempoolMints.insert(pubCoin); -} - -void CLelantusMempoolState::RemoveMintFromMempool(const GroupElement& pubCoin) { - mempoolMints.erase(pubCoin); -} - -uint256 CLelantusMempoolState::GetMempoolConflictingTxHash(const Scalar& coinSerial) { - if (mempoolCoinSerials.count(coinSerial) == 0) - return uint256(); - - return mempoolCoinSerials[coinSerial]; -} - -void CLelantusMempoolState::RemoveSpendFromMempool(const Scalar &coinSerial) { - mempoolCoinSerials.erase(coinSerial); -} - -void CLelantusMempoolState::Reset() { - mempoolCoinSerials.clear(); - mempoolMints.clear(); -} - - -} // end of namespace lelantus. diff --git a/src/lelantus.h b/src/lelantus.h deleted file mode 100644 index 58e79f97bf..0000000000 --- a/src/lelantus.h +++ /dev/null @@ -1,311 +0,0 @@ -#ifndef _MAIN_LELANTUS_H__ -#define _MAIN_LELANTUS_H__ - -#include "amount.h" -#include "chain.h" -#include "liblelantus/coin.h" -#include "liblelantus/joinsplit.h" -#include "consensus/validation.h" -#include -#include -#include "liblelantus/params.h" -#include -#include -#include -#include "coin_containers.h" - -namespace lelantus_mintspend { struct lelantus_mintspend_test; } - -namespace lelantus { - -// Lelantus transaction info, added to the CBlock to ensure zerocoin mint/spend transactions got their info stored into index -class CLelantusTxInfo { -public: - // all the zerocoin transactions encountered so far - std::set zcTransactions; - - // Vector of > for all the mints. - std::vector>> mints; - - std::unordered_map, lelantus::CPublicCoinHash> encryptedJmintValues; - - // serial for every spend (map from serial to coin group id) - std::unordered_map spentSerials; - - // information about transactions in the block is complete - bool fInfoIsComplete; - - CLelantusTxInfo(): fInfoIsComplete(false) {} - - // finalize everything - void Complete(); -}; - -bool IsLelantusAllowed(); -bool IsLelantusAllowed(int height); -bool IsLelantusGraceFulPeriod(); - -bool IsAvailableToMint(const CAmount& amount); - -void GenerateMintSchnorrProof(const lelantus::PrivateCoin& coin, CDataStream& serializedSchnorrProof); -bool VerifyMintSchnorrProof(const uint64_t& v, const secp_primitives::GroupElement& commit, const SchnorrProof& schnorrProof); -void ParseLelantusMintScript(const CScript& script, secp_primitives::GroupElement& pubcoin, SchnorrProof& schnorrProof, uint256& mintTag); -void ParseLelantusJMintScript(const CScript& script, secp_primitives::GroupElement& pubcoin, std::vector& encryptedValue); -void ParseLelantusJMintScript(const CScript& script, secp_primitives::GroupElement& pubcoin, std::vector& encryptedValue, uint256& mintTag); -void ParseLelantusMintScript(const CScript& script, secp_primitives::GroupElement& pubcoin); -std::unique_ptr ParseLelantusJoinSplit(const CTransaction& tx); - -size_t GetSpendInputs(const CTransaction &tx, const CTxIn& in); -size_t GetSpendInputs(const CTransaction &tx); -CAmount GetSpendTransparentAmount(const CTransaction& tx); - -bool CheckLelantusBlock(CValidationState &state, const CBlock& block); - -bool CheckLelantusTransaction( - const CTransaction &tx, - CValidationState &state, - uint256 hashTx, - bool isVerifyDB, - int nHeight, - bool isCheckWallet, - bool fStatefulSigmaCheck, - CLelantusTxInfo* lelantusTxInfo); - -void DisconnectTipLelantus(CBlock &block, CBlockIndex *pindexDelete); - -bool ConnectBlockLelantus( - CValidationState& state, - const CChainParams& chainparams, - CBlockIndex* pindexNew, - const CBlock *pblock, - bool fJustCheck=false); - -uint256 GetTxHashFromPubcoin(const lelantus::PublicCoin& pubCoin); - -/* - * Get COutPoint(txHash, index) from the chain using pubcoin value alone. - */ -bool GetOutPointFromBlock(COutPoint& outPoint, const GroupElement &pubCoinValue, const CBlock &block); -bool GetOutPoint(COutPoint& outPoint, const lelantus::PublicCoin &pubCoin); -bool GetOutPoint(COutPoint& outPoint, const GroupElement &pubCoinValue); -// This one gets outpoint from hash of full Lelantus commitment -bool GetOutPoint(COutPoint& outPoint, const uint256 &pubCoinValueHash); -// This one gets outpoint from hash of reduced Lelantus commitment -bool GetOutPointFromMintTag(COutPoint& outPoint, const uint256 &pubCoinTag); - - -bool BuildLelantusStateFromIndex(CChain *chain); - -std::vector GetLelantusJoinSplitSerialNumbers(const CTransaction &tx, const CTxIn &txin); -std::vector GetLelantusJoinSplitIds(const CTransaction &tx, const CTxIn &txin); - -/* - * Util functions - */ -size_t CountCoinInBlock(CBlockIndex const *index, int id); - -class CLelantusMempoolState { -private: - // serials of spends currently in the mempool mapped to tx hashes - std::unordered_map mempoolCoinSerials; - // mints in the mempool - std::unordered_set mempoolMints; - -public: - // Check if there is a conflicting tx in the blockchain or mempool - bool HasCoinSerial(const Scalar& coinSerial); - - bool HasMint(const GroupElement& pubCoin); - - // Add spend into the mempool. - bool AddSpendToMempool(const Scalar &coinSerial, uint256 txHash); - - void AddMintToMempool(const GroupElement& pubCoins); - void RemoveMintFromMempool(const GroupElement& pubCoin); - - // Get conflicting tx hash by coin serial number - uint256 GetMempoolConflictingTxHash(const Scalar& coinSerial); - - // Remove spend from the mempool (usually as the result of adding tx to the block) - void RemoveSpendFromMempool(const Scalar& coinSerial); - - std::unordered_map const & GetMempoolCoinSerials() const { return mempoolCoinSerials; } - - void Reset(); -}; - -/* - * State of minted/spent coins as extracted from the index - */ -class CLelantusState { -friend bool BuildLelantusStateFromIndex(CChain *, std::set &); -public: - // First and last block where mint with given id was seen - struct LelantusCoinGroupInfo { - LelantusCoinGroupInfo() : firstBlock(nullptr), lastBlock(nullptr), nCoins(0) {} - - // first and last blocks having coins with given id minted - CBlockIndex *firstBlock; - CBlockIndex *lastBlock; - // total number of minted coins with such parameters - int nCoins; - }; - -public: - CLelantusState( - size_t maxCoinInGroup = ZC_LELANTUS_MAX_MINT_NUM, - size_t startGroupSize = ZC_LELANTUS_SET_START_SIZE); - - // Add mints in block, automatically assigning id to it - void AddMintsToStateAndBlockIndex(CBlockIndex *index, const CBlock* pblock); - - // Add serial to the list of used ones - void AddSpend(const Scalar &serial, int coinGroupId); - - // Add everything from the block to the state - void AddBlock(CBlockIndex *index); - - // Disconnect block from the chain rolling back mints and spends - void RemoveBlock(CBlockIndex *index); - - // Query coin group with given id - bool GetCoinGroupInfo(int group_id, LelantusCoinGroupInfo &result); - - // Query if the coin serial was previously used - bool IsUsedCoinSerial(const Scalar& coinSerial); - // Query if the hash of a coin serial was previously used. If so, store preimage in coinSerial param - bool IsUsedCoinSerialHash(Scalar &coinSerial, const uint256 &coinSerialHash); - - // Query if there is a coin with given pubCoin value - bool HasCoin(const lelantus::PublicCoin& pubCoin); - // Query if there is a coin with given hash of a pubCoin value. If so, store preimage in pubCoin param - bool HasCoinHash(GroupElement &pubCoinValue, const uint256 &pubCoinValueHash); - // Query if there is a coin with given tag - bool HasCoinTag(GroupElement &pubCoinValue, const uint256 &pubCoinTag); - - - // Given id returns latest anonymity set and corresponding block hash - // Do not take into account coins with height more than maxHeight - // Returns number of coins satisfying conditions - int GetCoinSetForSpend( - CChain *chain, - int maxHeight, - int id, - uint256& blockHash_out, - std::vector& coins_out, - std::vector& setHash_out, - std::string start_block_hash = ""); - - void GetAnonymitySet( - int coinGroupID, - bool fStartLelantusBlacklist, - std::vector& coins_out); - - void GetCoinsForRecovery( - CChain *chain, - int maxHeight, - int coinGroupID, - std::string start_block_hash, - uint256& blockHash_out, - std::vector>>& coins, - std::vector& setHash_out); - - // Return height of mint transaction and id of minted coin - std::pair GetMintedCoinHeightAndId(const lelantus::PublicCoin& pubCoin); - - // Reset to initial values - void Reset(); - - // Check if there is a conflicting tx in the blockchain or mempool - bool CanAddSpendToMempool(const Scalar& coinSerial); - - bool CanAddMintToMempool(const GroupElement& pubCoin); - - // Add spend into the mempool. - // Check if there is a coin with such serial in either blockchain or mempool - bool AddSpendToMempool(const std::vector& coinSerials, uint256 txHash); - - void AddMintsToMempool(const std::vector& pubCoins); - void RemoveMintFromMempool(const GroupElement& pubCoin); - - // Get conflicting tx hash by coin serial number - uint256 GetMempoolConflictingTxHash(const Scalar& coinSerial); - - // Remove spend from the mempool (usually as the result of adding tx to the block) - void RemoveSpendFromMempool(const std::vector& coinSerials); - - static CLelantusState* GetState(); - - int GetLatestCoinID() const; - - mint_info_container const & GetMints() const; - std::unordered_map const & GetSpends() const; - std::unordered_map const & GetCoinGroups() const ; - std::unordered_map const & GetMempoolCoinSerials() const; - - std::size_t GetTotalCoins() const { return GetMints().size(); } - - bool IsSurgeConditionDetected() const; - -private: - size_t CountLastNCoins(int groupId, size_t required, CBlockIndex* &first); - -private: - // Group Limit - size_t maxCoinInGroup; - size_t startGroupSize; - - // Collection of coin groups. Map from id to LelantusCoinGroupInfo structure - std::unordered_map coinGroups; - - // Latest anonymity set id; - int latestCoinId; - - std::atomic surgeCondition; - - struct Containers { - Containers(std::atomic & surgeCondition); - - void AddMint(lelantus::PublicCoin const & pubCoin, CMintedCoinInfo const & coinInfo, const uint256& tag); - void RemoveMint(lelantus::PublicCoin const & pubCoin); - - void AddSpend(Scalar const & serial, int coinGroupId); - void RemoveSpend(Scalar const & serial); - - void AddExtendedMints(int group, size_t mints); - void RemoveExtendedMints(int group); - - void Reset(); - - mint_info_container const & GetMints() const; - std::unordered_map const & GetSpends() const; - std::unordered_map& GetTagToPublicCoin(); - bool IsSurgeCondition() const; - private: - // Set of all minted pubCoin values, keyed by the public coin. - // Used for checking if the given coin already exists. - mint_info_container mintedPubCoins; - // Set of all used coin serials. - std::unordered_map usedCoinSerials; - - //this map keeps hash(G^s*H0^r|seedId) to G^s*H0^r*H1^v - std::unordered_map tagToPublicCoin; - - std::atomic & surgeCondition; - - typedef std::map metainfo_container_t; - metainfo_container_t extendedMintMetaInfo, mintMetaInfo, spendMetaInfo; - - void CheckSurgeCondition(); - - friend struct lelantus_mintspend::lelantus_mintspend_test; - }; - - Containers containers; - - friend struct lelantus_mintspend::lelantus_mintspend_test; -}; - -} // end of namespace lelantus - -#endif // _MAIN_LELANTUS_H__ diff --git a/src/liblelantus/challenge_generator.h b/src/liblelantus/challenge_generator.h deleted file mode 100644 index d599d54525..0000000000 --- a/src/liblelantus/challenge_generator.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef FIRO_LELANTUS_CHALLENGE_GENERATOR_H -#define FIRO_LELANTUS_CHALLENGE_GENERATOR_H -#include -#include -namespace lelantus { -using namespace secp_primitives; - -class ChallengeGenerator { -public: - virtual ~ChallengeGenerator() {}; - virtual void add(const GroupElement& group_element) = 0; - virtual void add(const std::vector& group_elements) = 0; - virtual void add(const Scalar& scalar) = 0; - virtual void add(const std::vector& scalars) = 0; - virtual void add(const std::vector& data_) = 0; - virtual void get_challenge(Scalar& result_out) = 0; -}; - -}// namespace lelantus - -#endif //FIRO_LELANTUS_CHALLENGE_GENERATOR_H diff --git a/src/liblelantus/challenge_generator_impl.h b/src/liblelantus/challenge_generator_impl.h deleted file mode 100644 index c584a19a3c..0000000000 --- a/src/liblelantus/challenge_generator_impl.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef FIRO_LELANTUS_CHALLENGE_GENERATOR_IMPL_H -#define FIRO_LELANTUS_CHALLENGE_GENERATOR_IMPL_H - -#include -#include -#include "../../crypto/sha256.h" -#include "../hash.h" -#include "challenge_generator.h" -#include -namespace lelantus { - -using namespace secp_primitives; - -template -class ChallengeGeneratorImpl : public ChallengeGenerator { - -public: - ChallengeGeneratorImpl(int version_ = 0) { - version = version_; - data.resize(GroupElement::serialize_size); - scalar_data.resize(32); - } - - void add(const GroupElement& group_element) override { - group_element.serialize(data.data()); - hash.Write(data.data(), data.size()); - } - - void add(const std::vector& group_elements) override { - addSize(group_elements.size()); - for (size_t i = 0; i < group_elements.size(); ++i) { - add(group_elements[i]); - } - } - - void add(const Scalar& scalar) override { - scalar.serialize(scalar_data.data()); - hash.Write(scalar_data.data(), scalar_data.size()); - } - - void add(const std::vector& scalars) override { - addSize(scalars.size()); - for (size_t i = 0; i < scalars.size(); ++i) { - add(scalars[i]); - } - } - - void add(const std::vector& data_) override { - hash.Write(data_.data(), data_.size()); - } - - void get_challenge(Scalar& result_out) override { - unsigned char result_data[CSHA256::OUTPUT_SIZE]; - do { - Hasher temp_hash = hash; - hash.Finalize(result_data); - hash = temp_hash; - result_out = result_data; - add(result_out); - } while (result_out.isZero() || !result_out.isMember()); - } - -private: - void addSize(uint32_t size) { - if (version >= 1) { - Scalar s(size); - add(s); - } - } -private: - int version; - Hasher hash; - std::vector data; - std::vector scalar_data; -}; - -}// namespace lelantus - -#endif //FIRO_LELANTUS_CHALLENGE_GENERATOR_IMPL_H diff --git a/src/liblelantus/coin.cpp b/src/liblelantus/coin.cpp deleted file mode 100644 index 8448696158..0000000000 --- a/src/liblelantus/coin.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include "coin.h" -#include "primitives/mint_spend.h" -#include "lelantus_primitives.h" -#include -#include - -namespace lelantus { - -static std::string zpts("PUBLICKEY_TO_SERIALNUMBER"); - -//class PublicCoin -PublicCoin::PublicCoin() { -} - -PublicCoin::PublicCoin(const GroupElement& coin): - value(coin) { -} - -const GroupElement& PublicCoin::getValue() const { - return this->value; -} - -uint256 PublicCoin::getValueHash() const { - return primitives::GetPubCoinValueHash(value); -} - -bool PublicCoin::operator==(const PublicCoin& other) const { - return (*this).value == other.value; -} - -bool PublicCoin::operator!=(const PublicCoin& other) const { - return (*this).value != other.value; -} - -bool PublicCoin::validate() const { - return this->value.isMember() && !this->value.isInfinity(); -} - -size_t PublicCoin::GetSerializeSize() const { - return value.memoryRequired(); -} - -//class PrivateCoin -PrivateCoin::PrivateCoin(const Params* p, uint64_t v): - params(p) { - this->randomize(); - this->mintCoin(v); -} - -PrivateCoin::PrivateCoin( - const Params* p, - const Scalar& serial, - uint64_t v, - const Scalar& random, - const std::vector& seckey, - int version_) : - params(p), - serialNumber(serial), - randomness(random), - version(version_) { - this->setEcdsaSeckey(seckey); - this->mintCoin(v); -} - -const Params* PrivateCoin::getParams() const { - return this->params; -} - -const PublicCoin& PrivateCoin::getPublicCoin() const { - return this->publicCoin; -} - -const Scalar& PrivateCoin::getSerialNumber() const { - return this->serialNumber; -} - -const Scalar& PrivateCoin::getRandomness() const { - return this->randomness; -} - -uint64_t PrivateCoin::getV() const { - return this->value; -} - -Scalar PrivateCoin::getVScalar() const { - return Scalar(this->value); -} - -unsigned int PrivateCoin::getVersion() const { - return this->version; -} - -void PrivateCoin::setPublicCoin(const PublicCoin& p) { - publicCoin = p; -} - -void PrivateCoin::setRandomness(const Scalar& n) { - randomness = n; -} - -const unsigned char* PrivateCoin::getEcdsaSeckey() const { - return this->ecdsaSeckey; -} - -void PrivateCoin::setEcdsaSeckey(const std::vector &seckey) { - if (seckey.size() == sizeof(ecdsaSeckey)) - std::copy(seckey.cbegin(), seckey.cend(), &ecdsaSeckey[0]); - else - throw std::invalid_argument("EcdsaSeckey size does not match."); -} - -void PrivateCoin::setEcdsaSeckey(const uint256& seckey) { - if (seckey.size() == sizeof(ecdsaSeckey)) - std::copy(seckey.begin(), seckey.end(), &ecdsaSeckey[0]); - else - throw std::invalid_argument("EcdsaSeckey size does not match."); -} - -void PrivateCoin::setSerialNumber(const Scalar& n) { - serialNumber = n; -} - -void PrivateCoin::setV(uint64_t n) { - value = n; -} - -void PrivateCoin::setVersion(unsigned int nVersion) { - version = nVersion; -} - -void PrivateCoin::randomize() { - // Create a key pair - secp256k1_pubkey pubkey; - do { - if (RAND_bytes(this->ecdsaSeckey, sizeof(this->ecdsaSeckey)) != 1) { - throw std::invalid_argument("Unable to generate randomness"); - } - } while (!secp256k1_ec_pubkey_create( - OpenSSLContext::get_context(), &pubkey, this->ecdsaSeckey)); - - // Hash the public key in the group to obtain a serial number - serialNumber = serialNumberFromSerializedPublicKey( - OpenSSLContext::get_context(), &pubkey); - - randomness.randomize(); -} - -void PrivateCoin::mintCoin(uint64_t v) { - value = v; - GroupElement commit = LelantusPrimitives::double_commit( - params->get_g(), serialNumber, params->get_h1(), getVScalar(), params->get_h0(), randomness); - publicCoin = PublicCoin(commit); -} - -Scalar PrivateCoin::serialNumberFromSerializedPublicKey( - const secp256k1_context *context, - secp256k1_pubkey *pubkey) { - std::vector pubkey_hash(32, 0); - - static const unsigned char one[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 - }; - - // We use secp256k1_ecdh instead of secp256k1_serialize_pubkey to avoid a timing channel. - if (1 != secp256k1_ecdh(context, pubkey_hash.data(), pubkey, &one[0])) { - throw std::invalid_argument("Unable to compute public key hash with secp256k1_ecdh."); - } - - std::vector pre(zpts.begin(), zpts.end()); - std::copy(pubkey_hash.begin(), pubkey_hash.end(), std::back_inserter(pre)); - - unsigned char hash[CSHA256::OUTPUT_SIZE]; - CSHA256().Write(pre.data(), pre.size()).Finalize(hash); - - // Use 32 bytes of hash as coin serial. - return Scalar(hash); -} - -} //namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/coin.h b/src/liblelantus/coin.h deleted file mode 100644 index f8723d9de6..0000000000 --- a/src/liblelantus/coin.h +++ /dev/null @@ -1,183 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_COIN_H -#define FIRO_LIBLELANTUS_COIN_H - -#include "params.h" -#include "../firo_params.h" -#include "../uint256.h" -#include "openssl_context.h" -#include "crypto/sha256.h" - -// keep this just to not break old index -namespace sigma { -enum class CoinDenomination : std::uint8_t { - SIGMA_DENOM_0_05 = 5, - SIGMA_DENOM_0_1 = 0, - SIGMA_DENOM_0_5 = 1, - SIGMA_DENOM_1 = 2, - SIGMA_DENOM_10 = 3, - SIGMA_DENOM_25 = 6, - SIGMA_DENOM_100 = 4 -}; -// Serialization support for CoinDenomination - -template -void Serialize(Stream& os, CoinDenomination d) -{ - Serialize(os, static_cast(d)); -} - -template -void Unserialize(Stream& is, CoinDenomination& d) -{ - std::uint8_t v; - Unserialize(is, v); - d = static_cast(v); -} - -class PublicCoin { -public: - PublicCoin() {} - template - inline void Serialize(Stream& s) const { - constexpr int size = GroupElement::memoryRequired(); - unsigned char buffer[size + sizeof(int32_t)]; - value.serialize(buffer); - std::memcpy(buffer + size, &denomination, sizeof(denomination)); - char* b = (char*)buffer; - s.write(b, size + sizeof(int32_t)); - } - - template - inline void Unserialize(Stream& s) { - constexpr int size = GroupElement::memoryRequired(); - unsigned char buffer[size + sizeof(int32_t)]; - char* b = (char*)buffer; - s.read(b, size + sizeof(int32_t)); - value.deserialize(buffer); - std::memcpy(&denomination, buffer + size, sizeof(denomination)); - } - -private: - GroupElement value; - CoinDenomination denomination; -}; - -struct CSpendCoinInfo { - CoinDenomination denomination; - int coinGroupId; - - template - void Serialize(Stream& s) const { - int64_t tmp = uint8_t(denomination); - s << tmp; - tmp = coinGroupId; - s << tmp; - } - template - void Unserialize(Stream& s) { - int64_t tmp; - s >> tmp; denomination = CoinDenomination(tmp); - s >> tmp; coinGroupId = int(tmp); - } - -}; - -struct CScalarHash { - std::size_t operator ()(const Scalar& bn) const noexcept { - std::vector bnData(bn.memoryRequired()); - bn.serialize(&bnData[0]); - unsigned char hash[CSHA256::OUTPUT_SIZE]; - CSHA256().Write(&bnData[0], bnData.size()).Finalize(hash); - // take the first bytes of "hash". - std::size_t result; - std::memcpy(&result, hash, sizeof(std::size_t)); - return result; - } -}; - -using spend_info_container = std::unordered_map; - -} - -namespace lelantus { - -class PublicCoin { -public: - PublicCoin(); - - PublicCoin(const GroupElement& coin); - - const GroupElement& getValue() const; - uint256 getValueHash() const; - bool operator==(const PublicCoin& other) const; - bool operator!=(const PublicCoin& other) const; - bool validate() const; - size_t GetSerializeSize() const; - - template - inline void Serialize(Stream& s) const { - std::vector buffer(GetSerializeSize()); - value.serialize(buffer.data()); - s.write((const char *)buffer.data(), buffer.size()); - } - - template - inline void Unserialize(Stream& s) { - std::vector buffer(GetSerializeSize()); - s.read((char *)buffer.data(), buffer.size()); - value.deserialize(buffer.data()); - } - -private: - GroupElement value; -}; - -class PrivateCoin { -public: - - PrivateCoin(const Params* p, uint64_t v); - PrivateCoin(const Params* p, - const Scalar& serial, - uint64_t v, - const Scalar& random, - const std::vector& seckey, - int version_); - - const Params * getParams() const; - const PublicCoin& getPublicCoin() const; - const Scalar& getSerialNumber() const; - const Scalar& getRandomness() const; - uint64_t getV() const; - Scalar getVScalar() const; - unsigned int getVersion() const; - void setPublicCoin(const PublicCoin& p); - void setRandomness(const Scalar& n); - void setSerialNumber(const Scalar& n); - void setV(uint64_t n); - void setVersion(unsigned int nVersion); - const unsigned char* getEcdsaSeckey() const; - - void setEcdsaSeckey(const std::vector &seckey); - void setEcdsaSeckey(const uint256& seckey); - - static Scalar serialNumberFromSerializedPublicKey( - const secp256k1_context *context, - secp256k1_pubkey *pubkey); - -private: - const Params* params; - PublicCoin publicCoin; - Scalar serialNumber; - uint64_t value; - Scalar randomness; - unsigned int version = 0; - unsigned char ecdsaSeckey[32]; - -private: - void randomize(); - void mintCoin(uint64_t v); -}; - -}// namespace lelantus - -#endif //FIRO_LIBLELANTUS_COIN_H diff --git a/src/liblelantus/innerproduct_proof.h b/src/liblelantus/innerproduct_proof.h deleted file mode 100755 index 5f1c28a362..0000000000 --- a/src/liblelantus/innerproduct_proof.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_INNERPRODUCTPROOF_H -#define FIRO_LIBLELANTUS_INNERPRODUCTPROOF_H - -#include -#include "params.h" - -namespace lelantus { - -// Storage of the proof. -class InnerProductProof { -public: - - inline std::size_t memoryRequired(std::size_t size) const { - return a_.memoryRequired() * 3 + 34 * 2 * size; - } - - ADD_SERIALIZE_METHODS; - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(a_); - READWRITE(b_); - READWRITE(c_); - READWRITE(L_); - READWRITE(R_); - } - - Scalar a_; - Scalar b_; - Scalar c_; - std::vector L_; - std::vector R_; -}; - -} // namespace lelantus - -#endif //FIRO_LIBLELANTUS_INNERPRODUCTPROOF_H diff --git a/src/liblelantus/innerproduct_proof_generator.cpp b/src/liblelantus/innerproduct_proof_generator.cpp deleted file mode 100755 index f113f1941e..0000000000 --- a/src/liblelantus/innerproduct_proof_generator.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include "innerproduct_proof_generator.h" - -namespace lelantus { - -InnerProductProofGenerator::InnerProductProofGenerator( - const std::vector& g, - const std::vector& h, - const GroupElement& u, - int version) - : g_(g) - , h_(h) - , u_(u) - , version_(version) -{ -} - -InnerProductProofGenerator::InnerProductProofGenerator( - const std::vector& g, - const std::vector& h, - const GroupElement& u, - const GroupElement& P, - int version) - : g_(g) - , h_(h) - , u_(u) - , P_(P) - , version_(version) -{ -} - -void InnerProductProofGenerator::generate_proof( - const std::vector& a, - const std::vector& b, - const Scalar& x, - std::unique_ptr& challengeGenerator, - InnerProductProof& proof_out) { - const Scalar c = LelantusPrimitives::scalar_dot_product(a.begin(), a.end(), b.begin(), b.end()); - compute_P(a, b, P_initial); - u_ *= x; - proof_out.c_ = c; - if (version_ >=3) - challengeGenerator->add(c); - P_ = (P_initial + u_ * c); - generate_proof_util(a, b, challengeGenerator, proof_out); -} - -void InnerProductProofGenerator::generate_proof_util( - const std::vector& a, - const std::vector& b, - std::unique_ptr& challengeGenerator, - InnerProductProof& proof_out) { - - if(a.size() != b.size()) - throw std::runtime_error("Sizes of a and b are not equal."); - - if(a.size() == 1 && b.size() == 1) { //Protocol 2 line 15 - proof_out.a_ = a[0]; - proof_out.b_ = b[0]; - return; - } - - std::size_t n = a.size() / 2; - // Computes cL then L - Scalar cL = LelantusPrimitives::scalar_dot_product(a.begin() ,a.begin() + n, b.begin() + n, b.end()); - GroupElement L; - l(a.begin() ,a.begin() + n, b.begin() + n, b.end(), cL, L); - - //Computes cR then R - Scalar cR = LelantusPrimitives::scalar_dot_product(a.begin() + n, a.end(), b.begin(), b.begin() + n); - GroupElement R; - r(a.begin() + n, a.end(), b.begin(), b.begin() + n, cR, R); - - //Push L and R - proof_out.L_.emplace_back(L); - proof_out.R_.emplace_back(R); - - //Get challenge x - Scalar x; - std::vector group_elements = {L, R}; - - // if(version_ >= 2) we should be using CHash256, - // we want to link transcripts from previous iteration in each step, so we are not restarting in that case, - if (version_ < 2) { - challengeGenerator.reset(new ChallengeGeneratorImpl(0)); - } - challengeGenerator->add(group_elements); - challengeGenerator->get_challenge(x); - - //Compute g prime and p prime - std::vector g_p; - LelantusPrimitives::g_prime(g_, x, g_p); - std::vector h_p; - LelantusPrimitives::h_prime(h_, x, h_p); - - //Compute a prime and b prime - std::vector a_p = a_prime(x, a); - std::vector b_p = b_prime(x, b); - - //Compute P prime - GroupElement p_p = LelantusPrimitives::p_prime(P_, L, R, x); - - // Recursive call of protocol 2 - InnerProductProofGenerator(g_p, h_p, u_, p_p, version_).generate_proof_util(a_p, b_p, challengeGenerator, proof_out); -} - -void InnerProductProofGenerator::compute_P( - const std::vector& a, - const std::vector& b, - GroupElement& result_out) { - - secp_primitives::MultiExponent g_mult(g_, a); - secp_primitives::MultiExponent h_mult(h_, b); - GroupElement g = g_mult.get_multiple(); - GroupElement h = h_mult.get_multiple(); - result_out = (g + h); -} - -void InnerProductProofGenerator::l( - typename std::vector::const_iterator a_start, - typename std::vector::const_iterator a_end, - typename std::vector::const_iterator b_start, - typename std::vector::const_iterator b_end, - const Scalar& cL, - GroupElement& result_out) { - std::vector a, b; - std::vector gens_g, gens_h; - gens_g.reserve(g_.size() / 2 + 1); - gens_h.reserve(h_.size() / 2 + 1); - a.reserve(g_.size() / 2 + 1); - b.reserve(h_.size() / 2 + 1); - - gens_g.insert(gens_g.end(), g_.begin() + g_.size() / 2, g_.end()); - a.insert(a.end(), a_start, a_start + g_.size() / 2); - - gens_h.insert(gens_h.end(), h_.begin(), h_.begin() + h_.size() / 2); - b.insert(b.end(), b_start, b_start + h_.size() / 2); - - LelantusPrimitives::commit(u_, cL, gens_g, a, gens_h, b, result_out); -} - -void InnerProductProofGenerator::r( - typename std::vector::const_iterator a_start, - typename std::vector::const_iterator a_end, - typename std::vector::const_iterator b_start, - typename std::vector::const_iterator b_end, - const Scalar& cR, - GroupElement& result_out) { - std::vector a, b; - std::vector gens_g, gens_h; - gens_g.reserve(g_.size() / 2 + 1); - gens_h.reserve(h_.size() / 2 + 1); - a.reserve(g_.size() / 2 + 1); - b.reserve(h_.size() / 2 + 1); - - gens_g.insert(gens_g.end(), g_.begin(), g_.begin() + g_.size() / 2); - a.insert(a.end(), a_start, a_start + g_.size() / 2); - - gens_h.insert(gens_h.end(), h_.begin() + h_.size() / 2, h_.end()); - b.insert(b.end(), b_start, b_start + h_.size() / 2); - - LelantusPrimitives::commit(u_, cR, gens_g, a, gens_h, b, result_out); -} - -std::vector InnerProductProofGenerator::a_prime( - const Scalar& x, - const std::vector& a){ - Scalar x_inverse = x.inverse(); - std::vector result; - result.reserve(a.size() / 2); - for(std::size_t i = 0; i < a.size() / 2; ++i) - { - result.emplace_back(a[i] * x + a[a.size() / 2 + i] * x_inverse); - } - return result; -} - -std::vector InnerProductProofGenerator::b_prime( - const Scalar& x, - const std::vector& b) { - Scalar x_inverse = x.inverse(); - std::vector result; - result.reserve(b.size() / 2); - for(std::size_t i = 0; i < b.size() / 2; ++i) - { - result.emplace_back(b[i] * x_inverse + b[b.size() / 2 + i] * x); - } - return result; -} - -const GroupElement& InnerProductProofGenerator::get_P() { - return P_initial; -} -} // namespace lelantus diff --git a/src/liblelantus/innerproduct_proof_generator.h b/src/liblelantus/innerproduct_proof_generator.h deleted file mode 100755 index 6dcb4119c9..0000000000 --- a/src/liblelantus/innerproduct_proof_generator.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_INNERP_RODUCT_PROOF_GENERATOR_H -#define FIRO_LIBLELANTUS_INNERP_RODUCT_PROOF_GENERATOR_H - -#include "lelantus_primitives.h" -#include "challenge_generator_impl.h" - -namespace lelantus { - -class InnerProductProofGenerator { - -public: - //g and h are being kept by reference, be sure it will not be modified from outside - InnerProductProofGenerator( - const std::vector& g, - const std::vector& h, - const GroupElement& u, - int version); // if(version >= 2) we should pass CHash256 in generate_proof function - - void generate_proof( - const std::vector& a, - const std::vector& b, - const Scalar& x, - std::unique_ptr& challengeGenerator, - InnerProductProof& proof_out); - - const GroupElement& get_P(); - -private: - - InnerProductProofGenerator( - const std::vector& g, - const std::vector& h, - const GroupElement& u, - const GroupElement& P, - int version); - - void generate_proof_util( - const std::vector& a, - const std::vector& b, - std::unique_ptr& challengeGenerator, - InnerProductProof& proof_out); - - void l(typename std::vector::const_iterator a_start, - typename std::vector::const_iterator a_end, - typename std::vector::const_iterator b_start, - typename std::vector::const_iterator b_end, - const Scalar& cL, - GroupElement& result_out); - - void r(typename std::vector::const_iterator a_start, - typename std::vector::const_iterator a_end, - typename std::vector::const_iterator b_start, - typename std::vector::const_iterator b_end, - const Scalar& cR, - GroupElement& result_out); - - std::vector a_prime(const Scalar& x, const std::vector& a); - - std::vector b_prime(const Scalar& x, const std::vector& b); - - void compute_P( - const std::vector& a, - const std::vector& b, - GroupElement& result_out); - -private: - const std::vector& g_; - const std::vector& h_; - GroupElement u_; - GroupElement P_; - GroupElement P_initial; - int version_; - -}; - -} // namespace lelantus - -#endif //FIRO_LIBLELANTUS_INNERP_RODUCT_PROOF_GENERATOR_H diff --git a/src/liblelantus/innerproduct_proof_verifier.cpp b/src/liblelantus/innerproduct_proof_verifier.cpp deleted file mode 100755 index 99a6fc08ba..0000000000 --- a/src/liblelantus/innerproduct_proof_verifier.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "innerproduct_proof_verifier.h" - -namespace lelantus { - -InnerProductProofVerifier::InnerProductProofVerifier( - const std::vector& g, - const std::vector& h, - const GroupElement& u, - const GroupElement& P, - int version) - : g_(g) - , h_(h) - , u_(u) - , P_(P) - , version_(version) -{ -} - -bool InnerProductProofVerifier::verify( - const Scalar& x, - const InnerProductProof& proof, - std::unique_ptr& challengeGenerator) { - auto itr_l = proof.L_.begin(); - auto itr_r = proof.R_.begin(); - u_ *= x; - P_ += u_ * proof.c_; - if (version_ >= 3) - challengeGenerator->add(proof.c_); - return verify_util(proof, itr_l, itr_r, challengeGenerator); -} - -bool InnerProductProofVerifier::verify_util( - const InnerProductProof& proof, - typename std::vector::const_iterator itr_l, - typename std::vector::const_iterator itr_r, - std::unique_ptr& challengeGenerator) { - if(itr_l == proof.L_.end()){ - Scalar c = proof.a_ * proof.b_; - GroupElement uc = u_ * c; - GroupElement P = g_[0] * proof.a_ + h_[0] * proof.b_ + uc; - return P_ == P; - } - - //Get challenge x - Scalar x; - std::vector group_elements = {*itr_l, *itr_r}; - - // if(version >= 2) we should be using CHash256, - // we want to link transcripts from previous iteration in each step, so we are not restarting in that case, - if (version_ < 2) { - challengeGenerator.reset(new ChallengeGeneratorImpl(0)); - } - challengeGenerator->add(group_elements); - challengeGenerator->get_challenge(x); - - //Compute g prime and p prime - std::vector g_p; - LelantusPrimitives::g_prime(g_, x, g_p); - std::vector h_p; - LelantusPrimitives::h_prime(h_, x, h_p); - - //Compute P prime - GroupElement p_p = LelantusPrimitives::p_prime(P_, *itr_l, *itr_r, x); - return InnerProductProofVerifier(g_p, h_p, u_, p_p, version_).verify_util(proof, itr_l + 1, itr_r + 1, challengeGenerator); -} - -bool InnerProductProofVerifier::verify_fast(std::size_t n, const Scalar& x, const InnerProductProof& proof, std::unique_ptr& challengeGenerator) { - u_ *= x; - P_ += u_ * proof.c_; - return verify_fast_util(n, proof, challengeGenerator); -} - -bool InnerProductProofVerifier::verify_fast_util( - std::size_t n, - const InnerProductProof& proof, - std::unique_ptr& challengeGenerator){ - std::size_t log_n = proof.L_.size(); - std::vector x_j; - x_j.resize(log_n); - for (std::size_t i = 0; i < log_n; ++i) - { - std::vector group_elements = {proof.L_[i], proof.R_[i]}; - - // if(version_ >= 2) we should be using CHash256, - // we want to link transcripts from previous iteration in each step, so we are not restarting in that case, - if (version_ < 2) { - challengeGenerator.reset(new ChallengeGeneratorImpl(0)); - } - challengeGenerator->add(group_elements); - challengeGenerator->get_challenge(x_j[i]); - } - std::vector s, s_inv; - s.resize(n); - s_inv.resize(n); - for (std::size_t i = 0; i < n; ++i) - { - Scalar x_i(uint64_t(1)); - for (std::size_t j = 0; j < log_n; ++j) - { - if((i >> j) & 1) { - x_i *= x_j[log_n - j - 1]; - } else{ - x_i *= x_j[log_n - j - 1].inverse(); - } - - } - s[i] = x_i; - s_inv[i] = x_i.inverse(); - } - - secp_primitives::MultiExponent g_mult(g_, s); - secp_primitives::MultiExponent h_mult(h_, s_inv); - GroupElement g = g_mult.get_multiple(); - GroupElement h = h_mult.get_multiple(); - - GroupElement left; - left += g * proof.a_ + h * proof.b_ + u_ * (proof.a_ * proof.b_); - GroupElement right = P_; - GroupElement multi; - for (std::size_t j = 0; j < log_n; ++j) - multi += (proof.L_[j] * (x_j[j].square()) + proof.R_[j] * (x_j[j].square().inverse())); - right += multi; - if(left != right) - return false; - return true; -} - -}// namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/innerproduct_proof_verifier.h b/src/liblelantus/innerproduct_proof_verifier.h deleted file mode 100755 index 1e0880dae8..0000000000 --- a/src/liblelantus/innerproduct_proof_verifier.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_INNER_PRODUCT_PROOF_VERIFIER_H -#define FIRO_LIBLELANTUS_INNER_PRODUCT_PROOF_VERIFIER_H - -#include "lelantus_primitives.h" -#include "challenge_generator_impl.h" - -namespace lelantus { - -class InnerProductProofVerifier { - -public: - //g and h are being kept by reference, be sure it will not be modified from outside - InnerProductProofVerifier( - const std::vector& g, - const std::vector& h, - const GroupElement& u, - const GroupElement& P, - int version); // if(version >= 2) we should pass CHash256 in verify - - bool verify(const Scalar& x, const InnerProductProof& proof, std::unique_ptr& challengeGenerator); - bool verify_fast(std::size_t n, const Scalar& x, const InnerProductProof& proof, std::unique_ptr& challengeGenerator); - -private: - bool verify_util( - const InnerProductProof& proof, - typename std::vector::const_iterator ltr_l, - typename std::vector::const_iterator itr_r, - std::unique_ptr& challengeGenerator); - - bool verify_fast_util(std::size_t n, const InnerProductProof& proof, std::unique_ptr& challengeGenerator); - -private: - const std::vector& g_; - const std::vector& h_; - GroupElement u_; - GroupElement P_; - int version_; - -}; - -} // namespace lelantus - -#endif //FIRO_LIBLELANTUS_INNER_PRODUCT_PROOF_VERIFIER_H diff --git a/src/liblelantus/joinsplit.cpp b/src/liblelantus/joinsplit.cpp deleted file mode 100644 index 52d479792e..0000000000 --- a/src/liblelantus/joinsplit.cpp +++ /dev/null @@ -1,235 +0,0 @@ -#include "joinsplit.h" -#include "lelantus_prover.h" -#include "lelantus_verifier.h" -#include "openssl_context.h" -#include "hash.h" -#include "util.h" - -namespace lelantus { - -JoinSplit::JoinSplit(const Params *p, - const std::vector>& Cin, - const std::map>& anonymity_sets, - const std::vector>& anonymity_set_hashes, - const Scalar& Vout, - const std::vector& Cout, - uint64_t fee, - const std::map& groupBlockHashes, - const uint256& txHash, - unsigned int nVersion) - : - params (p), - version (nVersion), - fee (fee) { - - serialNumbers.reserve(Cin.size()); - for(size_t i = 0; i < Cin.size(); i++) { - serialNumbers.emplace_back(Cin[i].first.getSerialNumber()); - } - - if (!HasValidSerials()) { - throw std::invalid_argument("JoinSplit has invalid serial number"); - } - - std::vector indexes; - for(size_t i = 0; i < Cin.size(); i++) { - size_t index; - const auto& set = anonymity_sets.find(Cin[i].second); - if(set == anonymity_sets.end()) - throw std::invalid_argument("No such anonymity set"); - - if(!getIndex(Cin[i].first.getPublicCoin(), set->second, index)) - throw std::invalid_argument("No such coin in this anonymity set"); - - groupIds.push_back(Cin[i].second); - indexes.emplace_back(index); - } - - coinNum = Cin.size(); - - // generate public keys here, as we need it for challenge generation starting from LELANTUS_TX_VERSION_4_5 - generatePubKeys(Cin); - - LelantusProver prover(p, version); - prover.proof(anonymity_sets, anonymity_set_hashes, uint64_t(0), Cin, indexes, ecdsaPubkeys, Vout, Cout, fee, lelantusProof, qkSchnorrProof); - - if(groupBlockHashes.size() != anonymity_sets.size()) - throw std::invalid_argument("Mismatch blockHashes and anonymity sets sizes."); - - SpendMetaData m(groupBlockHashes, txHash); - - signMetaData(Cin, m, Cout.size()); - - coinGroupIdAndBlockHash = m.coinGroupIdAndBlockHash; -} - -void JoinSplit::generatePubKeys(const std::vector>& Cin) { - ecdsaPubkeys.resize(Cin.size()); - for(size_t i = 0; i < Cin.size(); i++) { - // Sign each spend under the public key associate with the serial number. - secp256k1_pubkey pubkey; - size_t pubkeyLen = 33; - - ecdsaPubkeys[i].resize(pubkeyLen); - // TODO timing channel, since secp256k1_ec_pubkey_serialize does not expect its output to be secret. - // See main_impl.h of ecdh module on secp256k1 - if (!secp256k1_ec_pubkey_create( - OpenSSLContext::get_context(), &pubkey, Cin[i].first.getEcdsaSeckey())) { - throw std::invalid_argument("Invalid secret key"); - } - if (1 != secp256k1_ec_pubkey_serialize( - OpenSSLContext::get_context(), - &this->ecdsaPubkeys[i][0], &pubkeyLen, &pubkey, SECP256K1_EC_COMPRESSED)) { - throw std::invalid_argument("Unable to serialize public key"); - } - } -} - -void JoinSplit::signMetaData(const std::vector>& Cin, const SpendMetaData& m, size_t coutSize) { - // Proves that the coin is correct w.r.t. serial number and hidden coin secret - // (This proof is bound to the coin 'metadata', i.e., transaction hash) - uint256 metahash = signatureHash(m, coutSize); - - - ecdsaSignatures.resize(Cin.size()); - for(size_t i = 0; i < Cin.size(); i++) { - // Sign each spend under the public key associate with the serial number. - secp256k1_ecdsa_signature sig; - ecdsaSignatures[i].resize(64); - if (1 != secp256k1_ecdsa_sign( - OpenSSLContext::get_context(), &sig, - metahash.begin(), Cin[i].first.getEcdsaSeckey(), NULL, NULL)) { - throw std::invalid_argument("Unable to sign with EcdsaSeckey."); - } - if (1 != secp256k1_ecdsa_signature_serialize_compact( - OpenSSLContext::get_context(), &this->ecdsaSignatures[i][0], &sig)) { - throw std::invalid_argument("Unable to serialize ecdsa_signature."); - } - } -} - -bool JoinSplit::Verify( - const std::map>& anonymity_sets, - const std::vector>& anonymity_set_hashes, - const std::vector& Cout, - uint64_t Vout, - const uint256& txHash) const { - Scalar challenge; - bool fSkipVerification = false; - return Verify(anonymity_sets, anonymity_set_hashes, Cout, Vout, txHash, challenge, fSkipVerification); -} - -bool JoinSplit::Verify( - const std::map>& anonymity_sets, - const std::vector>& anonymity_set_hashes, - const std::vector& Cout, - uint64_t Vout, - const uint256& txHash, - Scalar& challenge, - bool fSkipVerification ) const { - std::map groupBlockHashes; - - for(const auto& idAndHash : coinGroupIdAndBlockHash) { - groupBlockHashes[idAndHash.first] = idAndHash.second; - } - - - SpendMetaData m(groupBlockHashes, txHash); - - uint256 metahash = signatureHash(m, Cout.size()); - - if(serialNumbers.size() != ecdsaSignatures.size() || serialNumbers.size() != ecdsaPubkeys.size()) { - LogPrintf("Sigma spend failed due to serialNumbers and ecdsaSignatures/ecdsaPubkeys number mismatch."); - return false; - } - - for(size_t i = 0; i < serialNumbers.size(); i++) { - // Verify ecdsa_signature, to make sure someone did not change the output of transaction. - // Check sizes - if (this->ecdsaPubkeys[i].size() != 33 || this->ecdsaSignatures[i].size() != 64) { - LogPrintf("Lelantus joinsplit failed due to incorrect size of ecdsaSignature."); - return false; - } - - // Verify signature - secp256k1_pubkey pubkey; - secp256k1_ecdsa_signature signature; - - if (!secp256k1_ec_pubkey_parse(OpenSSLContext::get_context(), &pubkey, ecdsaPubkeys[i].data(), 33)) { - LogPrintf("Lelantus joinsplit failed due to unable to parse ecdsaPubkey."); - return false; - } - - // Recompute and compare hash of public key - Scalar coinSerialNumberExpected = PrivateCoin::serialNumberFromSerializedPublicKey(OpenSSLContext::get_context(), &pubkey); - if (serialNumbers[i] != coinSerialNumberExpected) { - LogPrintf("Lelantus joinsplit failed due to serial number does not match public key hash."); - return false; - } - - if (1 != secp256k1_ecdsa_signature_parse_compact(OpenSSLContext::get_context(), &signature, ecdsaSignatures[i].data()) ) { - LogPrintf("Lelantus joinsplit failed due to signature cannot be parsed."); - return false; - } - - if (!secp256k1_ecdsa_verify( - OpenSSLContext::get_context(), &signature, metahash.begin(), &pubkey)) { - LogPrintf("Lelantus joinsplit failed due to signature cannot be verified."); - return false; - } - } - - // Now verify lelantus proof - LelantusVerifier verifier(params, version); - return verifier.verify(anonymity_sets, anonymity_set_hashes, serialNumbers, ecdsaPubkeys, groupIds, uint64_t(0),Vout, fee, Cout, lelantusProof, qkSchnorrProof, challenge, fSkipVerification); -} - - -uint256 JoinSplit::signatureHash(const SpendMetaData& m, size_t coutSize) const { - CHashWriter h(0,0); - h << m << lelantusProof; - return h.GetHash(); -} - -const std::vector& JoinSplit::getCoinGroupIds() { - return this->groupIds; -} - -const std::vector>& JoinSplit::getIdAndBlockHashes() { - return this->coinGroupIdAndBlockHash; -} - -const std::vector& JoinSplit::getCoinSerialNumbers() { - return this->serialNumbers; -} - -const LelantusProof& JoinSplit::getLelantusProof() { - return this->lelantusProof; -} - -uint64_t JoinSplit::getFee() { - return this->fee; -} - -bool JoinSplit::getIndex(const PublicCoin& coin, const std::vector& anonymity_set, size_t& index) { - for (std::size_t j = 0; j < anonymity_set.size(); ++j) { - if(anonymity_set[j] == coin){ - index = j; - return true; - } - } - return false; -} - -bool JoinSplit::HasValidSerials() const { - for(size_t i = 0; i < serialNumbers.size(); i++) - if(!serialNumbers[i].isMember() || serialNumbers[i].isZero()) - return false; - return true; -} - -bool JoinSplit::isSigmaToLelantus() const { - return version == SIGMA_TO_LELANTUS_JOINSPLIT || version == SIGMA_TO_LELANTUS_JOINSPLIT_FIXED || version == SIGMA_TO_LELANTUS_TX_TPAYLOAD; -} - -} //namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/joinsplit.h b/src/liblelantus/joinsplit.h deleted file mode 100644 index f290b2a989..0000000000 --- a/src/liblelantus/joinsplit.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_JOINSPLIT_H -#define FIRO_LIBLELANTUS_JOINSPLIT_H - -#include "coin.h" -#include "lelantus_proof.h" -#include "spend_metadata.h" - -namespace lelantus { - -class JoinSplit { -public: - template - JoinSplit(const Params* p, Stream& strm): - params(p) { - strm >> *this; - } - - JoinSplit(const Params* p, - const std::vector>& Cin, - const std::map>& anonymity_sets, - const std::vector>& anonymity_set_hashes, - const Scalar& Vout, - const std::vector& Cout, - uint64_t fee, - const std::map& groupBlockHashes, - const uint256& txHash, - unsigned int nVersion); - - bool Verify(const std::map>& anonymity_sets, - const std::vector>& anonymity_set_hashes, - const std::vector& Cout, - uint64_t Vout, - const uint256& txHash) const; - - bool Verify(const std::map>& anonymity_sets, - const std::vector>& anonymity_set_hashes, - const std::vector& Cout, - uint64_t Vout, - const uint256& txHash, - Scalar& challenge, - bool fSkipVerification = false) const; - - void generatePubKeys(const std::vector>& Cin); - - void signMetaData(const std::vector>& Cin, const SpendMetaData& m, size_t coutSize); - - uint256 signatureHash(const SpendMetaData& m, size_t coutSize) const; - - void setVersion(unsigned int nVersion) { - version = nVersion; - } - - const std::vector& getCoinSerialNumbers(); - - const LelantusProof& getLelantusProof(); - - uint64_t getFee(); - - const std::vector& getCoinGroupIds(); - - const std::vector>& getIdAndBlockHashes(); - - int getVersion() const { - return version; - } - - bool getIndex(const PublicCoin& coin, const std::vector& anonymity_set, size_t& index); - - bool HasValidSerials() const; - - std::vector> const & GetEcdsaPubkeys() const { - return ecdsaPubkeys; - } - - bool isSigmaToLelantus() const; - - ADD_SERIALIZE_METHODS; - template - void SerializationOp(Stream& s, Operation ser_action) - { - READWRITE(lelantusProof); - READWRITE(coinNum); - - if (ser_action.ForRead()) - { - groupIds.resize(coinNum); - ecdsaSignatures.resize(coinNum); - ecdsaPubkeys.resize(coinNum); - } - - for(uint8_t i = 0; i < coinNum; i++) - { - READWRITE(groupIds[i]); - size_t sigSize = 64; - size_t pubKeySize = 33; - if (ser_action.ForRead()) - { - ecdsaSignatures[i].resize(sigSize); - ecdsaPubkeys[i].resize(pubKeySize); - } - - for (size_t j = 0; j < sigSize; j++) - READWRITE(ecdsaSignatures[i][j]); - for (size_t j = 0; j < pubKeySize; j++) - READWRITE(ecdsaPubkeys[i][j]); - } - - READWRITE(coinGroupIdAndBlockHash); - READWRITE(fee); - READWRITE(version); - - if (version >= LELANTUS_TX_VERSION_4_5) - READWRITE(qkSchnorrProof); - - if (ser_action.ForRead()) { - serialNumbers.resize(coinNum); - for(size_t i = 0; i < coinNum; i++) { - secp256k1_pubkey pubkey; - if (!secp256k1_ec_pubkey_parse(OpenSSLContext::get_context(), &pubkey, ecdsaPubkeys[i].data(), 33)) { - throw std::invalid_argument("Lelantus joinsplit unserialize failed due to unable to parse ecdsaPubkey."); - } - - serialNumbers[i] = PrivateCoin::serialNumberFromSerializedPublicKey(OpenSSLContext::get_context(), &pubkey); - } - } - } - -private: - const Params* params; - unsigned int version = 0; - LelantusProof lelantusProof; - SchnorrProof qkSchnorrProof; - uint8_t coinNum; - std::vector serialNumbers; - std::vector groupIds; - std::vector> ecdsaSignatures; - std::vector> ecdsaPubkeys; - std::vector> coinGroupIdAndBlockHash; - uint64_t fee; - -}; - -} //namespace lelantus - -#endif //FIRO_LIBLELANTUS_JOINSPLIT_H diff --git a/src/liblelantus/lelantus_primitives.cpp b/src/liblelantus/lelantus_primitives.cpp deleted file mode 100644 index 77d31955dc..0000000000 --- a/src/liblelantus/lelantus_primitives.cpp +++ /dev/null @@ -1,274 +0,0 @@ -#include "lelantus_primitives.h" -#include "challenge_generator_impl.h" - -namespace lelantus { - -static std::string lts("LELANTUS_SIGMA"); - -// Invert a vector of scalars -// -// This _requires_ that all scalars be nonzero! -std::vector LelantusPrimitives::invert(const std::vector& scalars) { - std::size_t n = scalars.size(); - - std::vector result; - result.reserve(n); - result.resize(n); - std::vector scratch; - scratch.reserve(n); - Scalar acc(uint64_t(1)); - Scalar temp; - - for (std::size_t i = 0; i < n; i++) { - if (scalars[i] == Scalar(uint64_t(0))) { - throw std::runtime_error("Cannot invert a zero scalar"); - } - scratch.emplace_back(acc); - acc *= scalars[i]; - } - acc = acc.inverse(); - for (std::size_t i = n; i > 0; i--) { - temp = acc * scalars[i - 1]; - result[i - 1] = acc * scratch[i - 1]; - acc = temp; - } - - return result; -} - -void LelantusPrimitives::generate_challenge( - const std::vector& group_elements, - const std::string& domain_separator, - Scalar& result_out) { - if (group_elements.empty()) - throw std::runtime_error("Group elements empty while generating a challenge."); - - std::unique_ptr challengeGenerator; - if (domain_separator != "") { - challengeGenerator = std::make_unique>(1); - std::vector pre(domain_separator.begin(), domain_separator.end()); - challengeGenerator->add(pre); - } else { - challengeGenerator = std::make_unique>(0); - } - - challengeGenerator->add(group_elements); - challengeGenerator->get_challenge(result_out); -} - -void LelantusPrimitives::commit(const GroupElement& g, - const std::vector& h, - const std::vector& exp, - const Scalar& r, - GroupElement& result_out) { - secp_primitives::MultiExponent mult(h, exp); - result_out = g * r + mult.get_multiple(); -} - -GroupElement LelantusPrimitives::commit( - const GroupElement& g, - const Scalar& m, - const GroupElement& h, - const Scalar& r) { - return g * m + h * r; -} - -GroupElement LelantusPrimitives::double_commit( - const GroupElement& g, - const Scalar& m, - const GroupElement& hV, - const Scalar& v, - const GroupElement& hR, - const Scalar& r) { - return g * m + hV * v + hR * r; -} - -void LelantusPrimitives::convert_to_sigma( - std::size_t num, - std::size_t n, - std::size_t m, - std::vector& out) { - out.reserve(n * m); - Scalar one(uint64_t(1)); - Scalar zero(uint64_t(0)); - - for (std::size_t j = 0; j < m; ++j) - { - for (std::size_t i = 0; i < n; ++i) - { - if(i == (num % n)) - out.emplace_back(one); - else - out.emplace_back(zero); - } - num /= n; - } -} - -std::vector LelantusPrimitives::convert_to_nal( - std::size_t num, - std::size_t n, - std::size_t m) { - std::vector result; - result.reserve(m); - while (num != 0) - { - result.emplace_back(num % n); - num /= n; - } - result.resize(m); - return result; -} - -void LelantusPrimitives::generate_Lelantus_challenge( - const std::vector& proofs, - const std::vector>& anonymity_set_hashes, - const std::vector& serialNumbers, - const std::vector>& ecdsaPubkeys, - const std::vector& Cout, - unsigned int version, - std::unique_ptr& challengeGenerator, - Scalar& result_out) { - - result_out = uint64_t(1); - - // starting from LELANTUS_TX_VERSION_4_5 we are using CHash256, and adding domain separator, version, pubkeys and serials into it - if (version >= LELANTUS_TX_VERSION_4_5) { - challengeGenerator = std::make_unique>(1); - std::string domainSeparator = lts + std::to_string(version); - std::vector pre(domainSeparator.begin(), domainSeparator.end()); - challengeGenerator->add(pre); - for (const auto& hash : anonymity_set_hashes) - challengeGenerator->add(hash); - for (const auto& pubkey : ecdsaPubkeys) - challengeGenerator->add(pubkey); - challengeGenerator->add(serialNumbers); - } else { - challengeGenerator = std::make_unique>(0); - } - - if (Cout.size() > 0) { - for (auto coin : Cout) - challengeGenerator->add(coin); - } - - if (proofs.size() > 0) { - for (std::size_t i = 0; i < proofs.size(); ++i) { - challengeGenerator->add(proofs[i].A_); - challengeGenerator->add(proofs[i].B_); - challengeGenerator->add(proofs[i].C_); - challengeGenerator->add(proofs[i].D_); - challengeGenerator->add(proofs[i].Gk_); - challengeGenerator->add(proofs[i].Qk); - } - - challengeGenerator->get_challenge(result_out); - } -} - -void LelantusPrimitives::new_factor( - const Scalar& x, - const Scalar& a, - std::vector& coefficients) { - if(coefficients.empty()) - throw std::runtime_error("Empty coefficients array."); - - std::size_t degree = coefficients.size(); - coefficients.push_back(x * coefficients[degree-1]); - for (std::size_t d = degree-1; d >= 1; --d) - coefficients[d] = a * coefficients[d] + x * coefficients[d-1]; - coefficients[0] *= a; -} - -void LelantusPrimitives::commit( - const GroupElement& h, - const Scalar& h_exp, - const std::vector& g_, - const std::vector& L, - const std::vector& h_, - const std::vector& R, - GroupElement& result_out) { - secp_primitives::MultiExponent g_mult(g_, L); - secp_primitives::MultiExponent h_mult(h_, R); - result_out += h * h_exp + g_mult.get_multiple() + h_mult.get_multiple(); -} - -Scalar LelantusPrimitives::scalar_dot_product( - typename std::vector::const_iterator a_start, - typename std::vector::const_iterator a_end, - typename std::vector::const_iterator b_start, - typename std::vector::const_iterator b_end) { - Scalar result(uint64_t(0)); - auto itr_a = a_start; - auto itr_b = b_start; - while (itr_a != a_end || itr_b != b_end) - { - result += ((*itr_a) * (*itr_b)); - ++itr_a; - ++itr_b; - } - return result; -} - -void LelantusPrimitives::g_prime( - const std::vector& g_, - const Scalar& x, - std::vector& result){ - Scalar x_inverse = x.inverse(); - result.reserve(g_.size() / 2); - for (std::size_t i = 0; i < g_.size() / 2; ++i) - { - result.push_back(((g_[i] * x_inverse) + (g_[g_.size() / 2 + i] * x))); - } -} - -void LelantusPrimitives::h_prime( - const std::vector& h_, - const Scalar& x, - std::vector& result) { - Scalar x_inverse = x.inverse(); - result.reserve(h_.size() / 2); - for (std::size_t i = 0; i < h_.size() / 2; ++i) - { - result.push_back(((h_[i] * x) + (h_[h_.size() / 2 + i] * x_inverse))); - } -} - -GroupElement LelantusPrimitives::p_prime( - const GroupElement& P_, - const GroupElement& L, - const GroupElement& R, - const Scalar& x){ - Scalar x_square = x.square(); - return L * x_square + P_ + R * (x_square.inverse()); -} - -Scalar LelantusPrimitives::delta(const Scalar& y, const Scalar& z, std::size_t n, std::size_t m){ - Scalar y_; - Scalar two_; - NthPower y_n(y); - NthPower two_n(uint64_t(2)); - NthPower z_j(z, z.exponent(uint64_t(3))); - Scalar z_sum(uint64_t(0)); - - for(std::size_t j = 0; j < m; ++j) - { - for(std::size_t i = 0; i < n; ++i) - { - y_ += y_n.pow; - y_n.go_next(); - } - z_sum += z_j.pow; - z_j.go_next(); - } - - for(std::size_t i = 0; i < n; ++i) - { - two_ += two_n.pow; - two_n.go_next(); - } - - return (z - z.square()) * y_ - z_sum * two_; -} - -}//namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/lelantus_primitives.h b/src/liblelantus/lelantus_primitives.h deleted file mode 100644 index 7f28c34d0d..0000000000 --- a/src/liblelantus/lelantus_primitives.h +++ /dev/null @@ -1,123 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_LELANTUSPRIMITIVES_H -#define FIRO_LIBLELANTUS_LELANTUSPRIMITIVES_H - -#include -#include -#include -#include "sigmaextended_proof.h" -#include "lelantus_proof.h" -#include "schnorr_proof.h" -#include "innerproduct_proof.h" -#include "range_proof.h" -#include "challenge_generator.h" -#include "../firo_params.h" - -#include "serialize.h" - -#include -#include - -namespace lelantus { - -struct NthPower { - Scalar num; - Scalar pow; - - NthPower(const Scalar& num_) : num(num_), pow(uint64_t(1)) {} - NthPower(const Scalar& num_, const Scalar& pow_) : num(num_), pow(pow_) {} - - // be careful and verify that you have catch on upper level - void go_next() { - pow *= num; - if (pow == Scalar(uint64_t(1))) - throw std::invalid_argument("NthPower resulted 1"); - } -}; - -class LelantusPrimitives { - -public: -////common functions - static std::vector invert(const std::vector& scalars); - - static void generate_challenge( - const std::vector& group_elements, - const std::string& domain_separator, - Scalar& result_out); - - static GroupElement commit( - const GroupElement& g, - const Scalar& m, - const GroupElement& h, - const Scalar& r); - - static GroupElement double_commit( - const GroupElement& g, - const Scalar& m, - const GroupElement& hV, - const Scalar& v, - const GroupElement& hR, - const Scalar& r); -////functions for sigma - static void commit( - const GroupElement& g, - const std::vector& h, - const std::vector& exp, - const Scalar& r, - GroupElement& result_out); - - static void convert_to_sigma(std::size_t num, std::size_t n, std::size_t m, std::vector& out); - - static std::vector convert_to_nal(std::size_t num, std::size_t n, std::size_t m); - - static void generate_Lelantus_challenge( - const std::vector& proofs, - const std::vector>& anonymity_set_hashes, - const std::vector& serialNumbers, - const std::vector>& ecdsaPubkeys, - const std::vector& Cout, - unsigned int version, - std::unique_ptr& challengeGenerator, - Scalar& result_out); - - static void new_factor(const Scalar& x, const Scalar& a, std::vector& coefficients); -//// functions for bulletproofs - static void commit( - const GroupElement& h, - const Scalar& h_exp, - const std::vector& g_, - const std::vector& L, - const std::vector& h_, - const std::vector& R, - GroupElement& result_out); - - // computes dot product of two Scalar vectors - static Scalar scalar_dot_product( - typename std::vector::const_iterator a_start, - typename std::vector::const_iterator a_end, - typename std::vector::const_iterator b_start, - typename std::vector::const_iterator b_end); - - static void g_prime( - const std::vector& g_, - const Scalar& x, - std::vector& result); - - static void h_prime( - const std::vector& h_, - const Scalar& x, - std::vector& result); - - static GroupElement p_prime( - const GroupElement& P_, - const GroupElement& L, - const GroupElement& R, - const Scalar& x); - - static Scalar delta(const Scalar& y, const Scalar& z, std::size_t n, std::size_t m); - -}; - -}// namespace lelantus - -#endif //FIRO_LIBLELANTUS_LELANTUSPRIMITIVES_H diff --git a/src/liblelantus/lelantus_proof.h b/src/liblelantus/lelantus_proof.h deleted file mode 100644 index 463c7ac276..0000000000 --- a/src/liblelantus/lelantus_proof.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_LELANTUSPROOF_H -#define FIRO_LIBLELANTUS_LELANTUSPROOF_H - -#include "sigmaextended_proof.h" -#include "schnorr_proof.h" -#include "range_proof.h" -#include "params.h" - -using namespace secp_primitives; - -namespace lelantus { - -class LelantusProof { -public: - //n is the number of input coins, bulletproof_n is number of output coins, - inline std::size_t memoryRequired(int n, int bulletproof_n, int bulletproof_m) const { - return sigma_proofs[0].memoryRequired() * n - + bulletproofs.memoryRequired(bulletproof_n, bulletproof_m) - + schnorrProof.memoryRequired(); - } - - ADD_SERIALIZE_METHODS; - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(sigma_proofs); - READWRITE(bulletproofs); - READWRITE(schnorrProof); - } - -public: - std::vector sigma_proofs; - RangeProof bulletproofs; - SchnorrProof schnorrProof; -}; -}//namespace lelantus - -#endif //FIRO_LIBLELANTUS_LELANTUSPROOF_H diff --git a/src/liblelantus/lelantus_prover.cpp b/src/liblelantus/lelantus_prover.cpp deleted file mode 100644 index 4450d6fd30..0000000000 --- a/src/liblelantus/lelantus_prover.cpp +++ /dev/null @@ -1,282 +0,0 @@ -#include "lelantus_prover.h" -#include "threadpool.h" -#include "util.h" - -namespace lelantus { - -LelantusProver::LelantusProver(const Params* p, unsigned int v) : params(p), version(v) { -} - -void LelantusProver::proof( - const std::map>& anonymity_sets, - const std::vector>& anonymity_set_hashes, - const Scalar& Vin, - const std::vector>& Cin, - const std::vector& indexes, - const std::vector>& ecdsaPubkeys, - const Scalar& Vout, - const std::vector& Cout, - const Scalar& fee, - LelantusProof& proof_out, - SchnorrProof& qkSchnorrProof) { - Scalar input = Vin; - for (std::size_t i = 0; i < Cin.size(); ++i) - input += Cin[i].first.getV(); - - Scalar out = Vout; - for (std::size_t i = 0; i < Cout.size(); ++i) - out += Cout[i].getV(); - out += fee; - - if (input != out) - throw std::runtime_error("Input and output are not equal"); - - Scalar x; - std::vector Yk_sum; - Yk_sum.resize(Cin.size()); - // we are passing challengeGenerator ptr here, as after LELANTUS_TX_VERSION_4_5 we need it back, with filled data, to use in schnorr proof, - std::unique_ptr challengeGenerator; - generate_sigma_proofs(anonymity_sets, anonymity_set_hashes, Cin, Cout, indexes, ecdsaPubkeys, x, challengeGenerator, Yk_sum, proof_out.sigma_proofs, qkSchnorrProof); - - generate_bulletproofs(Cout, proof_out.bulletproofs); - - Scalar x_m = x.exponent(params->get_sigma_m()); - - Scalar X_; - Scalar So; - Scalar Ro; - GroupElement A; - for (std::size_t i = 0; i < Cout.size(); ++i) - { - So += Cout[i].getSerialNumber(); - Ro += Cout[i].getRandomness(); - A += Cout[i].getPublicCoin().getValue(); - } - X_ = So * x_m; - A *= x_m; - A += params->get_h1() * ((Vout + fee) * x_m); - Scalar Y_; - Scalar Ri; - Scalar Vi = Vin; - for (std::size_t i = 0; i < Cin.size(); ++i) - { - Ri += Cin[i].first.getRandomness() * x_m + Yk_sum[i]; - Vi += Cin[i].first.getVScalar(); - } - Y_ = Ro * x_m - Ri; - Vi *= x_m; - // we are calculating A, B amd Y here as after LELANTUS_TX_VERSION_4_5 we need them for challenge generation in schnorr proof - // also we are getting challengeGenerator with filled data from sigma, - GroupElement B = params->get_h1() * Vi + params->get_h0() * Ri; - GroupElement Y = A + B.inverse(); - SchnorrProver schnorrProver(params->get_g(), params->get_h0(), version >= LELANTUS_TX_VERSION_4_5); - schnorrProver.proof(X_, Y_, Y, A, B, challengeGenerator, proof_out.schnorrProof); -} - -void LelantusProver::generate_sigma_proofs( - const std::map>& c, - const std::vector>& anonymity_set_hashes, - const std::vector>& Cin, - const std::vector& Cout, - const std::vector& indexes, - const std::vector>& ecdsaPubkeys, - Scalar& x, - std::unique_ptr& challengeGenerator, - std::vector& Yk_sum, - std::vector& sigma_proofs, - SchnorrProof& qkSchnorrProof) { - SigmaExtendedProver sigmaProver(params->get_g(), params->get_sigma_h(), params->get_sigma_n(), params->get_sigma_m()); - sigma_proofs.resize(Cin.size()); - std::size_t N = Cin.size(); - std::vector rA, rB, rC, rD; - rA.resize(N); - rB.resize(N); - rC.resize(N); - rD.resize(N); - std::vector> sigma; - sigma.resize(N); - std::vector> Tk, Pk, Yk; - Tk.resize(N); - Pk.resize(N); - Yk.resize(N); - std::vector> a; - a.resize(N); - std::vector serialNumbers; - serialNumbers.reserve(N); - - std::size_t threadsMaxCount = std::min((unsigned int)N, boost::thread::hardware_concurrency()); - std::vector> parallelTasks; - parallelTasks.reserve(threadsMaxCount); - ParallelOpThreadPool threadPool(threadsMaxCount); - - std::vector> C_; - C_.resize(N); - DoNotDisturb dnd; - for (std::size_t j = 0; j < N; j += threadsMaxCount) { - for (std::size_t i = j; i < j + threadsMaxCount; ++i) { - if (i < N) { - if (!c.count(Cin[i].second)) - throw std::invalid_argument("No such anonymity set or id is not correct"); - - GroupElement gs = (params->get_g() * Cin[i].first.getSerialNumber().negate()); - serialNumbers.emplace_back(Cin[i].first.getSerialNumber()); - - C_[i].reserve(c.size()); - - const auto& set = c.find(Cin[i].second); - if (set == c.end()) - throw std::invalid_argument("No such anonymity set"); - - for (auto const &coin : set->second) - C_[i].emplace_back(coin.getValue() + gs); - - rA[i].randomize(); - rB[i].randomize(); - rC[i].randomize(); - rD[i].randomize(); - Tk[i].resize(params->get_sigma_m()); - Pk[i].resize(params->get_sigma_m()); - Yk[i].resize(params->get_sigma_m()); - a[i].resize(params->get_sigma_n() * params->get_sigma_m()); - - auto& sigma_i = sigma[i]; - auto& rA_i = rA[i]; - auto& rB_i = rB[i]; - auto& rC_i = rC[i]; - auto& rD_i = rD[i]; - auto& a_i = a[i]; - auto& Tk_i = Tk[i]; - auto& Pk_i = Pk[i]; - auto& Yk_i = Yk[i]; - auto& prover = sigmaProver; - auto& commits = C_[i]; - auto& index = indexes[i]; - auto& proof = sigma_proofs[i]; - parallelTasks.emplace_back(threadPool.PostTask([&]() { - try { - prover.sigma_commit(commits, index, rA_i, rB_i, rC_i, rD_i, a_i, Tk_i, Pk_i, Yk_i, sigma_i, proof); - } catch (const std::exception &) { - return false; - } - return true; - })); - } else - break; - } - - bool isFail = false; - for (auto& th : parallelTasks) { - if (!th.get()) - isFail = true; - } - - if (isFail) - throw std::runtime_error("Lelantus proof creation failed."); - - parallelTasks.clear(); - } - - std::vector PubcoinsOut; - PubcoinsOut.reserve(Cout.size()); - for(auto coin : Cout) - PubcoinsOut.emplace_back(coin.getPublicCoin().getValue()); - LelantusPrimitives::generate_Lelantus_challenge( - sigma_proofs, - anonymity_set_hashes, - serialNumbers, - ecdsaPubkeys, - PubcoinsOut, - version, - challengeGenerator, - x); - - std::vector x_ks; - x_ks.reserve(params->get_sigma_m()); - NthPower x_k(x); - for (int k = 0; k < params->get_sigma_m(); ++k) { - x_ks.emplace_back(x_k.pow); - x_k.go_next(); - } - - for (std::size_t i = 0; i < N; ++i) { - for (int k = 0; k < params->get_sigma_m(); ++k) { - Yk_sum[i] += Yk[i][k] * x_ks[k]; - } - } - - for (std::size_t i = 0; i < N; ++i){ - const Scalar& v = Cin[i].first.getV(); - const Scalar& r = Cin[i].first.getRandomness(); - sigmaProver.sigma_response(sigma[i], a[i], rA[i], rB[i], rC[i], rD[i], v, r, Tk[i], Pk[i], x, sigma_proofs[i]); - } - - // generate schnorr proof to prove that Q_k is generated honestly; - if (version >= LELANTUS_TX_VERSION_4_5) { - Scalar q_k_x; - challengeGenerator->get_challenge(q_k_x); - NthPower qk_x_n(q_k_x); - - Scalar Pk_sum(uint64_t(0)); - Scalar Tk_Yk_sum(uint64_t(0)); - std::vector Qk; - Qk.reserve(N * params->get_sigma_m()); - for (std::size_t i = 0; i < N; ++i) { - for (std::size_t j = 0; cmp::less(j, params->get_sigma_m()); ++j) { - Pk_sum += (Pk[i][j] * qk_x_n.pow); - Tk_Yk_sum += ((Tk[i][j] + Yk[i][j]) * qk_x_n.pow); - qk_x_n.go_next(); - Qk.emplace_back(sigma_proofs[i].Qk[j]); - } - } - - SchnorrProver schnorrProver(params->get_h1(), params->get_h0(), true); - schnorrProver.proof(Pk_sum, Tk_Yk_sum, Qk, qkSchnorrProof); - } -} - -void LelantusProver::generate_bulletproofs( - const std::vector& Cout, - RangeProof& bulletproofs) { - if (Cout.empty()) - return; - - std::vector v_s, serials, randoms; - std::size_t n = params->get_bulletproofs_n(); - std::size_t m = Cout.size() * 2; - - while (m & (m - 1)) - m++; - - v_s.reserve(m); - serials.reserve(m); - randoms.reserve(m); - // NOTE: this prepends zero-value group elements, apparently as an earlier coding error - // This doesn't hurt anything, and so is retained here for compatibility reasons - std::vector commitments(Cout.size()); - for (std::size_t i = 0; i < Cout.size(); ++i) - { - v_s.push_back(Cout[i].getV()); // Ensure that v >= 0 - v_s.push_back(Cout[i].getVScalar() + params->get_limit_range()); // Ensure that v <= ZC_LELANTUS_MAX_MINT - serials.insert(serials.end(), 2, Cout[i].getSerialNumber()); - randoms.insert(randoms.end(), 2, Cout[i].getRandomness()); - commitments.emplace_back(Cout[i].getPublicCoin().getValue()); - } - - v_s.resize(m); - serials.resize(m); - randoms.resize(m); - - std::vector g_, h_; - g_.reserve(n * m); - h_.reserve(n * m); - - - g_.insert(g_.end(), params->get_bulletproofs_g().begin(), params->get_bulletproofs_g().begin() + (n * m)); - h_.insert(h_.end(), params->get_bulletproofs_h().begin(), params->get_bulletproofs_h().begin() + (n * m)); - - RangeProver rangeProver(params->get_h1(), params->get_h0(), params->get_g(), g_, h_, n, version); - rangeProver.proof(v_s, serials, randoms, commitments, bulletproofs); - -} - -}//namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/lelantus_prover.h b/src/liblelantus/lelantus_prover.h deleted file mode 100644 index c271873b77..0000000000 --- a/src/liblelantus/lelantus_prover.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_LELANTUSPROVER_H -#define FIRO_LIBLELANTUS_LELANTUSPROVER_H - -#include "schnorr_prover.h" -#include "sigmaextended_prover.h" -#include "range_prover.h" -#include "coin.h" - -namespace lelantus { - -class LelantusProver { -public: - LelantusProver(const Params* p, unsigned int v); - void proof( - const std::map>& anonymity_sets, - const std::vector>& anonymity_set_hashes, - const Scalar& Vin, - const std::vector>& Cin, - const std::vector & indexes, - const std::vector>& ecdsaPubkeys, - const Scalar& Vout, - const std::vector & Cout, - const Scalar& fee, - LelantusProof& proof_out, - SchnorrProof& qkSchnorrProof); - -private: - void generate_sigma_proofs( - const std::map>& c, - const std::vector>& anonymity_set_hashes, - const std::vector>& Cin, - const std::vector& Cout, - const std::vector& indexes, - const std::vector>& ecdsaPubkeys, - Scalar& x, - std::unique_ptr& challengeGenerator, - std::vector& Yk_sum, - std::vector& sigma_proofs, - SchnorrProof& qkSchnorrProof); - - void generate_bulletproofs( - const std::vector & Cout, - RangeProof& bulletproofs); - -private: - const Params* params; - unsigned int version; -}; -}// namespace lelantus - -#endif //FIRO_LIBLELANTUS_LELANTUSPROVER_H diff --git a/src/liblelantus/lelantus_verifier.cpp b/src/liblelantus/lelantus_verifier.cpp deleted file mode 100644 index 5eebc57d7a..0000000000 --- a/src/liblelantus/lelantus_verifier.cpp +++ /dev/null @@ -1,294 +0,0 @@ -#include "lelantus_verifier.h" -#include "../amount.h" -#include "chainparams.h" -#include "util.h" - -namespace lelantus { - -LelantusVerifier::LelantusVerifier(const Params* p, unsigned int v) : params(p), version(v) { -} - -bool LelantusVerifier::verify( - const std::map>& anonymity_sets, - const std::vector>& anonymity_set_hashes, - const std::vector& serialNumbers, - const std::vector>& ecdsaPubkeys, - const std::vector& groupIds, - const Scalar& Vin, - uint64_t Vout, - uint64_t fee, - const std::vector& Cout, - const LelantusProof& proof, - const SchnorrProof& qkSchnorrProof) { - Scalar x; - bool fSkipVerification = 0; - return verify(anonymity_sets, anonymity_set_hashes, serialNumbers, ecdsaPubkeys, groupIds, Vin, Vout, fee, Cout, proof, qkSchnorrProof, x, fSkipVerification); -} - -bool LelantusVerifier::verify( - const std::map>& anonymity_sets, - const std::vector>& anonymity_set_hashes, - const std::vector& serialNumbers, - const std::vector>& ecdsaPubkeys, - const std::vector& groupIds, - const Scalar& Vin, - uint64_t Vout, - uint64_t fee, - const std::vector& Cout, - const LelantusProof& proof, - const SchnorrProof& qkSchnorrProof, - Scalar& x, - bool fSkipVerification) { - //check the overflow of Vout and fee - if (!(Vout <= uint64_t(::Params().GetConsensus().nMaxValueLelantusSpendPerTransaction) && fee < (1000 * CENT))) { // 1000 * CENT is the value of max fee defined at validation.h - LogPrintf("Lelantus verification failed due to transparent values check failed."); - return false; - } - - // number of serials should be equal to number of sigma proofs, we need one proof for each serial - if (serialNumbers.size() != proof.sigma_proofs.size()) { - LogPrintf("Lelantus verification failed due to sizes of serials and sigma proofs are not equal."); - return false; - } - - // max possible number of output coins is 8, - if (cmp::greater(Cout.size(), (params->get_bulletproofs_max_m() / 2))) { - LogPrintf("Number of output coins are more than allowed."); - return false; - } - - std::vector> vAnonymity_sets; - std::vector> vSin; - vAnonymity_sets.reserve(anonymity_sets.size()); - vSin.resize(anonymity_sets.size()); - - size_t i = 0; - auto itr = vSin.begin(); - for (const auto& set : anonymity_sets) { - vAnonymity_sets.emplace_back(set.second); - - while (i < groupIds.size() && groupIds[i] == set.first) { - itr->push_back(serialNumbers[i++]); - } - itr++; - } - - Scalar zV, zR; - std::unique_ptr challengeGenerator; - try { - // we are passing challengeGenerator ptr here, as after LELANTUS_TX_VERSION_4_5 we need it back, with filled data, to use in schnorr proof, - if (!(verify_sigma(vAnonymity_sets, anonymity_set_hashes, vSin, serialNumbers, ecdsaPubkeys, Cout, proof.sigma_proofs, qkSchnorrProof, x, challengeGenerator, zV, zR, fSkipVerification) && - verify_rangeproof(Cout, proof.bulletproofs, fSkipVerification) && - verify_schnorrproof(x, zV, zR, Vin, Vout, fee, Cout, proof, challengeGenerator))) - return false; - } catch (std::invalid_argument&) { - return false; - } - - return true; -} - -bool LelantusVerifier::verify_sigma( - const std::vector>& anonymity_sets, - const std::vector>& anonymity_set_hashes, - const std::vector>& Sin, - const std::vector& serialNumbers, - const std::vector>& ecdsaPubkeys, - const std::vector& Cout, - const std::vector &sigma_proofs, - const SchnorrProof& qkSchnorrProof, - Scalar& x, - std::unique_ptr& challengeGenerator, - Scalar& zV, - Scalar& zR, - bool fSkipVerification) { - std::vector PubcoinsOut; - PubcoinsOut.reserve(Cout.size()); - for (auto coin : Cout) - PubcoinsOut.emplace_back(coin.getValue()); - - LelantusPrimitives::generate_Lelantus_challenge( - sigma_proofs, - anonymity_set_hashes, - serialNumbers, - ecdsaPubkeys, - PubcoinsOut, - version, - challengeGenerator, - x); - - SigmaExtendedVerifier sigmaVerifier(params->get_g(), params->get_sigma_h(), params->get_sigma_n(), - params->get_sigma_m()); - - if (Sin.size() != anonymity_sets.size()) - throw std::invalid_argument("Number of anonymity sets and number of vectors containing serial numbers must be equal"); - - int t = 0; - for (std::size_t k = 0; k < Sin.size(); k++) { - - std::vector sigma_proofs_k; - for (std::size_t i = 0; i < Sin[k].size(); ++i, ++t) { - zV += sigma_proofs[t].zV_; - zR += sigma_proofs[t].zR_; - sigma_proofs_k.emplace_back(sigma_proofs[t]); - } - - //skip verification if we are collecting proofs for later batch verification - if (fSkipVerification) - continue; - - std::vector C_; - C_.reserve(anonymity_sets[k].size()); - for (std::size_t j = 0; j < anonymity_sets[k].size(); ++j) - C_.emplace_back(anonymity_sets[k][j].getValue()); - - if (!sigmaVerifier.batchverify(C_, x, Sin[k], sigma_proofs_k)) { - LogPrintf("Lelantus verification failed due sigma verification failed."); - return false; - } - } - - // verify schnorr proof to verify that Q_k is generated honestly; - if (version >= LELANTUS_TX_VERSION_4_5) { - Scalar q_k_x; - challengeGenerator->get_challenge(q_k_x); - - NthPower qK_x_n(q_k_x); - GroupElement Gk_sum; - std::vector Qks; - Qks.reserve(sigma_proofs.size() * params->get_sigma_m()); - for (std::size_t t = 0; t < sigma_proofs.size(); ++t) - { - const std::vector& Qk = sigma_proofs[t].Qk; - for (std::size_t k = 0; k < Qk.size(); ++k) - { - Gk_sum += (Qk[k]) * qK_x_n.pow; - qK_x_n.go_next(); - - Qks.emplace_back(Qk[k]); - } - } - - SchnorrVerifier schnorrVerifier(params->get_h1(), params->get_h0(), version >= LELANTUS_TX_VERSION_4_5); - if (!schnorrVerifier.verify(Gk_sum, Qks, qkSchnorrProof)) { - LogPrintf("Lelantus verification failed due to Qk schnorr proof verification failed."); - return false; - } - } - - return true; -} - -bool LelantusVerifier::verify_rangeproof( - const std::vector& Cout, - const RangeProof& bulletproof, - bool fSkipVerification) { - if (Cout.empty() || fSkipVerification) - return true; - - std::size_t n = params->get_bulletproofs_n(); - std::size_t m = Cout.size() * 2; - - while (m & (m - 1)) - m++; - - // NOTE: for actual deployment, need to ensure this vector is constructed to accommodate the largest proof - std::vector g_, h_; - g_.reserve(n * m); - h_.reserve(n * m); - g_.insert(g_.end(), params->get_bulletproofs_g().begin(), params->get_bulletproofs_g().begin() + (n * m)); - h_.insert(h_.end(), params->get_bulletproofs_h().begin(), params->get_bulletproofs_h().begin() + (n * m)); - - std::vector > V; - V.reserve(1); // size of batch - V.resize(1); - V[0].reserve(m); // aggregation size - std::vector > commitments; - commitments.reserve(1); // size of batch - commitments.resize(1); - commitments[0].reserve(2 * Cout.size()); - commitments[0].resize(Cout.size()); // prepend zero elements, to match the prover's behavior - for (std::size_t i = 0; i < Cout.size(); ++i) { - V[0].push_back(Cout[i].getValue()); - V[0].push_back(Cout[i].getValue() + params->get_h1_limit_range()); - commitments[0].emplace_back(Cout[i].getValue()); - } - - std::vector proofs; - proofs.reserve(1); // size of batch - proofs.emplace_back(bulletproof); - - // Pad with zero elements - for (std::size_t i = Cout.size() * 2; i < m; ++i) - V[0].push_back(GroupElement()); - - RangeVerifier rangeVerifier(params->get_h1(), params->get_h0(), params->get_g(), g_, h_, n, version); - if (!rangeVerifier.verify(V, commitments, proofs)) { - LogPrintf("Lelantus verification failed due range proof verification failed."); - return false; - } - return true; -} - -bool LelantusVerifier::verify_schnorrproof( - const Scalar& x, - const Scalar& zV, - const Scalar& zR, - const Scalar& Vin, - const Scalar& Vout, - const Scalar fee, - const std::vector& Cout, - const LelantusProof& proof, - std::unique_ptr& challengeGenerator) { - // x^m - Scalar x_m = x.exponent(params->get_sigma_m()); - - // A is computed directly, to take advantage of point addition - GroupElement A; - for (std::size_t j = 0; j < Cout.size(); ++j) { - A += Cout[j].getValue(); - } - A += params->get_h1() * (Vout + fee); - A *= x_m; - - // B is computed via multiscalar multiplication - std::vector B_points; - std::vector B_scalars; - B_points.emplace_back(params->get_h1()); - B_scalars.emplace_back(Vin*x_m + zV); - B_points.emplace_back(params->get_h0()); - B_scalars.emplace_back(zR); - - NthPower x_k(x); - std::vector x_ks; - x_ks.reserve(params->get_sigma_m()); - for (int k = 0; k < params->get_sigma_m(); ++k) - { - x_ks.emplace_back(x_k.pow); - x_k.go_next(); - } - - for (std::size_t t = 0; t < proof.sigma_proofs.size(); ++t) - { - const std::vector& Qk = proof.sigma_proofs[t].Qk; - for (std::size_t k = 0; k < Qk.size(); ++k) - { - B_points.emplace_back(Qk[k]); - B_scalars.emplace_back(x_ks[k]); - } - } - - GroupElement B = secp_primitives::MultiExponent(B_points, B_scalars).get_multiple(); - - SchnorrVerifier schnorrVerifier(params->get_g(), params->get_h0(), version >= LELANTUS_TX_VERSION_4_5); - const SchnorrProof& schnorrProof = proof.schnorrProof; - GroupElement Y = A + B * (Scalar(uint64_t(1)).negate()); - // after LELANTUS_TX_VERSION_4_5 we are getting challengeGenerator with filled data from sigma, - if (!schnorrVerifier.verify(Y, A, B, schnorrProof, challengeGenerator)) { - LogPrintf("Lelantus verification failed due schnorr proof verification failed."); - return false; - } - return true; -} - -}//namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/lelantus_verifier.h b/src/liblelantus/lelantus_verifier.h deleted file mode 100644 index 26bd54673b..0000000000 --- a/src/liblelantus/lelantus_verifier.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_LELANTUSVERIFIER_H -#define FIRO_LIBLELANTUS_LELANTUSVERIFIER_H - -#include "schnorr_verifier.h" -#include "sigmaextended_verifier.h" -#include "range_verifier.h" -#include "lelantus_primitives.h" -#include "coin.h" - -namespace lelantus { -class LelantusVerifier { -public: - LelantusVerifier(const Params* p, unsigned int v); - - bool verify( - const std::map>& anonymity_sets, - const std::vector>& anonymity_set_hashes, - const std::vector& serialNumbers, - const std::vector>& ecdsaPubkeys, - const std::vector& groupIds, - const Scalar& Vin, - uint64_t Vout, - uint64_t fee, - const std::vector& Cout, - const LelantusProof& proof, - const SchnorrProof& qkSchnorrProof); - - bool verify( - const std::map>& anonymity_sets, - const std::vector>& anonymity_set_hashes, - const std::vector& serialNumbers, - const std::vector>& ecdsaPubkeys, - const std::vector& groupIds, - const Scalar& Vin, - uint64_t Vout, - uint64_t fee, - const std::vector& Cout, - const LelantusProof& proof, - const SchnorrProof& qkSchnorrProof, - Scalar& x, - bool fSkipVerification = false); - -private: - bool verify_sigma( - const std::vector>& anonymity_sets, - const std::vector>& anonymity_set_hashes, - const std::vector>& Sin, - const std::vector& serialNumbers, - const std::vector>& ecdsaPubkeys, - const std::vector& Cout, - const std::vector &sigma_proofs, - const SchnorrProof& qkSchnorrProof, - Scalar& x, - std::unique_ptr& challengeGenerator, - Scalar& zV, - Scalar& zR, - bool fSkipVerification = false); - bool verify_rangeproof( - const std::vector& Cout, - const RangeProof& bulletproofs, - bool fSkipVerification); - bool verify_schnorrproof( - const Scalar& x, - const Scalar& zV, - const Scalar& zR, - const Scalar& Vin, - const Scalar& Vout, - const Scalar fee, - const std::vector& Cout, - const LelantusProof& proof, - std::unique_ptr& challengeGenerator); - -private: - const Params* params; - unsigned int version; - -}; -}// namespace lelantus - -#endif //FIRO_LIBLELANTUS_LELANTUSVERIFIER_H diff --git a/src/liblelantus/openssl_context.h b/src/liblelantus/openssl_context.h deleted file mode 100644 index 430cb7c9d0..0000000000 --- a/src/liblelantus/openssl_context.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef _OPENSSL_CONTEXT_H__ -#define _OPENSSL_CONTEXT_H__ - -#include -#include -#include - -// This class is created for creation of a global openSSL context. -class OpenSSLContext { -public: - static secp256k1_context* get_context() { - return get_instance().ctx; - } - static const OpenSSLContext& get_instance() { - static OpenSSLContext instance; - return instance; - } - - OpenSSLContext() { - ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - unsigned char seed[32]; - if (RAND_bytes(seed, sizeof(seed)) != 1) { - throw std::runtime_error("Unable to generate randomness for context"); - } - if (secp256k1_context_randomize(ctx, seed) != 1) { - throw std::runtime_error("Unable to randomize context"); - }; - } - - ~OpenSSLContext(){ - secp256k1_context_destroy(ctx); - } - -private: - secp256k1_context* ctx; - -}; -#endif // _OPENSSL_CONTEXT_H__ diff --git a/src/liblelantus/params.cpp b/src/liblelantus/params.cpp deleted file mode 100644 index 79bf2ba3b2..0000000000 --- a/src/liblelantus/params.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "params.h" -#include "chainparams.h" -#include -namespace lelantus { - - CCriticalSection Params::cs_instance; - std::unique_ptr Params::instance; - -Params const* Params::get_default() { - if (instance) { - return instance.get(); - } else { - LOCK(cs_instance); - if (instance) { - return instance.get(); - } - - //fixing generator G; - GroupElement g; - if (!(::Params().GetConsensus().IsTestnet())) { - unsigned char buff[32] = {0}; - GroupElement base; - base.set_base_g(); - base.normalSha256(buff); - g.generate(buff); - } - else - g = GroupElement("9216064434961179932092223867844635691966339998754536116709681652691785432045", - "33986433546870000256104618635743654523665060392313886665479090285075695067131"); - - - //fixing n and m; N = n^m = 65,536 - int n = 16; - int m = 4; - - //fixing bulletproof params - int n_rangeProof = 64; - int max_m_rangeProof = 16; - - instance.reset(new Params(g, n, m, n_rangeProof, max_m_rangeProof)); - return instance.get(); - } -} - -Params::Params(const GroupElement& g_, int n_sigma_, int m_sigma_, int n_rangeProof_, int max_m_rangeProof_): - g(g_), - n_sigma(n_sigma_), - m_sigma(m_sigma_), - n_rangeProof(n_rangeProof_), - max_m_rangeProof(max_m_rangeProof_) -{ - - //creating generators for sigma - this->h_sigma.resize(n_sigma * m_sigma); - unsigned char buff0[32] = {0}; - g.normalSha256(buff0); - h_sigma[0].generate(buff0); - for (int i = 1; i < n_sigma * m_sigma; ++i) - { - unsigned char buff[32] = {0}; - h_sigma[i - 1].normalSha256(buff); - h_sigma[i].generate(buff); - } - - //creating generators for bulletproofs - g_rangeProof.resize(n_rangeProof * max_m_rangeProof); - h_rangeProof.resize(n_rangeProof * max_m_rangeProof); - g_rangeProof[0].generate(buff0); - unsigned char buff1[32] = {0}; - g_rangeProof[0].normalSha256(buff1); - h_rangeProof[0].generate(buff1); - for (int i = 1; i < n_rangeProof * max_m_rangeProof; ++i) - { - unsigned char buff[32] = {0}; - h_rangeProof[i-1].normalSha256(buff); - g_rangeProof[i].generate(buff); - unsigned char buff2[32] = {0}; - g_rangeProof[i].normalSha256(buff2); - h_rangeProof[i].generate(buff2); - } - - limit_range = Scalar(uint64_t(2)).exponent(get_bulletproofs_n()) - ::Params().GetConsensus().nMaxValueLelantusMint; - h1_limit_range = get_h1() * limit_range; -} - -const GroupElement& Params::get_g() const { - return g; -} - -const GroupElement& Params::get_h0() const { - return h_sigma[0]; -} - -const GroupElement& Params::get_h1() const { - return h_sigma[1]; -} - -const std::vector& Params::get_sigma_h() const { - return h_sigma; -} - -const std::vector& Params::get_bulletproofs_g() const { - return g_rangeProof; -} -const std::vector& Params::get_bulletproofs_h() const { - return h_rangeProof; -} - -int Params::get_sigma_n() const { - return n_sigma; -} - -int Params::get_sigma_m() const { - return m_sigma; -} - -int Params::get_bulletproofs_n() const { - return n_rangeProof; -} - -int Params::get_bulletproofs_max_m() const { - return max_m_rangeProof; -} - -const Scalar& Params::get_limit_range() const { - return limit_range; -} - -const GroupElement& Params::get_h1_limit_range() const { - return h1_limit_range; -} - -} //namespace lelantus diff --git a/src/liblelantus/params.h b/src/liblelantus/params.h deleted file mode 100644 index a1760f5e58..0000000000 --- a/src/liblelantus/params.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_PARAMS_H -#define FIRO_LIBLELANTUS_PARAMS_H - -#include -#include -#include -#include - -using namespace secp_primitives; - -namespace lelantus { - -class Params { -public: - static Params const* get_default(); - const GroupElement& get_g() const; - const GroupElement& get_h0() const; - const GroupElement& get_h1() const; - const std::vector& get_sigma_h() const; - const std::vector& get_bulletproofs_g() const; - const std::vector& get_bulletproofs_h() const; - int get_sigma_n() const; - int get_sigma_m() const; - int get_bulletproofs_n() const; - int get_bulletproofs_max_m() const; - const Scalar& get_limit_range() const; - const GroupElement& get_h1_limit_range() const; - -private: - Params(const GroupElement& g_sigma_, int n, int m, int n_rangeProof_, int max_m_rangeProof_); - -private: - static CCriticalSection cs_instance; - static std::unique_ptr instance; - - //sigma params - GroupElement g; - std::vector h_sigma; - int n_sigma; - int m_sigma; - - //bulletproof params - int n_rangeProof; - int max_m_rangeProof; - std::vector g_rangeProof; - std::vector h_rangeProof; - Scalar limit_range; - GroupElement h1_limit_range; -}; - -} // namespace lelantus - -#endif // FIRO_LIBLELANTUS_PARAMS_H diff --git a/src/liblelantus/range_proof.h b/src/liblelantus/range_proof.h deleted file mode 100644 index 44bff5ea4e..0000000000 --- a/src/liblelantus/range_proof.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_RANGEPROOF_H -#define FIRO_LIBLELANTUS_RANGEPROOF_H - -#include "innerproduct_proof.h" -#include - -namespace lelantus { - -class RangeProof{ -public: - - static inline int int_log2(uint64_t number) { - assert(number != 0); - - int l2 = 0; - while ((number >>= 1) != 0) - l2++; - - return l2; - } - - inline std::size_t memoryRequired(int n, int m) const { - int size = int_log2(n * m); - return A.memoryRequired() * 4 - + T_x1.memoryRequired() * 3 - + innerProductProof.memoryRequired(size); - } - - ADD_SERIALIZE_METHODS; - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(A); - READWRITE(S); - READWRITE(T1); - READWRITE(T2); - READWRITE(T_x1); - READWRITE(T_x2); - READWRITE(u); - READWRITE(innerProductProof); - } - - GroupElement A; - GroupElement S; - GroupElement T1; - GroupElement T2; - Scalar T_x1; - Scalar T_x2; - Scalar u; - InnerProductProof innerProductProof; - -}; -}//namespace lelantus - -#endif //FIRO_LIBLELANTUS_RANGE_PROOF_H diff --git a/src/liblelantus/range_prover.cpp b/src/liblelantus/range_prover.cpp deleted file mode 100644 index 66c077be8c..0000000000 --- a/src/liblelantus/range_prover.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include "range_prover.h" -#include "challenge_generator_impl.h" - -namespace lelantus { - -RangeProver::RangeProver( - const GroupElement& g, - const GroupElement& h1, - const GroupElement& h2, - const std::vector& g_vector, - const std::vector& h_vector, - std::size_t n, - unsigned int v) - : g (g) - , h1 (h1) - , h2 (h2) - , g_(g_vector) - , h_(h_vector) - , n (n) - , version (v) -{} - -void RangeProver::proof( - const std::vector& v, - const std::vector& serialNumbers, - const std::vector& randomness, - const std::vector& commitments, - RangeProof& proof_out) { - // Size checks - std::size_t m = v.size(); - if (m == 0) { - throw std::invalid_argument("Range proof cannot use zero inputs"); - } - if ((m & (m - 1)) != 0) { - throw std::invalid_argument("Range proof requires a power-of-two number of aggregated inputs"); - } - if (v.size() != serialNumbers.size() || v.size() != randomness.size()) { - throw std::invalid_argument("Range proof input vector sizes do not match"); - } - if (g_.size() != n * m || h_.size() != n * m) { - throw std::invalid_argument("Range proof generator vector has incorrect size"); - } - - std::vector> bits; - bits.resize(m); - for (std::size_t i = 0; i < v.size(); i++) - v[i].get_bits(bits[i]); - - std::vector aL, aR; - aL.reserve(n * m); - aR.reserve(n * m); - for (std::size_t j = 0; j < m; ++j) - { - for (std::size_t i = 1; i <= n; ++i) - { - aL.emplace_back(uint64_t(bits[j][bits[j].size() - i])); - aR.emplace_back(Scalar(uint64_t(bits[j][bits[j].size() - i])) - Scalar(uint64_t(1))); - } - } - - Scalar alpha; - alpha.randomize(); - LelantusPrimitives::commit(h1, alpha, g_, aL, h_, aR, proof_out.A); - - std::vector sL, sR; - sL.resize(n * m); - sR.resize(n * m); - for (std::size_t i = 0; i < n * m; ++i) - { - sL[i].randomize(); - sR[i].randomize(); - } - - Scalar ro; - ro.randomize(); - LelantusPrimitives::commit(h1, ro, g_, sL, h_, sR, proof_out.S); - - Scalar y, z; - std::unique_ptr challengeGenerator; - if (version >= LELANTUS_TX_VERSION_4_5) { - challengeGenerator = std::make_unique>(1); - // add domain separator and transaction version into transcript - std::string domain_separator = "RANGE_PROOF" + std::to_string(version); - std::vector pre(domain_separator.begin(), domain_separator.end()); - challengeGenerator->add(pre); - challengeGenerator->add(commitments); - } else { - challengeGenerator = std::make_unique>(0); - } - - challengeGenerator->add({proof_out.A, proof_out.S}); - challengeGenerator->get_challenge(y); - challengeGenerator->get_challenge(z); - - //compute l(x) and r(x) polynomials - std::vector> l_x, r_x; - l_x.resize(n * m); - r_x.resize(n * m); - NthPower y_nm(y); - NthPower z_j(z, z.square()); - Scalar z_sum1(uint64_t(0)); - Scalar z_sum2(uint64_t(0)); - - NthPower two_n_(uint64_t(2)); - std::vector two_n; - two_n.reserve(n); - for (std::size_t k = 0; k < n; ++k) - { - two_n.emplace_back(two_n_.pow); - two_n_.go_next(); - } - - for (std::size_t j = 0; j < m; ++j) - { - for (std::size_t i = 0; i < n; ++i) - { - int index = j * n + i; - l_x[index].emplace_back(aL[index] - z); - l_x[index].emplace_back(sL[index]); - - r_x[index].emplace_back(y_nm.pow * (aR[index] + z) + z_j.pow * two_n[i]); - r_x[index].emplace_back(y_nm.pow * sR[index]); - // - y_nm.go_next(); - } - z_sum1 += z_j.pow * randomness[j]; - z_sum2 += z_j.pow * serialNumbers[j]; - z_j.go_next(); - } - - //compute t1 and t2 coefficients - Scalar t0, t1, t2; - for (std::size_t i = 0; i < n * m; ++i) - { - t0 += l_x[i][0] * r_x[i][0]; - t1 += l_x[i][0] * r_x[i][1] + l_x[i][1] * r_x[i][0]; - t2 += l_x[i][1] * r_x[i][1]; - } - - //computing T11 T12 T21 T22; - Scalar T_11, T_12, T_21, T_22; - T_11.randomize(); - T_12.randomize(); - T_21.randomize(); - T_22.randomize(); - proof_out.T1 = LelantusPrimitives::double_commit(g, t1, h1, T_11, h2, T_21); - proof_out.T2 = LelantusPrimitives::double_commit(g, t2, h1, T_12, h2, T_22); - - Scalar x; - challengeGenerator->add({proof_out.T1, proof_out.T2}); - challengeGenerator->get_challenge(x); - - //computing l and r - std::vector l; - std::vector r; - l.reserve(n * m); - r.reserve(n * m); - for (std::size_t i = 0; i < n * m; i++) - { - l.emplace_back(l_x[i][0] + l_x[i][1] * x); - r.emplace_back(r_x[i][0] + r_x[i][1] * x); - } - - proof_out.T_x1 = T_12 * x.square() + T_11 * x + z_sum1; - proof_out.T_x2 = T_22 * x.square() + T_21 * x + z_sum2; - proof_out.u = alpha + ro * x; - - //compute h' - std::vector h_prime; - h_prime.reserve(h_.size()); - NthPower y_i_inv(y.inverse()); - for (std::size_t i = 0; i < h_.size(); ++i) - { - h_prime.emplace_back(h_[i] * y_i_inv.pow); - y_i_inv.go_next(); - } - - int inner_product_version = version >= LELANTUS_TX_VERSION_4_5 ? 2 : 1; - if (version >= LELANTUS_TX_TPAYLOAD) - inner_product_version = 3; - - InnerProductProofGenerator InnerProductProofGenerator(g_, h_prime, g, inner_product_version); - //t^ is calculated inside inner product proof generation with name c - Scalar x_u; - challengeGenerator->add({proof_out.T_x1, proof_out.T_x2, proof_out.u}); - challengeGenerator->get_challenge(x_u); - - if (version >= LELANTUS_TX_VERSION_4_5) { - // add domain separator in each step - std::string domain_separator = "INNER_PRODUCT"; - std::vector pre(domain_separator.begin(), domain_separator.end()); - challengeGenerator->add(pre); - } - - // if(inner_product_version >= 2) link range proof data to inner product transcript with passing already filled challengeGenerator - InnerProductProofGenerator.generate_proof(l, r, x_u, challengeGenerator, proof_out.innerProductProof); -} - -}//namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/range_prover.h b/src/liblelantus/range_prover.h deleted file mode 100644 index a8151d5aca..0000000000 --- a/src/liblelantus/range_prover.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_RANGE_PROVER_H -#define FIRO_LIBLELANTUS_RANGE_PROVER_H - -#include "innerproduct_proof_generator.h" - -namespace lelantus { - -class RangeProver { -public: - RangeProver( - const GroupElement& g - , const GroupElement& h1 - , const GroupElement& h2 - , const std::vector& g_vector - , const std::vector& h_vector - , std::size_t n - , unsigned int v); - - // commitments are included into transcript if version >= LELANTUS_TX_VERSION_4_5 - void proof( - const std::vector& v - , const std::vector& serialNumbers - , const std::vector& randomness - , const std::vector& commitments - , RangeProof& proof_out); - -private: - GroupElement g; - GroupElement h1; - GroupElement h2; - std::vector g_; - std::vector h_; - std::size_t n; - unsigned int version; - -}; - -}//namespace lelantus - -#endif //FIRO_LIBLELANTUS_RANGE_PROVER_H diff --git a/src/liblelantus/range_verifier.cpp b/src/liblelantus/range_verifier.cpp deleted file mode 100644 index e0cb136047..0000000000 --- a/src/liblelantus/range_verifier.cpp +++ /dev/null @@ -1,301 +0,0 @@ -#include "range_verifier.h" -#include "challenge_generator_impl.h" - -#include "../../compat_layer.h" - -// This is based on the 1 Jul 2018 revision of the Bulletproofs preprint: -// https://eprint.iacr.org/2017/1066 - -namespace lelantus { - -RangeVerifier::RangeVerifier( - const GroupElement& g, - const GroupElement& h1, - const GroupElement& h2, - const std::vector& g_vector, - const std::vector& h_vector, - std::size_t n, - unsigned int v) - : g (g) - , h1 (h1) - , h2 (h2) - , g_(g_vector) - , h_(h_vector) - , n (n) - , version (v) -{} - -// Verify a single proof by building a trivial batch -bool RangeVerifier::verify(const std::vector& V, const std::vector& commitments, const RangeProof& proof) { - std::vector > V_batch = {V}; - std::vector > commitments_batch = {commitments}; - std::vector proof_batch = {proof}; - - return verify(V_batch, commitments_batch, proof_batch); -} - -bool RangeVerifier::verify(const std::vector >& V, const std::vector >& commitments, const std::vector& proofs) { - // Preprocess all proofs - if (V.size() != commitments.size() || commitments.size() != proofs.size()) { - return false; - } - std::size_t N_proofs = proofs.size(); - std::size_t max_m = 0; // maximum number of aggregated values - - // Check aggregated input consistency - for (std::size_t k = 0; k < N_proofs; k++) { - std::size_t m = V[k].size(); // number of aggregated inputs - - // Require a power of 2 (if no commitments, valid by default) - if (m == 0) { - return true; - } - if ((m & (m - 1)) != 0) { - return false; - } - - // Track maximum value - if (m > max_m) { - max_m = m; - } - - // Check inner product proof size consistency - std::size_t log_mn = proofs[k].innerProductProof.L_.size(); - if (proofs[k].innerProductProof.R_.size() != log_mn) { - return false; - } - if (cmp::not_equal(RangeProof::int_log2(m*n), log_mn)) { - return false; - } - - // Group membership checks - if (!membership_checks(proofs[k])) { - return false; - } - } - - // Ensure this batch is within bounds - if (max_m*n > g_.size() || max_m*n > h_.size()) { - return false; - } - - // Set up final multiscalar multiplication and common scalars - std::vector points; - std::vector scalars; - Scalar g_scalar(uint64_t(0)); - Scalar h1_scalar(uint64_t(0)); - Scalar h2_scalar(uint64_t(0)); - - // Elements from g- and h-vectors are interleaved in order at the start of the final vectors - for (std::size_t i = 0; i < max_m*n; i++) { - points.emplace_back(g_[i]); - scalars.emplace_back(uint64_t(0)); - points.emplace_back(h_[i]); - scalars.emplace_back(uint64_t(0)); - } - - // Process each proof and add to the batch - for (std::size_t k_proofs = 0; k_proofs < N_proofs; k_proofs++) { - const RangeProof proof = proofs[k_proofs]; - const std::size_t m = V[k_proofs].size(); // number of aggregated inputs - const std::size_t log_mn = proof.innerProductProof.L_.size(); // round count - - // Choose random nonzero weights for batching purposes - // Each weight is used for one of the two verifier equations (98 and 105) - Scalar w1; // equation (98) - w1.randomize(); - Scalar w2; // equation (105) - w2.randomize(); - - // Reconstruct all challenges from this proof - Scalar x, x_u, y, z; - std::unique_ptr challengeGenerator; - - // Newer proofs use domain separation and statement parameters - if (version >= LELANTUS_TX_VERSION_4_5) { - challengeGenerator = std::make_unique>(1); - std::string domain_separator = "RANGE_PROOF" + std::to_string(version); - std::vector pre(domain_separator.begin(), domain_separator.end()); - challengeGenerator->add(pre); - challengeGenerator->add(commitments[k_proofs]); - } else { - challengeGenerator = std::make_unique>(0); - } - challengeGenerator->add({proof.A, proof.S}); - challengeGenerator->get_challenge(y); - challengeGenerator->get_challenge(z); - - challengeGenerator->add({proof.T1, proof.T2}); - challengeGenerator->get_challenge(x); - Scalar x_neg = x.negate(); - - challengeGenerator->add({proof.T_x1, proof.T_x2, proof.u}); - challengeGenerator->get_challenge(x_u); - - const InnerProductProof& innerProductProof = proof.innerProductProof; - std::vector x_j, x_j_inv; - x_j.resize(log_mn); - x_j_inv.reserve(log_mn); - - // Newer proofs use domain separation - if (version >= LELANTUS_TX_VERSION_4_5) { - std::string domain_separator = "INNER_PRODUCT"; - std::vector pre(domain_separator.begin(), domain_separator.end()); - challengeGenerator->add(pre); - } - - if (version >= LELANTUS_TX_TPAYLOAD) - challengeGenerator->add(innerProductProof.c_); - - for (std::size_t i = 0; i < log_mn; ++i) - { - std::vector group_elements_i = {innerProductProof.L_[i], innerProductProof.R_[i]}; - - // if(version >= LELANTUS_TX_VERSION_4_5) we should be using CHash256, - // we want to link transcripts from range proof and from previous iteration in each step, so we are not restarting in that case, - if (version < LELANTUS_TX_VERSION_4_5) { - challengeGenerator.reset(new ChallengeGeneratorImpl(0)); - } - - challengeGenerator->add(group_elements_i); - challengeGenerator->get_challenge(x_j[i]); - } - - // In the event of an attempt to invert a zero scalar, the batch is bad - try { - x_j_inv = LelantusPrimitives::invert(x_j); // NOTE: these could be batched across proofs as well for improved efficiency - } catch (const std::runtime_error&) { - return false; - } - - Scalar z_square_neg = (z.square()).negate(); - Scalar delta = LelantusPrimitives::delta(y, z, n, m); - - NthPower z_m(z); - for (std::size_t j = 0; j < m; ++j) - { - points.emplace_back(V[k_proofs][j]); - scalars.emplace_back(z_square_neg * z_m.pow * w1); - z_m.go_next(); - } - - NthPower y_n_(y.inverse()); - NthPower z_j(z, z.square()); - - NthPower two_n_(uint64_t(2)); - std::vector two_n; - two_n.reserve(n); - for (std::size_t k = 0; k < n; ++k) - { - two_n.emplace_back(two_n_.pow); - two_n_.go_next(); - } - - for (std::size_t t = 0; t < m ; ++t) - { - for (std::size_t k = 0; k < n; ++k) - { - std::size_t i = t * n + k; - Scalar x_il(uint64_t(1)); - Scalar x_ir(uint64_t(1)); - for (std::size_t j = 0; j < log_mn; ++j) - { - if ((i >> j) & 1) { - x_il *= x_j[log_mn - j - 1]; - x_ir *= x_j_inv[log_mn - j - 1]; - } else { - x_il *= x_j_inv[log_mn - j - 1]; - x_ir *= x_j[log_mn - j - 1]; - } - - } - - // g-vector - scalars[2*i] += (x_il * innerProductProof.a_ + z) * w2; - - // h-vector - scalars[2*i + 1] += (y_n_.pow * (x_ir * innerProductProof.b_ - (z_j.pow * two_n[k])) - z) * w2; - - y_n_.go_next(); - } - z_j.go_next(); - } - - // Update common scalars - g_scalar += (innerProductProof.c_ - delta) * w1; - g_scalar += x_u * (innerProductProof.a_ * innerProductProof.b_ - innerProductProof.c_) * w2; - h1_scalar += proof.T_x1 * w1; - h1_scalar += proof.u * w2; - h2_scalar += proof.T_x2 * w1; - - // Add per-proof elements - points.emplace_back(proof.A); - scalars.emplace_back(w2.negate()); - points.emplace_back(proof.T1); - scalars.emplace_back(x_neg * w1); - points.emplace_back(proof.T2); - scalars.emplace_back((x.square()).negate() * w1); - points.emplace_back(proof.S); - scalars.emplace_back(x_neg * w2); - - for (std::size_t j = 0; j < log_mn; ++j) - { - points.emplace_back(innerProductProof.L_[j]); - scalars.emplace_back(x_j[j].square().negate() * w2); - points.emplace_back(innerProductProof.R_[j]); - scalars.emplace_back(x_j_inv[j].square().negate() * w2); - } - } - - // Add common elements - points.emplace_back(g); - scalars.emplace_back(g_scalar); - points.emplace_back(h1); - scalars.emplace_back(h1_scalar); - points.emplace_back(h2); - scalars.emplace_back(h2_scalar); - - // Perform the batch check - secp_primitives::MultiExponent mult(points, scalars); - if(!mult.get_multiple().isInfinity()) { - return false; - } - return true; -} - -// Note: the infinity/zero checks are not required by the protocol; they are only included for historical implementation reasons -bool RangeVerifier::membership_checks(const RangeProof& proof) { - if(!(proof.A.isMember() - && proof.S.isMember() - && proof.T1.isMember() - && proof.T2.isMember() - && proof.T_x1.isMember() - && proof.T_x2.isMember() - && proof.u.isMember() - && proof.innerProductProof.a_.isMember() - && proof.innerProductProof.b_.isMember() - && proof.innerProductProof.c_.isMember()) - || proof.A.isInfinity() - || proof.S.isInfinity() - || proof.T1.isInfinity() - || proof.T2.isInfinity() - || proof.T_x1.isZero() - || proof.T_x2.isZero() - || proof.u.isZero() - || proof.innerProductProof.a_.isZero() - || proof.innerProductProof.b_.isZero() - || proof.innerProductProof.c_.isZero()) - return false; - - for (std::size_t i = 0; i < proof.innerProductProof.L_.size(); ++i) - { - if (!(proof.innerProductProof.L_[i].isMember() && proof.innerProductProof.R_[i].isMember()) - || proof.innerProductProof.L_[i].isInfinity() || proof.innerProductProof.R_[i].isInfinity()) { - return false; - } - } - return true; -} - - -}//namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/range_verifier.h b/src/liblelantus/range_verifier.h deleted file mode 100644 index c8b4e6e11b..0000000000 --- a/src/liblelantus/range_verifier.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_RANGE_VERIFIER_H -#define FIRO_LIBLELANTUS_RANGE_VERIFIER_H - -#include "innerproduct_proof_verifier.h" - -namespace lelantus { - -class RangeVerifier { -public: - //g_vector and h_vector are being kept by reference, be sure it will not be modified from outside - RangeVerifier( - const GroupElement& g - , const GroupElement& h1 - , const GroupElement& h2 - , const std::vector& g_vector - , const std::vector& h_vector - , std::size_t n - , unsigned int v); - - // commitments are included into transcript if version >= LELANTUS_TX_VERSION_4_5 - bool verify(const std::vector& V, const std::vector& commitments, const RangeProof& proof); // single proof - bool verify(const std::vector >& V, const std::vector >& commitments, const std::vector& proof); // batch of proofs - -private: - bool membership_checks(const RangeProof& proof); - -private: - GroupElement g; - GroupElement h1; - GroupElement h2; - const std::vector& g_; - const std::vector& h_; - std::size_t n; - unsigned int version; -}; - -}//namespace lelantus - -#endif //FIRO_LIBLELANTUS_RANGE_VERIFIER_H diff --git a/src/liblelantus/schnorr_proof.h b/src/liblelantus/schnorr_proof.h deleted file mode 100644 index a7894dc800..0000000000 --- a/src/liblelantus/schnorr_proof.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_SCHNORR_PROOF_H -#define FIRO_LIBLELANTUS_SCHNORR_PROOF_H - -#include "params.h" - -namespace lelantus { - -class SchnorrProof{ -public: - inline std::size_t memoryRequired() const { - return u.memoryRequired() + P1.memoryRequired() * 2; - } - - ADD_SERIALIZE_METHODS; - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(u); - READWRITE(P1); - READWRITE(T1); - } - -public: - GroupElement u; - Scalar P1; - Scalar T1; -}; -}//namespace lelantus - -#endif //FIRO_LIBLELANTUS_SCHNORR_PROOF_H diff --git a/src/liblelantus/schnorr_prover.cpp b/src/liblelantus/schnorr_prover.cpp deleted file mode 100644 index a2e5cfb8fb..0000000000 --- a/src/liblelantus/schnorr_prover.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "schnorr_prover.h" -#include "challenge_generator_impl.h" -#include "challenge_generator_impl.h" - -namespace lelantus { - -SchnorrProver::SchnorrProver(const GroupElement& g, const GroupElement& h, bool withFixes_): - g_(g), h_(h), withFixes(withFixes_) { -} - -void SchnorrProver::proof( - const Scalar& P, - const Scalar& T, - const GroupElement& y, - const GroupElement& a, - const GroupElement& b, - std::unique_ptr& challengeGenerator, - SchnorrProof& proof_out){ - Scalar P0; - Scalar T0; - P0.randomize(); - T0.randomize(); - GroupElement u = LelantusPrimitives::commit(g_,P0, h_, T0); - proof_out.u = u; - Scalar c; - std::vector group_elements = {u}; - - std::string shts = ""; - if (withFixes) { - shts = "SCHNORR_PROOF"; - std::vector pre(shts.begin(), shts.end()); - group_elements = {u, y, a, b}; - challengeGenerator->add(pre); - } else { - challengeGenerator.reset(new ChallengeGeneratorImpl(0)); - } - challengeGenerator->add(group_elements); - challengeGenerator->get_challenge(c); - proof_out.P1 = P0 - c * P; - proof_out.T1 = T0 - c * T; -} - -void SchnorrProver::proof( - const Scalar& P, - const Scalar& T, - const std::vector& group_elements, - SchnorrProof& proof_out){ - Scalar P0; - Scalar T0; - P0.randomize(); - T0.randomize(); - GroupElement u = LelantusPrimitives::commit(g_,P0, h_, T0); - proof_out.u = u; - Scalar c; - - ChallengeGeneratorImpl challengeGenerator(1); - std::string shts = "SCHNORR_PROOF"; - std::vector pre(shts.begin(), shts.end()); - challengeGenerator.add(pre); - challengeGenerator.add(group_elements); - challengeGenerator.add(u); - - challengeGenerator.get_challenge(c); - - proof_out.P1 = P0 - c * P; - proof_out.T1 = T0 - c * T; -} - -}//namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/schnorr_prover.h b/src/liblelantus/schnorr_prover.h deleted file mode 100644 index 8936252623..0000000000 --- a/src/liblelantus/schnorr_prover.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_SCHNORR_PROOVER_H -#define FIRO_LIBLELANTUS_SCHNORR_PROOVER_H - -#include "lelantus_primitives.h" - -namespace lelantus { - -class SchnorrProver { -public: - //g and h are being kept by reference, be sure it will not be modified from outside - SchnorrProver(const GroupElement& g, const GroupElement& h, bool withFixes_); - - // values a, b and y are included into transcript if(withFixes_), also better to use CHash256 in that case - void proof(const Scalar& P, const Scalar& T, const GroupElement& y, const GroupElement& a, const GroupElement& b, std::unique_ptr& challengeGenerator, SchnorrProof& proof_out); - void proof(const Scalar& P, const Scalar& T, const std::vector& groupElements, SchnorrProof& proof_out); - -private: - const GroupElement& g_; - const GroupElement& h_; - bool withFixes; -}; - -}//namespace lelantus - -#endif //FIRO_LIBLELANTUS_SCHNORR_PROOVER_H diff --git a/src/liblelantus/schnorr_verifier.cpp b/src/liblelantus/schnorr_verifier.cpp deleted file mode 100644 index 549dac54f4..0000000000 --- a/src/liblelantus/schnorr_verifier.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "schnorr_verifier.h" -#include "challenge_generator_impl.h" - -namespace lelantus { - -SchnorrVerifier::SchnorrVerifier(const GroupElement& g, const GroupElement& h, bool withFixes_): - g_(g), h_(h), withFixes(withFixes_) { -} - -bool SchnorrVerifier::verify( - const GroupElement& y, - const GroupElement& a, - const GroupElement& b, - const SchnorrProof& proof, - std::unique_ptr& challengeGenerator){ - - const GroupElement& u = proof.u; - Scalar c; - std::vector group_elements = {u}; - - std::string shts = ""; - if (withFixes) { - shts = "SCHNORR_PROOF"; - std::vector pre(shts.begin(), shts.end()); - group_elements = {u, y, a, b}; - challengeGenerator->add(pre); - } else { - challengeGenerator.reset(new ChallengeGeneratorImpl(0)); - } - challengeGenerator->add(group_elements); - challengeGenerator->get_challenge(c); - - const Scalar P1 = proof.P1; - const Scalar T1 = proof.T1; - - if (!(u.isMember() && y.isMember() && P1.isMember() && T1.isMember()) || - u.isInfinity() || y.isInfinity() || P1.isZero() || T1.isZero()) - return false; - - GroupElement right = y * c + g_ * P1 + h_ * T1; - if (u == right) { - return true; - } - - return false; -} - -bool SchnorrVerifier::verify( - const GroupElement& y, - const std::vector& groupElements, - const SchnorrProof& proof){ - - const GroupElement& u = proof.u; - Scalar c; - - ChallengeGeneratorImpl challengeGenerator(1); - std::string shts = "SCHNORR_PROOF"; - std::vector pre(shts.begin(), shts.end()); - challengeGenerator.add(pre); - challengeGenerator.add(groupElements); - challengeGenerator.add(u); - - challengeGenerator.get_challenge(c); - - const Scalar P1 = proof.P1; - const Scalar T1 = proof.T1; - - if (!(u.isMember() && y.isMember() && P1.isMember() && T1.isMember()) || - u.isInfinity() || y.isInfinity() || P1.isZero() || T1.isZero()) - return false; - - GroupElement right = y * c + g_ * P1 + h_ * T1; - if (u == right) { - return true; - } - - return false; -} - -}//namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/schnorr_verifier.h b/src/liblelantus/schnorr_verifier.h deleted file mode 100644 index 89693eabf9..0000000000 --- a/src/liblelantus/schnorr_verifier.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_SCHNORR_VERIFIER_H -#define FIRO_LIBLELANTUS_SCHNORR_VERIFIER_H - -#include "lelantus_primitives.h" - -namespace lelantus { - -class SchnorrVerifier { -public: - //g and h are being kept by reference, be sure it will not be modified from outside - SchnorrVerifier(const GroupElement& g, const GroupElement& h, bool withFixes_); - - // values a, b and y are included into transcript if(withFixes_), also better to use CHash256 in that case - bool verify(const GroupElement& y, const GroupElement& a, const GroupElement& b,const SchnorrProof& proof, std::unique_ptr& challengeGenerator); - bool verify(const GroupElement& y, const std::vector& groupElements,const SchnorrProof& proof); - -private: - const GroupElement& g_; - const GroupElement& h_; - bool withFixes; -}; - -}//namespace lelantus - -#endif //FIRO_LIBLELANTUS_SCHNORR_VERIFIER_H diff --git a/src/liblelantus/sigmaextended_proof.h b/src/liblelantus/sigmaextended_proof.h deleted file mode 100644 index 4cf3c02611..0000000000 --- a/src/liblelantus/sigmaextended_proof.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_SIGMAEXTENDED_PROOF_H -#define FIRO_LIBLELANTUS_SIGMAEXTENDED_PROOF_H - -#include -#include "params.h" - -namespace lelantus { - -class SigmaExtendedProof{ -public: - SigmaExtendedProof() = default; - - inline std::size_t memoryRequired() const { - return B_.memoryRequired() * 4 - + ZA_.memoryRequired() * (f_.size() + 2) - + B_.memoryRequired() * Gk_.size() * 2 - + zR_.memoryRequired() * 2; - } - - inline std::size_t memoryRequired(int n, int m) const { - return B_.memoryRequired() * 4 - + ZA_.memoryRequired() * (m*(n - 1) + 2) - + B_.memoryRequired() * m * 2 - + zR_.memoryRequired() * 2; - } - - ADD_SERIALIZE_METHODS; - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(A_); - READWRITE(B_); - READWRITE(C_); - READWRITE(D_); - READWRITE(f_); - READWRITE(ZA_); - READWRITE(ZC_); - READWRITE(Gk_); - READWRITE(Qk); - READWRITE(zV_); - READWRITE(zR_); - } - -public: - GroupElement A_; - GroupElement B_; - GroupElement C_; - GroupElement D_; - std::vector f_; - Scalar ZA_; - Scalar ZC_; - std::vector Gk_; - std::vector Qk; - Scalar zV_; - Scalar zR_; -}; - -} //namespace lelantus - -#endif //FIRO_LIBLELANTUS_SIGMAEXTENDED_PROOF_H diff --git a/src/liblelantus/sigmaextended_prover.cpp b/src/liblelantus/sigmaextended_prover.cpp deleted file mode 100644 index 0c8db5b86f..0000000000 --- a/src/liblelantus/sigmaextended_prover.cpp +++ /dev/null @@ -1,212 +0,0 @@ -#include "sigmaextended_prover.h" - -namespace lelantus { - -SigmaExtendedProver::SigmaExtendedProver( - const GroupElement& g, - const std::vector& h_gens, - std::size_t n, - std::size_t m) - : g_(g) - , h_(h_gens) - , n_(n) - , m_(m) { -} - -// Generate the initial portion of a one-of-many proof -// Internal prover state is maintained separately, in order to facilitate the last part of the proof later -void SigmaExtendedProver::sigma_commit( - const std::vector& commits, - std::size_t l, - const Scalar& rA, - const Scalar& rB, - const Scalar& rC, - const Scalar& rD, - std::vector& a, - std::vector& Tk, - std::vector& Pk, - std::vector& Yk, - std::vector& sigma, - SigmaExtendedProof& proof_out) { - // Sanity checks - if (n_ < 2 || m_ < 2) { - throw std::invalid_argument("Prover parameters are invalid"); - } - std::size_t N = (std::size_t)pow(n_, m_); - std::size_t setSize = commits.size(); - if (setSize == 0) { - throw std::invalid_argument("Cannot have empty commitment set"); - } - if (setSize > N) { - throw std::invalid_argument("Commitment set is too large"); - } - if (l >= setSize) { - throw std::invalid_argument("Signing index is out of range"); - } - if (h_.size() != n_ * m_) { - throw std::invalid_argument("Generator vector size is invalid"); - } - - LelantusPrimitives::convert_to_sigma(l, n_, m_, sigma); - for (std::size_t k = 0; k < m_; ++k) - { - Tk[k].randomize(); - Pk[k].randomize(); - Yk[k].randomize(); - } - - //compute B - LelantusPrimitives::commit(g_, h_, sigma, rB, proof_out.B_); - - //compute A - for (std::size_t j = 0; j < m_; ++j) - { - for (std::size_t i = 1; i < n_; ++i) - { - a[j * n_ + i].randomize(); - a[j * n_] -= a[j * n_ + i]; - } - } - LelantusPrimitives::commit(g_, h_, a, rA, proof_out.A_); - - //compute C - std::vector c; - c.resize(n_ * m_); - Scalar one(uint64_t(1)); - Scalar two(uint64_t(2)); - for (std::size_t i = 0; i < n_ * m_; ++i) - { - c[i] = a[i] * (one - two * sigma[i]); - } - LelantusPrimitives::commit(g_,h_, c, rC, proof_out.C_); - - //compute D - std::vector d; - d.resize(n_ * m_); - for (std::size_t i = 0; i < n_ * m_; i++) - { - d[i] = a[i].square().negate(); - } - LelantusPrimitives::commit(g_,h_, d, rD, proof_out.D_); - - std::vector> P_i_k; - P_i_k.resize(setSize); - for (std::size_t i = 0; i < setSize - 1; ++i) - { - std::vector& coefficients = P_i_k[i]; - std::vector I = LelantusPrimitives::convert_to_nal(i, n_, m_); - coefficients.push_back(a[I[0]]); - coefficients.push_back(sigma[I[0]]); - for (std::size_t j = 1; j < m_; ++j) { - LelantusPrimitives::new_factor(sigma[j * n_ + I[j]], a[j * n_ + I[j]], coefficients); - } - } - - /* - * To optimize calculation of sum of all polynomials indices 's' = setSize-1 through 'n^m-1' we use the - * fact that sum of all of elements in each row of 'a' array is zero. Computation is done by going - * through n-ary representation of 's' and increasing "digit" at each position to 'n-1' one by one. - * During every step digits at higher positions are fixed and digits at lower positions go through all - * possible combinations with a total corresponding polynomial sum of 'x^j'. - * - * The math behind optimization (TeX notation): - * - * \sum_{i=s+1}^{N-1}p_i(x) = - * \sum_{j=0}^{m-1} - * \left[ - * \left( \sum_{i=s_j+1}^{n-1}(\delta_{l_j,i}x+a_{j,i}) \right) - * \left( \prod_{k=j}^{m-1}(\delta_{l_k,s_k}x+a_{k,s_k}) \right) - * x^j - * \right] - */ - - std::vector I = LelantusPrimitives::convert_to_nal(setSize - 1, n_, m_); - std::vector lj = LelantusPrimitives::convert_to_nal(l, n_, m_); - - std::vector p_i_sum; - p_i_sum.emplace_back(one); - std::vector> partial_p_s; - - // Pre-calculate product parts and calculate p_s(x) at the same time, put the latter into p_i_sum - for (std::ptrdiff_t j = m_ - 1; j >= 0; j--) { - partial_p_s.push_back(p_i_sum); - LelantusPrimitives::new_factor(sigma[j*n_ + I[j]], a[j*n_ + I[j]], p_i_sum); - } - - for (std::size_t j = 0; j < m_; j++) { - // \sum_{i=s_j+1}^{n-1}(\delta_{l_j,i}x+a_{j,i}) - Scalar a_sum(uint64_t(0)); - for (std::size_t i = I[j] + 1; i < n_; i++) - a_sum += a[j * n_ + i]; - Scalar x_sum(uint64_t(lj[j] >= I[j]+1 ? 1 : 0)); - - // Multiply by \prod_{k=j}^{m-1}(\delta_{l_k,s_k}x+a_{k,s_k}) - std::vector &polynomial = partial_p_s[m_ - j - 1]; - LelantusPrimitives::new_factor(x_sum, a_sum, polynomial); - - // Multiply by x^j and add to the result - for (std::size_t k = 0; k < m_ - j; k++) - p_i_sum[j + k] += polynomial[k]; - } - - P_i_k[setSize - 1] = p_i_sum; - - proof_out.Gk_.reserve(m_); - proof_out.Qk.reserve(m_); - for (std::size_t k = 0; k < m_; ++k) - { - std::vector P_i; - P_i.reserve(setSize); - for (std::size_t i = 0; i < setSize; ++i){ - P_i.emplace_back(P_i_k[i][k]); - } - secp_primitives::MultiExponent mult(commits, P_i); - GroupElement c_k = mult.get_multiple(); - proof_out.Gk_.emplace_back(c_k + h_[0] * Yk[k].negate()); - proof_out.Qk.emplace_back(LelantusPrimitives::double_commit(g_, Scalar(uint64_t(0)), h_[1], Pk[k], h_[0], Tk[k]) + h_[0] * Yk[k]); - - } -} - -// Generate the rest of the one-of-many proof -void SigmaExtendedProver::sigma_response( - const std::vector& sigma, - const std::vector& a, - const Scalar& rA, - const Scalar& rB, - const Scalar& rC, - const Scalar& rD, - const Scalar& v, - const Scalar& r, - const std::vector& Tk, - const std::vector& Pk, - const Scalar& x, - SigmaExtendedProof& proof_out) { - - //f - proof_out.f_.reserve(m_ * (n_ - 1)); - for (std::size_t j = 0; j < m_; j++) - { - for (std::size_t i = 1; i < n_; i++) - proof_out.f_.emplace_back(sigma[(j * n_) + i] * x + a[(j * n_) + i]); - } - //zA, zC - proof_out.ZA_ = rB * x + rA; - proof_out.ZC_ = rC * x + rD; - - //computing z - proof_out.zV_ = v * x.exponent(uint64_t(m_)); - proof_out.zR_ = r * x.exponent(uint64_t(m_)); - Scalar sumV, sumR; - - NthPower x_k(x); - for (std::size_t k = 0; k < m_; ++k) { - sumV += (Pk[k] * x_k.pow); - sumR += (Tk[k] * x_k.pow); - x_k.go_next(); - } - proof_out.zV_ -= sumV; - proof_out.zR_ -= sumR; -} - -}//namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/sigmaextended_prover.h b/src/liblelantus/sigmaextended_prover.h deleted file mode 100644 index fe2e79c914..0000000000 --- a/src/liblelantus/sigmaextended_prover.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_SIGMAEXTENDED_PROVER_H -#define FIRO_LIBLELANTUS_SIGMAEXTENDED_PROVER_H - -#include "lelantus_primitives.h" - -namespace lelantus { - -class SigmaExtendedProver{ - -public: - SigmaExtendedProver(const GroupElement& g, - const std::vector& h_gens, std::size_t n, std::size_t m); - - void sigma_commit( - const std::vector& commits, - std::size_t l, - const Scalar& rA, - const Scalar& rB, - const Scalar& rC, - const Scalar& rD, - std::vector& a, - std::vector& Tk, - std::vector& Pk, - std::vector& Yk, - std::vector& sigma, - SigmaExtendedProof& proof_out); - - void sigma_response( - const std::vector& sigma, - const std::vector& a, - const Scalar& rA, - const Scalar& rB, - const Scalar& rC, - const Scalar& rD, - const Scalar& v, - const Scalar& r, - const std::vector& Tk, - const std::vector& Pk, - const Scalar& x, - SigmaExtendedProof& proof_out); - - -private: - GroupElement g_; - std::vector h_; - std::size_t n_; - std::size_t m_; -}; - -}//namespace lelantus - -#endif //FIRO_LIBLELANTUS_SIGMAEXTENDED_PROVER_H diff --git a/src/liblelantus/sigmaextended_verifier.cpp b/src/liblelantus/sigmaextended_verifier.cpp deleted file mode 100644 index 46dcceeb08..0000000000 --- a/src/liblelantus/sigmaextended_verifier.cpp +++ /dev/null @@ -1,438 +0,0 @@ -#include "sigmaextended_verifier.h" -#include "util.h" - -namespace lelantus { - -SigmaExtendedVerifier::SigmaExtendedVerifier( - const GroupElement& g, - const std::vector& h_gens, - std::size_t n, - std::size_t m) - : g_(g) - , h_(h_gens) - , n(n) - , m(m){ -} - -// Verify a single one-of-many proof -// In this case, there is an implied input set size -bool SigmaExtendedVerifier::singleverify( - const std::vector& commits, - const Scalar& x, - const Scalar& serial, - const SigmaExtendedProof& proof) const { - std::vector challenges = { x }; - std::vector serials = { serial }; - std::vector setSizes = { }; - std::vector proofs = { proof }; - - return verify( - commits, - challenges, - serials, - setSizes, - true, - false, - proofs - ); -} - -// Verify a single one-of-many proof -// In this case, there is a specified set size -bool SigmaExtendedVerifier::singleverify( - const std::vector& commits, - const Scalar& x, - const Scalar& serial, - const std::size_t setSize, - const SigmaExtendedProof& proof) const { - std::vector challenges = { x }; - std::vector serials = { serial }; - std::vector setSizes = { setSize }; - std::vector proofs = { proof }; - - return verify( - commits, - challenges, - serials, - setSizes, - true, - true, - proofs - ); -} - -// Verify a batch of one-of-many proofs from the same transaction -// In this case, there is a single common challenge and implied input set size -bool SigmaExtendedVerifier::batchverify( - const std::vector& commits, - const Scalar& x, - const std::vector& serials, - const std::vector& proofs) const { - std::vector challenges = { x }; - std::vector setSizes = { }; - - return verify( - commits, - challenges, - serials, - setSizes, - true, - false, - proofs - ); -} - -// Verify a general batch of one-of-many proofs -// In this case, each proof has a separate challenge and specified set size -bool SigmaExtendedVerifier::batchverify( - const std::vector& commits, - const std::vector& challenges, - const std::vector& serials, - const std::vector& setSizes, - const std::vector& proofs) const { - - return verify( - commits, - challenges, - serials, - setSizes, - false, - true, - proofs - ); -} - -// Verify a batch of one-of-many proofs -bool SigmaExtendedVerifier::verify( - const std::vector& commits, - const std::vector& challenges, - const std::vector& serials, - const std::vector& setSizes, - const bool commonChallenge, - const bool specifiedSetSizes, - const std::vector& proofs) const { - // Sanity checks - if (n < 2 || m < 2) { - LogPrintf("Verifier parameters are invalid"); - return false; - } - std::size_t M = proofs.size(); - std::size_t N = (std::size_t)pow(n, m); - - if (commits.size() == 0) { - LogPrintf("Cannot have empty commitment set"); - return false; - } - if (commits.size() > N) { - LogPrintf("Commitment set is too large"); - return false; - } - if (h_.size() != n * m) { - LogPrintf("Generator vector size is invalid"); - return false; - } - if (serials.size() != M) { - LogPrintf("Invalid number of serials provided"); - return false; - } - - // For separate challenges, we must have enough - if (!commonChallenge && challenges.size() != M) { - LogPrintf("Invalid challenge vector size"); - return false; - } - - // If we have specified set sizes, we must have enough - if (specifiedSetSizes && setSizes.size() != M) { - LogPrintf("Invalid set size vector size"); - return false; - } - - // All proof elements must be valid - for (std::size_t t = 0; t < M; ++t) { - if (!membership_checks(proofs[t])) { - LogPrintf("Sigma verification failed due to membership checks failed."); - return false; - } - } - - // Final batch multiscalar multiplication - Scalar g_scalar = Scalar(uint64_t(0)); // associated to g_ - Scalar h1_scalar = Scalar(uint64_t(0)); // associated to h1 - Scalar h2_scalar = Scalar(uint64_t(0)); // associated to h2 - std::vector h_scalars; // associated to h_ - std::vector commit_scalars; // associated to commitment list - h_scalars.reserve(n * m); - h_scalars.resize(n * m); - for (std::size_t i = 0; i < n * m; i++) { - h_scalars[i] = Scalar(uint64_t(0)); - } - commit_scalars.reserve(commits.size()); - commit_scalars.resize(commits.size()); - for (size_t i = 0; i < commits.size(); i++) { - commit_scalars[i] = Scalar(uint64_t(0)); - } - - // Set up the final batch elements - std::vector points; - std::vector scalars; - std::size_t final_size = 3 + m * n + commits.size(); // g, h1, h2, (h_), (commits) - for (std::size_t t = 0; t < M; t++) { - final_size += 4 + proofs[t].Gk_.size() + proofs[t].Qk.size(); // A, B, C, D, (G), (Q) - } - points.reserve(final_size); - scalars.reserve(final_size); - - // Index decomposition, which is common among all proofs - std::vector > I_; - I_.reserve(commits.size()); - I_.resize(commits.size()); - for (std::size_t i = 0; i < commits.size(); i++) { - I_[i] = LelantusPrimitives::convert_to_nal(i, n, m); - } - - // Process all proofs - for (std::size_t t = 0; t < M; t++) { - SigmaExtendedProof proof = proofs[t]; - - // The challenge depends on whether or not we're in common mode - Scalar x; - if (commonChallenge) { - x = challenges[0]; - } - else { - x = challenges[t]; - } - - // Generate random verifier weights - Scalar w1, w2, w3; - w1.randomize(); - w2.randomize(); - w3.randomize(); - - // Reconstruct f-matrix - std::vector f_; - if (!compute_fs(proof, x, f_)) { - LogPrintf("Invalid matrix reconstruction"); - return false; - } - - // Effective set size - std::size_t setSize; - if (!specifiedSetSizes) { - setSize = commits.size(); - } - else { - setSize = setSizes[t]; - } - - // A, B, C, D (and associated commitments) - points.emplace_back(proof.A_); - scalars.emplace_back(w1.negate()); - points.emplace_back(proof.B_); - scalars.emplace_back(x.negate() * w1); - points.emplace_back(proof.C_); - scalars.emplace_back(x.negate() * w2); - points.emplace_back(proof.D_); - scalars.emplace_back(w2.negate()); - - g_scalar += proof.ZA_ * w1 + proof.ZC_ * w2; - for (std::size_t i = 0; i < m * n; i++) { - h_scalars[i] += f_[i] * (w1 + (x - f_[i]) * w2); - } - - // Input sets - h1_scalar += proof.zV_ * w3.negate(); - h2_scalar += proof.zR_ * w3.negate(); - - Scalar f_i(uint64_t(1)); - Scalar e; - std::vector::iterator ptr; - if (!specifiedSetSizes) { - ptr = commit_scalars.begin(); - compute_batch_fis(f_i, m, f_, w3, e, ptr, ptr, ptr + setSize - 1); - } - else { - ptr = commit_scalars.begin() + commits.size() - setSize; - compute_batch_fis(f_i, m, f_, w3, e, ptr, ptr, ptr + setSize - 1); - } - - Scalar pow(uint64_t(1)); - std::vector f_part_product; - for (std::ptrdiff_t j = m - 1; j >= 0; j--) { - f_part_product.push_back(pow); - pow *= f_[j*n + I_[setSize - 1][j]]; - } - - NthPower xj(x); - for (std::size_t j = 0; j < m; j++) { - Scalar fi_sum(uint64_t(0)); - for (std::size_t i = I_[setSize - 1][j] + 1; i < n; i++) - fi_sum += f_[j*n + i]; - pow += fi_sum * xj.pow * f_part_product[m - j - 1]; - xj.go_next(); - } - - commit_scalars[commits.size() - 1] += pow * w3; - e += pow; - - e *= serials[t] * w3.negate(); - g_scalar += e; - - NthPower x_k(x); - for (std::size_t k = 0; k < m; k++) { - points.emplace_back(proof.Gk_[k]); - scalars.emplace_back(x_k.pow.negate() * w3); - points.emplace_back(proof.Qk[k]); - scalars.emplace_back(x_k.pow.negate() * w3); - x_k.go_next(); - } - } - - // Add common generators - points.emplace_back(g_); - scalars.emplace_back(g_scalar); - points.emplace_back(h_[1]); - scalars.emplace_back(h1_scalar); - points.emplace_back(h_[0]); - scalars.emplace_back(h2_scalar); - for (std::size_t i = 0; i < m * n; i++) { - points.emplace_back(h_[i]); - scalars.emplace_back(h_scalars[i]); - } - for (std::size_t i = 0; i < commits.size(); i++) { - points.emplace_back(commits[i]); - scalars.emplace_back(commit_scalars[i]); - } - - // Verify the batch - secp_primitives::MultiExponent result(points, scalars); - if (result.get_multiple().isInfinity()) { - return true; - } - return false; -} - -bool SigmaExtendedVerifier::membership_checks(const SigmaExtendedProof& proof) const { - if (!(proof.A_.isMember() && - proof.B_.isMember() && - proof.C_.isMember() && - proof.D_.isMember()) || - (proof.A_.isInfinity() || - proof.B_.isInfinity() || - proof.C_.isInfinity() || - proof.D_.isInfinity())) - return false; - - for (std::size_t i = 0; i < proof.f_.size(); i++) - { - if (!proof.f_[i].isMember() || proof.f_[i].isZero()) - return false; - } - const std::vector & Gk = proof.Gk_; - const std::vector & Qk = proof.Qk; - for (std::size_t k = 0; k < m; ++k) - { - if (!(Gk[k].isMember() && Qk[k].isMember()) - || Gk[k].isInfinity() || Qk[k].isInfinity()) - return false; - } - if(!(proof.ZA_.isMember() && - proof.ZC_.isMember() && - proof.zV_.isMember() && - proof.zR_.isMember()) || - (proof.ZA_.isZero() || - proof.ZC_.isZero() || - proof.zV_.isZero() || - proof.zR_.isZero())) - return false; - return true; -} - -bool SigmaExtendedVerifier::compute_fs( - const SigmaExtendedProof& proof, - const Scalar& x, - std::vector& f_) const { - for (std::size_t j = 0; j < proof.f_.size(); ++j) { - if(proof.f_[j] == x) - return false; - } - - f_.reserve(n * m); - for (std::size_t j = 0; j < m; ++j) - { - f_.push_back(Scalar(uint64_t(0))); - Scalar temp; - std::size_t k = n - 1; - for (std::size_t i = 0; i < k; ++i) - { - temp += proof.f_[j * k + i]; - f_.emplace_back(proof.f_[j * k + i]); - } - f_[j * n] = x - temp; - } - return true; -} - -void SigmaExtendedVerifier::compute_fis(int j, const std::vector& f, std::vector& f_i_) const { - Scalar f_i(uint64_t(1)); - std::vector::iterator ptr = f_i_.begin(); - compute_fis(f_i, m, f, ptr, f_i_.end()); -} - -void SigmaExtendedVerifier::compute_fis( - const Scalar& f_i, - int j, - const std::vector& f, - std::vector::iterator& ptr, - std::vector::iterator end_ptr) const { - j--; - if (j == -1) - { - if (ptr < end_ptr) - *ptr++ += f_i; - return; - } - - Scalar t; - - for (std::size_t i = 0; i < n; i++) - { - t = f[j * n + i]; - t *= f_i; - - compute_fis(t, j, f, ptr, end_ptr); - } -} - -void SigmaExtendedVerifier::compute_batch_fis( - const Scalar& f_i, - int j, - const std::vector& f, - const Scalar& y, - Scalar& e, - std::vector::iterator& ptr, - std::vector::iterator start_ptr, - std::vector::iterator end_ptr) const { - j--; - if (j == -1) - { - if(ptr >= start_ptr && ptr < end_ptr){ - *ptr++ += f_i * y; - e += f_i; - } - return; - } - - Scalar t; - - for (std::size_t i = 0; i < n; i++) - { - t = f[j * n + i]; - t *= f_i; - - compute_batch_fis(t, j, f, y, e, ptr, start_ptr, end_ptr); - } -} - -} //namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/sigmaextended_verifier.h b/src/liblelantus/sigmaextended_verifier.h deleted file mode 100644 index f0521a41da..0000000000 --- a/src/liblelantus/sigmaextended_verifier.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_SIGMAEXTENDED_VERIFIER_H -#define FIRO_LIBLELANTUS_SIGMAEXTENDED_VERIFIER_H - -#include "lelantus_primitives.h" - -namespace lelantus { - -class SigmaExtendedVerifier{ - -public: - SigmaExtendedVerifier(const GroupElement& g, - const std::vector& h_gens, - std::size_t n_, std::size_t m_); - - // Verify a single one-of-many proof - // In this case, there is an implied input set size - bool singleverify(const std::vector& commits, - const Scalar& x, - const Scalar& serial, - const SigmaExtendedProof& proof) const; - - // Verify a single one-of-many proof - // In this case, there is a specified set size - bool singleverify(const std::vector& commits, - const Scalar& x, - const Scalar& serial, - const size_t setSize, - const SigmaExtendedProof& proof) const; - - // Verify a batch of one-of-many proofs from the same transaction - // In this case, there is a single common challenge and implied input set size - bool batchverify(const std::vector& commits, - const Scalar& x, - const std::vector& serials, - const std::vector& proofs) const; - // Verify a general batch of one-of-many proofs - // In this case, each proof has a separate challenge and specified set size - bool batchverify(const std::vector& commits, - const std::vector& challenges, - const std::vector& serials, - const std::vector& setSizes, - const std::vector& proofs) const; - -private: - // Utility function that actually performs verification - bool verify(const std::vector& commits, - const std::vector& challenges, - const std::vector& serials, - const std::vector& setSizes, - const bool commonChallenge, - const bool specifiedSetSizes, - const std::vector& proofs) const; - //auxiliary functions - bool membership_checks(const SigmaExtendedProof& proof) const; - bool compute_fs( - const SigmaExtendedProof& proof, - const Scalar& x, - std::vector& f_) const; - - void compute_fis(int j, const std::vector& f, std::vector& f_i_) const; - void compute_fis( - const Scalar& f_i, - int j, - const std::vector& f, - std::vector::iterator& ptr, - std::vector::iterator end_ptr) const; - void compute_batch_fis( - const Scalar& f_i, - int j, - const std::vector& f, - const Scalar& y, - Scalar& e, - std::vector::iterator& ptr, - std::vector::iterator start_ptr, - std::vector::iterator end_ptr) const; - -private: - GroupElement g_; - std::vector h_; - std::size_t n; - std::size_t m; -}; - -} // namespace lelantus - -#endif //FIRO_LIBLELANTUS_SIGMAEXTENDED_VERIFIER_H diff --git a/src/liblelantus/spend_metadata.cpp b/src/liblelantus/spend_metadata.cpp deleted file mode 100644 index 64d78ca407..0000000000 --- a/src/liblelantus/spend_metadata.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "spend_metadata.h" - -namespace lelantus { - -SpendMetaData::SpendMetaData( - const std::map& groupBlockHashes, - const uint256& txHash) - : txHash(txHash) { - coinGroupIdAndBlockHash.reserve(groupBlockHashes.size()); - - for(const auto& idAndHash : groupBlockHashes) { - coinGroupIdAndBlockHash.emplace_back(idAndHash.first, idAndHash.second); - } -} - -} // namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/spend_metadata.h b/src/liblelantus/spend_metadata.h deleted file mode 100644 index 3141935722..0000000000 --- a/src/liblelantus/spend_metadata.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_SPENDMETADATA_H_ -#define FIRO_LIBLELANTUS_SPENDMETADATA_H_ - -#include "../uint256.h" -#include "../serialize.h" -#include "coin.h" - -namespace lelantus { - -class SpendMetaData { -public: - - SpendMetaData( - const std::map& groupBlockHashes, - const uint256& txHash); - - std::vector> coinGroupIdAndBlockHash; - - uint256 txHash; // The Hash of the rest of the transaction the spend proof is in. - - // Allows us to sign the transaction. - ADD_SERIALIZE_METHODS; - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(coinGroupIdAndBlockHash); - READWRITE(txHash); - } -}; - -} // namespace lelantus - -#endif // FIRO_LIBLELANTUS_SPENDMETADATA_H_ diff --git a/src/liblelantus/test/challenge_generator_tests.cpp b/src/liblelantus/test/challenge_generator_tests.cpp deleted file mode 100644 index 21e7fa5b5d..0000000000 --- a/src/liblelantus/test/challenge_generator_tests.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "lelantus_test_fixture.h" - -#include "../challenge_generator_impl.h" - -#include -#include - -#include - -namespace lelantus { - -class ChallengeGeneratorTests : public LelantusTestingSetup { -public: - ChallengeGeneratorImpl generator; -}; - -BOOST_FIXTURE_TEST_SUITE(lelantus_challenge_generator_tests, ChallengeGeneratorTests) - -BOOST_AUTO_TEST_CASE(no_input) -{ - Scalar out; - - generator.get_challenge(out); - - // hash of empty - BOOST_CHECK_EQUAL( - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - out.GetHex()); -} - -BOOST_AUTO_TEST_CASE(add_one_at_a_time) -{ - auto gs = GenerateGroupElements(2); - - generator.add(gs[0]); - generator.add(gs[1]); - - Scalar out; - generator.get_challenge(out); - - // hash of gs[0] and gs[1] - BOOST_CHECK_EQUAL( - "e89ba7cb6379a9b940ed9ed3cda18e0f2177019938fbac57e38e5e541e080fc3", - out.GetHex()); -} - -BOOST_AUTO_TEST_CASE(add_bulk) -{ - auto gs = GenerateGroupElements(2); - - generator.add(gs); - - Scalar out; - generator.get_challenge(out); - - // hash of gs[0] and gs[1] - BOOST_CHECK_EQUAL( - "e89ba7cb6379a9b940ed9ed3cda18e0f2177019938fbac57e38e5e541e080fc3", - out.GetHex()); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/test/coin_tests.cpp b/src/liblelantus/test/coin_tests.cpp deleted file mode 100644 index 6abc99b54d..0000000000 --- a/src/liblelantus/test/coin_tests.cpp +++ /dev/null @@ -1,168 +0,0 @@ -#include "../coin.h" -#include "../params.h" -#include "../../version.h" -#include "../lelantus_primitives.h" - -#include "lelantus_test_fixture.h" - -#include "streams.h" - -#include - -namespace lelantus { - -BOOST_FIXTURE_TEST_SUITE(lelantus_coin_tests, lelantus::LelantusTestingSetup) - -BOOST_AUTO_TEST_CASE(privatecoin) -{ - auto params = Params::get_default(); - - uint64_t v = 1; - - // default constructor - PrivateCoin priv(params, v); - auto &serial = priv.getSerialNumber(); - auto &randomness = priv.getRandomness(); - std::vector seckey(32, 0); - - // calculate commitment - auto commitment = LelantusPrimitives::double_commit( - params->get_g(), serial, - params->get_h1(), priv.getV(), - params->get_h0(), randomness); - - // verify - BOOST_CHECK(serial.isMember()); - BOOST_CHECK(randomness.isMember()); - BOOST_CHECK_EQUAL(0, priv.getVersion()); - BOOST_CHECK_EQUAL(v, priv.getV()); - BOOST_CHECK_EQUAL(commitment, priv.getPublicCoin().getValue()); - - // Construct the identical coin by another constructor - PrivateCoin anotherPriv(params, serial, v, randomness, seckey, 0); - - // verify - BOOST_CHECK_EQUAL(serial, anotherPriv.getSerialNumber()); - BOOST_CHECK_EQUAL(randomness, anotherPriv.getRandomness()); - BOOST_CHECK_EQUAL(0, anotherPriv.getVersion()); - BOOST_CHECK_EQUAL(v, anotherPriv.getV()); - BOOST_CHECK_EQUAL(commitment, anotherPriv.getPublicCoin().getValue()); -} - -BOOST_AUTO_TEST_CASE(privcoin_getset) -{ - auto const params = Params::get_default(); - - PrivateCoin coin1(params, 1); - PrivateCoin coin2(params, 2); - - BOOST_CHECK(coin1.getPublicCoin() != coin2.getPublicCoin()); - BOOST_CHECK(coin1.getRandomness() != coin2.getRandomness()); - BOOST_CHECK(coin1.getSerialNumber() != coin2.getSerialNumber()); - BOOST_CHECK(coin1.getV() != coin2.getV()); - - // set - coin2.setPublicCoin(coin1.getPublicCoin()); - coin2.setRandomness(coin1.getRandomness()); - coin2.setSerialNumber(coin1.getSerialNumber()); - coin2.setV(coin1.getV()); - coin2.setVersion(10); - - // verify - BOOST_CHECK(coin1.getPublicCoin() == coin2.getPublicCoin()); - BOOST_CHECK(coin1.getRandomness() == coin2.getRandomness()); - BOOST_CHECK(coin1.getSerialNumber() == coin2.getSerialNumber()); - BOOST_CHECK(coin1.getV() == coin2.getV()); - BOOST_CHECK(10 == coin2.getVersion()); -} - -BOOST_AUTO_TEST_CASE(publiccoin_constructor) -{ - // default - PublicCoin defaultPub; - - BOOST_CHECK(defaultPub.getValue().isInfinity()); - - // specify commitment - GroupElement coin; - coin.randomize(); - PublicCoin pub(coin); - - BOOST_CHECK_EQUAL(coin, pub.getValue()); -} - -BOOST_AUTO_TEST_CASE(publiccoin_hash) -{ - // seed - std::array seed; // 32 bytes of 0s - std::fill(seed.begin(), seed.end(), 0); - - GroupElement r; - r.generate(seed.data()); - - PublicCoin coin(r); - - BOOST_CHECK(coin.validate()); - - // double hash of 7ed851c84a73fce904dc38fd709836dba446d875846522d4f016bba815a21fc10000 - // the result of generated group element using 32 bytes of 0 as seed - BOOST_CHECK_EQUAL( - "c3ca472773e3334f057875b8268dabd16dcda3d2b4cad3e924ff8c31affacf67", - coin.getValueHash().GetHex()); -} - -BOOST_AUTO_TEST_CASE(validate_publiccoin) -{ - // default pubcoin - BOOST_CHECK(!PublicCoin().validate()); - - // inf coin - GroupElement infCoin; - BOOST_CHECK(infCoin.isMember() && infCoin.isInfinity()); - BOOST_CHECK(!PublicCoin(infCoin).validate()); - - // valid coin - GroupElement coin; - coin.randomize(); - BOOST_CHECK(coin.isMember() && !coin.isInfinity()); - BOOST_CHECK(PublicCoin(coin).validate()); -} - -BOOST_AUTO_TEST_CASE(publiccoin_equality) -{ - GroupElement a, b; - a.randomize(); - b.randomize(); - - PublicCoin pubA(a); - PublicCoin pubB(b); - PublicCoin pubC(a); - - BOOST_CHECK(pubA == pubA); - BOOST_CHECK(pubA == pubC); - BOOST_CHECK(pubA != pubB); -} - -BOOST_AUTO_TEST_CASE(publiccoin_serialization) -{ - GroupElement coin; - coin.randomize(); - - PublicCoin pub(coin); - - CDataStream serialize(SER_NETWORK, PROTOCOL_VERSION); - serialize << pub; - - CDataStream deserialize( - std::vector(serialize.begin(), serialize.end()), - SER_NETWORK, PROTOCOL_VERSION); - - PublicCoin deserializedCoin; - deserialize >> deserializedCoin; - - BOOST_CHECK(pub == deserializedCoin); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/test/inner_product_test.cpp b/src/liblelantus/test/inner_product_test.cpp deleted file mode 100755 index e197bf8329..0000000000 --- a/src/liblelantus/test/inner_product_test.cpp +++ /dev/null @@ -1,168 +0,0 @@ -#include "../innerproduct_proof_generator.h" -#include "../innerproduct_proof_verifier.h" -#include "../challenge_generator_impl.h" - -#include "./lelantus_test_fixture.h" - -#include - -namespace lelantus { - -class InnerProductTests : public LelantusTestingSetup { -public: - typedef InnerProductProofGenerator ProofGenerator; - typedef InnerProductProofVerifier ProofVerifier; - typedef InnerProductProof Proof; - -public: - InnerProductTests() {} - -public: - void Generate(size_t n) { - gens_g = RandomizeGroupElements(n); - gens_h = RandomizeGroupElements(n); - a = RandomizeScalars(n); - b = RandomizeScalars(n); - u.randomize(); - } - - Scalar ComputeC() const { - return Primitives::scalar_dot_product(a.begin(), a.end(), b.begin(), b.end()); - } - - GroupElement ComputePInit() const { - return ComputeMultiExponent(gens_g, a) + ComputeMultiExponent(gens_h, b); - } - - GroupElement ComputeP(Scalar const &x) const { - return ComputePInit() + u * ComputeC() * x; - } - -public: - std::vector gens_g; - std::vector gens_h; - std::vector a; - std::vector b; - GroupElement u; -}; - -BOOST_FIXTURE_TEST_SUITE(lelantus_inner_product_tests, InnerProductTests) - -BOOST_AUTO_TEST_CASE(prove_verify_one) -{ - size_t n = 1; - size_t log2_n = 0; - - Generate(n); - - Scalar x; - x.randomize(); - std::unique_ptr challengeGenerator = std::make_unique>(1); - - // generating proofs - Proof proof; - ProofGenerator prover(gens_g, gens_h, u, 2); - prover.generate_proof(a, b, x, challengeGenerator, proof); - - BOOST_CHECK_EQUAL(ComputePInit(), prover.get_P()); - - // validate - BOOST_CHECK_EQUAL(ComputeC(), proof.c_); - BOOST_CHECK_EQUAL(a.front(), proof.a_); - BOOST_CHECK_EQUAL(b.front(), proof.b_); - BOOST_CHECK_EQUAL(log2_n, proof.L_.size()); - BOOST_CHECK_EQUAL(log2_n, proof.R_.size()); - - // verify - challengeGenerator.reset(new ChallengeGeneratorImpl(1)); - BOOST_CHECK(ProofVerifier(gens_g, gens_h, u, ComputePInit(), 2).verify(x, proof, challengeGenerator)); - challengeGenerator.reset(new ChallengeGeneratorImpl(1)); - BOOST_CHECK(ProofVerifier(gens_g, gens_h, u, ComputePInit(), 2).verify_fast(n, x, proof, challengeGenerator)); -} - -BOOST_AUTO_TEST_CASE(prove_verify) -{ - size_t n = 32; - size_t log2_n = 5; - - Generate(n); - - Scalar x; - x.randomize(); - std::unique_ptr challengeGenerator = std::make_unique>(1); - - // generating proofs - Proof proof; - ProofGenerator prover(gens_g, gens_h, u, 2); - prover.generate_proof(a, b, x, challengeGenerator, proof); - - BOOST_CHECK_EQUAL(ComputePInit(), prover.get_P()); - - // validate - BOOST_CHECK_EQUAL(ComputeC(), proof.c_); - BOOST_CHECK_EQUAL(log2_n, proof.L_.size()); - BOOST_CHECK_EQUAL(log2_n, proof.R_.size()); - - // verify - challengeGenerator.reset(new ChallengeGeneratorImpl(1)); - BOOST_CHECK(ProofVerifier(gens_g, gens_h, u, ComputePInit(), 2).verify(x, proof, challengeGenerator)); - challengeGenerator.reset(new ChallengeGeneratorImpl(1)); - BOOST_CHECK(ProofVerifier(gens_g, gens_h, u, ComputePInit(), 2).verify_fast(n, x, proof, challengeGenerator)); -} - -BOOST_AUTO_TEST_CASE(fake_proof_not_verify) -{ - size_t n = 32; - - // generating needed objects - Generate(n); - - Scalar x; - x.randomize(); - std::unique_ptr challengeGenerator = std::make_unique>(1); - - // generating genertor - Proof proof; - ProofGenerator(gens_g, gens_h, u, 2).generate_proof(a, b, x, challengeGenerator, proof); - - // verify with fake P - GroupElement fakeP; - fakeP.randomize(); - - challengeGenerator.reset(new ChallengeGeneratorImpl(1)); - BOOST_CHECK(!ProofVerifier(gens_g, gens_h, u, fakeP, 2).verify(x, proof, challengeGenerator)); - challengeGenerator.reset(new ChallengeGeneratorImpl(1)); - BOOST_CHECK(!ProofVerifier(gens_g, gens_h, u, fakeP, 2).verify_fast(n, x, proof, challengeGenerator)); - - // verify with fake proof - auto verify = [&](Scalar const &_x, Proof const &_p) -> void { - challengeGenerator.reset(new ChallengeGeneratorImpl(1)); - BOOST_CHECK(!ProofVerifier(gens_g, gens_h, u, ComputePInit(), 2).verify(_x, _p, challengeGenerator)); - challengeGenerator.reset(new ChallengeGeneratorImpl(1)); - BOOST_CHECK(!ProofVerifier(gens_g, gens_h, u, ComputePInit(), 2).verify_fast(n, _x, _p, challengeGenerator)); - }; - - auto fakeProof = proof; - fakeProof.a_.randomize(); - verify(x, fakeProof); - - fakeProof = proof; - fakeProof.b_.randomize(); - verify(x, fakeProof); - - fakeProof = proof; - fakeProof.c_.randomize(); - verify(x, fakeProof); - - fakeProof = proof; - fakeProof.L_[0].randomize(); - verify(x, fakeProof); - - fakeProof = proof; - fakeProof.R_[0].randomize(); - verify(x, fakeProof); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/test/joinsplit_tests.cpp b/src/liblelantus/test/joinsplit_tests.cpp deleted file mode 100644 index a3bb30d7e7..0000000000 --- a/src/liblelantus/test/joinsplit_tests.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "lelantus_test_fixture.h" - -#include "../openssl_context.h" -#include "../joinsplit.h" - -#include -#include - -namespace lelantus { - -class JoinSplitTests : public LelantusTestingSetup -{ -public: - JoinSplitTests() - { - } - -public: - std::vector GenerateCoins(std::vector const &amounts) { - std::vector privs; - - for (auto a : amounts) { - std::vector ecdsaKey; - ecdsaKey.resize(32); - - // Create a key pair - secp256k1_pubkey pubkey; - do { - if (RAND_bytes(ecdsaKey.data(), ecdsaKey.size()) != 1) { - throw std::runtime_error("Unable to generate randomness"); - } - } while (!secp256k1_ec_pubkey_create( - OpenSSLContext::get_context(), &pubkey, ecdsaKey.data())); - - // Hash the public key in the group to obtain a serial number - auto serial = PrivateCoin::serialNumberFromSerializedPublicKey( - OpenSSLContext::get_context(), &pubkey); - - Scalar randomness; - randomness.randomize(); - - privs.emplace_back(params, serial, a, randomness, ecdsaKey, LELANTUS_TX_VERSION_4); - } - - return privs; - } - - std::vector BuildPublicCoins(std::vector const &es) { - std::vector pubs; - pubs.reserve(es.size()); - - for (auto const &e : es) { - pubs.emplace_back(e); - } - - return pubs; - } -}; - -BOOST_FIXTURE_TEST_SUITE(lelantus_joinsplit_tests, JoinSplitTests) - -BOOST_AUTO_TEST_CASE(verify) -{ - auto privs = GenerateCoins({1 * COIN, 10 * COIN, 100 * COIN, 99 * COIN}); - std::vector> cin = { - {privs[0], 1}, - {privs[1], 1}, - {privs[2], 2} - }; - - std::map> anons = { - {1, BuildPublicCoins(GenerateGroupElements(10))}, - {2, BuildPublicCoins(GenerateGroupElements(10))}, - }; - - anons[1][0] = privs[0].getPublicCoin(); - anons[1][1] = privs[1].getPublicCoin(); - anons[2][0] = privs[2].getPublicCoin(); - - std::map groupBlockHashes = { - {1, ArithToUint256(1)}, - {2, ArithToUint256(3)}, - }; - - // inputs = 111 - // outputs = 0.01(fee) + 99(mint) + 11.99(vout) - auto vout = 12 * COIN - CENT; - JoinSplit joinSplit( - params, - cin, - anons, - {}, - vout, // vout - {privs[3]}, // cout - CENT, // fee - groupBlockHashes, - ArithToUint256(3), - 0); - - std::vector expectedGroupIds = {1, 1, 2}; - BOOST_CHECK(expectedGroupIds == joinSplit.getCoinGroupIds()); - BOOST_CHECK(joinSplit.Verify(anons, {}, {privs[3].getPublicCoin()}, vout, ArithToUint256(3))); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/test/lelantus_primitives_tests.cpp b/src/liblelantus/test/lelantus_primitives_tests.cpp deleted file mode 100644 index f0d1aed34c..0000000000 --- a/src/liblelantus/test/lelantus_primitives_tests.cpp +++ /dev/null @@ -1,275 +0,0 @@ -#include "lelantus_test_fixture.h" - -#include "../lelantus_primitives.h" -#include "../challenge_generator_impl.h" -#include "../../test/test_bitcoin.h" -#include "../../utilstrencodings.h" - -#include -#include - -namespace lelantus { - -typedef LelantusPrimitives Primitives; - -BOOST_FIXTURE_TEST_SUITE(lelantus_primitives_tests, LelantusTestingSetup) - -BOOST_AUTO_TEST_CASE(generate_challenge) -{ - auto gs0 = GenerateGroupElements(10); - auto gs1 = GenerateGroupElements(5); - - secp_primitives::Scalar s0, s1; - Primitives::generate_challenge(gs0, "", s0); - Primitives::generate_challenge(gs1, "", s1); - - BOOST_CHECK_EQUAL( - "7486c200ca76a53a40715a64982705276181c4c8fe6335425607ddc696ca739f", - s0.GetHex()); - - BOOST_CHECK_EQUAL( - "b2eb32c975a2bde90afa85812aea832744f7905759b4edf56b10cf420f848b1e", - s1.GetHex()); -} - -BOOST_AUTO_TEST_CASE(multi_commit) -{ - auto h = GenerateGroupElements(3); - auto g = h.back(); - h.resize(2); - - std::vector exps; - for (int i = 0; i != 3; i++) { - exps.emplace_back(i + 10); - } - auto r = exps.back(); - exps.resize(2); - - GroupElement out; - Primitives::commit(g, h, exps, r, out); - - BOOST_CHECK_EQUAL( - "(6185bcfc7e56b1a66b6a64176c5474befa11047acac0d96043399d729c2523e4," - "4d1930b2ef5d097e0c6f390f7b3818419131a43a15b5d699c9ece70ef162683a)", - out.GetHex()); -} - -BOOST_AUTO_TEST_CASE(commit) -{ - auto gs = GenerateGroupElements(2); - auto g = gs[0]; - auto h = gs[1]; - - Scalar m(10), r(11); - - auto commitment = Primitives::commit(g, m, h, r); - BOOST_CHECK_EQUAL( - "(ef2c9e683a4985e7435993d7b6637254aa5acbd20501aa896815976d35d7bb6e," - "5e84e985869a661f9241dfff097c28eb9deda97fc945f27eadf8adea8c6ae929)", - commitment.GetHex()); -} - -BOOST_AUTO_TEST_CASE(double_commit) -{ - auto gs = GenerateGroupElements(3); - auto g = gs[0]; - auto hV = gs[1]; - auto hR = gs[2]; - - Scalar m(10), v(11), r(12); - - auto commitment = Primitives::double_commit(g, m, hV, v, hR, r); - BOOST_CHECK_EQUAL( - "(6185bcfc7e56b1a66b6a64176c5474befa11047acac0d96043399d729c2523e4," - "4d1930b2ef5d097e0c6f390f7b3818419131a43a15b5d699c9ece70ef162683a)", - commitment.GetHex()); -} - -BOOST_AUTO_TEST_CASE(convert_to_sigma) -{ - uint64_t n = 4, m = 6; - uint64_t num = - 2 + n + 0 + - 3 * std::pow(n, 3) + - 2 * std::pow(n, 4) + - 1 * std::pow(n, 5); - - std::vector rawExpected = - { - 0, 0, 1, 0, - 0, 1, 0, 0, - 1, 0, 0, 0, - 0, 0, 0, 1, - 0, 0, 1, 0, - 0, 1, 0, 0 - }; - - Scalar one(1), zero(uint64_t(0)); - std::vector expected; - expected.reserve(rawExpected.size()); - for (auto const &d : rawExpected) { - expected.push_back(d == 0 ? zero : one); - } - - std::vector out; - Primitives::convert_to_sigma(num, n, m, out); - - BOOST_CHECK(expected == out); -} - -BOOST_AUTO_TEST_CASE(convert_to_nal) -{ - uint64_t n = 4, m = 6; - uint64_t num = - 2 + 3 * n + 0 + - 1 * std::pow(n, 3) + - 2 * std::pow(n, 4); - - std::vector expected = {2, 3, 0, 1, 2, 0}; - auto result = Primitives::convert_to_nal(num, n, m); - - BOOST_CHECK_EQUAL_COLLECTIONS( - expected.begin(), expected.end(), - result.begin(), result.end()); -} - -BOOST_AUTO_TEST_CASE(generate_lelantus_challenge) -{ - std::vector proofs(2); - auto gs = GenerateGroupElements(proofs.size() * 8); - auto it = gs.begin(); - - for (auto &proof : proofs) { - proof.A_ = *it++; - proof.B_ = *it++; - proof.C_ = *it++; - proof.D_ = *it++; - proof.Gk_ = {*it++, *it++}; - proof.Qk = {*it++, *it++}; - } - - Scalar out; - std::unique_ptr challengeGenerator = std::make_unique>(); - Primitives::generate_Lelantus_challenge(proofs, {}, {}, {}, {}, 0, challengeGenerator, out); - - BOOST_CHECK_EQUAL( - "0739d8484b29d53410510c38ffd5b6a43187fa0775175f97d12c61e81147245b", - out.GetHex()); -} - -BOOST_AUTO_TEST_CASE(new_factor) -{ - std::vector coefs = {6, 5, 4, 3, 2, 1}; - Scalar x = 2, a = 4; - - std::vector expected = {24, 32, 26, 20, 14, 8, 2}; - - Primitives::new_factor(x, a, coefs); - - BOOST_CHECK_EQUAL_COLLECTIONS( - expected.begin(), expected.end(), - coefs.begin(), coefs.end()); -} - -BOOST_AUTO_TEST_CASE(commit_two_generators) -{ - auto items = GenerateGroupElements(5); - auto h = items[0]; - std::vector g_(items.begin() + 1, items.begin() + 3); - std::vector h_(items.begin() + 3, items.end()); - - Scalar exp = 10; - std::vector l = {Scalar(11), Scalar(12)}; - std::vector r = {Scalar(13), Scalar(14)}; - - GroupElement out; - Primitives::commit(h, exp, g_, l, h_, r, out); - - BOOST_CHECK_EQUAL( - "(ab6a35cc172ce38d52ad2b5eeda2de4d2c22d18a7dd8bfadbcb839c368a1d8d,75e7dd7a4e098d5eb8f46ad57eb72da067ed826d307d4bff9faea1715d58289a)", - out.GetHex()); -} - -BOOST_AUTO_TEST_CASE(scalar_dot_product) -{ - Scalar x0 = 10, x1 = 20, x2 = 30; - Scalar y0 = 11, y1 = 21, y2 = 31; - - std::vector x = {x0, x1, x2}; - std::vector y = {y0, y1, y2}; - - auto result = Primitives::scalar_dot_product(x.begin(), x.end(), y.begin(), y.end()); - - BOOST_CHECK_EQUAL(x0 * y0 + x1 * y1 + x2 * y2, result); -} - -BOOST_AUTO_TEST_CASE(g_prime) -{ - auto gs = GenerateGroupElements(4); - Scalar x(10); - - auto raw0 = ParseHex("1eddd58977aab5a476bc82ea0f48e11fa5a79419476ddfe5b764540eac7947000100"); - auto raw1 = ParseHex("72771902e20dda39200116e694385fc520b82150fcf8cff12671cdcff68e23f70000"); - std::vector expected(2); - expected[0].deserialize(raw0.data()); - expected[1].deserialize(raw1.data()); - - std::vector result; - Primitives::g_prime(gs, x, result); - - BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), result.begin(), result.end()); -} - -BOOST_AUTO_TEST_CASE(h_prime) -{ - auto h = GenerateGroupElements(4); - Scalar x(10); - - auto raw0 = ParseHex("289d10cc8571a829839c9df9607fc5cc38c06f4d4a0dba069fc384bffc8ae3560000"); - auto raw1 = ParseHex("ceb87b21b9be8161c6200bbd54931582d586e4075819f601bb685b26e7707e280100"); - std::vector expected(2); - expected[0].deserialize(raw0.data()); - expected[1].deserialize(raw1.data()); - - std::vector result; - Primitives::h_prime(h, x, result); - - BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), result.begin(), result.end()); -} - -BOOST_AUTO_TEST_CASE(p_prime) -{ - auto gs = GenerateGroupElements(3); - auto P = gs[0]; - auto L = gs[1]; - auto R = gs[2]; - - Scalar x(10); - - auto result = Primitives::p_prime(P, L, R, x); - - auto raw = ParseHex("7fb5138c96f0e994eb2fca28480e5d6e572fa07ed1b49b6d87d1e475164cac900100"); - GroupElement expected; - expected.deserialize(raw.data()); - - BOOST_CHECK_EQUAL(expected, result); -} - -BOOST_AUTO_TEST_CASE(delta) -{ - Scalar y = 100, z = 200, one = 1, two = 2; - uint64_t n = 4, m = 6; - - auto y_ = (y.exponent(m * n) - 1) * ((y - 1).inverse()); // 1 + y + y^2 + ... + y^(mn - 1) - auto z_ = (z.exponent(m + 3) - 1) * ((z - 1).inverse()) - 1 - z - z * z; // 1 + z + z^2 + z^3 + ... + z^m+2 - 1 - z - z^2 - auto two_ = (two.exponent(n) - 1); // 1 + 2 + 2^2 + ... + 2^(n-1) - - auto expected = (z - z * z) * y_ - z_ * two_; - auto result = Primitives::delta(y, z, n, m); - - BOOST_CHECK_EQUAL(expected, result); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/test/lelantus_test.cpp b/src/liblelantus/test/lelantus_test.cpp deleted file mode 100644 index 35e856987b..0000000000 --- a/src/liblelantus/test/lelantus_test.cpp +++ /dev/null @@ -1,235 +0,0 @@ -#include "lelantus_test_fixture.h" - -#include "../lelantus_prover.h" -#include "../lelantus_verifier.h" - -#include - -namespace lelantus { - -class ProtocolTests : public LelantusTestingSetup -{ -public: - ProtocolTests() - : m_params(Params::get_default()) - { - } - -public: - std::vector ExtractPublicCoins(std::vector const &coins) const { - std::vector pubs; - pubs.reserve(coins.size()); - - for (auto const &c : coins) { - pubs.push_back(c.getPublicCoin()); - } - - return pubs; - } - - std::vector ExtractSerials( - size_t anonymitySets, - std::vector> const &Cin, - std::vector& groupIds) const { - std::vector serials; - for (auto const &in : Cin) { - serials.push_back(in.first.getSerialNumber()); - groupIds.push_back(in.second); - } - - return serials; - } - - std::map> GenerateAnonymitySets(std::initializer_list sizes) const { - std::map> sets; - - uint32_t id = 0; - for (size_t s : sizes) { - std::vector set; - GenerateGroupElements(s, std::back_inserter(set)); - sets[id] = set; - id++; - } - - return sets; - } - -public: - Params const *m_params; -}; - -BOOST_FIXTURE_TEST_SUITE(lelantus_protocol_tests, ProtocolTests) - -BOOST_AUTO_TEST_CASE(prove_verify) -{ - size_t N = 100; - - uint64_t v1(5); - PrivateCoin input_coin1(m_params ,v1); - std::vector> Cin = {{input_coin1, 0}}; - - std::vector indexes = {0}; - - auto anonymity_sets = GenerateAnonymitySets({N}); - anonymity_sets[0][0] = Cin[0].first.getPublicCoin(); - - Scalar Vin(5); - uint64_t Vout(6); - std::vector Cout = {{m_params, 2}, {m_params, 1}}; - - uint64_t f(1); - LelantusProof proof; - SchnorrProof qkSchnorrProof; - - LelantusProver prover(m_params, LELANTUS_TX_VERSION_4_5); - prover.proof(anonymity_sets, {}, Vin, Cin, indexes, {}, Vout, Cout, f, proof, qkSchnorrProof); - - std::vector groupIds; - auto Sin = ExtractSerials(anonymity_sets.size(), Cin, groupIds); - auto Cout_Public = ExtractPublicCoins(Cout); - - lelantus::LelantusVerifier verifier(m_params, LELANTUS_TX_VERSION_4_5); - BOOST_CHECK(verifier.verify(anonymity_sets, {}, Sin, {}, groupIds, Vin, Vout, f, Cout_Public, proof, qkSchnorrProof)); -} - -BOOST_AUTO_TEST_CASE(prove_verify_many_coins) -{ - FIRO_UNUSED size_t N = 100; - - PrivateCoin input1(m_params ,2), input2(m_params, 2), input3(m_params, 1); - std::vector> Cin = { - {input1, 0}, {input2, 0}, {input3, 1} - }; - - std::vector indexes = {0, 1, 0}; - - auto anonymity_sets = GenerateAnonymitySets({N, N}); - anonymity_sets[0][0] = Cin[0].first.getPublicCoin(); - anonymity_sets[0][1] = Cin[1].first.getPublicCoin(); - anonymity_sets[1][0] = Cin[2].first.getPublicCoin(); - - Scalar Vin(5); - uint64_t Vout(6), f(1); - std::vector Cout = {{m_params, 2}, {m_params, 1}}; - - LelantusProof proof; - SchnorrProof qkSchnorrProof; - - LelantusProver prover(m_params, LELANTUS_TX_VERSION_4_5); - prover.proof(anonymity_sets, {}, Vin, Cin, indexes, {}, Vout, Cout, f, proof, qkSchnorrProof); - - std::vector groupIds; - auto Sin = ExtractSerials(anonymity_sets.size(), Cin, groupIds); - auto Cout_Public = ExtractPublicCoins(Cout); - - lelantus::LelantusVerifier verifier(m_params, LELANTUS_TX_VERSION_4_5); - BOOST_CHECK(verifier.verify(anonymity_sets, {}, Sin, {}, groupIds, Vin, Vout, f, Cout_Public, proof, qkSchnorrProof)); - //After Lelantus new update (after version LELANTUS_TX_VERSION_4_5) following 2 verifications will fail, as schnorr proof challenge depends also on Vout, Vin and fee values -// BOOST_CHECK(verifier.verify(anonymity_sets, {}, Sin, {}, groupIds, Vin + 1, Vout + 1, f, Cout_Public, proof)); -// BOOST_CHECK(verifier.verify(anonymity_sets, {}, Sin, {}, groupIds, Vin, Vout + f, uint64_t(0), Cout_Public, proof)); -} - -BOOST_AUTO_TEST_CASE(imbalance_proof_should_fail) -{ - size_t N = 100; - - // Input - PrivateCoin p1(m_params, 3); - std::vector> Cin = {{p1, 0}}; - - std::vector indexs = {0}; - - auto anonymitySets = GenerateAnonymitySets({N}); - anonymitySets[0][0] = Cin[0].first.getPublicCoin(); - - Scalar Vin(2); // Use this to verify - Scalar FakeVin(4); // Use this to generate proof - - // Output - std::vector Cout = {{m_params, 3}}; - uint64_t Vout(3); - uint64_t f(1); - - // Proof - LelantusProof proof; - SchnorrProof qkSchnorrProof; - - // Should be prevent from prover - LelantusProver prover(m_params, LELANTUS_TX_VERSION_4_5); - BOOST_CHECK_THROW(prover.proof(anonymitySets, {}, Vin, Cin, indexs, {}, Vout, Cout, f, proof, qkSchnorrProof), std::runtime_error); - - // Use fake vin - prover.proof(anonymitySets, {}, FakeVin, Cin, indexs, {}, Vout, Cout, f, proof, qkSchnorrProof); - - // Verify - std::vector groupIds; - auto Sin = ExtractSerials(anonymitySets.size(), Cin, groupIds); - auto publicCoins = ExtractPublicCoins(Cout); - - LelantusVerifier verifier(m_params, LELANTUS_TX_VERSION_4_5); - - // input: 2 + 3(anonymous), output: 3 + 3(anonymous) + 1(fee) - BOOST_CHECK(!verifier.verify(anonymitySets, {}, Sin, {}, groupIds, Vin, Vout, f, publicCoins, proof, qkSchnorrProof)); - - // Verify with output which is less than input also should fail - // input: 99 + 3(anonymous), output: 3 + 3(anonymous) + 1(fee) - Scalar newVin(99); - BOOST_CHECK(!verifier.verify(anonymitySets, {}, Sin, {}, groupIds, newVin, Vout, f, publicCoins, proof, qkSchnorrProof)); -} - -BOOST_AUTO_TEST_CASE(other_fail_to_validate) -{ - size_t N = 100; - - // Input - PrivateCoin p1(m_params, 1), p2(m_params, 2); - std::vector> Cin = {{p1, 0}, {p2, 0}}; - - std::vector indexs = {0, 1}; - - auto anonymitySets = GenerateAnonymitySets({N}); - anonymitySets[0][0] = Cin[0].first.getPublicCoin(); - anonymitySets[0][1] = Cin[1].first.getPublicCoin(); - - Scalar Vin(4); - - // Output - std::vector Cout = {{m_params, 1}, {m_params, 2}}; - uint64_t Vout(3); - uint64_t f(1); - - // Proof - LelantusProof proof; - SchnorrProof qkSchnorrProof; - - // Should be prevent from prover - LelantusProver prover(m_params, LELANTUS_TX_VERSION_4_5); - prover.proof(anonymitySets, {}, Vin, Cin, indexs, {}, Vout, Cout, f, proof, qkSchnorrProof); - - // Verify - std::vector groupIds; - auto Sin = ExtractSerials(anonymitySets.size(), Cin, groupIds); - auto publicCoins = ExtractPublicCoins(Cout); - - LelantusVerifier verifier(m_params, LELANTUS_TX_VERSION_4_5); - - BOOST_CHECK(verifier.verify(anonymitySets, {}, Sin, {}, groupIds, Vin, Vout, f, publicCoins, proof, qkSchnorrProof)); - - // Invalid group - auto invalidAnonymitySets = anonymitySets; - invalidAnonymitySets[0].pop_back(); - BOOST_CHECK(!verifier.verify(invalidAnonymitySets, {}, Sin, {}, groupIds, Vin, Vout, f, publicCoins, proof, qkSchnorrProof)); - - invalidAnonymitySets = anonymitySets; - invalidAnonymitySets[0].push_back(PrivateCoin(m_params, 1).getPublicCoin()); - BOOST_CHECK(!verifier.verify(invalidAnonymitySets, {}, Sin, {}, groupIds, Vin, Vout, f, publicCoins, proof, qkSchnorrProof)); - - // Invalid serial - auto invalidSin = Sin; - invalidSin[1].randomize(); - BOOST_CHECK(!verifier.verify(anonymitySets, {}, invalidSin, {}, groupIds, Vin, Vout, f, publicCoins, proof, qkSchnorrProof)); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/test/lelantus_test_fixture.cpp b/src/liblelantus/test/lelantus_test_fixture.cpp deleted file mode 100644 index 7af6c76121..0000000000 --- a/src/liblelantus/test/lelantus_test_fixture.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "lelantus_test_fixture.h" - -namespace lelantus { - -std::vector LelantusTestingSetup::GenerateGroupElements(size_t size) const { - std::vector gs; - GenerateGroupElements(size, std::back_inserter(gs)); - - return gs; -} - -std::vector LelantusTestingSetup::RandomizeGroupElements(size_t size) const { - std::vector gs(size); - for (auto &g : gs) { - g.randomize(); - } - - return gs; -} - -std::vector LelantusTestingSetup::RandomizeScalars(size_t size) const { - std::vector ss(size); - for (auto &s : ss) { - s.randomize(); - } - - return ss; -} - -} // namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/test/lelantus_test_fixture.h b/src/liblelantus/test/lelantus_test_fixture.h deleted file mode 100644 index 762112d9c1..0000000000 --- a/src/liblelantus/test/lelantus_test_fixture.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef FIRO_LIBLELANTUS_TEST_FIXTURE_H -#define FIRO_LIBLELANTUS_TEST_FIXTURE_H - -#include "../../test/test_bitcoin.h" - -#include "../lelantus_primitives.h" - -#include - -namespace lelantus { - -class LelantusTestingSetup : public BasicTestingSetup { -protected: - typedef LelantusPrimitives Primitives; - -public: - LelantusTestingSetup(const std::string& chainName = CBaseChainParams::MAIN) - : BasicTestingSetup(chainName), params(Params::get_default()) { - } - -public: - GroupElement ComputeMultiExponent(std::vector const &gs, std::vector const &s) const { - return secp_primitives::MultiExponent(gs, s).get_multiple(); - } - - template - void GenerateGroupElements(size_t size, Output output) const { - - std::array seed; - std::fill(seed.begin(), seed.end(), 0); - - for (size_t i = 0; i != size; i++) { - // 'LE' - std::copy( - reinterpret_cast(&i), - reinterpret_cast(&i) + sizeof(i), - seed.begin()); - - GroupElement e; - e.generate(seed.begin()); - - if (!e.isMember() || e.isInfinity()) { - throw std::runtime_error("Fail to generate group elements"); - } - - *output++ = e; - } - } - - // Generate group elements deterministically - std::vector GenerateGroupElements(size_t size) const; - std::vector RandomizeGroupElements(size_t size) const; - std::vector RandomizeScalars(size_t size) const; -public: - Params const *params; -}; - -} // namespace lelantus - -#endif // FIRO_LIBLELANTUS_TEST_FIXTURE_H \ No newline at end of file diff --git a/src/liblelantus/test/range_proof_test.cpp b/src/liblelantus/test/range_proof_test.cpp deleted file mode 100644 index 58794f0987..0000000000 --- a/src/liblelantus/test/range_proof_test.cpp +++ /dev/null @@ -1,346 +0,0 @@ -#include "lelantus_test_fixture.h" - -#include "../range_prover.h" -#include "../range_verifier.h" - -#include - -namespace lelantus { - -// All versions to be tested -unsigned int test_versions[] = { - LELANTUS_TX_VERSION_4, - SIGMA_TO_LELANTUS_JOINSPLIT, - LELANTUS_TX_VERSION_4_5, - SIGMA_TO_LELANTUS_JOINSPLIT_FIXED, - LELANTUS_TX_TPAYLOAD, - SIGMA_TO_LELANTUS_TX_TPAYLOAD -}; - -BOOST_FIXTURE_TEST_SUITE(lelantus_range_proof_tests, LelantusTestingSetup) - -// A single valid aggregated range proof -BOOST_AUTO_TEST_CASE(prove_verify_single) -{ - // Parameters - std::size_t n = 64; - std::size_t max_m = 8; - - // Generators - secp_primitives::GroupElement g_gen, h_gen1, h_gen2; - g_gen.randomize(); - h_gen1.randomize(); - h_gen2.randomize(); - - auto prove_verify = [&] (std::size_t m, unsigned int version) { - auto g_ = RandomizeGroupElements(n * m); - auto h_ = RandomizeGroupElements(n * m); - - // Input data - auto serials = RandomizeScalars(m); - auto randoms = RandomizeScalars(m); - - std::vector v_s; - std::vector V; - for (std::size_t i = 0; i < m; ++i){ - v_s.emplace_back(i); - V.push_back(g_gen * v_s.back() + h_gen1 * randoms[i] + h_gen2 * serials[i]); - } - - // Prove - RangeProver rangeProver(g_gen, h_gen1, h_gen2, g_, h_, n, version); - RangeProof proof; - rangeProver.proof(v_s, serials, randoms, V, proof); - - // Verify - RangeVerifier rangeVerifier(g_gen, h_gen1, h_gen2, g_, h_, n, version); - BOOST_CHECK(rangeVerifier.verify(V, V, proof)); - }; - - // Test powers of 2 - std::size_t i = 1; - while (i <= max_m) { - for (auto version : test_versions) - prove_verify(i, version); - i *= 2; - } - -} - -// A batch of valid aggregated range proofs of different size -BOOST_AUTO_TEST_CASE(prove_verify_batch) -{ - // Parameters - const std::size_t n = 64; - const std::vector m = {1,2,4,8}; - - // Generators - secp_primitives::GroupElement g_gen, h_gen1, h_gen2; - g_gen.randomize(); - h_gen1.randomize(); - h_gen2.randomize(); - std::size_t max_m = *std::max_element(m.begin(), m.end()); - auto g_ = RandomizeGroupElements(n * max_m); - auto h_ = RandomizeGroupElements(n * max_m); - - for (auto version : test_versions) - { - // Proofs - std::vector > V_batch; - V_batch.reserve(m.size()); - std::vector proof_batch; - proof_batch.reserve(m.size()); - for (std::size_t i = 0; i < m.size(); i++) { - RangeProver rangeProver(g_gen, h_gen1, h_gen2, std::vector(g_.begin(), g_.begin() + n * m[i]), std::vector(h_.begin(), h_.begin() + n * m[i]), n, version); - - // Input data - auto serials = RandomizeScalars(m[i]); - auto randoms = RandomizeScalars(m[i]); - - std::vector v_s; - std::vector V; - for (std::size_t j = 0; j < m[i]; ++j){ - v_s.emplace_back(j); - V.push_back(g_gen * v_s.back() + h_gen1 * randoms[j] + h_gen2 * serials[j]); - } - - // Prove - RangeProof proof; - rangeProver.proof(v_s, serials, randoms, V, proof); - V_batch.emplace_back(V); - proof_batch.emplace_back(proof); - } - - // Verify - RangeVerifier rangeVerifier(g_gen, h_gen1, h_gen2, g_, h_, n, version); - BOOST_CHECK(rangeVerifier.verify(V_batch, V_batch, proof_batch)); - } -} - -// A single out-of-range aggregated range proof -BOOST_AUTO_TEST_CASE(out_of_range_single_proof) -{ - // Parameters - std::size_t n = 4; - std::size_t m = 4; - - // Generators - secp_primitives::GroupElement g_gen, h_gen1, h_gen2; - g_gen.randomize(); - h_gen1.randomize(); - h_gen2.randomize(); - auto g_ = RandomizeGroupElements(n * m); - auto h_ = RandomizeGroupElements(n * m); - - // Input data - auto randoms = RandomizeScalars(m); - auto serials = RandomizeScalars(m); - - auto testF = [&] (std::vector const v_s, unsigned int version) { - std::vector V; - for (std::size_t i = 0; i < m; ++i) { - V.push_back(g_gen * v_s[i] + h_gen1 * randoms[i] + h_gen2 * serials[i]); - } - - lelantus::RangeProver rangeProver(g_gen, h_gen1, h_gen2, g_, h_, n, version); - lelantus::RangeProof proof; - rangeProver.proof(v_s, serials, randoms, V, proof ); - - lelantus::RangeVerifier rangeVerifier(g_gen, h_gen1, h_gen2, g_, h_, n, version); - BOOST_CHECK(!rangeVerifier.verify(V, V, proof)); - }; - - for (auto version : test_versions) - { - // All values are out of range - std::vector vs; - for(std::size_t i = 0; i < m; ++i){ - vs.emplace_back((1 << n) + i); - } - testF(vs, version); - - // [0, 2 ^ n - 1] - Scalar l(uint64_t(0)); - Scalar r((1 << n) - 1); - - // One value is out of range - vs = {l, l + 1, r, r + 1}; - testF(vs, version); - - vs = {l - 1, l, r - 1, r}; - testF(vs, version); - } -} - -// A single aggreated range proof, with successively invalid proof elements -BOOST_AUTO_TEST_CASE(invalid_elements) -{ - // Parameters - std::size_t n = 64; - std::size_t m = 4; - - // Generators - secp_primitives::GroupElement g_gen, h_gen1, h_gen2; - g_gen.randomize(); - h_gen1.randomize(); - h_gen2.randomize(); - auto g_ = RandomizeGroupElements(n * m); - auto h_ = RandomizeGroupElements(n * m); - - // Inputs - auto randoms = RandomizeScalars(m); - auto serials = RandomizeScalars(m); - - // Set up valid proof - std::vector v_s; - std::vector V; - for(std::size_t i = 0; i < m; ++i){ - v_s.emplace_back(i); - V.push_back(g_gen * v_s.back() + h_gen1 * randoms[i] + h_gen2 * serials[i]); - } - - for (auto version : test_versions) - { - // Initial correctness check - RangeProver rangeProver(g_gen, h_gen1, h_gen2, g_, h_, n, version); - RangeProof proof; - rangeProver.proof(v_s, serials, randoms, V, proof); - RangeVerifier rangeVerifier(g_gen, h_gen1, h_gen2, g_, h_, n, version); - BOOST_CHECK(rangeVerifier.verify(V, V, proof)); - - // Invalidate successive values and then restore them - GroupElement group; - Scalar scalar; - - group = GroupElement(proof.A); - proof.A.randomize(); - BOOST_CHECK(!rangeVerifier.verify(V, V, proof)); - proof.A = GroupElement(group); - BOOST_CHECK(rangeVerifier.verify(V, V, proof)); - - group = GroupElement(proof.S); - proof.S.randomize(); - BOOST_CHECK(!rangeVerifier.verify(V, V, proof)); - proof.S = GroupElement(group); - BOOST_CHECK(rangeVerifier.verify(V, V, proof)); - - group = GroupElement(proof.T1); - proof.T1.randomize(); - BOOST_CHECK(!rangeVerifier.verify(V, V, proof)); - proof.T1 = GroupElement(group); - BOOST_CHECK(rangeVerifier.verify(V, V, proof)); - - group = GroupElement(proof.T2); - proof.T2.randomize(); - BOOST_CHECK(!rangeVerifier.verify(V, V, proof)); - proof.T2 = GroupElement(group); - BOOST_CHECK(rangeVerifier.verify(V, V, proof)); - - for (std::size_t j = 0; j < proof.innerProductProof.L_.size(); j++) { - group = GroupElement(proof.innerProductProof.L_[j]); - proof.innerProductProof.L_[j].randomize(); - BOOST_CHECK(!rangeVerifier.verify(V, V, proof)); - proof.innerProductProof.L_[j] = GroupElement(group); - BOOST_CHECK(rangeVerifier.verify(V, V, proof)); - - group = GroupElement(proof.innerProductProof.R_[j]); - proof.innerProductProof.R_[j].randomize(); - BOOST_CHECK(!rangeVerifier.verify(V, V, proof)); - proof.innerProductProof.R_[j] = GroupElement(group); - BOOST_CHECK(rangeVerifier.verify(V, V, proof)); - } - - scalar = Scalar(proof.T_x1); - proof.T_x1.randomize(); - BOOST_CHECK(!rangeVerifier.verify(V, V, proof)); - proof.T_x1 = Scalar(scalar); - BOOST_CHECK(rangeVerifier.verify(V, V, proof)); - - scalar = Scalar(proof.T_x2); - proof.T_x2.randomize(); - BOOST_CHECK(!rangeVerifier.verify(V, V, proof)); - proof.T_x2 = Scalar(scalar); - BOOST_CHECK(rangeVerifier.verify(V, V, proof)); - - scalar = Scalar(proof.u); - proof.u.randomize(); - BOOST_CHECK(!rangeVerifier.verify(V, V, proof)); - proof.u = Scalar(scalar); - BOOST_CHECK(rangeVerifier.verify(V, V, proof)); - - scalar = Scalar(proof.innerProductProof.a_); - proof.innerProductProof.a_.randomize(); - BOOST_CHECK(!rangeVerifier.verify(V, V, proof)); - proof.innerProductProof.a_ = Scalar(scalar); - BOOST_CHECK(rangeVerifier.verify(V, V, proof)); - - scalar = Scalar(proof.innerProductProof.b_); - proof.innerProductProof.b_.randomize(); - BOOST_CHECK(!rangeVerifier.verify(V, V, proof)); - proof.innerProductProof.b_ = Scalar(scalar); - BOOST_CHECK(rangeVerifier.verify(V, V, proof)); - - scalar = Scalar(proof.innerProductProof.c_); - proof.innerProductProof.c_.randomize(); - BOOST_CHECK(!rangeVerifier.verify(V, V, proof)); - proof.innerProductProof.c_ = Scalar(scalar); - BOOST_CHECK(rangeVerifier.verify(V, V, proof)); - } -} - -// A batch of range proofs, one of which is invalid -BOOST_AUTO_TEST_CASE(invalid_batch) -{ - // Parameters - const std::size_t n = 64; - const std::vector m = {1,2,4,8}; - - // Generators - secp_primitives::GroupElement g_gen, h_gen1, h_gen2; - g_gen.randomize(); - h_gen1.randomize(); - h_gen2.randomize(); - std::size_t max_m = *std::max_element(m.begin(), m.end()); - auto g_ = RandomizeGroupElements(n * max_m); - auto h_ = RandomizeGroupElements(n * max_m); - - for (auto version : test_versions) - { - // Proofs - std::vector > V_batch; - V_batch.reserve(m.size()); - std::vector proof_batch; - proof_batch.reserve(m.size()); - for (std::size_t i = 0; i < m.size(); i++) { - RangeProver rangeProver(g_gen, h_gen1, h_gen2, std::vector(g_.begin(), g_.begin() + n * m[i]), std::vector(h_.begin(), h_.begin() + n * m[i]), n, version); - - // Input data - auto serials = RandomizeScalars(m[i]); - auto randoms = RandomizeScalars(m[i]); - - std::vector v_s; - std::vector V; - for (std::size_t j = 0; j < m[i]; ++j){ - v_s.emplace_back(j); - V.push_back(g_gen * v_s.back() + h_gen1 * randoms[j] + h_gen2 * serials[j]); - } - - // Prove - RangeProof proof; - rangeProver.proof(v_s, serials, randoms, V, proof); - V_batch.emplace_back(V); - proof_batch.emplace_back(proof); - } - - // Invalidate one of the proofs - proof_batch[0].A.randomize(); - - // Verify - RangeVerifier rangeVerifier(g_gen, h_gen1, h_gen2, g_, h_, n, version); - BOOST_CHECK(!rangeVerifier.verify(V_batch, V_batch, proof_batch)); - } -} - -BOOST_AUTO_TEST_SUITE_END() - -} // namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/test/schnorr_test.cpp b/src/liblelantus/test/schnorr_test.cpp deleted file mode 100644 index ca92aa7be1..0000000000 --- a/src/liblelantus/test/schnorr_test.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "../schnorr_proof.h" -#include "../schnorr_prover.h" -#include "../schnorr_verifier.h" -#include "../challenge_generator_impl.h" -#include "../../streams.h" -#include "../../version.h" - - -#include - -namespace lelantus { - -class SchnorrProofTests { -public: - SchnorrProofTests() - { - g.randomize(); - h.randomize(); - P.randomize(); - T.randomize(); - a.randomize(); - b.randomize(); - } - -public: - GroupElement g, h, a, b; - Scalar P, T; -}; - -BOOST_FIXTURE_TEST_SUITE(lelantus_schnorr_proof_tests, SchnorrProofTests) - -BOOST_AUTO_TEST_CASE(serialization) -{ - std::unique_ptr challengeGenerator = std::make_unique>(1); - SchnorrProver prover(g, h, true); - GroupElement y; - y.randomize(); - SchnorrProof proof; - prover.proof(P, T, y, a, b, challengeGenerator, proof); - - CDataStream serialized(SER_NETWORK, PROTOCOL_VERSION); - serialized << proof; - - SchnorrProof deserialized; - serialized >> deserialized; - - BOOST_CHECK(proof.u == deserialized.u); - BOOST_CHECK(proof.P1 == deserialized.P1); - BOOST_CHECK(proof.T1 == deserialized.T1); -} - -BOOST_AUTO_TEST_CASE(prove_verify) -{ - auto y = LelantusPrimitives::commit(g, P, h, T); - std::unique_ptr challengeGenerator = std::make_unique>(1); - - SchnorrProver prover(g, h, true); - SchnorrProof proof; - prover.proof(P, T, y, a, b, challengeGenerator, proof); - - SchnorrVerifier verifier(g, h, true); - challengeGenerator.reset(new ChallengeGeneratorImpl(1)); - BOOST_CHECK(verifier.verify(y, a, b, proof, challengeGenerator)); -} - -BOOST_AUTO_TEST_CASE(fake_prove_not_verify) -{ - auto y = LelantusPrimitives::commit(g, P, h, T); - std::unique_ptr challengeGenerator = std::make_unique>(1); - - SchnorrProver prover(g, h, true); - SchnorrProof proof; - prover.proof(P, T, y, a, b, challengeGenerator, proof); - - GroupElement fakeY; - fakeY.randomize(); - - SchnorrVerifier verifier(g, h, true); - challengeGenerator.reset(new ChallengeGeneratorImpl(1)); - BOOST_CHECK(!verifier.verify(fakeY, a, b, proof, challengeGenerator)); - - auto fakeProof = proof; - fakeProof.P1.randomize(); - challengeGenerator.reset(new ChallengeGeneratorImpl(1)); - BOOST_CHECK(!verifier.verify(y, a, b, fakeProof, challengeGenerator)); - - fakeProof = proof; - fakeProof.T1.randomize(); - challengeGenerator.reset(new ChallengeGeneratorImpl(1)); - BOOST_CHECK(!verifier.verify(y, a, b, fakeProof, challengeGenerator)); - - fakeProof = proof; - fakeProof.u.randomize(); - challengeGenerator.reset(new ChallengeGeneratorImpl(1)); - BOOST_CHECK(!verifier.verify(y, a, b, fakeProof, challengeGenerator)); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/test/serialize_test.cpp b/src/liblelantus/test/serialize_test.cpp deleted file mode 100644 index 70147ca65e..0000000000 --- a/src/liblelantus/test/serialize_test.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "../lelantus_prover.h" -#include "../lelantus_verifier.h" - -#include "streams.h" - -#include "lelantus_test_fixture.h" - -#include - -BOOST_FIXTURE_TEST_SUITE(lelantus_serialize_tests, lelantus::LelantusTestingSetup) - -BOOST_AUTO_TEST_CASE(serialize) -{ - auto params = lelantus::Params::get_default(); - std::map> anonymity_sets; - int N = 100; - - std::vector> Cin; - lelantus::PrivateCoin input_coin1(params, 5); - Cin.emplace_back(std::make_pair(input_coin1, 0)); - std::vector indexes; - indexes.push_back(0); - - std::vector anonymity_set; - anonymity_set.reserve(N); - anonymity_set.push_back(Cin[0].first.getPublicCoin()); - for(int i = 0; i < N; ++i){ - secp_primitives::GroupElement coin; - coin.randomize(); - anonymity_set.emplace_back(lelantus::PublicCoin(coin)); - } - - anonymity_sets[0] = anonymity_set; - - secp_primitives::Scalar Vin(uint64_t(5)); - secp_primitives::Scalar Vout(uint64_t(6)); - std::vector Cout; - Cout.push_back(lelantus::PrivateCoin(params, 2)); - Cout.push_back(lelantus::PrivateCoin(params, 1)); - secp_primitives::Scalar f(uint64_t(1)); - - lelantus::LelantusProof initial_proof; - lelantus::SchnorrProof qkSchnorrProof; - - lelantus::LelantusProver prover(params, LELANTUS_TX_VERSION_4_5); - prover.proof(anonymity_sets, {}, Vin, Cin, indexes, {}, Vout, Cout, f, initial_proof, qkSchnorrProof); - - CDataStream serialized(SER_NETWORK, PROTOCOL_VERSION); - serialized << initial_proof; - - lelantus::LelantusProof resulted_proof; - serialized >> resulted_proof; - - for(size_t i = 0; i < initial_proof.sigma_proofs.size(); ++i){ - BOOST_CHECK(initial_proof.sigma_proofs[i].B_ == resulted_proof.sigma_proofs[i].B_); - BOOST_CHECK(initial_proof.sigma_proofs[i].A_ == resulted_proof.sigma_proofs[i].A_); - BOOST_CHECK(initial_proof.sigma_proofs[i].C_ == resulted_proof.sigma_proofs[i].C_); - BOOST_CHECK(initial_proof.sigma_proofs[i].D_ == resulted_proof.sigma_proofs[i].D_); - BOOST_CHECK(initial_proof.sigma_proofs[i].f_ == resulted_proof.sigma_proofs[i].f_); - BOOST_CHECK(initial_proof.sigma_proofs[i].ZA_ == resulted_proof.sigma_proofs[i].ZA_); - BOOST_CHECK(initial_proof.sigma_proofs[i].ZC_ == resulted_proof.sigma_proofs[i].ZC_); - BOOST_CHECK(initial_proof.sigma_proofs[i].Gk_ == resulted_proof.sigma_proofs[i].Gk_); - BOOST_CHECK(initial_proof.sigma_proofs[i].zV_ == resulted_proof.sigma_proofs[i].zV_); - BOOST_CHECK(initial_proof.sigma_proofs[i].zR_ == resulted_proof.sigma_proofs[i].zR_); - } - - BOOST_CHECK(initial_proof.bulletproofs.A == resulted_proof.bulletproofs.A); - BOOST_CHECK(initial_proof.bulletproofs.S == resulted_proof.bulletproofs.S); - BOOST_CHECK(initial_proof.bulletproofs.T1 == resulted_proof.bulletproofs.T1); - BOOST_CHECK(initial_proof.bulletproofs.T2 == resulted_proof.bulletproofs.T2); - BOOST_CHECK(initial_proof.bulletproofs.T_x1 == resulted_proof.bulletproofs.T_x1); - BOOST_CHECK(initial_proof.bulletproofs.T_x2 == resulted_proof.bulletproofs.T_x2); - BOOST_CHECK(initial_proof.bulletproofs.u == resulted_proof.bulletproofs.u); - BOOST_CHECK(initial_proof.bulletproofs.innerProductProof.a_ == resulted_proof.bulletproofs.innerProductProof.a_); - BOOST_CHECK(initial_proof.bulletproofs.innerProductProof.b_ == resulted_proof.bulletproofs.innerProductProof.b_); - BOOST_CHECK(initial_proof.bulletproofs.innerProductProof.c_ == resulted_proof.bulletproofs.innerProductProof.c_); - BOOST_CHECK(initial_proof.bulletproofs.innerProductProof.L_ == resulted_proof.bulletproofs.innerProductProof.L_); - BOOST_CHECK(initial_proof.bulletproofs.innerProductProof.R_ == resulted_proof.bulletproofs.innerProductProof.R_); - - BOOST_CHECK(initial_proof.schnorrProof.u == resulted_proof.schnorrProof.u); - BOOST_CHECK(initial_proof.schnorrProof.P1 == resulted_proof.schnorrProof.P1); - BOOST_CHECK(initial_proof.schnorrProof.T1 == resulted_proof.schnorrProof.T1); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/src/liblelantus/test/sigma_extended_test.cpp b/src/liblelantus/test/sigma_extended_test.cpp deleted file mode 100644 index 14ffbe5eb5..0000000000 --- a/src/liblelantus/test/sigma_extended_test.cpp +++ /dev/null @@ -1,238 +0,0 @@ -#include "../sigmaextended_prover.h" -#include "../sigmaextended_verifier.h" - -#include "lelantus_test_fixture.h" - -#include - -namespace lelantus { - -class SigmaExtendedTests : public LelantusTestingSetup { -public: - struct Secret { - public: - Secret(std::size_t l) : l(l) { - s.randomize(); - v.randomize(); - r.randomize(); - } - - public: - std::size_t l; - Scalar s, v, r; - }; - -public: - typedef SigmaExtendedProver Prover; - typedef SigmaExtendedProof Proof; - typedef SigmaExtendedVerifier Verifier; - -public: - SigmaExtendedTests() {} - -public: - void GenerateParams(std::size_t _N, std::size_t _n, std::size_t _m = 0) { - N = _N; - n = _n; - m = _m; - if (!m) { - if (n <= 1) { - throw std::logic_error("Try to get value of m from invalid n"); - } - - m = (std::size_t)std::round(log(N) / log(n)); - } - - h_gens = RandomizeGroupElements(n * m); - g.randomize(); - } - - void GenerateBatchProof( - Prover &prover, - std::vector const &coins, - std::size_t l, - Scalar const &s, - Scalar const &v, - Scalar const &r, - Scalar const &x, - Proof &proof - ) { - auto gs = g * s.negate(); - std::vector commits(coins.begin(), coins.end()); - for (auto &c : commits) { - c += gs; - } - - Scalar rA, rB, rC, rD; - rA.randomize(); - rB.randomize(); - rC.randomize(); - rD.randomize(); - - std::vector sigma; - std::vector Tk, Pk, Yk; - Tk.resize(m); - Pk.resize(m); - Yk.resize(m); - - std::vector a; - a.resize(n * m); - - prover.sigma_commit( - commits, l, rA, rB, rC, rD, a, Tk, Pk, Yk, sigma, proof); - - prover.sigma_response( - sigma, a, rA, rB, rC, rD, v, r, Tk, Pk, x, proof); - } - -public: - std::size_t N; - std::size_t n; - std::size_t m; - - std::vector h_gens; - GroupElement g; -}; - -BOOST_FIXTURE_TEST_SUITE(lelantus_sigma_tests, SigmaExtendedTests) - -BOOST_AUTO_TEST_CASE(one_out_of_N_variable_batch) -{ - GenerateParams(64, 4); - - std::size_t commit_size = 60; // require padding - auto commits = RandomizeGroupElements(commit_size); - - // Generate - std::vector secrets; - std::vector indexes = { 0, 1, 3, 59 }; - std::vector set_sizes = { 60, 60, 59, 16 }; - - for (auto index : indexes) { - secrets.emplace_back(index); - - auto &s = secrets.back(); - - commits[index] = Primitives::double_commit( - g, s.s, h_gens[1], s.v, h_gens[0], s.r - ); - } - - Prover prover(g, h_gens, n, m); - Verifier verifier(g, h_gens, n, m); - std::vector proofs; - std::vector serials; - std::vector challenges; - - for (std::size_t i = 0; i < indexes.size(); i++) { - Scalar x; - x.randomize(); - proofs.emplace_back(); - serials.push_back(secrets[i].s); - std::vector commits_(commits.begin() + commit_size - set_sizes[i], commits.end()); - GenerateBatchProof( - prover, - commits_, - secrets[i].l - (commit_size - set_sizes[i]), - secrets[i].s, - secrets[i].v, - secrets[i].r, - x, - proofs.back() - ); - challenges.emplace_back(x); - - // Verify individual proofs as a sanity check - BOOST_CHECK(verifier.singleverify(commits, x, secrets[i].s, set_sizes[i], proofs.back())); - BOOST_CHECK(verifier.singleverify(commits_, x, secrets[i].s, proofs.back())); - } - - BOOST_CHECK(verifier.batchverify(commits, challenges, serials, set_sizes, proofs)); -} - -BOOST_AUTO_TEST_CASE(one_out_of_N_batch) -{ - GenerateParams(16, 4); - - auto commits = RandomizeGroupElements(N); - - // Generate - std::vector secrets; - - for (auto index : {1, 3, 5, 9, 15}) { - secrets.emplace_back(index); - - auto &s = secrets.back(); - - commits[index] = Primitives::double_commit( - g, s.s, h_gens[1], s.v, h_gens[0], s.r); - } - - Prover prover(g, h_gens, n, m); - std::vector proofs; - std::vector serials; - - Scalar x; - x.randomize(); - - for (auto const &s : secrets) { - proofs.emplace_back(); - serials.push_back(s.s); - GenerateBatchProof( - prover, commits, s.l, s.s, s.v, s.r, x, proofs.back()); - } - - Verifier verifier(g, h_gens, n, m); - BOOST_CHECK(verifier.batchverify(commits, x, serials, proofs)); - - // verify subset of valid proofs should success also - serials.pop_back(); - proofs.pop_back(); - BOOST_CHECK(verifier.batchverify(commits, x, serials, proofs)); -} - -BOOST_AUTO_TEST_CASE(one_out_of_N_batch_with_some_invalid_proof) -{ - GenerateParams(16, 4); - - auto commits = RandomizeGroupElements(N); - - // Generate - std::vector secrets; - - for (auto index : {1, 3}) { - secrets.emplace_back(index); - - auto &s = secrets.back(); - - commits[index] = Primitives::double_commit( - g, s.s, h_gens[1], s.v, h_gens[0], s.r); - } - - Prover prover(g, h_gens, n, m); - std::vector proofs; - std::vector serials; - - Scalar x; - x.randomize(); - - for (auto const &s : secrets) { - proofs.emplace_back(); - serials.push_back(s.s); - GenerateBatchProof( - prover, commits, s.l, s.s, s.v, s.r, x, proofs.back()); - } - - // Add an invalid - proofs.push_back(proofs.back()); - - serials.emplace_back(serials.back()); - serials.back().randomize(); - - Verifier verifier(g, h_gens, n, m); - BOOST_CHECK(!verifier.batchverify(commits, x, serials, proofs)); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // namespace lelantus \ No newline at end of file diff --git a/src/liblelantus/threadpool.h b/src/liblelantus/threadpool.h deleted file mode 100644 index 1fa04369d4..0000000000 --- a/src/liblelantus/threadpool.h +++ /dev/null @@ -1,139 +0,0 @@ -#ifndef THREADPOOL_H -#define THREADPOOL_H - -#include -#include -#include -#include -#include -//#include - -#define BOOST_THREAD_PROVIDES_FUTURE - -#include -#include -#include -#include -#include - - -// our code currently relies on boost disable_interruption. This will go away with core upgrade -//#include - -// Number of seconds before thread shuts down if idle -constexpr static int secondsBeforeThreadShutdown = 60; - -// Simple thread pool class for using multiple cores effeciently - -template -class ParallelOpThreadPool { -private: - std::list threads; - std::queue> task_queue; - boost::mutex task_queue_mutex; - boost::condition_variable task_queue_condition; - - bool shutdown; - size_t const number_of_threads; - - void ThreadProc() { - for (;;) { - boost::packaged_task job; - { - boost::unique_lock lock(task_queue_mutex); - - task_queue_condition.wait_for(lock, boost::chrono::seconds(secondsBeforeThreadShutdown), - [this] { return !task_queue.empty() || shutdown; }); - if (task_queue.empty()) { - // Either timeout or shutdown. If it's a timeout we need to delete ourself from the thread list and detach the thread - // In case of shutdown thread list will be empty and destructor will wait for this thread completion - boost::thread::id currentId = boost::this_thread::get_id(); - auto pThread = std::find_if(threads.begin(), threads.end(), - [currentId](const boost::thread &t) { return t.get_id() == currentId; }); - if (pThread != threads.end()) { - pThread->detach(); - threads.erase(pThread); - } - break; - } - job = std::move(task_queue.front()); - task_queue.pop(); - } - job(); - } - } - - void StartThreads() { - // should be called with mutex aquired - // start missing threads - while (threads.size() < number_of_threads) - threads.emplace_back(std::bind(&ParallelOpThreadPool::ThreadProc, this)); - } - -public: - ParallelOpThreadPool(std::size_t thread_number) : shutdown(false), number_of_threads(thread_number) {} - - ~ParallelOpThreadPool() { - Shutdown(); - } - - // Post a task to the thread pool and return a future to wait for its completion - boost::future PostTask(std::function task) { - boost::packaged_task packagedTask(task); - boost::future ret = packagedTask.get_future(); - - boost::mutex::scoped_lock lock(task_queue_mutex); - - // lazy start threads on first request or after shutdown - if (threads.size() < number_of_threads) - StartThreads(); - - task_queue.emplace(std::move(packagedTask)); - task_queue_condition.notify_one(); - - return ret; - } - - int GetNumberOfThreads() const { - return number_of_threads; - } - - void Shutdown() { - std::list threadsToJoin; - - { - boost::mutex::scoped_lock lock(task_queue_mutex); - shutdown = true; - task_queue_condition.notify_all(); - - // move the list to separate variable to wait for the shutdown process to complete - threadsToJoin.swap(threads); - } - - // wait for all the threads - for (boost::thread &t: threadsToJoin) - t.join(); - } - - bool IsPoolShutdown() { - boost::mutex::scoped_lock lock(task_queue_mutex); - return shutdown; - } - - std::size_t GetPendingTaskCount() { - boost::mutex::scoped_lock lock(task_queue_mutex); - return task_queue.size(); - } -}; - - -// helper class to put thread interruption on pause -class DoNotDisturb { -private: - boost::this_thread::disable_interruption dnd; -public: - DoNotDisturb() {} -}; - - -#endif \ No newline at end of file diff --git a/src/libspark/coin.h b/src/libspark/coin.h index 664d522643..eb16bd6e3f 100644 --- a/src/libspark/coin.h +++ b/src/libspark/coin.h @@ -7,6 +7,8 @@ #include "aead.h" #include "util.h" #include "../uint256.h" +#include "crypto/sha256.h" +#include "primitives/mint_spend.h" namespace spark { @@ -138,6 +140,135 @@ class Coin { } }; +} // namespace spark + +// keep this just to not break old index (chain index serialization) +namespace sigma { +enum class CoinDenomination : std::uint8_t { + SIGMA_DENOM_0_05 = 5, + SIGMA_DENOM_0_1 = 0, + SIGMA_DENOM_0_5 = 1, + SIGMA_DENOM_1 = 2, + SIGMA_DENOM_10 = 3, + SIGMA_DENOM_25 = 6, + SIGMA_DENOM_100 = 4 +}; +// Serialization support for CoinDenomination + +template +void Serialize(Stream& os, CoinDenomination d) +{ + Serialize(os, static_cast(d)); +} + +template +void Unserialize(Stream& is, CoinDenomination& d) +{ + std::uint8_t v; + Unserialize(is, v); + d = static_cast(v); } +class PublicCoin { +public: + PublicCoin() {} + template + inline void Serialize(Stream& s) const { + constexpr int size = GroupElement::memoryRequired(); + unsigned char buffer[size + sizeof(int32_t)]; + value.serialize(buffer); + std::memcpy(buffer + size, &denomination, sizeof(denomination)); + char* b = (char*)buffer; + s.write(b, size + sizeof(int32_t)); + } + + template + inline void Unserialize(Stream& s) { + constexpr int size = GroupElement::memoryRequired(); + unsigned char buffer[size + sizeof(int32_t)]; + char* b = (char*)buffer; + s.read(b, size + sizeof(int32_t)); + value.deserialize(buffer); + std::memcpy(&denomination, buffer + size, sizeof(denomination)); + } + +private: + GroupElement value; + CoinDenomination denomination; +}; + +struct CSpendCoinInfo { + CoinDenomination denomination; + int coinGroupId; + + template + void Serialize(Stream& s) const { + int64_t tmp = uint8_t(denomination); + s << tmp; + tmp = coinGroupId; + s << tmp; + } + template + void Unserialize(Stream& s) { + int64_t tmp; + s >> tmp; denomination = CoinDenomination(tmp); + s >> tmp; coinGroupId = int(tmp); + } + +}; + +struct CScalarHash { + std::size_t operator ()(const Scalar& bn) const noexcept { + std::vector bnData(bn.memoryRequired()); + bn.serialize(&bnData[0]); + unsigned char hash[CSHA256::OUTPUT_SIZE]; + CSHA256().Write(&bnData[0], bnData.size()).Finalize(hash); + // take the first bytes of "hash". + std::size_t result; + std::memcpy(&result, hash, sizeof(std::size_t)); + return result; + } +}; + +using spend_info_container = std::unordered_map; + +} + +namespace lelantus { + +using namespace secp_primitives; + +// Stub for chain index serialization only (Lelantus protocol removed). +class PublicCoin { +public: + PublicCoin() : value() {} + PublicCoin(const GroupElement& coin) : value(coin) {} + + const GroupElement& getValue() const { return value; } + uint256 getValueHash() const { return primitives::GetPubCoinValueHash(value); } + bool operator==(const PublicCoin& other) const { return value == other.value; } + bool operator!=(const PublicCoin& other) const { return !(*this == other); } + bool validate() const { return value.isMember() && !value.isInfinity(); } + size_t GetSerializeSize() const { return value.memoryRequired(); } + + template + inline void Serialize(Stream& s) const { + std::vector buffer(GetSerializeSize()); + value.serialize(buffer.data()); + s.write((const char *)buffer.data(), buffer.size()); + } + + template + inline void Unserialize(Stream& s) { + std::vector buffer(GetSerializeSize()); + s.read((char *)buffer.data(), buffer.size()); + value.deserialize(buffer.data()); + } + +private: + GroupElement value; +}; + +} // namespace lelantus + #endif diff --git a/src/libspark/grootle.h b/src/libspark/grootle.h index f0d5dd4b27..e0efbffa0f 100644 --- a/src/libspark/grootle.h +++ b/src/libspark/grootle.h @@ -1,9 +1,11 @@ #ifndef FIRO_LIBSPARK_GROOTLE_H #define FIRO_LIBSPARK_GROOTLE_H +// Include standard library first to avoid macro conflicts with system headers (e.g. ) +#include + #include "grootle_proof.h" #include -#include #include "util.h" namespace spark { diff --git a/src/libspark/spend_transaction.h b/src/libspark/spend_transaction.h index 9ad013aa03..49cefb8c03 100644 --- a/src/libspark/spend_transaction.h +++ b/src/libspark/spend_transaction.h @@ -1,6 +1,11 @@ #ifndef FIRO_SPARK_SPEND_TRANSACTION_H #define FIRO_SPARK_SPEND_TRANSACTION_H + + #include +#include + +#include "grootle_proof.h" #include "keys.h" #include "coin.h" #include "schnorr.h" diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp index ebc0302027..2fb025a551 100644 --- a/src/llmq/quorums_instantsend.cpp +++ b/src/llmq/quorums_instantsend.cpp @@ -490,23 +490,13 @@ bool CInstantSendManager::CheckCanLock(const CTransaction& tx, bool printDebug, return false; } - if (tx.nType == isutils::INSTANTSEND_ADAPTED_TX ) { - if (tx.IsLelantusJoinSplit()) { - for (CTxIn const & in : tx.vin) { - Scalar serial; - serial.deserialize(&in.scriptSig.front()); - LOCK(cs_main); - if (lelantus::CLelantusState::GetState()->IsUsedCoinSerial(serial)) - return false; - } - } else if (tx.IsSparkSpend()) { - for (CTxIn const & in : tx.vin) { - GroupElement lTag; - lTag.deserialize(&in.scriptSig.front()); - LOCK(cs_main); - if (spark::CSparkState::GetState()->IsUsedLTag(lTag)) - return false; - } + if (tx.IsSparkSpend()) { + for (CTxIn const & in : tx.vin) { + GroupElement lTag; + lTag.deserialize(&in.scriptSig.front()); + LOCK(cs_main); + if (spark::CSparkState::GetState()->IsUsedLTag(lTag)) + return false; } return true; } @@ -617,7 +607,7 @@ void CInstantSendManager::HandleNewInputLockRecoveredSig(const CRecoveredSig& re return; } - if (tx && (tx->IsLelantusJoinSplit() || tx->IsSparkSpend())) { + if (tx && tx->IsSparkSpend()) { tx = MakeTransactionRef(isutils::AdaptPrivateTx(*tx)); } @@ -1577,31 +1567,6 @@ bool CInstantSendManager::IsNewInstantSendEnabled() const namespace isutils { -CTransaction AdaptJsplitTx(CTransaction const & tx) -{ - static size_t const jsplitSerialSize = 32; - - CTransaction result{tx}; - std::unique_ptr jsplit; - try { - jsplit = lelantus::ParseLelantusJoinSplit(tx); - } - catch (...) { - return result; - } - const_cast*>(&result.vin)->clear(); //This const_cast was done intentionally as the current design allows for this way only - for (Scalar const & serial : jsplit->getCoinSerialNumbers()) { - CTxIn newin; - newin.scriptSig.resize(jsplitSerialSize); - serial.serialize(&newin.scriptSig.front()); - newin.prevout.hash = primitives::GetSerialHash(serial); - newin.prevout.n = 0; - const_cast*>(&result.vin)->push_back(newin); - } - *const_cast(&result.nType) = INSTANTSEND_ADAPTED_TX; - assert(result.GetHash() == tx.GetHash()); - return result; -} CTransaction AdaptSparkTx(CTransaction const & tx) { @@ -1632,9 +1597,7 @@ CTransaction AdaptSparkTx(CTransaction const & tx) CTransaction AdaptPrivateTx(CTransaction const & tx) { - if (tx.IsLelantusJoinSplit()) { - return AdaptJsplitTx(tx); - } else if (tx.IsSparkSpend()) { + if (tx.IsSparkSpend()) { return AdaptSparkTx(tx); } return tx; diff --git a/src/miner.cpp b/src/miner.cpp index 86817cf6de..eba49030e2 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -33,7 +33,6 @@ #include "crypto/MerkleTreeProof/mtp.h" #include "crypto/Lyra2Z/Lyra2Z.h" #include "crypto/Lyra2Z/Lyra2.h" -#include "lelantus.h" #include "evo/spork.h" #include #include @@ -430,22 +429,6 @@ bool BlockAssembler::TestForBlock(CTxMemPool::txiter iter) const CTransaction &tx = iter->GetTx(); - // Check transaction against lelantus limits - if(tx.IsLelantusJoinSplit()) { - CAmount spendAmount = lelantus::GetSpendTransparentAmount(tx); - size_t spendNumber = lelantus::GetSpendInputs(tx); - const auto ¶ms = chainparams.GetConsensus(); - - if (spendNumber > params.nMaxLelantusInputPerTransaction || spendAmount > params.nMaxValueLelantusSpendPerTransaction) - return false; - - if (spendNumber + nLelantusSpendInputs > params.nMaxLelantusInputPerBlock) - return false; - - if (spendAmount + nLelantusSpendAmount > params.nMaxValueLelantusSpendPerBlock) - return false; - } - // Check transaction against spark limits if(tx.IsSparkSpend()) { CAmount spendAmount = spark::GetSpendTransparentAmount(tx); @@ -465,21 +448,6 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) { const CTransaction &tx = iter->GetTx(); - if(tx.IsLelantusJoinSplit()) { - CAmount spendAmount = lelantus::GetSpendTransparentAmount(tx); - size_t spendNumber = lelantus::GetSpendInputs(tx); - const auto ¶ms = chainparams.GetConsensus(); - - if (spendAmount > params.nMaxValueLelantusSpendPerTransaction) - return; - - if ((nLelantusSpendAmount += spendAmount) > params.nMaxValueLelantusSpendPerBlock) - return; - - if ((nLelantusSpendInputs += spendNumber) > params.nMaxLelantusInputPerBlock) - return; - } - if(tx.IsSparkSpend()) { CAmount spendAmount = spark::GetSpendTransparentAmount(tx); const auto ¶ms = chainparams.GetConsensus(); @@ -987,13 +955,6 @@ void BlockAssembler::FillBlackListForBlockTemplate() { } } - // Now if we have limit on lelantus transparent outputs scan mempool and drop all the transactions exceeding the limit - if (sporkMap.count(CSporkAction::featureLelantusTransparentLimit) > 0) { - BlacklistTxsExceedingLimit(sporkMap[CSporkAction::featureLelantusTransparentLimit].second, - [](const CTransaction &tx)->bool { return tx.IsLelantusJoinSplit(); }, - [](const CTransaction &tx)->CAmount { return lelantus::GetSpendTransparentAmount(tx); }); - } - // Same for spark spends if (sporkMap.count(CSporkAction::featureSparkTransparentLimit) > 0) { BlacklistTxsExceedingLimit(sporkMap[CSporkAction::featureSparkTransparentLimit].second, diff --git a/src/primitives/mint_spend.cpp b/src/primitives/mint_spend.cpp index 486ca75996..ca55d43cb5 100644 --- a/src/primitives/mint_spend.cpp +++ b/src/primitives/mint_spend.cpp @@ -1,24 +1,4 @@ #include "mint_spend.h" -#include "../liblelantus/coin.h" - -GroupElement const & MintMeta::GetPubCoinValue() const { - return pubCoinValue; -} - - -void MintMeta::SetPubCoinValue(GroupElement const & other) { - if (other == pubCoinValue) - return; - pubCoinValueHash.reset(); - pubCoinValue = other; -} - - -uint256 MintMeta::GetPubCoinValueHash() const { - if(!pubCoinValueHash) - pubCoinValueHash.reset(primitives::GetPubCoinValueHash(pubCoinValue)); - return *pubCoinValueHash; -} namespace primitives { diff --git a/src/primitives/mint_spend.h b/src/primitives/mint_spend.h index 9e55d06f78..28371a210c 100644 --- a/src/primitives/mint_spend.h +++ b/src/primitives/mint_spend.h @@ -10,87 +10,9 @@ #include #include #include "key.h" -#include "liblelantus/coin.h" #include "serialize.h" #include "firo_params.h" - -//struct that is safe to store essential mint data, without holding any information that allows for actual spending (serial, randomness, private key) -struct MintMeta -{ - int nHeight; - int nId; - GroupElement const & GetPubCoinValue() const; - void SetPubCoinValue(GroupElement const & other); - uint256 GetPubCoinValueHash() const ; - uint256 hashSerial; - uint8_t nVersion; - uint256 txid; - bool isUsed; - bool isArchived; - bool isSeedCorrect; -protected: - GroupElement pubCoinValue; - mutable boost::optional pubCoinValueHash; -}; - -struct CLelantusMintMeta : MintMeta -{ - uint64_t amount; -}; - -struct CLelantusEntry { - //public - GroupElement value; - - //private - Scalar randomness; - Scalar serialNumber; - - // Signature over partial transaction - // to make sure the outputs are not changed by attacker. - std::vector ecdsaSecretKey; - - bool IsUsed; - int nHeight; - int id; - - // Starting from Version 3 == sigma, this number is coin value * COIN, - // I.E. it is set to 100.000.000 for 1 firo. - int64_t amount; -}; - -class CLelantusSpendEntry -{ -public: - Scalar coinSerial; - uint256 hashTx; - GroupElement pubCoin; - int id; - int64_t amount; - - CLelantusSpendEntry() - { - SetNull(); - } - - void SetNull() - { - coinSerial = Scalar(uint64_t(0)); - pubCoin = GroupElement(); - id = 0; - amount = 0; - } - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(coinSerial); - READWRITE(hashTx); - READWRITE(pubCoin); - READWRITE(id); - READWRITE(amount); - } -}; +#include "../secp256k1/include/GroupElement.h" namespace primitives { uint256 GetSerialHash(const secp_primitives::Scalar& bnSerial); diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index af8ae02326..949d500948 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -59,7 +59,6 @@ res/icons/tools.png res/icons/exchange.png res/icons/balances.png - res/icons/lelantus.png res/icons/paymentcode.png res/icons/ext_add.png res/icons/ext_add_light.png diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index c6bc97e4cb..fa13480e0b 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -33,7 +33,6 @@ #include "chainparams.h" #include "init.h" -#include "lelantus.h" #include "util.h" #include "evo/deterministicmns.h" @@ -121,7 +120,6 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * openRPCConsoleAction(0), openAction(0), showHelpMessageAction(0), - lelantusAction(0), masternodeAction(0), logoAction(0), trayIcon(0), @@ -344,14 +342,6 @@ void BitcoinGUI::createActions() tabGroup->addAction(historyAction); #ifdef ENABLE_WALLET - lelantusAction = new QAction(tr("&Lelantus"), this); - lelantusAction->setStatusTip(tr("Anonymize your coins")); - lelantusAction->setToolTip(lelantusAction->statusTip()); - lelantusAction->setCheckable(true); - lelantusAction->setShortcut(QKeySequence(QString("Alt+%1").arg(key++))); - tabGroup->addAction(lelantusAction); - lelantusAction->setVisible(false); - // These showNormalIfMinimized are needed because Send Coins and Receive Coins // can be triggered from the tray menu, and need to show the GUI to be useful. masternodeAction = new QAction(tr("&Masternodes"), this); @@ -524,7 +514,6 @@ void BitcoinGUI::createToolBars() toolbar->addAction(sendCoinsAction); toolbar->addAction(receiveCoinsAction); toolbar->addAction(historyAction); - toolbar->addAction(lelantusAction); toolbar->addAction(masternodeAction); logoLabel = new QLabel(); @@ -646,7 +635,6 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled) receiveCoinsAction->setEnabled(enabled); receiveCoinsMenuAction->setEnabled(enabled); historyAction->setEnabled(enabled); - lelantusAction->setEnabled(enabled); masternodeAction->setEnabled(enabled); encryptWalletAction->setEnabled(enabled); backupWalletAction->setEnabled(enabled); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 933d0898cb..3df8caf886 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -122,7 +122,6 @@ class BitcoinGUI : public QMainWindow QAction *openRPCConsoleAction; QAction *openAction; QAction *showHelpMessageAction; - QAction *lelantusAction; QAction *masternodeAction; QAction *logoAction; QToolBar *toolbar; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 0b7d35c85c..161da36f9e 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -509,7 +509,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog, bool a nQuantity++; // Amount - if(out.tx->tx->vout[out.i].scriptPubKey.IsLelantusJMint() || out.tx->tx->vout[out.i].scriptPubKey.IsSparkSMint()) { + if(out.tx->tx->vout[out.i].scriptPubKey.IsSparkSMint()) { nAmount += model->GetJMintCredit(out.tx->tx->vout[out.i]); } else { nAmount += out.tx->tx->vout[out.i].nValue; @@ -733,7 +733,7 @@ void CoinControlDialog::updateView() int nChildren = 0; BOOST_FOREACH(const COutput& out, coins.second) { CAmount amount; - if(out.tx->tx->vout[out.i].scriptPubKey.IsLelantusJMint() || out.tx->tx->vout[out.i].scriptPubKey.IsSparkSMint()) { + if(out.tx->tx->vout[out.i].scriptPubKey.IsSparkSMint()) { amount = model->GetJMintCredit(out.tx->tx->vout[out.i]); } else { amount = out.tx->tx->vout[out.i].nValue; diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index b567badcce..b7aeb0821b 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -163,46 +163,6 @@ - - - - Restore all Lelantus transactions following a reindex. - - - &Reindex Lelantus - - - - - - - - - - Lelantus - - - - - - Enable &auto-anonymize features - - - - - - - Enable &splitting when minting - - - - - - - Enable &lelantus manual-anonymize page - - - diff --git a/src/qt/locale/bitcoin_zh_CN.ts b/src/qt/locale/bitcoin_zh_CN.ts index 8eb35134fd..c5f915dbd0 100644 --- a/src/qt/locale/bitcoin_zh_CN.ts +++ b/src/qt/locale/bitcoin_zh_CN.ts @@ -854,10 +854,6 @@ Once this fee is paid, all future sends to this RAP address do not incur any add Send to RAP address 发送到 RAP 地址 - - Lelantus balance - Lelantus 余额 - Status 状态 @@ -880,7 +876,7 @@ Notification transactions use Lelantus facilities to enhance privacy. After the notification transaction is received by the RAP address issuer, funds can be privately sent to the RAP secret addresses. 向 RAP 地址发送资金需要付款人在第一次付款前发送通知交易。 -通知交易使用 Lelantus 功能来增强隐私。 +通知交易使用隐私功能来增强隐私。 在 RAP 地址发布者收到通知交易后,资金可以被私下发送到 RAP 秘密地址。 @@ -1445,22 +1441,10 @@ After the notification transaction is received by the RAP address issuer, funds Enable &auto-anonymize features 启用自动匿名化功能 - - Enable &lelantus manual-anonymize page - 启用 Lelantus 手动匿名化界面 - &Spend unconfirmed change 使用未经确认的零钱(&S) - - &Reindex Lelantus - 重新索引 Lelantus(&R) - - - Restore all Lelantus transactions following a reindex. - 在重新索引后恢复所有 Lelantus 交易。 - Automatically open the Firo client port on the router. This only works when your router supports UPnP and it is enabled. 自动在路由器中打开 Firo 端口。只有当你的路由器开启了 UPnP 选项时此功能才有效。 @@ -1601,10 +1585,6 @@ After the notification transaction is received by the RAP address issuer, funds The supplied proxy address is invalid. 提供的代理服务器地址无效。 - - Confirm Reindex Lelantus - 确认重新索引 Lelantus - Warning: On restart, this setting will wipe your transaction list, reindex the blockchain, and restore the list from the seed in your wallet. This will likely take a few hours. Are you sure? 警告:在重新启动时,该设置将擦除你的交易列表,重新索引区块链,并从你钱包中的种子恢复列表。这可能需要几个小时。你确定吗? @@ -1829,10 +1809,6 @@ After the notification transaction is received by the RAP address issuer, funds %n block(s) %n 个区块 - - Global Lelantus Pool: - 整个 Lelantus 池 - Total: 总数: diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 8eaf288f9f..6c7d0c3d84 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -164,7 +164,6 @@ void OptionsDialog::setModel(OptionsModel *_model) connect(ui->threadsScriptVerif, qOverload(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning); /* Wallet */ connect(ui->spendZeroConfChange, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); - connect(ui->reindexLelantus, &QCheckBox::clicked, this, &OptionsDialog::handleEnabledZapChanged); /* Network */ connect(ui->allowIncoming, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); connect(ui->connectSocks, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); @@ -183,17 +182,7 @@ void OptionsDialog::setMapper() /* Wallet */ mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange); - mapper->addMapping(ui->reindexLelantus, OptionsModel::ReindexLelantus); mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures); - - /* Lelantus */ - mapper->addMapping(ui->autoAnonymize, OptionsModel::AutoAnonymize); - mapper->addMapping(ui->fSplit, OptionsModel::Split); - if (!lelantus::IsLelantusAllowed()) { - ui->lelantusPage->setVisible(false); - } - mapper->addMapping(ui->lelantusPage, OptionsModel::LelantusPage); - /* Network */ mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); mapper->addMapping(ui->allowIncoming, OptionsModel::Listen); @@ -269,19 +258,7 @@ void OptionsDialog::on_hideTrayIcon_stateChanged(int fState) void OptionsDialog::handleEnabledZapChanged(){ QMessageBox msgBox; - if(ui->reindexLelantus->isChecked()){ - QMessageBox::StandardButton retval = QMessageBox::warning(this, tr("Confirm Reindex Lelantus"), - tr("Warning: On restart, this setting will wipe your transaction list, reindex the blockchain, and restore the list from the seed in your wallet. This will likely take a few hours. Are you sure?"), - QMessageBox::Yes|QMessageBox::Cancel, - QMessageBox::Cancel); - if(retval == QMessageBox::Cancel) { - ui->reindexLelantus->setChecked(false); - }else { - showRestartWarning(); - } - }else { - clearStatusLabel(); - } + clearStatusLabel(); } void OptionsDialog::showRestartWarning(bool fPersistent) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 8569b0cb81..1e8f7cca07 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -89,11 +89,6 @@ void OptionsModel::Init(bool resetSettings) settings.setValue("fSplit", true); fSplit = settings.value("fSplit", true).toBool(); - if (!settings.contains("fLelantusPage")) - settings.setValue("fLelantusPage", false); - fLelantusPage = settings.value("fLelantusPage", false).toBool(); - - // These are shared with the core or have a command-line parameter // and we want command-line parameters to overwrite the GUI settings. // @@ -123,21 +118,6 @@ void OptionsModel::Init(bool resetSettings) if (!SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool())) addOverriddenOption("-spendzeroconfchange"); - if (!settings.contains("bReindexLelantus")) - settings.setValue("bReindexLelantus", DEFAULT_ZAP_WALLET); - bool reindexLelantus = settings.value("bReindexLelantus").toBool(); - if (reindexLelantus) { - if (!SoftSetBoolArg("-zapwalletmints", true)) - addOverriddenOption("-zapwalletmints"); - if (!SoftSetBoolArg("-reindex", true)) - addOverriddenOption("-reindex"); - if (!SoftSetArg("-zapwallettxes", std::string("1"))) - addOverriddenOption("-zapwallettxes"); - } - - // Reset the flag to prevent unneeded reindex, - settings.setValue("bReindexLelantus", false); - #endif // Network @@ -269,9 +249,6 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const #ifdef ENABLE_WALLET case SpendZeroConfChange: return settings.value("bSpendZeroConfChange"); - - case ReindexLelantus: - return settings.value("bReindexLelantus"); #endif case DisplayUnit: return nDisplayUnit; @@ -285,8 +262,6 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return fAutoAnonymize; case Split: return fSplit; - case LelantusPage: - return fLelantusPage; case DatabaseCache: return settings.value("nDatabaseCache"); case ThreadsScriptVerif: @@ -402,12 +377,6 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in } break; - case ReindexLelantus: - if (settings.value("bReindexLelantus") != value) { - settings.setValue("bReindexLelantus", value); - setRestartRequired(true); - } - break; #endif case DisplayUnit: setDisplayUnit(value); @@ -439,11 +408,6 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in fSplit = value.toBool(); settings.setValue("fSplit", fSplit); break; - case LelantusPage: - fLelantusPage = value.toBool(); - settings.setValue("fLelantusPage", fLelantusPage); - Q_EMIT lelantusPageChanged(fLelantusPage); - break; case DatabaseCache: if (settings.value("nDatabaseCache") != value) { settings.setValue("nDatabaseCache", value); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 9130c53fd3..4715af8932 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -45,12 +45,10 @@ class OptionsModel : public QAbstractListModel ThreadsScriptVerif, // int DatabaseCache, // int SpendZeroConfChange, // bool - ReindexLelantus, // bool Listen, // bool TorSetup, // bool AutoAnonymize, // bool Split, // bool - LelantusPage, // bool enableRapAddresses, // bool OptionIDRowCount, }; @@ -75,7 +73,6 @@ class OptionsModel : public QAbstractListModel const QString& getOverriddenByCommandLine() { return strOverriddenByCommandLine; } bool getAutoAnonymize() { return fAutoAnonymize; } bool getfSplit() { return fSplit; } - bool getLelantusPage() {return fLelantusPage; } /* Restart flag helper */ void setRestartRequired(bool fRequired); @@ -92,7 +89,6 @@ class OptionsModel : public QAbstractListModel bool fCoinControlFeatures; bool fAutoAnonymize; bool fSplit; - bool fLelantusPage; bool fenableRapAddresses; /* settings that were overridden by command-line */ @@ -108,7 +104,6 @@ class OptionsModel : public QAbstractListModel void coinControlFeaturesChanged(bool); void enableRapAddressesChanged(bool); void autoAnonymizeChanged(bool); - void lelantusPageChanged(bool); void hideTrayIconChanged(bool); }; diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 55a91b5dcc..83111e4f33 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -2,8 +2,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "../lelantus.h" - #include "overviewpage.h" #include "ui_overviewpage.h" @@ -168,10 +166,6 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent) showOutOfSyncWarning(true); connect(ui->labelWalletStatus, &QPushButton::clicked, this, &OverviewPage::handleOutOfSyncWarningClicks); connect(ui->labelTransactionsStatus, &QPushButton::clicked, this, &OverviewPage::handleOutOfSyncWarningClicks); - - connect(&countDownTimer, &QTimer::timeout, this, &OverviewPage::countDown); - countDownTimer.start(30000); - connect(ui->migrateButton, &QPushButton::clicked, this, &OverviewPage::migrateClicked); } void OverviewPage::handleTransactionClicked(const QModelIndex &index) @@ -204,6 +198,60 @@ OverviewPage::~OverviewPage() delete ui; } +void OverviewPage::resizeEvent(QResizeEvent* event) +{ + QWidget::resizeEvent(event); + + // Retrieve new dimensions from the resize event + const int newWidth = event->size().width(); + const int newHeight = event->size().height(); + adjustTextSize(newWidth, newHeight); + + // Determine widths for specific widgets as percentages of total width + int labelWidth = static_cast(newWidth * 0.5); + int labelMinWidth = static_cast(newWidth * 0.15); + int labelMaxWidth = static_cast(newWidth * 0.35); + const int labelHeight = 20; + + // Configure the dimensions and constraints of each widget + ui->labelBalance->setFixedWidth(labelWidth); + ui->labelBalance->setMinimumWidth(labelMinWidth); + ui->labelBalance->setMaximumWidth(labelMaxWidth); + ui->labelBalance->setFixedHeight(labelHeight); + + ui->labelUnconfirmed->setFixedWidth(labelWidth); + ui->labelUnconfirmed->setMinimumWidth(labelMinWidth); + ui->labelUnconfirmed->setMaximumWidth(labelMaxWidth); + ui->labelUnconfirmed->setFixedHeight(labelHeight); + + int buttonWidth = static_cast(newWidth * 0.15); + FIRO_UNUSED int buttonHeight = static_cast(newHeight * 0.05); + int buttonMinHeight = static_cast(20); + int buttonMaxHeight = static_cast(45); + + ui->anonymizeButton->setMinimumWidth(buttonWidth); + ui->anonymizeButton->setMaximumWidth(buttonWidth * 2); + ui->anonymizeButton->setMinimumHeight(buttonMinHeight); + ui->anonymizeButton->setMaximumHeight(buttonMaxHeight); + + // Set the minimum width for all label widgets to ensure they maintain a consistent and readable size regardless of window resizing + ui->labelAnonymizable->setMinimumWidth(labelMinWidth); + ui->labelAlerts->setMinimumWidth(labelMinWidth); + ui->label->setMinimumWidth(labelMinWidth); + ui->labelWatchPending->setMinimumWidth(labelMinWidth); + ui->labelBalance->setMinimumWidth(labelMinWidth); + ui->labelSpendable->setMinimumWidth(labelMinWidth); + ui->labelWatchAvailable->setMinimumWidth(labelMinWidth); + ui->labelUnconfirmedPrivate->setMinimumWidth(labelMinWidth); + ui->labelWatchonly->setMinimumWidth(labelMinWidth); + ui->labelTotal->setMinimumWidth(labelMinWidth); + ui->labelWatchTotal->setMinimumWidth(labelMinWidth); + ui->labelUnconfirmed->setMinimumWidth(labelMinWidth); + ui->labelImmature->setMinimumWidth(labelMinWidth); + ui->labelPrivate->setMinimumWidth(labelMinWidth); + ui->label_4->setMinimumWidth(labelMinWidth); +} + void OverviewPage::on_anonymizeButton_clicked() { if (!walletModel) { @@ -280,7 +328,7 @@ void OverviewPage::setClientModel(ClientModel *model) this->clientModel = model; if(model) { - connect(model, &ClientModel::numBlocksChanged, this, &OverviewPage::onRefreshClicked); + connect(model, &ClientModel::numBlocksChanged, this, [this]() { ui->warningFrame->hide(); }); // Show warning if this is a prerelease version connect(model, &ClientModel::alertsChanged, this, &OverviewPage::updateAlerts); updateAlerts(model->getStatusBarWarnings()); @@ -290,7 +338,7 @@ void OverviewPage::setClientModel(ClientModel *model) void OverviewPage::setWalletModel(WalletModel *model) { this->walletModel = model; - onRefreshClicked(); + ui->warningFrame->hide(); if(model && model->getOptionsModel()) { // Set up transaction list @@ -358,190 +406,6 @@ void OverviewPage::showOutOfSyncWarning(bool fShow) ui->labelTransactionsStatus->setVisible(fShow); } -void OverviewPage::countDown() -{ - secDelay--; - if(secDelay <= 0) { - if(walletModel->getAvailableLelantusCoins() && spark::IsSparkAllowed() && chainActive.Height() < ::Params().GetConsensus().nLelantusGracefulPeriod){ - MigrateLelantusToSparkDialog migrate(walletModel); - } - countDownTimer.stop(); - } -} - -void OverviewPage::onRefreshClicked() -{ - auto privateBalance = walletModel->getWallet()->GetPrivateBalance(); - auto lGracefulPeriod = ::Params().GetConsensus().nLelantusGracefulPeriod; - int heightDifference = lGracefulPeriod - chainActive.Height(); - const int approxBlocksPerDay = 570; - int daysUntilMigrationCloses = heightDifference / approxBlocksPerDay; - - if(privateBalance.first > 0 && chainActive.Height() < lGracefulPeriod && spark::IsSparkAllowed()) { - ui->warningFrame->show(); - migrationWindowClosesIn = QString::fromStdString(std::to_string(daysUntilMigrationCloses)); - blocksRemaining = QString::fromStdString(std::to_string(heightDifference)); - migrateAmount = "" + BitcoinUnits::formatHtmlWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), privateBalance.first); - migrateAmount.append(""); - ui->textWarning1->setText(tr("We have detected Lelantus coins that have not been migrated to Spark. Migration window will close in %1 blocks (~ %2 days).").arg(blocksRemaining , migrationWindowClosesIn)); - ui->textWarning2->setText(tr("to migrate %1 ").arg(migrateAmount)); - QFont qFont = ui->migrateButton->font(); - qFont.setUnderline(true); - ui->migrateButton->setFont(qFont); - } else { - ui->warningFrame->hide(); - } -} - -void OverviewPage::migrateClicked() -{ - auto privateBalance = walletModel->getWallet()->GetPrivateBalance(); - FIRO_UNUSED auto lGracefulPeriod = ::Params().GetConsensus().nLelantusGracefulPeriod; - migrateAmount = "" + BitcoinUnits::formatHtmlWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), privateBalance.first); - migrateAmount.append(""); - QString info = tr("Your wallet needs to be unlocked to migrate your funds to Spark."); - - if(walletModel->getEncryptionStatus() == WalletModel::Locked) { - - AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this, info); - dlg.setModel(walletModel); - dlg.exec(); - } - if (walletModel->getEncryptionStatus() == WalletModel::Unlocked){ - if(walletModel->getAvailableLelantusCoins() && spark::IsSparkAllowed() && chainActive.Height() < ::Params().GetConsensus().nLelantusGracefulPeriod){ - MigrateLelantusToSparkDialog migrate(walletModel); - if(!migrate.getClickedButton()){ - ui->warningFrame->hide(); - } - } - } -} -MigrateLelantusToSparkDialog::MigrateLelantusToSparkDialog(WalletModel *_model):QMessageBox() -{ - this->model = _model; - QDialog::setWindowTitle("Migrate funds from Lelantus to Spark"); - QDialog::setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint); - - QLabel *ic = new QLabel(); - QIcon icon_; - icon_.addFile(QString::fromUtf8(":/icons/ic_info"), QSize(), QIcon::Normal, QIcon::On); - ic->setPixmap(icon_.pixmap(18, 18)); - ic->setFixedWidth(90); - ic->setAlignment(Qt::AlignRight); - ic->setStyleSheet("color:#92400E"); - - QLabel *text = new QLabel(); - text->setText(tr("Firo is migrating to Spark. Please migrate your funds.")); - text->setAlignment(Qt::AlignLeft); - text->setWordWrap(true); - text->setStyleSheet("color:#92400E;text-align:center;word-wrap: break-word;"); - - QPushButton *ignore = new QPushButton(this); - ignore->setText("Ignore"); - ignore->setStyleSheet("margin-top:30px;margin-bottom:60px;margin-left:20px;margin-right:50px;"); - QPushButton *migrate = new QPushButton(this); - migrate->setText("Migrate"); - migrate->setStyleSheet("color:#9b1c2e;background-color:none;margin-top:30px;margin-bottom:60px;margin-left:50px;margin-right:20px;border:1px solid #9b1c2e;"); - QHBoxLayout *groupButton = new QHBoxLayout(this); - groupButton->addWidget(ignore); - groupButton->addWidget(migrate); - - QHBoxLayout *hlayout = new QHBoxLayout(this); - hlayout->addWidget(ic); - hlayout->addWidget(text); - - QWidget *layout_ = new QWidget(); - layout_->setLayout(hlayout); - layout_->setStyleSheet("background-color:#FEF3C7;"); - - QVBoxLayout *vlayout = new QVBoxLayout(this); - vlayout->addWidget(layout_); - vlayout->addLayout(groupButton); - vlayout->setContentsMargins(0,0,0,0); - - QWidget *wbody = new QWidget(); - wbody->setLayout(vlayout); - - layout()->addWidget(wbody); - setContentsMargins(0, 0, 0, 0); - setStyleSheet("margin-right:-30px;"); - setStandardButtons(StandardButtons()); - - connect(ignore, &QPushButton::clicked, this, &MigrateLelantusToSparkDialog::onIgnoreClicked); - connect(migrate, &QPushButton::clicked, this, &MigrateLelantusToSparkDialog::onMigrateClicked); - exec(); -} - -void MigrateLelantusToSparkDialog::onIgnoreClicked() -{ - setVisible(false); - clickedButton = true; -} - -void MigrateLelantusToSparkDialog::onMigrateClicked() -{ - setVisible(false); - clickedButton = false; - model->migrateLelantusToSpark(); -} - -bool MigrateLelantusToSparkDialog::getClickedButton() -{ - return clickedButton; -} -void OverviewPage::resizeEvent(QResizeEvent* event) -{ - QWidget::resizeEvent(event); - - // Retrieve new dimensions from the resize event - const int newWidth = event->size().width(); - const int newHeight = event->size().height(); - adjustTextSize(newWidth, newHeight); - - // Determine widths for specific widgets as percentages of total width - int labelWidth = static_cast(newWidth * 0.5); - int labelMinWidth = static_cast(newWidth * 0.15); - int labelMaxWidth = static_cast(newWidth * 0.35); - const int labelHeight = 20; - - // Configure the dimensions and constraints of each widget - ui->labelBalance->setFixedWidth(labelWidth); - ui->labelBalance->setMinimumWidth(labelMinWidth); - ui->labelBalance->setMaximumWidth(labelMaxWidth); - ui->labelBalance->setFixedHeight(labelHeight); - - ui->labelUnconfirmed->setFixedWidth(labelWidth); - ui->labelUnconfirmed->setMinimumWidth(labelMinWidth); - ui->labelUnconfirmed->setMaximumWidth(labelMaxWidth); - ui->labelUnconfirmed->setFixedHeight(labelHeight); - - int buttonWidth = static_cast(newWidth * 0.15); - FIRO_UNUSED int buttonHeight = static_cast(newHeight * 0.05); - int buttonMinHeight = static_cast(20); - int buttonMaxHeight = static_cast(45); - - ui->anonymizeButton->setMinimumWidth(buttonWidth); - ui->anonymizeButton->setMaximumWidth(buttonWidth * 2); - ui->anonymizeButton->setMinimumHeight(buttonMinHeight); - ui->anonymizeButton->setMaximumHeight(buttonMaxHeight); - - // Set the minimum width for all label widgets to ensure they maintain a consistent and readable size regardless of window resizing - ui->labelAnonymizable->setMinimumWidth(labelMinWidth); - ui->labelAlerts->setMinimumWidth(labelMinWidth); - ui->label->setMinimumWidth(labelMinWidth); - ui->labelWatchPending->setMinimumWidth(labelMinWidth); - ui->labelBalance->setMinimumWidth(labelMinWidth); - ui->labelSpendable->setMinimumWidth(labelMinWidth); - ui->labelWatchAvailable->setMinimumWidth(labelMinWidth); - ui->labelUnconfirmedPrivate->setMinimumWidth(labelMinWidth); - ui->labelWatchonly->setMinimumWidth(labelMinWidth); - ui->labelTotal->setMinimumWidth(labelMinWidth); - ui->labelWatchTotal->setMinimumWidth(labelMinWidth); - ui->labelUnconfirmed->setMinimumWidth(labelMinWidth); - ui->labelImmature->setMinimumWidth(labelMinWidth); - ui->labelPrivate->setMinimumWidth(labelMinWidth); - ui->label_4->setMinimumWidth(labelMinWidth); -} void OverviewPage::adjustTextSize(int width, int height){ const double fontSizeScalingFactor = 133.0; diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 53a75aa594..e68c183a97 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -50,11 +50,9 @@ class OverviewPage : public QWidget public Q_SLOTS: void on_anonymizeButton_clicked(); - void migrateClicked(); - void onRefreshClicked(); void setBalance( - const CAmount& balance, + const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance, const CAmount& watchOnlyBalance, @@ -100,22 +98,6 @@ private Q_SLOTS: void updateAlerts(const QString &warnings); void updateWatchOnlyLabels(bool showWatchOnly); void handleOutOfSyncWarningClicks(); - void countDown(); -}; - -class MigrateLelantusToSparkDialog : public QMessageBox -{ - Q_OBJECT -private: - bool clickedButton; - WalletModel *model; -public: - MigrateLelantusToSparkDialog(WalletModel *model); - bool getClickedButton(); - -private Q_SLOTS: - void onIgnoreClicked(); - void onMigrateClicked(); }; #endif // BITCOIN_QT_OVERVIEWPAGE_H diff --git a/src/qt/res/css/firo.css b/src/qt/res/css/firo.css index 27afcf4324..78847566d7 100644 --- a/src/qt/res/css/firo.css +++ b/src/qt/res/css/firo.css @@ -1215,41 +1215,6 @@ QDialog#Intro QPushButton#ellipsisButton { min-width: 10px; } -/* lelantus */ - -QDialog#LelantusDialog QLabel#labelTotalAnonymizedCoinsText, -QDialog#LelantusDialog QLabel#labelLatestGroupText, -QDialog#LelantusDialog QLabel#labelUnspentText, -QDialog#LelantusDialog QLabel#spendableText, -QDialog#LelantusDialog QLabel#unconfirmedText, -QDialog#LelantusDialog QLabel#totalText { - qproperty-alignment: 'AlignVCenter | AlignRight'; - min-width: 100px; - margin-right: 5px; - padding-right: 5px; -} - -QDialog#LelantusDialog QLabel#labelGlobalLelantusPoolText, -QDialog#LelantusDialog QLabel#labelYourAnoymizedCoinsText { - margin-top: 0; - margin-right: 5px; -} - -QDialog#LelantusDialog QLabel#fallbackFeeWarningLabel, -QDialog#LelantusDialog QLabel#labelFeeHeadline { - qproperty-alignment: 'AlignVCenter'; - height: 28px; - font-weight: bold; -} - -QDialog#LelantusDialog QRadioButton#radioSmartFee { - margin-top: 2.5px; -} - -QDialog#LelantusDialog QRadioButton#radioCustomFee { - margin-top: 7px; -} - /* ManualMintDialog */ QDialog#ManualMintDialog QLabel#availableAmount, @@ -1664,7 +1629,6 @@ QDialog#SendCoinsDialog QLabel#labelCoinControlLowOutputText, QDialog#SendCoinsDialog QLabel#labelCoinControlFeeText, QDialog#SendCoinsDialog QLabel#labelCoinControlAfterFeeText, QDialog#SendCoinsDialog QLabel#labelCoinControlChangeText, -QDialog#SendCoinsDialog QLabel#labelGlobalLelantusPoolText, QDialog#SendCoinsDialog QLabel#labelYourAnoymizedCoinsText, QDialog#SendCoinsDialog QLabel#labelFeeHeadline, QDialog#SendCoinsDialog QLabel#fallbackFeeWarningLabel { diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 77d431ea11..8bf971e2eb 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -18,7 +18,6 @@ #include "base58.h" #include "chainparams.h" -#include "lelantus.h" #include "wallet/coincontrol.h" #include "validation.h" // mempool and minRelayTxFee #include "txmempool.h" @@ -367,17 +366,7 @@ void SendCoinsDialog::on_sendButton_clicked() CAmount mintSparkAmount = 0; CAmount txFee = 0; CAmount totalAmount = 0; - if (model->getWallet() && - model->getWallet()->GetPrivateBalance().first > 0 && - spark::IsSparkAllowed() && - chainActive.Height() < ::Params().GetConsensus().nLelantusGracefulPeriod) { - MigrateLelantusToSparkDialog migrateLelantusToSpark(model); - bool clickedButton = migrateLelantusToSpark.getClickedButton(); - if(!clickedButton) { - fNewRecipientAllowed = true; - return; - } - } + if ((fAnonymousMode == true) && spark::IsSparkAllowed()) { prepareStatus = model->prepareSpendSparkTransaction(currentTransaction, &ctrl); } else if ((fAnonymousMode == false) && (recipients.size() == sparkAddressCount)) { diff --git a/src/qt/sparkmodel.cpp b/src/qt/sparkmodel.cpp index 390eef963d..7eb877d94b 100644 --- a/src/qt/sparkmodel.cpp +++ b/src/qt/sparkmodel.cpp @@ -1,4 +1,3 @@ -#include "../lelantus.h" #include "../validation.h" #include "automintmodel.h" diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index dd2bed0d50..cb29a46aa0 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -253,29 +253,15 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco if (fAllToMe) { - if (wtx.tx->IsLelantusJoinSplit()) { - strHTML += "" + tr("Total debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -wtx.tx->GetValueOut()) + "
"; - strHTML += "" + tr("Total credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, wtx.tx->GetValueOut()) + "
"; - } else { - // Payment to self - CAmount nChange = wtx.GetChange(); - CAmount nValue = nCredit - nChange; - strHTML += "" + tr("Total debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -nValue) + "
"; - strHTML += "" + tr("Total credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "
"; - } + // Payment to self + CAmount nChange = wtx.GetChange(); + CAmount nValue = nCredit - nChange; + strHTML += "" + tr("Total debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -nValue) + "
"; + strHTML += "" + tr("Total credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "
"; } CAmount nTxFee = nDebit - wtx.tx->GetValueOut(); - if (wtx.tx->IsLelantusJoinSplit() && wtx.tx->vin.size() > 0) { - try { - nTxFee = lelantus::ParseLelantusJoinSplit(*wtx.tx)->getFee(); - } - catch (const std::exception &) { - //do nothing - } - } - if (wtx.tx->IsSparkSpend() && wtx.tx->vin.size() > 0) { try { nTxFee = spark::ParseSparkSpend(*wtx.tx).getFee(); diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index c50b6ec72a..17a6021df5 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -8,7 +8,6 @@ #include "base58.h" #include "consensus/consensus.h" -#include "lelantus.h" #include "validation.h" #include "timedata.h" #include "wallet/wallet.h" @@ -51,20 +50,8 @@ QList TransactionRecord::decomposeTransaction(const CWallet * isAllSigmaSpendFromMe = (wallet->IsMine(wtx.tx->vin[0], *wtx.tx) & ISMINE_SPENDABLE); } - bool isAllJoinSplitFromMe = false; - if (wtx.tx->vin[0].IsLelantusJoinSplit()) { - isAllJoinSplitFromMe = (wallet->IsMine(wtx.tx->vin[0], *wtx.tx) & ISMINE_SPENDABLE); - } - - if (wtx.tx->IsZerocoinSpend() || isAllSigmaSpendFromMe || isAllJoinSplitFromMe) { + if (wtx.tx->IsZerocoinSpend() || isAllSigmaSpendFromMe) { CAmount nTxFee = nDebit - wtx.tx->GetValueOut(); - if (isAllJoinSplitFromMe && wtx.tx->vin.size() > 0) { - try { - nTxFee = lelantus::ParseLelantusJoinSplit(*wtx.tx)->getFee(); - } catch (const std::exception &) { - // do nothing - } - } bool first = true; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 478665431f..3b149ee927 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -24,7 +24,6 @@ #include "wallet/walletexcept.h" #include "txmempool.h" #include "consensus/validation.h" -#include "lelantus.h" #include "bip47/account.h" #include "bip47/bip47utils.h" #include "cancelpassworddialog.h" @@ -1097,30 +1096,6 @@ std::pair WalletModel::getSparkBalance() return wallet->GetSparkBalance(); } -bool WalletModel::getAvailableLelantusCoins() -{ - if (!pwalletMain->zwallet) - return false; - - std::list coins = wallet->GetAvailableLelantusCoins(); - if (coins.size() > 0) { - return true; - } - - return false; -} - -bool WalletModel::migrateLelantusToSpark() -{ - std::string strFailReason; - bool res = wallet->LelantusToSpark(strFailReason); - if (!res) { - Q_EMIT message(tr("Lelantus To Spark"), QString::fromStdString(strFailReason), - CClientUIInterface::MSG_ERROR); - } - return res; -} - WalletModel::SendCoinsReturn WalletModel::prepareMintSparkTransaction(std::vector &transactions, QList recipients, std::vector >& wtxAndFees, std::list& reservekeys, const CCoinControl* coinControl) { CAmount total = 0; diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 847f6b6f43..3154047320 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -209,10 +209,6 @@ class WalletModel : public QObject std::list &reserveKeys ); - bool migrateLelantusToSpark(); - - bool getAvailableLelantusCoins(); - // Send coins to a list of recipients SendCoinsReturn sendCoins(WalletModelTransaction &transaction); // Wallet encryption diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index f874620af9..71c995a678 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -99,13 +99,3 @@ CReserveKey *WalletModelTransaction::getPossibleKeyChange() { return keyChange; } - -std::vector& WalletModelTransaction::getSpendCoins() -{ - return spendCoins; -} - -std::vector& WalletModelTransaction::getMintCoins() -{ - return mintCoins; -} \ No newline at end of file diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index 6792f6cd7f..f948bf55de 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -5,7 +5,6 @@ #ifndef BITCOIN_QT_WALLETMODELTRANSACTION_H #define BITCOIN_QT_WALLETMODELTRANSACTION_H -#include "../hdmint/hdmint.h" #include "../primitives/mint_spend.h" #include "spark/state.h" @@ -41,18 +40,15 @@ class WalletModelTransaction void reassignAmounts(int nChangePosRet); // needed for the subtract-fee-from-amount feature - std::vector& getSpendCoins(); - std::vector& getMintCoins(); - private: QList recipients; CWalletTx *walletTransaction; CReserveKey *keyChange; CAmount fee; - // lelantus transaction - std::vector spendCoins; - std::vector mintCoins; + // spark transaction + std::vector spendCoins; + std::vector mintCoins; }; #endif // BITCOIN_QT_WALLETMODELTRANSACTION_H diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 81db380844..51c203a7f0 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -89,7 +89,6 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listunspent", 0, "minconf" }, { "listunspent", 1, "maxconf" }, { "listunspent", 2, "addresses" }, - { "regeneratemintpool", 0 }, { "listunspentmintzerocoins", 0 }, { "listunspentmintzerocoins", 1 }, { "listunspentmintzerocoins", 2 }, @@ -173,31 +172,20 @@ static const CRPCConvertParam vRPCConvertParams[] = { "setmintzerocoinstatus", 2, {} }, { "setmintzerocoinstatus", 1, {} }, { "setsigmamintstatus", 1, {} }, - { "setlelantusmintstatus", 1, {} }, { "listmintzerocoins", 0, {} }, { "listsigmamints", 0, {} }, - { "listlelantusmints", 0, {} }, { "listpubcoins", 0, {} }, { "listsigmapubcoins", 0, {} }, { "listspendzerocoins", 0, {} }, { "listspendzerocoins", 1, {} }, { "listsigmaspends", 0, {} }, { "listsigmaspends", 1, {} }, - { "listlelantusjoinsplits", 0, {} }, - { "listlelantusjoinsplits", 1, {} }, - { "joinsplit", 0, {} }, - { "joinsplit", 1, {} }, - { "joinsplit", 2, {} }, { "spendallzerocoin", 0, {} }, { "remintzerocointosigma", 0, {} }, - { "getanonymityset", 0, {} }, - { "getmintmetadata", 0, {} }, - { "getusedcoinserials", 0, {} }, - { "getlatestcoinids", 0, {} }, { "getsparkmintmetadata", 0, {} }, { "getmempooltxs", 0, {} }, - //Lelantus + // Spark { "mintspark", 0, {} }, { "mintspark", 1, {} }, { "mintspark", 2, {} }, diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index af76eb6b37..8d5a72cd2e 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -592,41 +592,6 @@ UniValue signmessagewithprivkey(const JSONRPCRequest& request) return EncodeBase64(&vchSig[0], vchSig.size()); } -UniValue verifyprivatetxown(const JSONRPCRequest& request) -{ - if (request.fHelp || request.params.size() != 3) - throw std::runtime_error( - "verifyprivatetxown \"txid\" \"signature\" \"message\"\n" - "\nVerify a lelantus tx ownership\n" - "\nArguments:\n" - "1. \"txid\" (string, required) Txid, in which we spend lelantus coins.\n" - "2. \"proof\" (string, required) The signatures of the message encoded in base 64\n" - "3. \"message\" (string, required) The message that was signed.\n" - "\nResult:\n" - "true|false (boolean) If the signature is verified or not.\n" - "\nExamples:\n" - "\nVerify the signature\n" - + HelpExampleCli("verifyprivatetxown", "\"34df0ec7bcc8a2bda2c0df41ac560172d974c56ffc9adc0e2377d0fc54b4e8f9\" \"signature\" \"my message\"") + - "\nAs json rpc\n" - + HelpExampleRpc("verifyprivatetxown", "\"34df0ec7bcc8a2bda2c0df41ac560172d974c56ffc9adc0e2377d0fc54b4e8f9\", \"signature\", \"my message\"") - ); - - LOCK(cs_main); - - std::string strTxId = request.params[0].get_str(); - std::string strProof = request.params[1].get_str(); - std::string strMessage = request.params[2].get_str(); - - uint256 txid = uint256S(strTxId); - bool fInvalid = false; - std::vector vchSig = DecodeBase64(strProof.c_str(), &fInvalid); - - if (fInvalid) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); - - return VerifyPrivateTxOwn(txid, vchSig, strMessage); -} - UniValue setmocktime(const JSONRPCRequest& request) { @@ -1089,180 +1054,6 @@ UniValue getAddressNumWBalance(const JSONRPCRequest& request) return uint64_t(pblocktree->findAddressNumWBalance()); } -UniValue getanonymityset(const JSONRPCRequest& request) -{ - if (request.fHelp || request.params.size() != 2) - throw std::runtime_error( - "getanonymityset\n" - "\nReturns the anonymity set and latest block hash.\n" - "\nArguments:\n" - "{\n" - " \"coinGroupId\" (int)\n" - " \"startBlockHash\" (string)\n" // if this is empty it returns the full set - "}\n" - "\nResult:\n" - "{\n" - " \"blockHash\" (string) Latest block hash for anonymity set\n" - " \"setHash\" (string) Anonymity set hash\n" - " \"mints\" (Pair>) Serialized GroupElements paired with txhash which is paired with mint tag and mint value\n" - "}\n" - + HelpExampleCli("getanonymityset", "\"1\"" "{\"ca511f07489e35c9bc60ca62c82de225ba7aae7811ce4c090f95aa976639dc4e\"}") - + HelpExampleRpc("getanonymityset", "\"1\"" "{\"ca511f07489e35c9bc60ca62c82de225ba7aae7811ce4c090f95aa976639dc4e\"}") - ); - - - int coinGroupId; - std::string startBlockHash; - try { - coinGroupId = std::stol(request.params[0].get_str()); - startBlockHash = request.params[1].get_str(); - } catch (std::logic_error const & e) { - throw std::runtime_error(std::string("An exception occurred while parsing parameters: ") + e.what()); - } - - if(!GetBoolArg("-mobile", false)){ - throw std::runtime_error(std::string("Please rerun Firo with -mobile ")); - } - - uint256 blockHash; - std::vector>> coins; - std::vector setHash; - - { - LOCK(cs_main); - lelantus::CLelantusState* lelantusState = lelantus::CLelantusState::GetState(); - lelantusState->GetCoinsForRecovery( - &chainActive, - chainActive.Height() - (ZC_MINT_CONFIRMATIONS - 1), - coinGroupId, - startBlockHash, - blockHash, - coins, - setHash); - } - - UniValue ret(UniValue::VOBJ); - UniValue mints(UniValue::VARR); - - FIRO_UNUSED int i = 0; - for (const auto& coin : coins) { - std::vector vch = coin.first.getValue().getvch(); - std::vector data; - data.push_back(EncodeBase64(vch.data(), size_t(34))); - data.push_back(EncodeBase64(coin.second.second.begin(), coin.second.second.size())); - if (coin.second.first.isJMint) { - data.push_back(EncodeBase64(coin.second.first.encryptedValue.data(), coin.second.first.encryptedValue.size())); - } else { - data.push_back(coin.second.first.amount); - } - data.push_back(EncodeBase64(coin.second.first.txHash.begin(), coin.second.first.txHash.size())); - - UniValue entity(UniValue::VARR); - entity.push_backV(data); - mints.push_back(entity); - i++; - } - - ret.push_back(Pair("blockHash", EncodeBase64(blockHash.begin(), blockHash.size()))); - ret.push_back(Pair("setHash", UniValue(EncodeBase64(setHash.data(), setHash.size())))); - ret.push_back(Pair("coins", mints)); - - return ret; -} - -UniValue getmintmetadata(const JSONRPCRequest& request) -{ - if (request.fHelp || request.params.size() != 1) - throw std::runtime_error( - "getmintmetadata\n" - "\nReturns the anonymity set id and nHeight of mint.\n" - "\nArguments:\n" - " \"mints\"\n" - " [\n" - " {\n" - " \"pubcoin\" (string) The PubCoin value\n" - " }\n" - " ,...\n" - " ]\n" - "\nResult:\n" - "{\n" - " \"metadata\" (Pair) nHeight and id for each pubcoin\n" - "}\n" - + HelpExampleCli("getmintmetadata", "'{\"mints\": [{\"denom\":5000000, \"pubcoin\":\"b476ed2b374bb081ea51d111f68f0136252521214e213d119b8dc67b92f5a390\"}]}'") - + HelpExampleRpc("getmintmetadata", "{\"mints\": [{\"denom\":5000000, \"pubcoin\":\"b476ed2b374bb081ea51d111f68f0136252521214e213d119b8dc67b92f5a390\"}]}") - ); - - UniValue mintValues = find_value(request.params[0].get_obj(), "mints"); - if (!mintValues.isArray()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "mints is expected to be an array"); - } - lelantus::CLelantusState* lelantusState = lelantus::CLelantusState::GetState(); - UniValue ret(UniValue::VARR); - for(UniValue const & mintData : mintValues.getValues()){ - std::vector serializedCoin = ParseHex(find_value(mintData, "pubcoin").get_str().c_str()); - - secp_primitives::GroupElement pubCoin; - pubCoin.deserialize(serializedCoin.data()); - - std::pair coinHeightAndId; - { - LOCK(cs_main); - coinHeightAndId = lelantusState->GetMintedCoinHeightAndId(lelantus::PublicCoin(pubCoin)); - } - UniValue metaData(UniValue::VOBJ); - metaData.pushKV(std::to_string(coinHeightAndId.first), coinHeightAndId.second); - ret.push_back(metaData); - } - return ret; -} - -UniValue getusedcoinserials(const JSONRPCRequest& request) -{ - if (request.fHelp || request.params.size() != 1) - throw std::runtime_error( - "getusedcoinserials\n" - "\nReturns the set of used coin serial.\n" - "\nArguments:\n" - "{\n" - " \"startNumber \" (int) Number of elements already existing on user side\n" - "}\n" - "\nResult:\n" - "{\n" - " \"serials\" (std::string[]) array of Serialized Scalars\n" - "}\n" - ); - - int startNumber; - try { - startNumber = std::stol(request.params[0].get_str()); - } catch (std::logic_error const & e) { - throw std::runtime_error(std::string("An exception occurred while parsing parameters: ") + e.what()); - } - - lelantus::CLelantusState* lelantusState = lelantus::CLelantusState::GetState(); - std::unordered_map serials; - { - LOCK(cs_main); - serials = lelantusState->GetSpends(); - } - - UniValue serializedSerials(UniValue::VARR); - int i = 0; - for ( auto it = serials.begin(); it != serials.end(); ++it, ++i) { - if (cmp::less((serials.size() - i - 1), startNumber)) - continue; - std::vector serialized; - serialized.resize(32); - it->first.serialize(serialized.data()); - serializedSerials.push_back(EncodeBase64(serialized.data(), 32)); - } - - UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("serials", serializedSerials)); - - return ret; -} - UniValue getfeerate(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) @@ -1281,31 +1072,29 @@ UniValue getfeerate(const JSONRPCRequest& request) return ret; } -UniValue getlatestcoinid(const JSONRPCRequest& request) +// Lelantus RPC stubs (protocol removed; throw if called). +UniValue getanonymityset(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() != 0) - throw std::runtime_error( - "getlatestcoinid\n" - "\nReturns the set of used coin serial.\n" - "\nResult:\n" - "{\n" - " [\n" - " {\n" - " \"coinGroupId\" (int) The latest group id\n" - " }\n" - " ,...\n" - " ]\n" - "}\n" - ); + (void)request; + throw std::runtime_error("getanonymityset is disabled (Lelantus has been removed). Use getsparkanonymityset for Spark."); +} - lelantus::CLelantusState* lelantusState = lelantus::CLelantusState::GetState(); - int latestCoinId; - { - LOCK(cs_main); - latestCoinId = lelantusState->GetLatestCoinID(); - } +UniValue getmintmetadata(const JSONRPCRequest& request) +{ + (void)request; + throw std::runtime_error("getmintmetadata is disabled (Lelantus has been removed). Use getsparkmintmetadata for Spark."); +} - return UniValue(latestCoinId); +UniValue getusedcoinserials(const JSONRPCRequest& request) +{ + (void)request; + throw std::runtime_error("getusedcoinserials is disabled (Lelantus has been removed). Use getusedcoinstags for Spark."); +} + +UniValue getlatestcoinid(const JSONRPCRequest& request) +{ + (void)request; + throw std::runtime_error("getlatestcoinid is disabled (Lelantus has been removed). Use getsparklatestcoinid for Spark."); } UniValue getsparkanonymityset(const JSONRPCRequest& request) @@ -2332,8 +2121,6 @@ static const CRPCCommand commands[] = { "firo", "znsync", &mnsync, true, {} }, { "firo", "evoznsync", &mnsync, true, {} }, - { "firo", "verifyprivatetxown", &verifyprivatetxown, true, {} }, - /* Not shown in help */ { "hidden", "getinfoex", &getinfoex, false, {} }, { "addressindex", "gettotalsupply", &gettotalsupply, false, {} }, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index b3a7818328..95a53f7533 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -39,7 +39,6 @@ #include "llmq/quorums_instantsend.h" #include "llmq/quorums_chainlocks.h" #include "evo/providertx.h" -#include "lelantus.h" // Forward declaration with default parameter for calls within this file void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, bool includeChainlock = true); @@ -107,22 +106,6 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, UniValue in(UniValue::VOBJ); if (tx.IsCoinBase()) { in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); - } else if (txin.IsLelantusJoinSplit()) { - in.push_back("joinsplit"); - fillStdFields(in, txin); - std::unique_ptr jsplit; - try { - jsplit = lelantus::ParseLelantusJoinSplit(tx); - } - catch (const std::exception &) { - continue; - } - in.push_back(Pair("nFees", ValueFromAmount(jsplit->getFee()))); - UniValue serials(UniValue::VARR); - for (Scalar const & serial : jsplit->getCoinSerialNumbers()) { - serials.push_back(serial.GetHex()); - } - in.push_back(Pair("serials", serials)); } else if (tx.IsSparkSpend()) { in.push_back("sparkSpend"); fillStdFields(in, txin); diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index 4805d4c1e7..d34aebcce4 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -1401,10 +1401,8 @@ UniValue spork(const JSONRPCRequest& request) throw std::runtime_error("No spork actions specified"); std::set validFeatureNames { - CSporkAction::featureLelantus, CSporkAction::featureChainlocks, CSporkAction::featureInstantSend, - CSporkAction::featureLelantusTransparentLimit, CSporkAction::featureSpark, CSporkAction::featureSparkTransparentLimit }; diff --git a/src/spark/sparkwallet.cpp b/src/spark/sparkwallet.cpp index e8e5fdb41d..443968f8df 100644 --- a/src/spark/sparkwallet.cpp +++ b/src/spark/sparkwallet.cpp @@ -1,4 +1,3 @@ -#include "../liblelantus/threadpool.h" #include "sparkwallet.h" #include "state.h" #include "../wallet/wallet.h" @@ -83,18 +82,16 @@ CSparkWallet::CSparkWallet(const std::string& strWalletFile) { } } - threadPool = new ParallelOpThreadPool(boost::thread::hardware_concurrency()); + threadPool = nullptr; if (fWalletJustUnlocked) pwalletMain->Lock(); } CSparkWallet::~CSparkWallet() { - delete (ParallelOpThreadPool*)threadPool; } void CSparkWallet::FinishTasks() { - ((ParallelOpThreadPool*)threadPool)->Shutdown(); spark::ShutdownSparkState(); } @@ -514,36 +511,32 @@ void CSparkWallet::UpdateSpendState(const GroupElement& lTag, const uint256& txH } void CSparkWallet::UpdateSpendStateFromMempool(const std::vector& lTags, const uint256& txHash, bool fUpdateMint) { - ((ParallelOpThreadPool*)threadPool)->PostTask([=]() { - LOCK(cs_spark_wallet); - for (const auto& lTag : lTags) { - uint256 lTagHash = primitives::GetLTagHash(lTag); - if (coinMeta.count(lTagHash)) { - UpdateSpendState(lTag, lTagHash, txHash, fUpdateMint); - } + LOCK(cs_spark_wallet); + for (const auto& lTag : lTags) { + uint256 lTagHash = primitives::GetLTagHash(lTag); + if (coinMeta.count(lTagHash)) { + UpdateSpendState(lTag, lTagHash, txHash, fUpdateMint); } - }); + } } void CSparkWallet::UpdateSpendStateFromBlock(const CBlock& block) { const auto& transactions = block.vtx; - ((ParallelOpThreadPool*)threadPool)->PostTask([=]() { - LOCK(cs_spark_wallet); - for (const auto& tx : transactions) { - if (tx->IsSparkSpend()) { - try { - spark::SpendTransaction spend = spark::ParseSparkSpend(*tx); - const auto& txLTags = spend.getUsedLTags(); - for (const auto& txLTag : txLTags) { - uint256 txHash = tx->GetHash(); - uint256 lTagHash = primitives::GetLTagHash(txLTag); - UpdateSpendState(txLTag, lTagHash, txHash); - } - } catch (const std::exception &) { + LOCK(cs_spark_wallet); + for (const auto& tx : transactions) { + if (tx->IsSparkSpend()) { + try { + spark::SpendTransaction spend = spark::ParseSparkSpend(*tx); + const auto& txLTags = spend.getUsedLTags(); + for (const auto& txLTag : txLTags) { + uint256 txHash = tx->GetHash(); + uint256 lTagHash = primitives::GetLTagHash(txLTag); + UpdateSpendState(txLTag, lTagHash, txHash); } + } catch (const std::exception &) { } } - }); + } } bool CSparkWallet::isMine(spark::Coin coin) const { @@ -662,27 +655,22 @@ void CSparkWallet::UpdateMintState(const std::vector& coins, const } void CSparkWallet::UpdateMintStateFromMempool(const std::vector& coins, const uint256& txHash) { - ((ParallelOpThreadPool*)threadPool)->PostTask([=]() mutable { - LOCK(cs_spark_wallet); - CWalletDB walletdb(strWalletFile); - UpdateMintState(coins, txHash, walletdb); - }); + LOCK(cs_spark_wallet); + CWalletDB walletdb(strWalletFile); + UpdateMintState(coins, txHash, walletdb); } void CSparkWallet::UpdateMintStateFromBlock(const CBlock& block) { const auto& transactions = block.vtx; - - ((ParallelOpThreadPool*)threadPool)->PostTask([=] () mutable { - LOCK(cs_spark_wallet); - CWalletDB walletdb(strWalletFile); - for (const auto& tx : transactions) { - if (tx->IsSparkTransaction()) { - auto coins = spark::GetSparkMintCoins(*tx); - uint256 txHash = tx->GetHash(); - UpdateMintState(coins, txHash, walletdb); - } + LOCK(cs_spark_wallet); + CWalletDB walletdb(strWalletFile); + for (const auto& tx : transactions) { + if (tx->IsSparkTransaction()) { + auto coins = spark::GetSparkMintCoins(*tx); + uint256 txHash = tx->GetHash(); + UpdateMintState(coins, txHash, walletdb); } - }); + } } void CSparkWallet::RemoveSparkMints(const std::vector& mints) { diff --git a/src/spark/state.cpp b/src/spark/state.cpp index 427cde01ba..32c44a83b5 100644 --- a/src/spark/state.cpp +++ b/src/spark/state.cpp @@ -1,10 +1,10 @@ -#include "../liblelantus/threadpool.h" #include "state.h" #include "compat_layer.h" #include "sparkname.h" #include "../validation.h" #include "../batchproof_container.h" + #include namespace spark { @@ -15,17 +15,12 @@ struct ProofCheckState { // result of the check (if fChecked is true) bool fResult; - - // if this is non-null, then the proof is being checked right now - std::shared_ptr> checkInProgress; }; // map from transaction hash to the state of checking its proofs static std::map gCheckedSparkSpendTransactions; static CCriticalSection cs_checkedSparkSpendTransactions; -static ParallelOpThreadPool gCheckProofThreadPool(std::min(boost::thread::hardware_concurrency(), 4u)); - static CSparkState sparkState; static bool CheckLTag( @@ -274,7 +269,7 @@ bool ConnectBlockSpark( pindexNew->sparkSetHash.clear(); } - if (!CheckSparkBlock(state, *pblock)) { + if (!CheckSparkBlock(state, *pblock, pindexNew->nHeight)) { return false; } @@ -475,7 +470,7 @@ void DisconnectTipSpark(CBlock& block, CBlockIndex *pindexDelete) { RemoveSpendReferencingBlock(txpools.getStemTxPool(), pindexDelete); } -bool CheckSparkBlock(CValidationState &state, const CBlock& block) { +bool CheckSparkBlock(CValidationState &state, const CBlock& block, int nBlockHeight) { auto& consensus = ::Params().GetConsensus(); size_t blockSpendsValue = 0; @@ -483,14 +478,14 @@ bool CheckSparkBlock(CValidationState &state, const CBlock& block) { for (const auto& tx : block.vtx) { auto txSpendsValue = GetSpendTransparentAmount(*tx); - if (txSpendsValue > consensus.GetMaxValueSparkSpendPerTransaction(block.nHeight)) { + if (txSpendsValue > consensus.GetMaxValueSparkSpendPerTransaction(nBlockHeight)) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-spark-spend-invalid"); } blockSpendsValue += txSpendsValue; } - if (cmp::greater(blockSpendsValue, consensus.GetMaxValueSparkSpendPerBlock(block.nHeight))) { + if (cmp::greater(blockSpendsValue, consensus.GetMaxValueSparkSpendPerBlock(nBlockHeight))) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-spark-spend-invalid"); } @@ -792,57 +787,6 @@ bool CheckSparkSpendTransaction( fChecked = true; } } - // If the check is in progress and we are doing a stateful check, we need to wait - else if (checkState.checkInProgress && fStatefulSigmaCheck) { - // wait for the check to complete - auto future = checkState.checkInProgress; - cs_checkedSparkSpendTransactions.unlock(); - bool result = future->get(); - cs_checkedSparkSpendTransactions.lock(); - - // Entry may have been erased by DisconnectTipSpark during the unlock window - auto it = gCheckedSparkSpendTransactions.find(hashTx); - if (it == gCheckedSparkSpendTransactions.end()) { - fRecheckNeeded = true; - continue; - } - ProofCheckState& checkStateAfterWait = it->second; - - checkStateAfterWait.fChecked = true; - checkStateAfterWait.fResult = result; - checkStateAfterWait.checkInProgress = nullptr; - - if (!result) { - // unfortunately, it's possible that the proof was checked and failed - // because the anonymity set was incomplete at the time of checking. We need - // to recheck the proof again - fRecheckNeeded = true; - gCheckedSparkSpendTransactions.erase(hashTx); - } - else - fChecked = true; - } - } - else if (!fStatefulSigmaCheck && !gCheckProofThreadPool.IsPoolShutdown()) { - // not an urgent check, put the proof into the thread pool for verification - // don't post a request if there are too many tasks already - if (gCheckProofThreadPool.GetPendingTaskCount() < (std::size_t)gCheckProofThreadPool.GetNumberOfThreads()/2) { - auto future = gCheckProofThreadPool.PostTask([spend, cover_sets]() mutable { - try { - bool result = spark::SpendTransaction::verify(*spend, cover_sets); - spend.reset(); - cover_sets.clear(); - return result; - } catch (const std::exception &) { - return false; - } - }); - auto &checkState = gCheckedSparkSpendTransactions[hashTx]; - checkState.fChecked = false; - checkState.fResult = false; - checkState.checkInProgress = std::make_shared>(std::move(future)); - scheduledAsync = true; - } } } while (fRecheckNeeded); @@ -855,6 +799,10 @@ bool CheckSparkSpendTransaction( if (fStatefulSigmaCheck) { // we need the answer now, so verify and execute passVerify = spark::SpendTransaction::verify(*spend, cover_sets); + { + LOCK(cs_checkedSparkSpendTransactions); + gCheckedSparkSpendTransactions[hashTx] = {true, passVerify}; + } } else { if (scheduledAsync) { @@ -1018,7 +966,6 @@ bool CheckSparkTransaction( } void ShutdownSparkState() { - gCheckProofThreadPool.Shutdown(); } uint256 GetTxHashFromCoin(const spark::Coin& coin) { diff --git a/src/spark/state.h b/src/spark/state.h index 7d3ad95962..1d7b168c31 100644 --- a/src/spark/state.h +++ b/src/spark/state.h @@ -59,7 +59,7 @@ std::vector GetSparkMintCoins(const CTransaction &tx); size_t GetSpendInputs(const CTransaction &tx); CAmount GetSpendTransparentAmount(const CTransaction& tx); -bool CheckSparkBlock(CValidationState &state, const CBlock& block); +bool CheckSparkBlock(CValidationState &state, const CBlock& block, int nBlockHeight); //void DisconnectTipLelantus(CBlock &block, CBlockIndex *pindexDelete); diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index dad6ea0cca..62b481b552 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -47,9 +47,6 @@ add_executable(test_firo ${CMAKE_CURRENT_SOURCE_DIR}/hash_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/key_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dbwrapper_tests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/lelantus_tests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/lelantus_mintspend_test.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/lelantus_state_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/limitedmap_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/main_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mbstring_tests.cpp @@ -98,20 +95,7 @@ add_executable(test_firo ${CMAKE_CURRENT_SOURCE_DIR}/progpow_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/bls_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sparkname_tests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../hdmint/test/lelantus_tests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../liblelantus/test/challenge_generator_tests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../liblelantus/test/coin_tests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../liblelantus/test/inner_product_test.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../liblelantus/test/joinsplit_tests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../liblelantus/test/lelantus_primitives_tests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../liblelantus/test/lelantus_test.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../liblelantus/test/lelantus_test_fixture.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../liblelantus/test/lelantus_test_fixture.h - ${CMAKE_CURRENT_SOURCE_DIR}/../liblelantus/test/range_proof_test.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../liblelantus/test/schnorr_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../libspark/test/ownership_test.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../liblelantus/test/serialize_test.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../liblelantus/test/sigma_extended_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../libspark/test/transcript_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../libspark/test/schnorr_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../libspark/test/chaum_test.cpp diff --git a/src/test/fixtures.cpp b/src/test/fixtures.cpp index cd23b6736d..c9117d6a4d 100644 --- a/src/test/fixtures.cpp +++ b/src/test/fixtures.cpp @@ -16,7 +16,6 @@ #include "consensus/consensus.h" #include "consensus/validation.h" #include "key.h" -#include "liblelantus/openssl_context.h" #include "validation.h" #include "miner.h" #include "pubkey.h" @@ -36,7 +35,6 @@ #include #include #include -#include "lelantus.h" #include "../libspark/coin.h" @@ -203,106 +201,6 @@ CBlock MtpMalformedTestingSetup::CreateAndProcessBlock( return block; } -LelantusTestingSetup::LelantusTestingSetup() : - params(lelantus::Params::get_default()) { - CPubKey key; - { - LOCK(pwalletMain->cs_wallet); - key = pwalletMain->GenerateNewKey(); - } - - script = GetScriptForDestination(key.GetID()); -} - -CBlockIndex* LelantusTestingSetup::GenerateBlock(std::vector const &txns, CScript *script) { - auto last = chainActive.Tip(); - - CreateAndProcessBlock(txns, script ? *script : this->script); - auto block = chainActive.Tip(); - - if (block != last) { - pwalletMain->ScanForWalletTransactions(block, true); - } - - return block != last ? block : nullptr; -} - -void LelantusTestingSetup::GenerateBlocks(size_t blocks, CScript *script) { - while (blocks--) { - GenerateBlock({}, script); - } -} - -std::vector LelantusTestingSetup::GenerateMints( - std::vector const &amounts) { - - auto const &p = lelantus::Params::get_default(); - - std::vector coins; - for (auto a : amounts) { - std::vector k(32); - GetRandBytes(k.data(), k.size()); - - secp256k1_pubkey pubkey; - - if (!secp256k1_ec_pubkey_create(OpenSSLContext::get_context(), &pubkey, k.data())) { - throw std::runtime_error("Fail to create public key"); - } - - auto serial = lelantus::PrivateCoin::serialNumberFromSerializedPublicKey( - OpenSSLContext::get_context(), &pubkey); - - Scalar randomness; - randomness.randomize(); - - coins.emplace_back(p, serial, a, randomness, k, 0); - } - - return coins; -} - -std::vector LelantusTestingSetup::GenerateMints( - std::vector const &amounts, - std::vector &txs) { - - std::vector coins; - return GenerateMints(amounts, txs, coins); -} - -std::vector LelantusTestingSetup::GenerateMints( - std::vector const &amounts, - std::vector &txs, - std::vector &coins) { - - std::vector hdMints; - CWalletDB walletdb(pwalletMain->strWalletFile); - for (auto a : amounts) { - std::vector> wtxAndFee; - std::vector mints; - auto result = pwalletMain->MintAndStoreLelantus(a, wtxAndFee, mints); - - if (result != "") { - throw std::runtime_error(_("Fail to generate mints, ") + result); - } - - for(auto itr : wtxAndFee) - txs.emplace_back(itr.first); - - hdMints.insert(hdMints.end(), mints.begin(), mints.end()); - } - - return hdMints; -} - -CPubKey LelantusTestingSetup::GenerateAddress() { - LOCK(pwalletMain->cs_wallet); - return pwalletMain->GenerateNewKey(); -} - -LelantusTestingSetup::~LelantusTestingSetup() { - lelantus::CLelantusState::GetState()->Reset(); -} - // SparkTestingSetup SparkTestingSetup::SparkTestingSetup() : params(spark::Params::get_default()) { CPubKey key; diff --git a/src/test/fixtures.h b/src/test/fixtures.h index 7b21ab2a27..5f890a27ea 100644 --- a/src/test/fixtures.h +++ b/src/test/fixtures.h @@ -1,9 +1,7 @@ -#include "hdmint/hdmint.h" #include "primitives/transaction.h" #include "test/test_bitcoin.h" #include "test/testutil.h" #include "consensus/params.h" -#include "liblelantus/coin.h" #include @@ -72,35 +70,6 @@ struct MtpMalformedTestingSetup : public ZerocoinTestingSetupBase { const CScript&, bool); }; -struct LelantusTestingSetup : public TestChain100Setup { -public: - LelantusTestingSetup(); - -public: - CBlockIndex* GenerateBlock(std::vector const &txns = {}, CScript *script = nullptr); - void GenerateBlocks(size_t blocks, CScript *script = nullptr); - - std::vector GenerateMints( - std::vector const &amounts); - - std::vector GenerateMints( - std::vector const &amounts, - std::vector &txs); - - std::vector GenerateMints( - std::vector const &amounts, - std::vector &txs, - std::vector &coins); - - CPubKey GenerateAddress(); - - ~LelantusTestingSetup(); - -public: - lelantus::Params const *params; - CScript script; -}; - struct SparkTestingSetup : public TestChain100Setup { public: diff --git a/src/test/lelantus_mintspend_test.cpp b/src/test/lelantus_mintspend_test.cpp deleted file mode 100644 index 7cca76641a..0000000000 --- a/src/test/lelantus_mintspend_test.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include "util.h" - -#include -#include - -#include "chainparams.h" -#include "key.h" -#include "validation.h" -#include "pubkey.h" -#include "txdb.h" -#include "txmempool.h" -#include "lelantus.h" - -#include "test/fixtures.h" -#include "test/testutil.h" - -#include "wallet/db.h" -#include "wallet/wallet.h" -#include "wallet/walletexcept.h" - -#include -#include -#include - -BOOST_FIXTURE_TEST_SUITE(lelantus_mintspend, LelantusTestingSetup) - -BOOST_AUTO_TEST_CASE(lelantus_mintspend_test) -{ - GenerateBlocks(110); - - lelantus::CLelantusState *lelantusState = lelantus::CLelantusState::GetState(); - - // Make sure that transactions get to mempool - pwalletMain->SetBroadcastTransactions(true); - - std::vector mintTxs; - auto hdMints = GenerateMints({50 * COIN, 60 * COIN}, mintTxs); - - // Verify Mint gets in the mempool - BOOST_CHECK_MESSAGE(mempool.size() == hdMints.size(), "Mints were not added to mempool"); - - int previousHeight = chainActive.Height(); - auto blockIdx1 = GenerateBlock(mintTxs); - BOOST_CHECK(blockIdx1); - - BOOST_CHECK_MESSAGE(previousHeight + 1 == chainActive.Height(), "Block not added to chain"); - BOOST_CHECK_MESSAGE(mempool.size() == 0, "Mints were not removed from mempool"); - previousHeight = chainActive.Height(); - - // Generate address - CPubKey newKey; - BOOST_CHECK_MESSAGE(pwalletMain->GetKeyFromPool(newKey), "Fail to get new address"); - - const CBitcoinAddress randomAddr(newKey.GetID()); - std::vector recipients = { - {GetScriptForDestination(randomAddr.Get()), 30 * COIN, true}, - }; - - GenerateBlock({}); - - BOOST_CHECK_MESSAGE(previousHeight + 1 == chainActive.Height(), "Block not added to chain"); - BOOST_CHECK_MESSAGE(mempool.size() == 0, "Mempool must be empty"); - - CWalletTx wtx; - { - BOOST_CHECK_NO_THROW(pwalletMain->JoinSplitLelantus(recipients, {}, wtx)); //this must pass - } - BOOST_CHECK_MESSAGE(mempool.size() == 1, "JoinSplit is not added into mempool"); - - previousHeight = chainActive.Height(); - GenerateBlock({CMutableTransaction(*wtx.tx)}); - BOOST_CHECK_MESSAGE(previousHeight + 1 == chainActive.Height(), "Block not added to chain"); - BOOST_CHECK_MESSAGE(mempool.size() == 0, "JoinSplit is not removed from mempool"); - GenerateBlocks(6); - std::vector spendCoins; //spends - CWalletTx result; - { - std::vector mintCoins; // new mints - CAmount fee = 0; - result = pwalletMain->CreateLelantusJoinSplitTransaction(recipients, fee, {}, spendCoins, mintCoins); - pwalletMain->CommitLelantusTransaction(result, spendCoins, mintCoins); - } - BOOST_CHECK_MESSAGE(mempool.size() == 1, "Joinsplit was not added to mempool"); - - //Set mints unused, and try to spend again - for(auto mint : spendCoins) - pwalletMain->zwallet->GetTracker().SetLelantusPubcoinNotUsed(primitives::GetPubCoinValueHash(mint.value)); - - wtx.Init(NULL); - //try double spend - BOOST_CHECK_NO_THROW(pwalletMain->JoinSplitLelantus(recipients, {}, wtx)); - //Verify spend got into mempool - BOOST_CHECK_MESSAGE(mempool.size() == 1, "Double spend was added into mempool, but was not supposed"); - - previousHeight = chainActive.Height(); - GenerateBlock({CMutableTransaction(*result.tx)}); - BOOST_CHECK_MESSAGE(previousHeight + 1 == chainActive.Height(), "Block not added to chain"); - BOOST_CHECK_MESSAGE(mempool.size() == 0, "Mempool not cleared"); - GenerateBlocks(2); - for(auto mint : spendCoins) - pwalletMain->zwallet->GetTracker().SetLelantusPubcoinUsed(primitives::GetPubCoinValueHash(mint.value), uint256()); - - spendCoins.clear(); //spends - result.Init(NULL); - { - std::vector mintCoins; // new mints - CAmount fee = 0; - result = pwalletMain->CreateLelantusJoinSplitTransaction(recipients, fee, {}, spendCoins, mintCoins); - pwalletMain->CommitLelantusTransaction(result, spendCoins, mintCoins); - } - - //Verify spend got into mempool - BOOST_CHECK_MESSAGE(mempool.size() == 1, "Spend was not added to mempool"); - - previousHeight = chainActive.Height(); - GenerateBlock({CMutableTransaction(*result.tx)}); - BOOST_CHECK_MESSAGE(previousHeight + 1 == chainActive.Height(), "Block not added to chain"); - BOOST_CHECK_MESSAGE(mempool.size() == 0, "Mempool not cleared"); - GenerateBlocks(2); - - //Set mints unused, and try to spend again - for(auto mint : spendCoins) - pwalletMain->zwallet->GetTracker().SetLelantusPubcoinNotUsed(primitives::GetPubCoinValueHash(mint.value)); - //try double spend - wtx.Init(NULL); - - BOOST_CHECK_NO_THROW(pwalletMain->JoinSplitLelantus(recipients, {}, wtx)); - BOOST_CHECK_MESSAGE(mempool.size() == 0, "Mempool not empty although mempool should reject double spend"); - - //Temporary disable usedCoinSerials check to force double spend in mempool - auto tempSerials = lelantusState->containers.usedCoinSerials; - lelantusState->containers.usedCoinSerials.clear(); - - { - //Set mints unused, and try to spend again - for(auto mint : spendCoins) - pwalletMain->zwallet->GetTracker().SetLelantusPubcoinNotUsed(primitives::GetPubCoinValueHash(mint.value)); - wtx.Init(NULL); - BOOST_CHECK_NO_THROW(pwalletMain->JoinSplitLelantus(recipients, {}, wtx)); - BOOST_CHECK_MESSAGE(mempool.size() == 1, "Spend was not added to mempool"); - } - - lelantusState->containers.usedCoinSerials = tempSerials; - - BOOST_CHECK_EXCEPTION(CreateBlock({CMutableTransaction(*wtx.tx)}, script), std::runtime_error, no_check); - BOOST_CHECK_MESSAGE(mempool.size() == 1, "Mempool not set"); - tempSerials = lelantusState->containers.usedCoinSerials; - lelantusState->containers.usedCoinSerials.clear(); - CBlock b = CreateBlock({CMutableTransaction(*wtx.tx)}, script); - - lelantusState->containers.usedCoinSerials = tempSerials; - - mempool.clear(); - previousHeight = chainActive.Height(); - - const CChainParams& chainparams = Params(); - BOOST_CHECK_MESSAGE(ProcessNewBlock(chainparams, std::make_shared(b), true, NULL), "ProcessBlock failed"); - //This test confirms that a block containing a double spend is rejected and not added in the chain - BOOST_CHECK_MESSAGE(previousHeight == chainActive.Height(), "Double spend - Block added to chain even though same spend in previous block"); - - mempool.clear(); - lelantusState->Reset(); -} -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/src/test/lelantus_state_tests.cpp b/src/test/lelantus_state_tests.cpp deleted file mode 100644 index cea54173e2..0000000000 --- a/src/test/lelantus_state_tests.cpp +++ /dev/null @@ -1,591 +0,0 @@ -#include "../lelantus.h" -#include "../validation.h" - -#include "fixtures.h" -#include "test_bitcoin.h" - -#include - -namespace std { - -template -basic_ostream& operator<<(basic_ostream& os, const pair& p) -{ - return os << '(' << p.first << ", " << p.second << ')'; -} - -} - -namespace lelantus { - -class LelantusStateTests : public LelantusTestingSetup { -public: - LelantusStateTests() : LelantusTestingSetup(), - lelantusState(CLelantusState::GetState()) { - } - - ~LelantusStateTests() { - lelantusState->Reset(); - } - -public: - CBlock GetCBlock(CBlockIndex const *blockIdx) { - CBlock block; - if (!ReadBlockFromDisk(block, blockIdx, ::Params().GetConsensus())) { - throw std::invalid_argument("No block index data"); - } - - return block; - } - - void PopulateLelantusTxInfo( - CBlock &block, - std::vector>> const &mints, - std::vector> const &serials) { - block.lelantusTxInfo = std::make_shared(); - block.lelantusTxInfo->mints.insert(block.lelantusTxInfo->mints.end(), mints.begin(), mints.end()); - - for (auto const &s : serials) { - block.lelantusTxInfo->spentSerials.emplace(s); - } - } - - std::vector GenerateMintsInBlocks(CLelantusState &state, std::initializer_list const &mintsInBlocks) { - - std::vector indexes; - for (auto mintsInBlock : mintsInBlocks) { - - std::vector amounts(mintsInBlock, 1); - auto mints = GenerateMints(amounts); - - std::vector>> coins; - for (auto const &m : mints) { - coins.emplace_back(std::make_pair(m.getPublicCoin(), std::make_pair(m.getV(), uint256()))); - } - - auto index = GenerateBlock({}); - auto block = GetCBlock(index); - - PopulateLelantusTxInfo(block, coins, {}); - - state.AddMintsToStateAndBlockIndex(index, &block); - indexes.push_back(index); - } - - return indexes; - } - - CBlockIndex* GenerateSpendGroups( - CLelantusState &state, std::initializer_list> const &serials) { - - std::vector indexes; - auto index = GenerateBlock({}); - - for (auto const& s : serials) { - for (size_t i = 0; i != s.second; i++) { - Scalar serial; - serial.randomize(); - - index->lelantusSpentSerials[serial] = s.first; - } - } - - state.AddBlock(index); - return index; - } - - void RemoveBlocks(CLelantusState &state, std::vector indexes) { - for (auto it = indexes.rbegin(); it != indexes.rend(); it++) { - state.RemoveBlock(*it); - } - } - -public: - CLelantusState *lelantusState; -}; - -BOOST_FIXTURE_TEST_SUITE(lelantus_state_tests, LelantusStateTests) - -BOOST_AUTO_TEST_CASE(add_mints_to_state) -{ - // Try to add some mints to state. - - GenerateBlocks(110); - - std::vector txs; - auto mints = GenerateMints({1 * COIN, 2 * COIN, 1 * CENT}, txs); - - auto blockIdx1 = GenerateBlock({txs[0]}); - auto block1 = GetCBlock(blockIdx1); - PopulateLelantusTxInfo(block1, {{mints[0].GetPubcoinValue(), std::make_pair(mints[0].GetAmount(), uint256())}}, {}); - - auto blockIdx2 = GenerateBlock({txs[1]}); - auto block2 = GetCBlock(blockIdx2); - PopulateLelantusTxInfo(block2, {{mints[1].GetPubcoinValue(), std::make_pair(mints[1].GetAmount(), uint256())}}, {}); - - // verify heigh and id was assigned. - BOOST_CHECK_EQUAL(std::make_pair(chainActive.Height() - 1, 1), lelantusState->GetMintedCoinHeightAndId(mints[0].GetPubcoinValue())); - BOOST_CHECK_EQUAL(std::make_pair(chainActive.Height(), 1), lelantusState->GetMintedCoinHeightAndId(mints[1].GetPubcoinValue())); - BOOST_CHECK_EQUAL(std::make_pair(-1, -1), lelantusState->GetMintedCoinHeightAndId(mints[2].GetPubcoinValue())); - - // test has coin - BOOST_CHECK(lelantusState->HasCoin(mints[0].GetPubcoinValue())); - BOOST_CHECK(lelantusState->HasCoin(mints[1].GetPubcoinValue())); - BOOST_CHECK(!lelantusState->HasCoin(mints[2].GetPubcoinValue())); - - // test has coin hash - GroupElement received; - BOOST_CHECK(lelantusState->HasCoinHash(received, mints[0].GetPubCoinHash())); - BOOST_CHECK(mints[0].GetPubcoinValue() == received); - - BOOST_CHECK(!lelantusState->HasCoinHash(received, mints[2].GetPubCoinHash())); - - BOOST_CHECK_EQUAL(2, lelantusState->GetTotalCoins()); - - // check group info - CLelantusState::LelantusCoinGroupInfo group, fakeGroup; - BOOST_CHECK(lelantusState->GetCoinGroupInfo(1, group)); - BOOST_CHECK(!lelantusState->GetCoinGroupInfo(0, fakeGroup)); - BOOST_CHECK(!lelantusState->GetCoinGroupInfo(2, fakeGroup)); - - BOOST_CHECK(blockIdx1 == group.firstBlock); - BOOST_CHECK(blockIdx2 == group.lastBlock); - BOOST_CHECK_EQUAL(2, group.nCoins); - - BOOST_CHECK_EQUAL(1, lelantusState->GetLatestCoinID()); -} - -BOOST_AUTO_TEST_CASE(serial_adding) -{ - GenerateBlocks(110); - - std::vector txs; - auto mints = GenerateMints({1 * COIN, 2 * COIN, 1 * CENT}, txs); - - GenerateBlock(txs); - - auto blockIdx = chainActive.Tip(); - auto block = GetCBlock(blockIdx); - PopulateLelantusTxInfo(block, {{mints[0].GetPubcoinValue(), std::make_pair(mints[0].GetAmount(), uint256())}}, {}); - - lelantusState->AddMintsToStateAndBlockIndex(blockIdx, &block); - - Scalar serial1(1), serial2(2); - auto serialHash1 = primitives::GetSerialHash(serial1); - auto serialHash2 = primitives::GetSerialHash(serial2); - - lelantusState->AddSpend(serial1, 1); - - Scalar receivedSerial; - BOOST_CHECK(lelantusState->IsUsedCoinSerial(serial1)); - BOOST_CHECK(lelantusState->IsUsedCoinSerialHash(receivedSerial, serialHash1)); - BOOST_CHECK(serial1 == receivedSerial); - - BOOST_CHECK(!lelantusState->IsUsedCoinSerial(serial2)); - BOOST_CHECK(!lelantusState->IsUsedCoinSerialHash(receivedSerial, serialHash2)); -} - -BOOST_AUTO_TEST_CASE(mempool) -{ - GenerateBlocks(110); - - std::vector txs; - auto mint = GenerateMints({1 * COIN}, txs)[0]; - - GenerateBlock(txs); - - auto blockIdx = chainActive.Tip(); - auto block = GetCBlock(blockIdx); - PopulateLelantusTxInfo(block, {{mint.GetPubcoinValue(), std::make_pair(mint.GetAmount(), uint256())}}, {}); - - lelantusState->AddMintsToStateAndBlockIndex(blockIdx, &block); - - Scalar spendSerial(1); - lelantusState->AddSpend(spendSerial, 1); - - // test mint mempool - // - can not add on-chain coin - BOOST_CHECK(!lelantusState->CanAddMintToMempool(mint.GetPubcoinValue())); - - // - can not add duplicated coin - GroupElement randMint; - randMint.randomize(); - - BOOST_CHECK(lelantusState->CanAddMintToMempool(randMint)); - lelantusState->AddMintsToMempool({randMint}); - BOOST_CHECK(!lelantusState->CanAddMintToMempool(randMint)); - - // - remove from mempool then can add again - lelantusState->RemoveMintFromMempool(randMint); - BOOST_CHECK(lelantusState->CanAddMintToMempool(randMint)); - - // test spend mempool - // - can not add on-chain spend - BOOST_CHECK(!lelantusState->CanAddSpendToMempool(spendSerial)); - - // - can not add duplicated serial - Scalar anotherSerial(2); - auto txid = ArithToUint256(1); - - BOOST_CHECK(lelantusState->CanAddSpendToMempool(anotherSerial)); - lelantusState->AddSpendToMempool({anotherSerial}, txid); - BOOST_CHECK(!lelantusState->CanAddSpendToMempool(anotherSerial)); - - BOOST_CHECK(txid == - lelantusState->GetMempoolConflictingTxHash(anotherSerial)); - - Scalar fakeSerial(3); - BOOST_CHECK(uint256() == - lelantusState->GetMempoolConflictingTxHash(fakeSerial)); - - // - remove spend then can add again - lelantusState->RemoveSpendFromMempool({anotherSerial}); - BOOST_CHECK(lelantusState->CanAddSpendToMempool(anotherSerial)); - lelantusState->AddSpendToMempool({anotherSerial}, txid); - BOOST_CHECK(!lelantusState->CanAddSpendToMempool(anotherSerial)); -} - -BOOST_AUTO_TEST_CASE(add_remove_block) -{ - // No coins and serials - auto index1 = GenerateBlock({}); - auto block1 = GetCBlock(index1); - PopulateLelantusTxInfo(block1, {}, {}); - - lelantusState->AddBlock(index1); - - BOOST_CHECK_EQUAL(0, lelantusState->GetMints().size()); - BOOST_CHECK_EQUAL(0, lelantusState->GetSpends().size()); - - // some mints - GroupElement mint1, mint2; - mint1.randomize(); - mint2.randomize(); - - auto index2 = GenerateBlock({}); - auto block2 = GetCBlock(index2); - PopulateLelantusTxInfo(block2, {{mint1, {1, uint256()}}, {mint2, {1, uint256()}}}, {}); - - lelantusState->AddMintsToStateAndBlockIndex(index2, &block2); - lelantusState->AddBlock(index2); - - BOOST_CHECK_EQUAL(2, lelantusState->GetMints().size()); - BOOST_CHECK_EQUAL(0, lelantusState->GetSpends().size()); - - // some serials - Scalar serial1, serial2; - serial1.randomize(); - serial2.randomize(); - - auto index3 = GenerateBlock({}); - auto block3 = GetCBlock(index3); - PopulateLelantusTxInfo(block3, {}, {{serial1, 1}, {serial2, 1}}); - index3->lelantusSpentSerials = block3.lelantusTxInfo->spentSerials; - - lelantusState->AddBlock(index3); - - BOOST_CHECK_EQUAL(2, lelantusState->GetMints().size()); - BOOST_CHECK_EQUAL(2, lelantusState->GetSpends().size()); - - // both mint and serial - GroupElement mint3; - mint3.randomize(); - - Scalar serial3; - serial3.randomize(); - - auto index4 = GenerateBlock({}); - auto block4 = GetCBlock(index4); - PopulateLelantusTxInfo(block4, {{mint3, {1, uint256()}}}, {{serial3, 1}}); - lelantusState->AddMintsToStateAndBlockIndex(index4, &block4); - index4->lelantusSpentSerials = block4.lelantusTxInfo->spentSerials; - - lelantusState->AddBlock(index4); - - BOOST_CHECK_EQUAL(3, lelantusState->GetMints().size()); - BOOST_CHECK_EQUAL(3, lelantusState->GetSpends().size()); - - // remove last block - lelantusState->RemoveBlock(index4); - - BOOST_CHECK_EQUAL(2, lelantusState->GetMints().size()); - BOOST_CHECK_EQUAL(2, lelantusState->GetSpends().size()); - - // verify mints and spends on blocks - BOOST_CHECK(lelantusState->HasCoin(mint1)); - BOOST_CHECK(lelantusState->HasCoin(mint2)); - BOOST_CHECK(!lelantusState->HasCoin(mint3)); - - BOOST_CHECK(lelantusState->IsUsedCoinSerial(serial1)); - BOOST_CHECK(lelantusState->IsUsedCoinSerial(serial2)); - BOOST_CHECK(!lelantusState->IsUsedCoinSerial(serial3)); - - lelantusState->Reset(); -} - -BOOST_AUTO_TEST_CASE(get_coin_group) -{ - GenerateBlocks(120); - - std::vector amounts(12, COIN); - std::vector txs; - - auto mints = GenerateMints(amounts, txs); - - std::vector coins; - std::vector indexes; - std::vector blocks; - - for (size_t i = 0; i != mints.size(); i += 2) { - auto index = GenerateBlock({txs[i], txs[i + 1]}); - auto block = GetCBlock(index); - coins.push_back(mints[i + 1].GetPubcoinValue()); - coins.push_back(mints[i].GetPubcoinValue()); - - PopulateLelantusTxInfo( - block, - { - {mints[i].GetPubcoinValue(), {1, uint256()}}, - {mints[i + 1].GetPubcoinValue(), {1, uint256()}} - }, {}); - - indexes.push_back(index); - blocks.push_back(block); - - GenerateBlock({}); - } - - size_t maxSize = 6; - size_t startCoin = 2; - auto lelantusState = new CLelantusState(maxSize, startCoin); - - auto addMintsToState = [&](CBlockIndex *index, CBlock const &block) { - index->lelantusMintedPubCoins.clear(); - lelantusState->AddMintsToStateAndBlockIndex(index, &block); - }; - - auto verifyMints = [&](size_t i, size_t j, std::vector const &coinSet) { - std::vector expected(coins.begin() + i, coins.begin() + j); - std::reverse(expected.begin(), expected.end()); - - BOOST_CHECK(expected == coinSet); - }; - - auto verifyGroup = [&](int expectedId, size_t expectedCoins, CBlockIndex *expectedFirst, CBlockIndex *expectedLast, int testId = 0)->void { - if (!testId) { - testId = lelantusState->GetLatestCoinID(); - } - - CLelantusState::LelantusCoinGroupInfo group; - - BOOST_CHECK(lelantusState->GetCoinGroupInfo(testId, group)); - if (expectedId > 0) { // verify last Id - BOOST_CHECK_EQUAL(expectedId, testId); - } - - BOOST_CHECK_EQUAL(expectedCoins, group.nCoins); - BOOST_CHECK_EQUAL(expectedFirst, group.firstBlock); - BOOST_CHECK_EQUAL(expectedLast, group.lastBlock); - }; - - // 6 coins, 1(6), 2(0) - addMintsToState(indexes[0], blocks[0]); - addMintsToState(indexes[1], blocks[1]); - addMintsToState(indexes[2], blocks[2]); - - verifyGroup(1, 6, indexes[0], indexes[2]); - uint256 blockHashOut1; - std::vector coinOut1; - std::vector setHash; - BOOST_CHECK_EQUAL(6, lelantusState->GetCoinSetForSpend( - &chainActive, - indexes[2]->nHeight, - 1, - blockHashOut1, - coinOut1, - setHash)); - - verifyMints(0, 6, coinOut1); - BOOST_CHECK(indexes[2]->GetBlockHash() == blockHashOut1); - - // 8 coins, 1(6), 2(4) - addMintsToState(indexes[3], blocks[3]); - verifyGroup(2, 4, indexes[2], indexes[3]); - verifyGroup(1, 6, indexes[0], indexes[2], 1); - - uint256 blockHashOut2; - std::vector coinOut2; - BOOST_CHECK_EQUAL(4, lelantusState->GetCoinSetForSpend( - &chainActive, - indexes[3]->nHeight + 1, // specify limit with no mints block - 2, - blockHashOut2, - coinOut2, - setHash)); - - verifyMints(4, 8, coinOut2); - BOOST_CHECK(indexes[3]->GetBlockHash() == blockHashOut2); - - // 10 coins, 1(6), 2(6) - addMintsToState(indexes[4], blocks[4]); - - verifyGroup(2, 6, indexes[2], indexes[4]); - verifyGroup(1, 6, indexes[0], indexes[2], 1); - - uint256 blockHashOut3; - std::vector coinOut3; - BOOST_CHECK_EQUAL(6, lelantusState->GetCoinSetForSpend( - &chainActive, - indexes[4]->nHeight, - 2, - blockHashOut3, - coinOut3, - setHash)); - - verifyMints(4, 10, coinOut3); - BOOST_CHECK(indexes[4]->GetBlockHash() == blockHashOut3); - - // 12 coins, 1(6), 2(6), 3(4) - addMintsToState(indexes[5], blocks[5]); - - verifyGroup(3, 4, indexes[4], indexes[5]); - verifyGroup(2, 6, indexes[2], indexes[4], 2); - verifyGroup(1, 6, indexes[0], indexes[2], 1); - - uint256 blockHashOut4; - std::vector coinOut4; - BOOST_CHECK_EQUAL(4, lelantusState->GetCoinSetForSpend( - &chainActive, - indexes[5]->nHeight, - 3, - blockHashOut4, - coinOut4, - setHash)); - - verifyMints(8, 12, coinOut4); - - // Get first group - uint256 blockHashOut5; - std::vector coinOut5; - BOOST_CHECK_EQUAL(6, lelantusState->GetCoinSetForSpend( - &chainActive, - indexes[5]->nHeight, - 1, - blockHashOut5, - coinOut5, - setHash)); - - verifyMints(0, 6, coinOut5); - BOOST_CHECK(indexes[2]->GetBlockHash() == blockHashOut5); - - // Get first group with low max height - uint256 blockHashOut6; - std::vector coinOut6; - BOOST_CHECK_EQUAL(2, lelantusState->GetCoinSetForSpend( - &chainActive, - indexes[0]->nHeight, - 1, - blockHashOut6, - coinOut6, - setHash)); - - verifyMints(0, 2, coinOut6); - BOOST_CHECK(indexes[0]->GetBlockHash() == blockHashOut6); - - lelantusState->RemoveBlock(indexes[5]); - verifyGroup(2, 6, indexes[2], indexes[4]); - verifyGroup(1, 6, indexes[0], indexes[2], 1); - - lelantusState->Reset(); -} - -// Surge condition testing -#define Undetected BOOST_CHECK(!state.IsSurgeConditionDetected()) -#define Detected BOOST_CHECK(state.IsSurgeConditionDetected()) - -BOOST_AUTO_TEST_CASE(add_more_spend_and_trigger_surge_condition) -{ - size_t maxGroupSize = 6; - size_t startGroupSize = 2; - CLelantusState state(maxGroupSize, startGroupSize); - state.Reset(); - - // 6(1), 4(2), 4(3) - auto mintIndexes = GenerateMintsInBlocks(state, {2, 2, 2, 2, 2, 2, 2}); - Undetected; - - // spend 6 coins from 1, 4 coins from 2 and add one more should fail - auto index1 = GenerateSpendGroups(state, {{1, 6}, {2, 4}}); - Undetected; - - auto index2 = GenerateSpendGroups(state, {{1, 1}}); - Detected; - - RemoveBlocks(state, {index1, index2}); - Undetected; - - // spend 5 coins from 1, 5 coins from 2, 4 coins from 3, and add one more should fail - index1 = GenerateSpendGroups(state, {{1, 5}, {2, 5}, {3, 4}}); - Undetected; - - index2 = GenerateSpendGroups(state, {{1, 1}}); - Detected; - - RemoveBlocks(state, {index2}); - Undetected; - - index2 = GenerateSpendGroups(state, {{2, 1}}); - Detected; - - RemoveBlocks(state, {index2}); - Undetected; - - index2 = GenerateSpendGroups(state, {{3, 1}}); - Detected; - - RemoveBlocks(state, {index1}); - Undetected; - -} - -BOOST_AUTO_TEST_CASE(surge_condition_detected_and_no_serials_in_early_groups) -{ - size_t maxGroupSize = 6; - size_t startGroupSize = 2; - CLelantusState state(maxGroupSize, startGroupSize); - state.Reset(); - - // 0 + 6(1), 2 + 4(2), 2 + 4(3), 2 + 3(4), 3 + 3(5), 3 + 3(6), 3 + 3(7) - GenerateMintsInBlocks(state, {2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3}); - Undetected; - - auto index1 = GenerateSpendGroups(state, {{4, 5}}); - Undetected; - - auto index2 = GenerateSpendGroups(state, {{4, 1}}); - Detected; - - RemoveBlocks(state, {index1, index2}); - - index1 = GenerateSpendGroups(state, {{4, 5}, {5, 3}}); - Undetected; - - index1 = GenerateSpendGroups(state, {{4, 1}}); - Detected; - - RemoveBlocks(state, {index1}); - Undetected; - - index1 = GenerateSpendGroups(state, {{5, 1}}); - Detected; - - RemoveBlocks(state, {index1}); - Undetected; -} - -#undef Detected -#undef Undetected - -BOOST_AUTO_TEST_SUITE_END() - -} \ No newline at end of file diff --git a/src/test/lelantus_tests.cpp b/src/test/lelantus_tests.cpp deleted file mode 100644 index 6b4c6bc66a..0000000000 --- a/src/test/lelantus_tests.cpp +++ /dev/null @@ -1,962 +0,0 @@ -#include "../chainparams.h" -#include "../lelantus.h" -#include "../script/standard.h" -#include "../validation.h" -#include "../wallet/coincontrol.h" -#include "../wallet/wallet.h" -#include "../net.h" - -#include "test_bitcoin.h" -#include "fixtures.h" -#include -#include - -static bool CommitToMempool(const CTransaction &tx) -{ - CWallet *wallet = pwalletMain; - CWalletTx walletTx(wallet, MakeTransactionRef(tx)); - CReserveKey reserveKey(wallet); - CValidationState state; - wallet->CommitTransaction(walletTx, reserveKey, g_connman.get(), state); - return mempool.exists(tx.GetHash()); -} - -namespace lelantus { - -struct JoinSplitScriptGenerator { - Params const *params; - std::vector> coins; - std::map> anons; - CAmount vout; - std::vector coinsOut; - CAmount fee; - std::map groupBlockHashes; - uint256 txHash; - - std::pair Get() { - // auto p = params ? params : Params::get_default(); - auto p = Params::get_default(); - - CScript script; - - JoinSplit joinSplit(p, coins, anons, {}, vout, coinsOut, fee, groupBlockHashes, txHash, LELANTUS_TX_VERSION_4); - - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss << joinSplit; - - script << OP_LELANTUSJOINSPLIT; - script.insert(script.end(), ss.begin(), ss.end()); - - return {script, joinSplit}; - } -}; - -class LelantusTests : public LelantusTestingSetup { -public: - LelantusTests() : - LelantusTestingSetup(), - lelantusState(CLelantusState::GetState()), - consensus(::Params().GetConsensus()) { - } - - ~LelantusTests() { - lelantusState->Reset(); - } - -public: - - std::vector ExtractCoins(std::vector const &coins) { - std::vector pubs; - pubs.reserve(coins.size()); - - for (auto &c : coins) { - pubs.push_back(c.getPublicCoin()); - } - - return pubs; - } - - void ExtractJoinSplit(CMutableTransaction const &tx, - std::vector &newMints, - std::vector &serials) { - for (auto const &in : tx.vin) { - if (in.IsLelantusJoinSplit()) { - auto js = ParseLelantusJoinSplit(tx); - auto const &s = js->getCoinSerialNumbers(); - serials.insert(serials.end(), s.begin(), s.end()); - } - } - - for (auto const &out : tx.vout) { - if (out.scriptPubKey.IsLelantusJMint()) { - GroupElement coin; - ParseLelantusMintScript(out.scriptPubKey, coin); - - newMints.push_back(coin); - } - } - } - - CBlock GetCBlock(CBlockIndex const *blockIdx) { - CBlock block; - if (!ReadBlockFromDisk(block, blockIdx, ::Params().GetConsensus())) { - throw std::invalid_argument("No block index data"); - } - - return block; - } - - void PopulateLelantusTxInfo( - CBlock &block, - std::vector>> const &mints, - std::vector> const &serials) { - block.lelantusTxInfo = std::make_shared(); - block.lelantusTxInfo->mints.insert(block.lelantusTxInfo->mints.end(), mints.begin(), mints.end()); - - for (auto const &s : serials) { - block.lelantusTxInfo->spentSerials.emplace(s); - } - - block.lelantusTxInfo->Complete(); - } - - CTransaction GenerateJoinSplit( - std::vector const &outs, - std::vector const &mints, - CCoinControl const *coinControl = nullptr) { - - std::vector vecs; - for (auto const &out : outs) { - LOCK(pwalletMain->cs_wallet); - auto pub = pwalletMain->GenerateNewKey(); - - vecs.push_back( - { - GetScriptForDestination(pub.GetID()), - out, - false - }); - } - - std::vector spendCoins; - std::vector mintCoins; - - CAmount fee; - auto result = pwalletMain->CreateLelantusJoinSplitTransaction( - vecs, fee, mints, spendCoins, mintCoins, coinControl); - - if (!pwalletMain->CommitLelantusTransaction( - result, spendCoins, mintCoins)) { - throw std::runtime_error("Fail to commit transaction"); - } - - return result; - } - -public: - CLelantusState *lelantusState; - Consensus::Params const &consensus; -}; - -BOOST_FIXTURE_TEST_SUITE(lelantus_tests, LelantusTests) - -BOOST_AUTO_TEST_CASE(schnorr_proof) -{ - auto params = Params::get_default(); - - PrivateCoin coin(params, 1); - - CDataStream serializedSchnorrProof(SER_NETWORK, PROTOCOL_VERSION); - GenerateMintSchnorrProof(coin, serializedSchnorrProof); - - auto commitment = coin.getPublicCoin(); - SchnorrProof proof; - serializedSchnorrProof >> proof; - - BOOST_CHECK(VerifyMintSchnorrProof(1, commitment.getValue(), proof)); -} - -BOOST_AUTO_TEST_CASE(is_lelantus_allowed) -{ - auto start = ::Params().GetConsensus().nLelantusStartBlock; - BOOST_CHECK(!IsLelantusAllowed(0)); - BOOST_CHECK(!IsLelantusAllowed(start - 1)); - BOOST_CHECK(IsLelantusAllowed(start)); - BOOST_CHECK(IsLelantusAllowed(start + 1)); -} - -BOOST_AUTO_TEST_CASE(parse_lelantus_mintscript) -{ - // payload: op_code + pubcoin + schnorrproof - PrivateCoin priv(params, 1); - auto &pub = priv.getPublicCoin(); - - CDataStream proofSerialized(SER_NETWORK, PROTOCOL_VERSION); - - GenerateMintSchnorrProof(priv, proofSerialized); - - CScript script(OP_LELANTUSMINT); - - auto vch = pub.getValue().getvch(); - script.insert(script.end(), vch.begin(), vch.end()); - script.insert(script.end(), proofSerialized.begin(), proofSerialized.end()); - - // verify - secp_primitives::GroupElement parsedCoin; - ParseLelantusMintScript(script, parsedCoin); - - BOOST_CHECK(pub.getValue() == parsedCoin); - - SchnorrProof proof; - uint256 mintTag; - ParseLelantusMintScript(script, parsedCoin, proof, mintTag); - - BOOST_CHECK(pub.getValue() == parsedCoin); - BOOST_CHECK(VerifyMintSchnorrProof(1, parsedCoin, proof)); - - CDataStream parsedProof(SER_NETWORK, PROTOCOL_VERSION); - parsedProof << proof; - - BOOST_CHECK(proofSerialized.vch == parsedProof.vch); - - GroupElement parsedCoin2; - ParseLelantusMintScript(script, parsedCoin2); - - BOOST_CHECK(pub.getValue() == parsedCoin2); - - script.resize(script.size() - 1); - BOOST_CHECK_THROW(ParseLelantusMintScript(script, parsedCoin, proof, mintTag), std::invalid_argument); -} - -BOOST_AUTO_TEST_CASE(parse_lelantus_jmint) -{ - GroupElement val; - val.randomize(); - - CScript script(OP_LELANTUSJMINT); - - auto vch = val.getvch(); - script.insert(script.end(), vch.begin(), vch.end()); - - std::vector encrypted; - encrypted.resize(16); - - std::fill(encrypted.begin(), encrypted.end(), 0xff); - script.insert(script.end(), encrypted.begin(), encrypted.end()); - - // parse and verify - GroupElement outCoin; - std::vector outEnc; - ParseLelantusJMintScript(script, outCoin, outEnc); - - BOOST_CHECK(val == outCoin); - BOOST_CHECK(encrypted == outEnc); - - GroupElement outCoin2; - ParseLelantusMintScript(script, outCoin2); - - BOOST_CHECK(val == outCoin2); - - // parse invalid - script.resize(script.size() - 1); - BOOST_CHECK_THROW(ParseLelantusJMintScript(script, outCoin, outEnc), std::invalid_argument); -} - -BOOST_AUTO_TEST_CASE(get_outpoint) -{ - GenerateBlocks(110); - - // generate mints - std::vector txs; - auto mints = GenerateMints({2, 10}, txs); - auto mint = mints[0]; - auto nonCommitted = mints[1]; - auto tx = txs[0]; - size_t mintIdx = 0; - for (; mintIdx < tx.vout.size(); mintIdx++) { - if (tx.vout[mintIdx].scriptPubKey.IsLelantusMint()) { - break; - } - } - - auto blockIdx = GenerateBlock({txs[0]}); - - CBlock block; - BOOST_CHECK(ReadBlockFromDisk(block, blockIdx, ::Params().GetConsensus())); - - block.lelantusTxInfo = std::make_shared(); - block.lelantusTxInfo->mints.emplace_back(std::make_pair(mint.GetPubcoinValue(), std::make_pair(mint.GetAmount(), uint256()))); - - lelantusState->AddMintsToStateAndBlockIndex(blockIdx, &block); - lelantusState->AddBlock(blockIdx); - - // verify - COutPoint expectedOut(tx.GetHash(), mintIdx); - - // GetOutPointFromBlock - COutPoint out; - BOOST_CHECK(GetOutPointFromBlock(out, mint.GetPubcoinValue(), block)); - BOOST_CHECK(expectedOut == out); - - BOOST_CHECK(!GetOutPointFromBlock(out, nonCommitted.GetPubcoinValue(), block)); - - // GetOutPoint - // by pubcoin - out = COutPoint(); - BOOST_CHECK(GetOutPoint(out, PublicCoin(mint.GetPubcoinValue()))); - BOOST_CHECK(expectedOut == out); - - BOOST_CHECK(!GetOutPoint(out, PublicCoin(nonCommitted.GetPubcoinValue()))); - - // by pubcoin value - out = COutPoint(); - BOOST_CHECK(GetOutPoint(out, mint.GetPubcoinValue())); - BOOST_CHECK(expectedOut == out); - - BOOST_CHECK(!GetOutPoint(out, nonCommitted.GetPubcoinValue())); - - // by pubcoin hash - out = COutPoint(); - BOOST_CHECK(GetOutPoint(out, mint.GetPubCoinHash())); - BOOST_CHECK(expectedOut == out); - - BOOST_CHECK(!GetOutPoint(out, nonCommitted.GetPubCoinHash())); -} - -BOOST_AUTO_TEST_CASE(build_lelantus_state) -{ - GenerateBlocks(110); - - // generate mints - std::vector txs; - auto mints = GenerateMints({1 * COIN, 2 * COIN, 10 * COIN, 100 * COIN}, txs); - - GenerateBlock({txs[0], txs[1]}); - auto blockIdx1 = chainActive.Tip(); - auto block1 = GetCBlock(blockIdx1); - - GenerateBlock({txs[2], txs[3]}); - auto blockIdx2 = chainActive.Tip(); - auto block2 = GetCBlock(blockIdx2); - - block1.lelantusTxInfo = std::make_shared(); - block2.lelantusTxInfo = std::make_shared(); - - block1.lelantusTxInfo->mints.emplace_back(std::make_pair(mints[0].GetPubcoinValue(), std::make_pair(mints[0].GetAmount(), uint256()))); - block1.lelantusTxInfo->mints.emplace_back(std::make_pair(mints[1].GetPubcoinValue(), std::make_pair(mints[1].GetAmount(), uint256()))); - block2.lelantusTxInfo->mints.emplace_back(std::make_pair(mints[2].GetPubcoinValue(), std::make_pair(mints[2].GetAmount(), uint256()))); - block2.lelantusTxInfo->mints.emplace_back(std::make_pair(mints[3].GetPubcoinValue(), std::make_pair(mints[3].GetAmount(), uint256()))); - lelantusState->Reset(); - lelantusState->AddMintsToStateAndBlockIndex(blockIdx1, &block1); - lelantusState->AddMintsToStateAndBlockIndex(blockIdx2, &block2); - - BOOST_CHECK(BuildLelantusStateFromIndex(&chainActive)); - BOOST_CHECK(lelantusState->HasCoin(mints[0].GetPubcoinValue())); - BOOST_CHECK(lelantusState->HasCoin(mints[1].GetPubcoinValue())); - BOOST_CHECK(lelantusState->HasCoin(mints[2].GetPubcoinValue())); - BOOST_CHECK(lelantusState->HasCoin(mints[3].GetPubcoinValue())); -} - -BOOST_AUTO_TEST_CASE(connect_and_disconnect_block) -{ - // util function - auto reconnect = [](CBlock const &block) { - LOCK(cs_main); - - std::shared_ptr sharedBlock = - std::make_shared(block); - - CValidationState state; - ActivateBestChain(state, ::Params(), sharedBlock); - }; - - GenerateBlocks(110); - - std::vector mintTxs; - auto hdMints = GenerateMints({3 * COIN, 3 * COIN, 3 * COIN}, mintTxs); - - struct { - // expected state. - std::vector coins; - std::vector serials; - - // first group - CBlockIndex *first = nullptr; - CBlockIndex *last = nullptr; - - int lastId = 0; - - // real state - CLelantusState *state; - - void Verify() const { - auto const &spends = state->GetSpends(); - BOOST_CHECK_EQUAL(serials.size(), spends.size()); - for (auto const &s : serials) { - BOOST_CHECK_MESSAGE(spends.count(s), "serial is not found on state"); - } - - auto const &mints = state->GetMints(); - BOOST_CHECK_EQUAL(coins.size(), mints.size()); - for (auto const &c : coins) { - BOOST_CHECK_MESSAGE(mints.count(c), "public is not found on state"); - } - - auto retrievedId = state->GetLatestCoinID(); - - CLelantusState::LelantusCoinGroupInfo group; - state->GetCoinGroupInfo(retrievedId, group); - BOOST_CHECK_EQUAL(lastId, retrievedId); - BOOST_CHECK_EQUAL(first, group.firstBlock); - BOOST_CHECK_EQUAL(last, group.lastBlock); - BOOST_CHECK_EQUAL(coins.size(), group.nCoins); - } - } checker; - checker.state = lelantusState; - - // Cache empty checker - auto emptyChecker = checker; - - // Generate some txs which contain mints - auto blockIdx1 = GenerateBlock({mintTxs[0], mintTxs[1]}); - BOOST_CHECK(blockIdx1); - auto block1 = GetCBlock(blockIdx1); - - checker.coins.push_back(hdMints[0].GetPubcoinValue()); - checker.coins.push_back(hdMints[1].GetPubcoinValue()); - checker.first = blockIdx1; - checker.last = blockIdx1; - checker.lastId = 1; - checker.Verify(); - - // Generate empty blocks should not effect state - GenerateBlocks(10); - checker.Verify(); - - // Add spend tx - - // Create two txs which contains same serial. - CCoinControl coinControl; - - { - auto tx = mintTxs[0]; - auto it = std::find_if(tx.vout.begin(), tx.vout.end(), [](CTxOut const &out) -> bool { - return out.scriptPubKey.IsLelantusMint(); - }); - BOOST_CHECK(it != tx.vout.end()); - - coinControl.Select(COutPoint(tx.GetHash(), std::distance(tx.vout.begin(), it))); - } - - auto jsTx1 = GenerateJoinSplit({1 * COIN}, {}, &coinControl); - - // Update isused status - { - auto mint = hdMints[0]; - auto hash = primitives::GetPubCoinValueHash(mint.GetPubcoinValue()); - - CLelantusMintMeta meta; - BOOST_CHECK(pwalletMain->zwallet->GetTracker() - .GetLelantusMetaFromPubcoin(hash, meta)); - - BOOST_CHECK(meta.isUsed); - - meta.isUsed = false; - BOOST_CHECK(pwalletMain->zwallet->GetTracker().UpdateState(meta)); - - meta = CLelantusMintMeta(); - BOOST_CHECK(pwalletMain->zwallet->GetTracker() - .GetLelantusMetaFromPubcoin(hash, meta)); - BOOST_CHECK(!meta.isUsed); - } - - // Create duplicated serial tx and test this at the bottom - auto dupJsTx1 = GenerateJoinSplit({1 * COIN}, {}, &coinControl); - - std::vector dupNewCoins1; - std::vector dupSerials1; - ExtractJoinSplit(dupJsTx1, dupNewCoins1, dupSerials1); - - std::vector newCoins1; - std::vector serials1; - ExtractJoinSplit(jsTx1, newCoins1, serials1); - BOOST_CHECK_EQUAL(1, newCoins1.size()); - BOOST_CHECK_EQUAL(1, serials1.size()); - BOOST_CHECK(dupSerials1[0] == serials1[0]); - - auto blockIdx2 = GenerateBlock({jsTx1}); - BOOST_CHECK(blockIdx2); - auto block2 = GetCBlock(blockIdx2); - - auto cacheChecker = checker; - checker.coins.push_back(newCoins1.front()); - checker.serials.push_back(serials1.front()); - checker.last = blockIdx2; - - checker.Verify(); - - // state should be rolled back - DisconnectBlocks(1); - BOOST_CHECK_EQUAL(chainActive.Tip()->nHeight, blockIdx2->nHeight - 1); - cacheChecker.Verify(); - - // reconnect - reconnect(block2); - checker.Verify(); - - // add more block contain both mint and serial - auto jsTx2 = GenerateJoinSplit({1 * COIN}, {CENT}); - std::vector newCoins2; - std::vector serials2; - ExtractJoinSplit(jsTx2, newCoins2, serials2); - BOOST_CHECK_EQUAL(2, newCoins2.size()); - BOOST_CHECK_EQUAL(1, serials2.size()); - - auto blockIdx3 = GenerateBlock({mintTxs[2], jsTx2}); - BOOST_CHECK(blockIdx3); - auto block3 = GetCBlock(blockIdx3); - - checker.coins.insert(checker.coins.end(), newCoins2.begin(), newCoins2.end()); - checker.coins.push_back(hdMints[2].GetPubcoinValue()); - checker.serials.push_back(serials2[0]); - checker.last = blockIdx3; - - checker.Verify(); - - // Clear state and rebuild - lelantusState->Reset(); - emptyChecker.Verify(); - - BuildLelantusStateFromIndex(&chainActive); - checker.Verify(); - - // Disconnect all and reconnect - std::vector blocks; - while (chainActive.Tip() != chainActive.Genesis()) { - blocks.push_back(GetCBlock(chainActive.Tip())); - DisconnectBlocks(1); - } - - emptyChecker.Verify(); - - for (auto const &block : blocks) { - reconnect(block); - } - - checker.Verify(); - - // double spend - auto currentBlock = chainActive.Tip()->nHeight; - BOOST_CHECK(!GenerateBlock({dupJsTx1})); - BOOST_CHECK_EQUAL(currentBlock, chainActive.Tip()->nHeight); - mempool.clear(); - lelantusState->Reset(); -} - -BOOST_AUTO_TEST_CASE(checktransaction) -{ - GenerateBlocks(110); - - // mints - std::vector txs; - auto mints = GenerateMints({1 * CENT}, txs); - auto &tx = txs[0]; - - CValidationState state; - CLelantusTxInfo info; - BOOST_CHECK(CheckLelantusTransaction( - txs[0], state, tx.GetHash(), false, chainActive.Height(), true, true, &info)); - - std::vector>> expectedCoins = {{mints[0].GetPubcoinValue(), {1 * CENT, info.mints[0].second.second}}}; - - BOOST_CHECK(expectedCoins == info.mints); - - // join split - txs.clear(); - mints = GenerateMints({10 * CENT, 11 * CENT, 100 * CENT}, txs); - GenerateBlock(txs); - GenerateBlocks(10); - - auto outputAmount = 8 * CENT; - auto mintAmount = 2 * CENT - CENT; // a cent as fee - - CWalletTx wtx; - pwalletMain->JoinSplitLelantus( - {{script, outputAmount, false}}, - {mintAmount}, - wtx); - - CMutableTransaction joinsplitTx(wtx); - auto joinsplit = ParseLelantusJoinSplit(joinsplitTx); - - // test get join split amounts - BOOST_CHECK_EQUAL(1, GetSpendInputs(joinsplitTx)); - BOOST_CHECK_EQUAL(1, GetSpendInputs(joinsplitTx, joinsplitTx.vin[0])); - - info = CLelantusTxInfo(); - - BOOST_CHECK(CheckLelantusTransaction( - joinsplitTx, state, joinsplitTx.GetHash(), false, chainActive.Height(), false, true, &info)); - - auto &serials = joinsplit->getCoinSerialNumbers(); - auto &ids = joinsplit->getCoinGroupIds(); - - for (size_t i = 0; i != serials.size(); i++) { - bool hasSerial = false; - BOOST_CHECK_MESSAGE(hasSerial = (info.spentSerials.count(serials[i]) > 0), "No serial as expected"); - if (hasSerial) { - BOOST_CHECK_MESSAGE(cmp::equal(ids[i], info.spentSerials[serials[i]]), "Serials group id is invalid"); - } - } - - info = CLelantusTxInfo(); - BOOST_CHECK(CheckLelantusTransaction( - joinsplitTx, state, joinsplitTx.GetHash(), false, chainActive.Height(), false, true, &info)); - - // test surge dection. - while (!lelantusState->IsSurgeConditionDetected()) { - Scalar s; - s.randomize(); - - lelantusState->AddSpend(s, 1); - } - - BOOST_CHECK(!CheckLelantusTransaction( - joinsplitTx, state, joinsplitTx.GetHash(), false, chainActive.Height(), false, true, &info)); -} - -BOOST_AUTO_TEST_CASE(move_to_v3_payload) -{ - int prevHeight; - pwalletMain->SetBroadcastTransactions(true); - - for (int n=chainActive.Height(); n<300; n++) - GenerateBlock({}); - - std::vector lelantusMints; - GenerateMints({1*COIN, 2*COIN, 3*COIN, 4*COIN, 5*COIN}, lelantusMints); - GenerateBlock(lelantusMints); - - for (int i=0; i<6; i++) - GenerateBlock({}); - - // Shift HF block number to create joinsplit with v3 payload early - Consensus::Params &mutableParams = const_cast(::Params().GetConsensus()); - int v3PayloadHFBackup = mutableParams.nLelantusV3PayloadStartBlock; - mutableParams.nLelantusV3PayloadStartBlock = chainActive.Height(); - - CWalletTx jsWalletTx1, jsWalletTx2, jsWalletTx3; - - pwalletMain->JoinSplitLelantus({{script, COIN/10, false}}, {}, jsWalletTx1); - ::mempool.clear(); - - // Test transaction structure - BOOST_ASSERT(jsWalletTx1.tx->vin[0].scriptSig.size() == 1); - - CBlock blockWithPayloadJS = CreateBlock({*jsWalletTx1.tx}, coinbaseKey); - - mutableParams.nLelantusV3PayloadStartBlock = v3PayloadHFBackup; - - // transaction shouldn't get accepted into the mempool - BOOST_ASSERT(!::CommitToMempool(*jsWalletTx1.tx)); - // block with this transaction shouldn't be accepted either - prevHeight = chainActive.Height(); - ProcessNewBlock(::Params(), std::make_shared(blockWithPayloadJS), true, nullptr); - BOOST_ASSERT(chainActive.Height() == prevHeight); - - // Create wallet transaction with lelantus data in scriptSig - pwalletMain->JoinSplitLelantus({{script, COIN/10, false}}, {}, jsWalletTx2); - // ensure it has lelantus data in scriptSig - BOOST_ASSERT(jsWalletTx2.tx->vin[0].scriptSig.size() > 1); - // should get into the mempool - BOOST_ASSERT(::mempool.size() == 1); - // should get into the block - GenerateBlock({*jsWalletTx2.tx}); - BOOST_ASSERT(::mempool.size() == 0); - - // Create another wallet transaction with lelantus data in scriptSig - pwalletMain->JoinSplitLelantus({{script, COIN/10, false}}, {}, jsWalletTx3); - ::mempool.clear(); - - // fast forward to HF - for (int n=chainActive.Height(); n(blockWithNonpayloadJS), true, nullptr); - BOOST_ASSERT(chainActive.Height() == prevHeight); -} - -BOOST_AUTO_TEST_CASE(spend_limitation_per_tx) -{ - PrivateCoin coinOut(params, 0); - JoinSplitScriptGenerator invalidG, validG; - invalidG.fee = 0; - invalidG.vout = 0; - invalidG.coinsOut = {coinOut}; - invalidG.groupBlockHashes[1] = {ArithToUint256(0)}; - invalidG.txHash = ArithToUint256(0); - - validG.fee = 0; - validG.vout = 0; - validG.coinsOut = {coinOut}; - validG.groupBlockHashes[1] = ArithToUint256(0); - validG.txHash = ArithToUint256(0); - - for (size_t i = 0; i != consensus.nMaxLelantusInputPerTransaction + 1; i++) { - PrivateCoin coin(params, 0); - invalidG.coins.emplace_back(coin, 1); - invalidG.anons[1].push_back(coin.getPublicCoin()); - - if (i != 0) { // skip first - validG.coins.emplace_back(coin, 1); - validG.anons[1].push_back(coin.getPublicCoin()); - } - } - - CMutableTransaction invalidTx, validTx; - invalidTx.vin.resize(1); - invalidTx.vin[0].scriptSig = invalidG.Get().first; - - validTx.vin.resize(1); - validTx.vin[0].scriptSig = validG.Get().first; - - CBlock invalidBlock, validBlock; - invalidBlock.vtx.push_back(MakeTransactionRef(invalidTx)); - validBlock.vtx.push_back(MakeTransactionRef(validTx)); - - CValidationState state; - BOOST_CHECK(!CheckLelantusBlock(state, invalidBlock)); - BOOST_CHECK(CheckLelantusBlock(state, validBlock)); -} - -BOOST_AUTO_TEST_CASE(spend_limitation_per_block) -{ - CBlock block; - size_t spends = 0; - - for (size_t i = 0; spends <= consensus.nMaxLelantusInputPerBlock; i++) { - PrivateCoin coinOut(params, 0); - JoinSplitScriptGenerator g; - g.fee = 0; - g.vout = 0; - g.coinsOut = {coinOut}; - for (size_t i = 0; i != consensus.nMaxLelantusInputPerTransaction; i++) { - PrivateCoin coin(params, 0); - g.coins.emplace_back(coin, 1); - g.anons[1].push_back(coin.getPublicCoin()); - - spends++; - } - - g.groupBlockHashes[1] = ArithToUint256(i); - g.txHash = ArithToUint256(i); - - CMutableTransaction tx; - tx.vin.resize(1); - tx.vin[0].scriptSig = g.Get().first; - - - block.vtx.push_back(MakeTransactionRef(tx)); - } - - CValidationState state; - BOOST_CHECK(!CheckLelantusBlock(state, block)); - - block.vtx.pop_back(); - BOOST_CHECK(CheckLelantusBlock(state, block)); -} - -BOOST_AUTO_TEST_CASE(parse_joinsplit) -{ - auto coins = GenerateMints({1 * COIN, 10 * COIN, 1 * COIN, 1 * COIN}); - - JoinSplitScriptGenerator g; - g.params = params; - g.coins = {{coins[0], 1}, {coins[1], 1}, {coins[2], 2}}; - for (auto id : {1, 2}) { - for (size_t i = 0; i != 10; i++) { - GroupElement e; - e.randomize(); - - g.anons[id].emplace_back(e); - } - } - - g.anons[1][0] = coins[0].getPublicCoin(); - g.anons[1][1] = coins[1].getPublicCoin(); - g.anons[2][0] = coins[2].getPublicCoin(); - - g.vout = 11 * COIN - CENT; - g.coinsOut.push_back(coins[3]); - g.fee = CENT; - g.groupBlockHashes[1] = ArithToUint256(1); - g.groupBlockHashes[2] = ArithToUint256(2); - g.txHash = ArithToUint256(3); - - auto gs = g.Get(); - CTxIn inp(COutPoint(), gs.first); - CMutableTransaction inpTx; - inpTx.vin.push_back(inp); - - auto result = ParseLelantusJoinSplit(inpTx); - - BOOST_CHECK(gs.second.getCoinSerialNumbers() == result->getCoinSerialNumbers()); - BOOST_CHECK(gs.second.getFee() == result->getFee()); - BOOST_CHECK(gs.second.getCoinGroupIds() == result->getCoinGroupIds()); - BOOST_CHECK(gs.second.getIdAndBlockHashes() == result->getIdAndBlockHashes()); - BOOST_CHECK(gs.second.getVersion() == result->getVersion()); - BOOST_CHECK(gs.second.HasValidSerials() == result->HasValidSerials()); - - BOOST_CHECK(gs.second.Verify(g.anons, {}, ExtractCoins(g.coinsOut), g.vout, g.txHash)); - BOOST_CHECK(result->Verify(g.anons, {}, ExtractCoins(g.coinsOut), g.vout, g.txHash)); -} - -BOOST_AUTO_TEST_CASE(coingroup) -{ - GenerateBlocks(210); - - // util function - auto reconnect = [](CBlock const &block) { - LOCK2(cs_main, pwalletMain->cs_wallet); - LOCK(mempool.cs); - - std::shared_ptr sharedBlock = - std::make_shared(block); - - CValidationState state; - ActivateBestChain(state, ::Params(), sharedBlock); - }; - - struct { - // expected state. - std::vector coins; - - // first group - CBlockIndex *first = nullptr; - CBlockIndex *last = nullptr; - - int lastId = 0; - size_t lastGroupCoins = 0; - - // real state - CLelantusState *state; - - void Verify(std::string stateName = "") const { - auto const &mints = state->GetMints(); - BOOST_CHECK_EQUAL(coins.size(), mints.size()); - for (auto const &c : coins) { - BOOST_CHECK_MESSAGE(mints.count(c), "public is not found on state : " + stateName); - } - - auto retrievedId = state->GetLatestCoinID(); - - CLelantusState::LelantusCoinGroupInfo group; - state->GetCoinGroupInfo(retrievedId, group); - - BOOST_CHECK_EQUAL(lastId, retrievedId); - BOOST_CHECK_EQUAL(first, group.firstBlock); - BOOST_CHECK_EQUAL(last, group.lastBlock); - BOOST_CHECK_EQUAL(lastGroupCoins, group.nCoins); - } - } checker; - checker.state = lelantusState; - - lelantusState->~CLelantusState(); - new (lelantusState) CLelantusState(65, 16); - lelantusState->Reset(); - - // logic - std::vector txs; - std::vector coins; - auto hdMints = GenerateMints(std::vector(66, 1), txs, coins); - - auto txRange = [&](size_t start, size_t end) -> std::vector { - std::vector rangeTxs; - for (auto i = start; i < end && i < txs.size(); i++) { - rangeTxs.push_back(txs[i]); - } - - return rangeTxs; - }; - - std::vector pubCoins; - for (auto const &hdMint : hdMints) { - pubCoins.push_back(hdMint.GetPubcoinValue()); - } - - auto emptyChecker = checker; - emptyChecker.Verify(); - - // add one block - auto idx1 = GenerateBlock(txRange(0, 1)); - auto block1 = GetCBlock(idx1); - - checker.coins.push_back(pubCoins[0]); - checker.lastId = 1; - checker.first = idx1; - checker.last = idx1; - checker.lastGroupCoins = 1; - checker.Verify(); - - // add more - auto idx2 = GenerateBlock(txRange(1, 32)); - auto block2 = GetCBlock(idx2); - - checker.coins.insert(checker.coins.end(), pubCoins.begin() + 1, pubCoins.begin() + 32); - checker.last = idx2; - checker.lastGroupCoins = 32; - checker.Verify(); - - auto cacheIdx2Checker = checker; - - // add more to fill group - auto idx3 = GenerateBlock(txRange(32, 65)); - auto block3 = GetCBlock(idx3); - - checker.coins.insert(checker.coins.end(), pubCoins.begin() + 32, pubCoins.begin() + 65); - checker.last = idx3; - checker.lastGroupCoins = 65; - checker.Verify(); - - auto cacheIdx3Checker = checker; - - // add one more to create new group - auto idx4 = GenerateBlock(txRange(65, 66)); - auto block4 = GetCBlock(idx4); - - checker.coins.push_back(pubCoins[65]); - checker.lastId = 2; - checker.lastGroupCoins = 34; - checker.first = idx3; - checker.last = idx4; - - checker.Verify(); - - // remove last block check coingroup - DisconnectBlocks(1); - cacheIdx3Checker.Verify(); - - // remove one more block - DisconnectBlocks(1); - cacheIdx2Checker.Verify(); - - // reconnect them all and check state - reconnect(block2); - reconnect(block3); - checker.Verify(); - - lelantusState->~CLelantusState(); - new (lelantusState) CLelantusState(); - lelantusState->Reset(); -} - -BOOST_AUTO_TEST_SUITE_END() - -}; diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 5f3dd92a0a..1fd82d6b1b 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -120,12 +120,7 @@ TestingSetup::TestingSetup(const std::string& chainName, std::string suf) : Basi pwalletMain->SetBestChain(chainActive.GetLocator()); - pwalletMain->zwallet = std::make_unique(pwalletMain->strWalletFile); pwalletMain->sparkWallet = std::make_unique(pwalletMain->strWalletFile); - - pwalletMain->zwallet->GetTracker().Init(); - pwalletMain->zwallet->LoadMintPoolFromDB(); - pwalletMain->zwallet->SyncWithChain(); } TestingSetup::~TestingSetup() diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 324711428d..08ddd5e978 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -583,39 +583,6 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) sporkManager.RemovedFromMemoryPool(it->GetTx()); } - else if (it->GetTx().IsLelantusTransaction()) { - // Remove mints and spend serials from lelantus mempool state - const CTransaction &tx = it->GetTx(); - if (tx.IsLelantusJoinSplit()) { - std::vector serials; - try { - serials = lelantus::GetLelantusJoinSplitSerialNumbers(tx, tx.vin[0]); - for (const Scalar &serial: serials) - lelantusState.RemoveSpendFromMempool(serial); - } - catch (CBadTxIn&) { - } - } - - BOOST_FOREACH(const CTxOut &txout, tx.vout) - { - if (txout.scriptPubKey.IsLelantusMint() || txout.scriptPubKey.IsLelantusJMint()) { - GroupElement pubCoinValue; - try { - if (txout.scriptPubKey.IsLelantusMint()) { - lelantus::ParseLelantusMintScript(txout.scriptPubKey, pubCoinValue); - } else { - std::vector encryptedValue; - lelantus::ParseLelantusJMintScript(txout.scriptPubKey, pubCoinValue, encryptedValue); - } - lelantusState.RemoveMintFromMempool(pubCoinValue); - } - catch (std::invalid_argument&) { - } - } - } - } - else if (it->GetTx().IsSparkTransaction()) { // Remove mints and spends from spark mempool state const CTransaction &tx = it->GetTx(); @@ -1132,7 +1099,6 @@ void CTxMemPool::_clear() lastRollingFeeUpdate = GetTime(); blockSinceLastRollingFeeBump = false; rollingMinimumFeeRate = 0; - lelantusState.Reset(); sparkState.Reset(); ++nTransactionsUpdated; } diff --git a/src/txmempool.h b/src/txmempool.h index 1eb257ec70..09ff7a3dee 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -6,6 +6,10 @@ #ifndef BITCOIN_TXMEMPOOL_H #define BITCOIN_TXMEMPOOL_H +// Include C++ before any other headers to avoid macro conflicts with +// project headers (e.g. util.h, random.h) that break standard library parsing. +#include + #include #include #include "addressindex.h" @@ -23,7 +27,6 @@ #include "random.h" #include "netaddress.h" #include "bls/bls.h" -#include "lelantus.h" #include "spark/state.h" #include "evo/spork.h" @@ -522,7 +525,6 @@ class CTxMemPool const setEntries & GetMemPoolParents(txiter entry) const; const setEntries & GetMemPoolChildren(txiter entry) const; - lelantus::CLelantusMempoolState lelantusState; spark::CSparkMempoolState sparkState; std::map> sparkNames; // used to rule out duplicate names diff --git a/src/validation.cpp b/src/validation.cpp index 1373a6d7d2..0c2c48e63e 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -42,7 +42,6 @@ #include "wallet/walletdb.h" #endif // ENABLE_WALLET #include "batchproof_container.h" -#include "lelantus.h" #include "utilmoneystr.h" #include "utilstrencodings.h" #include "validationinterface.h" @@ -113,6 +112,7 @@ bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; +int64_t nMinimumInputValue = 0; bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT; uint256 hashAssumeValid; @@ -134,7 +134,6 @@ static void CheckBlockIndex(const Consensus::Params& consensusParams); CScript COINBASE_FLAGS; const std::string strMessageMagic = "Zcoin Signed Message:\n"; -const std::string strLelantusMessageMagic = "Lelantus signed Message:\n"; // Internal stuff namespace { @@ -327,74 +326,6 @@ bool CheckFinalTx(const CTransaction &tx, int flags) return IsFinalTx(tx, nBlockHeight, nBlockTime); } -bool VerifyPrivateTxOwn(const uint256& txid, const std::vector& vchSig, const std::string& message) -{ - CTransactionRef tx; - uint256 hashBlock; - if(!GetTransaction(txid, tx, Params().GetConsensus(), hashBlock, true)) - return false; - - if (tx->IsLelantusJoinSplit()) { - CHashWriter ss(SER_GETHASH, 0); - ss << strLelantusMessageMagic; - ss << message; - - std::unique_ptr joinsplit; - try { - joinsplit = lelantus::ParseLelantusJoinSplit(*tx); - } catch (const std::exception&) { - return false; - } - const auto& pubKeys = joinsplit->GetEcdsaPubkeys(); - - if((pubKeys.size() *64) != vchSig.size()) { - LogPrintf("Verification to serialNumbers and ecdsaSignatures/ecdsaPubkeys number mismatch."); - return false; - } - - uint32_t count = 0; - - for (const auto& pub : pubKeys) { - ss << count; - uint256 metahash = ss.GetHash(); - - // Check sizes - if (pub.size() != 33 ) { - LogPrintf("Verification failed due to incorrect size of ecdsaSignature."); - return false; - } - - // Verify signature - secp256k1_pubkey pubkey; - secp256k1_ecdsa_signature signature; - - if (!secp256k1_ec_pubkey_parse(OpenSSLContext::get_context(), &pubkey, pub.data(), 33)) { - LogPrintf("Verification failed due to unable to parse ecdsaPubkey."); - return false; - } - - if (1 != secp256k1_ecdsa_signature_parse_compact(OpenSSLContext::get_context(), &signature, &vchSig[count * 64]) ) { - LogPrintf("Verification failed due to signature cannot be parsed."); - return false; - } - - if (!secp256k1_ecdsa_verify( - OpenSSLContext::get_context(), &signature, metahash.begin(), &pubkey)) { - LogPrintf("Verification failed due to signature cannot be verified."); - return false; - } - - count++; - } - } else if (tx->IsCoinBase()) { - throw std::runtime_error("This is a coinbase transaction and not a private transaction"); - } else { - throw std::runtime_error("Currently this is allowed only for Lelantus transactions"); - } - - return true; -} - /** * Calculates the block height and previous block's median time past at * which the transaction will be considered final in the context of BIP 68. @@ -642,7 +573,7 @@ int GetUTXOConfirmations(const COutPoint& outpoint) return (nPrevoutHeight > -1 && chainActive.Tip()) ? chainActive.Height() - nPrevoutHeight + 1 : -1; } -bool CheckTransaction(const CTransaction &tx, CValidationState &state, bool fCheckDuplicateInputs, uint256 hashTx, bool isVerifyDB, int nHeight, bool isCheckWallet, bool fStatefulZerocoinCheck, lelantus::CLelantusTxInfo* lelantusTxInfo, spark::CSparkTxInfo* sparkTxInfo) +bool CheckTransaction(const CTransaction &tx, CValidationState &state, bool fCheckDuplicateInputs, uint256 hashTx, bool isVerifyDB, int nHeight, bool isCheckWallet, bool fStatefulZerocoinCheck, spark::CSparkTxInfo* sparkTxInfo) { LogPrintf("CheckTransaction nHeight=%d, isVerifyDB=%d, isCheckWallet=%d, txHash=%s\n", nHeight, (int)isVerifyDB, (int)isCheckWallet, tx.GetHash().ToString()); @@ -748,13 +679,6 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, bool fChe || tx.IsSparkSpend())) return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null"); - if (tx.IsLelantusTransaction()) { - if (hasExchangeUTXOs) - return state.DoS(100, false, REJECT_INVALID, "bad-exchange-address"); - if (!CheckLelantusTransaction(tx, state, hashTx, isVerifyDB, nHeight, isCheckWallet, fStatefulZerocoinCheck, lelantusTxInfo)) - return false; - } - if (tx.IsSparkTransaction()) { if (hasExchangeUTXOs) return state.DoS(100, false, REJECT_INVALID, "bad-exchange-address"); @@ -769,8 +693,13 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, bool fChe } if (tx.IsSigmaSpend() || tx.IsSigmaMint()) { - if (!isVerifyDB && nHeight >= (::Params().GetConsensus().nLelantusStartBlock + 5)) - return state.DoS(1, error( "Sigma already is not available, start using Lelantus.")); + if (!isVerifyDB && ::Params().GetConsensus().nLelantusStartBlock > 1 && nHeight >= (::Params().GetConsensus().nLelantusStartBlock + 5)) + return state.DoS(1, error("Sigma already is not available, start using Lelantus.")); + } + + if (tx.IsLelantusJoinSplit() || tx.IsLelantusMint()) { + if (!isVerifyDB && !IsInitialBlockDownload() && nHeight >= (::Params().GetConsensus().nSparkStartBlock + 5)) + return state.DoS(1, error("Lelantus already is not available, start using Spark.")); } if (tx.IsZerocoinRemint()) { @@ -818,8 +747,6 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, if (tx.nType == TRANSACTION_SPORK && !(nHeight >= consensusParams.nEvoSporkStartBlock && nHeight < consensusParams.nEvoSporkStopBlock)) return state.DoS(100, false, REJECT_INVALID, "bad-txns-type"); - if (tx.nType == TRANSACTION_LELANTUS && nHeight < consensusParams.nLelantusV3PayloadStartBlock) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-type"); if (tx.nType == TRANSACTION_SPARK && nHeight < consensusParams.nSparkStartBlock) return state.DoS(100, false, REJECT_INVALID, "bad-txns-type"); } @@ -872,8 +799,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C bool isCheckWalletTransaction, bool markFiroSpendTransactionSerial) { bool fTestNet = Params().GetConsensus().IsTestnet(); - LogPrintf("AcceptToMemoryPoolWorker(), lelantusJoinSplit=%d, fTestNet=%d\n", - (int)ptx->IsLelantusJoinSplit(), (int)fTestNet); const CTransaction& tx = *ptx; const uint256 hash = tx.GetHash(); @@ -915,13 +840,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C REJECT_INVALID, "bad-txns-zerocoin"); } } - - //lelantus - lelantus::CLelantusState *lelantusState = lelantus::CLelantusState::GetState(); - std::vector lelantusSpendSerials; - std::vector lelantusMintPubcoins; - std::vector lelantusAmounts; - // Spark spark::CSparkState *sparkState = spark::CSparkState::GetState(); std::vector sparkMintCoins; @@ -931,45 +849,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C { LOCK(pool.cs); - if (tx.IsLelantusJoinSplit()) { - if (tx.vin.size() > 1) { - return state.Invalid(false, REJECT_CONFLICT, "txn-invalid-lelantus-joinsplit"); - } - std::unique_ptr joinsplit; - - try { - joinsplit = lelantus::ParseLelantusJoinSplit(tx); - } - catch (CBadTxIn&) { - return state.Invalid(false, REJECT_CONFLICT, "txn-invalid-lelantus-joinsplit"); - } - catch (const std::exception &) { - return state.Invalid(false, REJECT_CONFLICT, "failed to deserialize joinsplit"); - } - - const std::vector &ids = joinsplit->getCoinGroupIds(); - const std::vector& serials = joinsplit->getCoinSerialNumbers(); - - if (joinsplit->isSigmaToLelantus() && chainActive.Height() >= consensus.nSigmaEndBlock) { - return state.DoS(100, error("Sigma pool already closed."), - REJECT_INVALID, "txn-invalid-lelantus-joinsplit"); - } - - if (serials.size() != ids.size()) - return state.Invalid(false, REJECT_CONFLICT, "txn-invalid-lelantus-joinsplit"); - - for (size_t i = 0; i < serials.size(); ++i) { - if (!serials[i].isMember() || serials[i].isZero()) - return state.Invalid(false, REJECT_INVALID, "txn-invalid-lelantus-joinsplit-serial"); - - if (lelantusState->IsUsedCoinSerial(serials[i]) || pool.lelantusState.HasCoinSerial(serials[i])) { - LogPrintf("AcceptToMemoryPool(): lelantus serial number %s has been used\n", - serials[i].tostring()); - return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict"); - } - lelantusSpendSerials.push_back(serials[i]); - } - } else if (tx.IsSparkSpend()) { + if (tx.IsSparkSpend()) { if (tx.vin.size() > 1) { return state.Invalid(false, REJECT_CONFLICT, "txn-invalid-spark-spend"); } @@ -1001,23 +881,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } } - BOOST_FOREACH(const CTxOut &txout, tx.vout) - { - if (txout.scriptPubKey.IsLelantusMint() || txout.scriptPubKey.IsLelantusJMint()) { - GroupElement pubCoinValue; - try { - lelantus::ParseLelantusMintScript(txout.scriptPubKey, pubCoinValue); - } catch (std::invalid_argument&) { - return state.DoS(100, false, PUBCOIN_NOT_VALIDATE, "bad-txns-zerocoin"); - } - if (lelantusState->HasCoin(pubCoinValue) || pool.lelantusState.HasMint(pubCoinValue)) { - LogPrintf("AcceptToMemoryPool(): lelantus mint with the same value %s is already in the mempool\n", pubCoinValue.tostring()); - return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict"); - } - lelantusMintPubcoins.push_back(pubCoinValue); - } - } - if (tx.IsSparkTransaction()) { try { sparkMintCoins = spark::GetSparkMintCoins(tx); @@ -1208,16 +1071,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C CAmount nFees; if (!tx.IsLelantusJoinSplit() && !tx.IsSparkSpend()) { nFees = nValueIn - nValueOut; - } else if (tx.IsLelantusJoinSplit()) { - try { - nFees = lelantus::ParseLelantusJoinSplit(tx)->getFee(); - } - catch (CBadTxIn&) { - return state.DoS(0, false, REJECT_INVALID, "unable to parse joinsplit"); - } - catch (const std::exception &) { - return state.DoS(0, false, REJECT_INVALID, "failed to deserialize joinsplit"); - } } else { try { nFees = spark::ParseSparkSpend(tx).getFee(); @@ -1556,20 +1409,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } } - if (tx.IsLelantusJoinSplit()) { - if(markFiroSpendTransactionSerial) { - for (const auto &spendSerial: lelantusSpendSerials) - pool.lelantusState.AddSpendToMempool(spendSerial, hash); - } - LogPrintf("Updating mint tracker state from Mempool..\n"); -#ifdef ENABLE_WALLET - if (!GetBoolArg("-disablewallet", false) && pwalletMain->zwallet) { - LogPrintf("Updating spend state from Mempool..\n"); - pwalletMain->zwallet->GetTracker().UpdateJoinSplitStateFromMempool(lelantusSpendSerials); - } -#endif - } - if (tx.IsSparkSpend()) { if(markFiroSpendTransactionSerial) { @@ -1596,32 +1435,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C LogPrintf("Adding Spark mints to Mempool..\n"); pwalletMain->sparkWallet->UpdateMintStateFromMempool(sparkMintCoins, hash); } - - if(tx.IsLelantusMint() && !GetBoolArg("-disablewallet", false) && pwalletMain->zwallet) { - LogPrintf("Updating mint state from Mempool..\n"); - BOOST_FOREACH(const CTxOut &txout, tx.vout) - { - if (txout.scriptPubKey.IsLelantusMint() || txout.scriptPubKey.IsLelantusJMint()) { - GroupElement pubCoinValue; - uint64_t amount = 0; - try { - if (txout.scriptPubKey.IsLelantusMint()) { - lelantus::ParseLelantusMintScript(txout.scriptPubKey, pubCoinValue); - amount = txout.nValue; - } else { - std::vector encryptedValue; - lelantus::ParseLelantusJMintScript(txout.scriptPubKey, pubCoinValue, encryptedValue); - if(!pwalletMain->DecryptMintAmount(encryptedValue, pubCoinValue, amount)) - amount = 0; - } - } catch (std::invalid_argument&) { - return state.DoS(100, false, PUBCOIN_NOT_VALIDATE, "bad-txns-zerocoin"); - } - lelantusAmounts.push_back(amount); - } - } - pwalletMain->zwallet->GetTracker().UpdateLelantusMintStateFromMempool(lelantusMintPubcoins, lelantusAmounts); - } #endif GetMainSignals().SyncTransaction(tx, NULL, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); @@ -2119,24 +1932,11 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins } CAmount nTxFee; - if(!tx.IsLelantusJoinSplit()) { - // at Lelantus JoinSplit we check balance inside cryptographic proof verification - if (nValueIn < tx.GetValueOut()) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, - strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()))); - // Tally transaction fees - nTxFee = nValueIn - tx.GetValueOut(); - } else { - try { - nTxFee = lelantus::ParseLelantusJoinSplit(tx)->getFee(); - } - catch (CBadTxIn&) { - return state.DoS(0, false, REJECT_INVALID, "unable to parse joinsplit"); - } - catch (const std::exception &) { - return state.DoS(0, false, REJECT_INVALID, "failed to deserialize joinsplit"); - } - } + if (nValueIn < tx.GetValueOut()) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, + strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()))); + // Tally transaction fees + nTxFee = nValueIn - tx.GetValueOut(); if (nTxFee < 0) return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative"); nFees += nTxFee; @@ -2328,13 +2128,6 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi } } } - } else if (tx.IsLelantusJoinSplit()) { - if(tx.vin.size() > 1 || !tx.vin[0].scriptSig.IsLelantusJoinSplit()) { - return state.DoS( - 100, false, - REJECT_MALFORMED, - " Can't mix Lelantus joinsplit input with regular ones or have more than one input"); - } } else if (tx.IsSparkSpend()) { if(tx.vin.size() > 1) { return state.DoS( @@ -2541,15 +2334,7 @@ static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& s // At this point, all of txundo.vprevout should have been moved out. } - if (tx.IsLelantusJoinSplit()) { - try { - nFees += lelantus::ParseLelantusJoinSplit(tx)->getFee(); - } - catch (const std::exception &) { - // do nothing - } - } - else if (tx.IsSparkSpend()) { + if (tx.IsSparkSpend()) { try { nFees += spark::ParseSparkSpend(tx).getFee(); } @@ -2885,8 +2670,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin batchProofContainer->fCollectProofs = ((GetSystemTimeInSeconds() - pindex->GetBlockTime()) > 86400) && GetBoolArg("-batching", true); batchProofContainer->init(); std::size_t nSigma = 0; + std::size_t nLelantus = 0; - block.lelantusTxInfo = std::make_shared(); block.sparkTxInfo = std::make_shared(); for (unsigned int i = 0; i < block.vtx.size(); i++) { @@ -2936,15 +2721,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if(tx.IsSigmaSpend()) ++nSigma; if(tx.IsLelantusJoinSplit()) { - try { - nFees += lelantus::ParseLelantusJoinSplit(tx)->getFee(); - } - catch (CBadTxIn&) { - return state.DoS(0, false, REJECT_INVALID, "unable to parse joinsplit"); - } - catch (const std::exception &) { - return state.DoS(0, false, REJECT_INVALID, "failed to deserialize joinsplit"); - } + ++nLelantus; } if(tx.IsSparkSpend()) { @@ -2963,7 +2740,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); // Check transaction against signa/lelantus state - if (!CheckTransaction(tx, state, false, txHash, false, pindex->nHeight, false, true, block.lelantusTxInfo.get(), block.sparkTxInfo.get())) + if (!CheckTransaction(tx, state, false, txHash, false, pindex->nHeight, false, true, block.sparkTxInfo.get())) return state.DoS(100, error("stateful zerocoin check failed"), REJECT_INVALID, "bad-txns-zerocoin"); } @@ -3008,7 +2785,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } - block.lelantusTxInfo->Complete(); block.sparkTxInfo->Complete(); int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2; @@ -3022,9 +2798,16 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin //btzc: Add time to check CAmount blockSubsidy = GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus(), pindex->nTime); CAmount blockReward = nFees + blockSubsidy; - // as we removed sigma lib, are are not able to get the fee, and as blocks are historical, we just skip fee check on blocks containing sigma tx - if(nSigma > 0 && pindex->nHeight >= ::Params().GetConsensus().nSigmaStartBlock && pindex->nHeight < ::Params().GetConsensus().nLelantusStartBlock) + const auto& consensusParams = ::Params().GetConsensus(); + // Sigma: use coinbase as limit when block has Sigma tx; on strip (nLelantusStartBlock<=1) any height, else only Sigma era (height < Lelantus) + if (nSigma > 0 && pindex->nHeight >= consensusParams.nSigmaStartBlock && + (consensusParams.nLelantusStartBlock <= 1 || pindex->nHeight < consensusParams.nLelantusStartBlock)) + blockReward = block.vtx[0]->GetValueOut(); + + // Lelantus: use coinbase as limit (Lelantus fees are not in nFees), including in Spark era + if (nLelantus > 0 && pindex->nHeight >= consensusParams.nLelantusStartBlock) blockReward = block.vtx[0]->GetValueOut(); + if (block.vtx[0]->GetValueOut() > blockReward) return state.DoS(100, error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", @@ -3098,8 +2881,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } } - if (!lelantus::ConnectBlockLelantus(state, chainparams, pindex, &block, fJustCheck) || - !spark::ConnectBlockSpark(state, chainparams, pindex, &block, fJustCheck)) + if (!spark::ConnectBlockSpark(state, chainparams, pindex, &block, fJustCheck)) return false; if (!sporkManager->IsBlockAllowed(block, pindex, state)) @@ -3181,38 +2963,9 @@ void static RemoveConflictingPrivacyTransactionsFromMempool(const CBlock &block) LOCK(mempool.cs); // Erase conflicting sigma/lelantus txs from the mempool - - lelantus::CLelantusState *lelantusState = lelantus::CLelantusState::GetState(); spark::CSparkState *sparkState = spark::CSparkState::GetState(); BOOST_FOREACH(CTransactionRef tx, block.vtx) { - if (tx->IsLelantusJoinSplit()) { - std::vector serials; - try { - serials = lelantus::GetLelantusJoinSplitSerialNumbers(*tx, tx->vin[0]); - } catch (CBadTxIn&) { - // nothing - } - - uint256 thisTxHash = tx->GetHash(); - uint256 conflictingTxHash; - for(const auto& serial : serials) { - conflictingTxHash = lelantusState->GetMempoolConflictingTxHash(serial); - if(!conflictingTxHash.IsNull()) - break; - } - if (!conflictingTxHash.IsNull() && conflictingTxHash != thisTxHash) { - std::list removed; - auto pTx = mempool.get(conflictingTxHash); - if (pTx) - mempool.removeRecursive(*pTx); - LogPrintf("ConnectBlock: removed conflicting lelantus joinsplit tx %s from the mempool\n", - conflictingTxHash.ToString()); - } - - // In any case we need to remove serial from mempool set - lelantusState->RemoveSpendFromMempool(serials); - } - else if (tx->IsSparkSpend()) { + if (tx->IsSparkSpend()) { std::vector lTags; try { lTags = spark::GetSparkUsedTags(*tx); @@ -3241,16 +2994,6 @@ void static RemoveConflictingPrivacyTransactionsFromMempool(const CBlock &block) } BOOST_FOREACH(const CTxOut &txout, tx->vout) { - if (txout.scriptPubKey.IsLelantusMint() || txout.scriptPubKey.IsLelantusJMint()) { - GroupElement pubCoinValue; - try { - lelantus::ParseLelantusMintScript(txout.scriptPubKey, pubCoinValue); - } catch (std::invalid_argument&) { - // nothing - } - lelantusState->RemoveMintFromMempool(pubCoinValue); - } - if (txout.scriptPubKey.IsSparkMint() || txout.scriptPubKey.IsSparkSMint()) { try { const spark::Params* params = spark::Params::get_default(); @@ -3471,39 +3214,14 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara // retrieve all mints - block.lelantusTxInfo = std::make_shared(); block.sparkTxInfo = std::make_shared(); - std::unordered_map lelantusSerialsToRemove; - std::vector rangeProofsToRemove; std::vector sparkTransactionsToRemove; for (CTransactionRef tx : block.vtx) { CheckTransaction(*tx, state, false, tx->GetHash(), false, pindexDelete->pprev->nHeight, - false, false, block.lelantusTxInfo.get(), block.sparkTxInfo.get()); + false, false, block.sparkTxInfo.get()); if(GetBoolArg("-batching", true)) { - if (tx->IsLelantusJoinSplit()) { - std::unique_ptr joinsplit; - - try { - joinsplit = lelantus::ParseLelantusJoinSplit(*tx); - } - catch (const std::exception &) { - continue; - } - - const std::vector &ids = joinsplit->getCoinGroupIds(); - const std::vector& serials = joinsplit->getCoinSerialNumbers(); - - if (serials.size() != ids.size()) { - continue; - } - - for (size_t i = 0; i < serials.size(); i++) { - lelantusSerialsToRemove.insert(std::make_pair(serials[i], ids[i])); - } - - rangeProofsToRemove.push_back(joinsplit->getLelantusProof().bulletproofs); - } else if (tx->IsSparkSpend()) { + if (tx->IsSparkSpend()) { try { spark::SpendTransaction spendTransaction = spark::ParseSparkSpend(*tx); sparkTransactionsToRemove.push_back(spendTransaction); @@ -3529,17 +3247,9 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara } LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); - lelantus::DisconnectTipLelantus(block, pindexDelete); spark::DisconnectTipSpark(block, pindexDelete); BatchProofContainer* batchProofContainer = BatchProofContainer::get_instance(); - if (lelantusSerialsToRemove.size() > 0) { - batchProofContainer->removeLelantus(lelantusSerialsToRemove); - } - - if (rangeProofsToRemove.size() > 0) { - batchProofContainer->remove(rangeProofsToRemove); - } for (auto& sparkTransaction : sparkTransactionsToRemove) { batchProofContainer->remove(sparkTransaction); @@ -3593,15 +3303,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara #ifdef ENABLE_WALLET // update mint/spend wallet - if (!GetBoolArg("-disablewallet", false) && pwalletMain->zwallet) { - if (block.lelantusTxInfo->spentSerials.size() > 0) { - pwalletMain->zwallet->GetTracker().UpdateSpendStateFromBlock(block.lelantusTxInfo->spentSerials); - } - - if (block.lelantusTxInfo->mints.size() > 0) { - pwalletMain->zwallet->GetTracker().UpdateMintStateFromBlock(block.lelantusTxInfo->mints); - } - + if (!GetBoolArg("-disablewallet", false) && pwalletMain->sparkWallet && block.sparkTxInfo) { if (block.sparkTxInfo->spentLTags.size() > 0) { pwalletMain->sparkWallet->RemoveSparkSpends(block.sparkTxInfo->spentLTags); } @@ -3669,6 +3371,8 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, RemoveConflictingPrivacyTransactionsFromMempool(blockConnecting); GetMainSignals().BlockChecked(blockConnecting, state); if (!rv) { + LogPrintf("ConnectTip(): ConnectBlock failed at height=%d, hash=%s: %s\n", + pindexNew->nHeight, pindexNew->GetBlockHash().ToString(), FormatStateMessage(state)); if (state.IsInvalid()) InvalidBlockFound(pindexNew, state); return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); @@ -3694,19 +3398,10 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, UpdateTip(pindexNew, chainparams); #ifdef ENABLE_WALLET - // Sync with HDMint wallet + // Sync with Spark wallet - if (!GetBoolArg("-disablewallet", false) && pwalletMain->zwallet && blockConnecting.lelantusTxInfo) { + if (!GetBoolArg("-disablewallet", false) && pwalletMain->sparkWallet && blockConnecting.sparkTxInfo) { LogPrintf("Checking if block contains wallet mints..\n"); - if (blockConnecting.lelantusTxInfo->spentSerials.size() > 0) { - LogPrintf("HDmint: UpdateSpendStateFromBlock. [height: %d]\n", GetHeight()); - pwalletMain->zwallet->GetTracker().UpdateSpendStateFromBlock(blockConnecting.lelantusTxInfo->spentSerials); - } - - if (blockConnecting.lelantusTxInfo->mints.size() > 0) { - LogPrintf("HDmint: UpdateSpendStateFromBlock. [height: %d]\n", GetHeight()); - pwalletMain->zwallet->GetTracker().UpdateMintStateFromBlock(blockConnecting.lelantusTxInfo->mints); - } if (blockConnecting.sparkTxInfo->spentLTags.size() > 0) { LogPrintf("SparkWallet: UpdateSpendStateFromBlock. [height: %d]\n", GetHeight()); @@ -3714,7 +3409,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, } if (blockConnecting.sparkTxInfo->mints.size() > 0) { - LogPrintf("SparkWallet: UpdateSpendStateFromBlock. [height: %d]\n", GetHeight()); + LogPrintf("SparkWallet: UpdateMintStateFromBlock. [height: %d]\n", GetHeight()); pwalletMain->sparkWallet->UpdateMintStateFromBlock(blockConnecting); } } @@ -4427,14 +4122,10 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const } bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot, int nHeight, bool isVerifyDB) { - // CheckBlock not only checks the block, but also fills up sparkTxInfo, lelantusTxInfo and sigmaTxInfo. - if (!block.lelantusTxInfo) - block.lelantusTxInfo = std::make_shared(); + // CheckBlock not only checks the block, but also fills up sparkTxInfo. if (!block.sparkTxInfo) block.sparkTxInfo = std::make_shared(); - LogPrintf("CheckBlock() nHeight=%d, blockHash= %s, isVerifyDB = %d\n", nHeight, block.GetHash().ToString(), isVerifyDB); - // These are checks that are independent of context. if (block.fChecked) @@ -4483,13 +4174,14 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P if (block.vtx[i]->IsCoinBase()) return state.DoS(100, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase"); - // Check transactions + // Check transactions (when called from ProcessNewBlock, nHeight is INT_MAX and we derive it here) if (nHeight == INT_MAX) nHeight = GetNHeight(block.GetBlockHeader()); + LogPrintf("CheckBlock() nHeight=%d, blockHash=%s, isVerifyDB=%d\n", nHeight, block.GetHash().ToString(), isVerifyDB); for (CTransactionRef tx : block.vtx) { // We don't check transactions against sigma/lelantus state here, we'll check it again later in ConnectBlock - if (!CheckTransaction(*tx, state, false, tx->GetHash(), isVerifyDB, nHeight, false, false, NULL, NULL)) + if (!CheckTransaction(*tx, state, false, tx->GetHash(), isVerifyDB, nHeight, false, false, NULL)) return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), state.GetDebugMessage())); @@ -4505,10 +4197,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P if (fCheckPOW && fCheckMerkleRoot) block.fChecked = true; - if (!lelantus::CheckLelantusBlock(state, block)) - return false; - - if (!spark::CheckSparkBlock(state, block)) + if (!spark::CheckSparkBlock(state, block, nHeight)) return false; return true; @@ -4978,6 +4667,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptrGetHash().ToString(), FormatStateMessage(state)); GetMainSignals().BlockChecked(*pblock, state); return error("%s: AcceptBlock FAILED", __func__); } @@ -4986,8 +4676,10 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptrnHeight % 1000 == 0) { LogPrint("reindex", "Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); } diff --git a/src/validation.h b/src/validation.h index 5e71dd92d8..48f1bd776d 100644 --- a/src/validation.h +++ b/src/validation.h @@ -189,7 +189,6 @@ extern uint64_t nLastBlockTx; extern uint64_t nLastBlockSize; extern uint64_t nLastBlockWeight; extern const std::string strMessageMagic; -extern const std::string strLelantusMessageMagic; extern CWaitableCriticalSection csBestBlock; extern CConditionVariable cvBlockChange; extern std::atomic_bool fImporting; @@ -419,7 +418,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); /** Transaction validation functions */ /** Context-independent validity checks */ -bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs, uint256 hashTx, bool isVerifyDB, int nHeight = INT_MAX, bool isCheckWallet = false, bool fStatefulZerocoinCheck = true, lelantus::CLelantusTxInfo* lelantusTxInfo = NULL, spark::CSparkTxInfo* sparkTxInfo = NULL); +bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs, uint256 hashTx, bool isVerifyDB, int nHeight = INT_MAX, bool isCheckWallet = false, bool fStatefulZerocoinCheck = true, spark::CSparkTxInfo* sparkTxInfo = NULL); namespace Consensus { @@ -449,9 +448,6 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime); */ bool CheckFinalTx(const CTransaction &tx, int flags = -1); - -bool VerifyPrivateTxOwn(const uint256& txid, const std::vector& vchSig, const std::string& message); - /** * Test whether the LockPoints height and time are still valid on the current chain */ diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index d21c2acb01..8ff481b344 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -7,11 +7,6 @@ add_library(firo_wallet STATIC EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/../activemasternode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../masternode-sync.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../lelantus.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../hdmint/hdmint.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../hdmint/mintpool.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../hdmint/wallet.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../hdmint/tracker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../spark/state.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../spark/sparkwallet.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../spark/primitives.cpp @@ -29,7 +24,6 @@ add_library(firo_wallet STATIC EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/rpcdump.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rpcwallet.cpp ${CMAKE_CURRENT_SOURCE_DIR}/txbuilder.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/lelantusjoinsplitbuilder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/walletexcept.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wallet.cpp ${CMAKE_CURRENT_SOURCE_DIR}/walletdb.cpp diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 52eb4aeebb..ca63701be7 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -224,13 +224,6 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn, const bool& fF return false; vMasterKey = vMasterKeyIn; fDecryptionThoroughlyChecked = true; - if(!fFirstUnlock && pwalletMain->zwallet){ - auto &hdChain = pwalletMain->GetHDChain(); - uint160 hashSeedMaster = hdChain.masterKeyID; - pwalletMain->SetHDChain(hdChain, false); // Used to upgrade normal keys to BIP44 - pwalletMain->zwallet->SetupWallet(hashSeedMaster, false); - pwalletMain->zwallet->SyncWithChain(); - } } NotifyStatusChanged(this); return true; diff --git a/src/wallet/lelantusjoinsplitbuilder.cpp b/src/wallet/lelantusjoinsplitbuilder.cpp deleted file mode 100644 index e1747e179f..0000000000 --- a/src/wallet/lelantusjoinsplitbuilder.cpp +++ /dev/null @@ -1,491 +0,0 @@ -#include "lelantusjoinsplitbuilder.h" -#include "walletexcept.h" - -#include "../primitives/transaction.h" - -#include "validation.h" -#include "../policy/policy.h" - - -#include "../lelantus.h" - -#include -#include - -struct CoinCompare -{ - bool operator()( const std::pair& left, const std::pair& right ) const { - return left.second < right.second; - } -}; - -LelantusJoinSplitBuilder::LelantusJoinSplitBuilder(CWallet& wallet, CHDMintWallet& mintWallet, const CCoinControl *coinControl) : - wallet(wallet), - mintWallet(mintWallet) -{ - cs_main.lock(); - - try { - wallet.cs_wallet.lock(); - } catch (...) { - cs_main.unlock(); - throw; - } - - this->coinControl = coinControl; -} - -LelantusJoinSplitBuilder::~LelantusJoinSplitBuilder() -{ - wallet.cs_wallet.unlock(); - cs_main.unlock(); -} - -CWalletTx LelantusJoinSplitBuilder::Build( - const std::vector& recipients, - CAmount &fee, - const std::vector& newMints, - std::function outModifier) -{ - if (recipients.empty() && newMints.empty()) { - throw std::runtime_error(_("Either recipients or newMints has to be nonempty.")); - } - - // calculate total value to spend - CAmount vOut = 0; - CAmount mint = 0; - unsigned recipientsToSubtractFee = 0; - - for (size_t i = 0; i < recipients.size(); i++) { - auto& recipient = recipients[i]; - - if (recipient.scriptPubKey.IsPayToExchangeAddress()) { - throw std::runtime_error("Exchange addresses cannot receive private funds. Please transfer your funds to a transparent address first before sending to an Exchange address"); - } - - if (!MoneyRange(recipient.nAmount)) { - throw std::runtime_error(boost::str(boost::format(_("Recipient has invalid amount")) % i)); - } - - vOut += recipient.nAmount; - - if (recipient.fSubtractFeeFromAmount) { - recipientsToSubtractFee++; - } - } - - for(const auto& mintValue : newMints) { - mint += mintValue; - } - - CWalletTx result; - CMutableTransaction tx; - - result.fTimeReceivedIsTxTime = true; - result.BindWallet(&wallet); - - - // Discourage fee sniping. - // - // For a large miner the value of the transactions in the best block and - // the mempool can exceed the cost of deliberately attempting to mine two - // blocks to orphan the current best block. By setting nLockTime such that - // only the next block can include the transaction, we discourage this - // practice as the height restricted and limited blocksize gives miners - // considering fee sniping fewer options for pulling off this attack. - // - // A simple way to think about this is from the wallet's point of view we - // always want the blockchain to move forward. By setting nLockTime this - // way we're basically making the statement that we only want this - // transaction to appear in the next block; we don't want to potentially - // encourage reorgs by allowing transactions to appear at lower heights - // than the next block in forks of the best chain. - // - // Of course, the subsidy is high enough, and transaction volume low - // enough, that fee sniping isn't a problem yet, but by implementing a fix - // now we ensure code won't be written that makes assumptions about - // nLockTime that preclude a fix later. - tx.nLockTime = chainActive.Height(); - - // Secondly occasionally randomly pick a nLockTime even further back, so - // that transactions that are delayed after signing for whatever reason, - // e.g. high-latency mix networks and some CoinJoin implementations, have - // better privacy. - if (GetRandInt(10) == 0) { - tx.nLockTime = std::max(0, static_cast(tx.nLockTime) - GetRandInt(100)); - } - - assert(tx.nLockTime <= static_cast(chainActive.Height())); - assert(tx.nLockTime < LOCKTIME_THRESHOLD); - - // Start with no fee and loop until there is enough fee; - uint32_t nCountNextUse = 0; - if (pwalletMain->zwallet) { - nCountNextUse = pwalletMain->zwallet->GetCount(); - } - - std::list coins = pwalletMain->GetAvailableLelantusCoins(coinControl); - std::tie(fee, std::ignore) = wallet.EstimateJoinSplitFee(vOut + mint, recipientsToSubtractFee, coins, coinControl); - - for (;;) { - // In case of not enough fee, reset mint seed counter - if (pwalletMain->zwallet) { - pwalletMain->zwallet->SetCount(nCountNextUse); - } - CAmount required = vOut + mint; - CAmount currentVout = vOut; - tx.vin.clear(); - tx.vout.clear(); - - result.fFromMe = true; - result.changes.clear(); - - // If no any recipients to subtract fee then the sender need to pay by themself. - if (!recipientsToSubtractFee) { - required += fee; - } else { - currentVout -= fee; - } - // fill outputs - bool remainderSubtracted = false; - - for (size_t i = 0; i < recipients.size(); i++) { - auto& recipient = recipients[i]; - CTxOut vout(recipient.nAmount, recipient.scriptPubKey); - - if (recipient.fSubtractFeeFromAmount) { - // Subtract fee equally from each selected recipient. - vout.nValue -= fee / recipientsToSubtractFee; - - if (!remainderSubtracted) { - // First receiver pays the remainder not divisible by output count. - vout.nValue -= fee % recipientsToSubtractFee; - remainderSubtracted = true; - } - } - - if (vout.IsDust(minRelayTxFee)) { - std::string err; - - if (recipient.fSubtractFeeFromAmount && fee > 0) { - if (vout.nValue < 0) { - err = boost::str(boost::format(_("Amount for recipient %1% is too small to pay the fee")) % i); - } else { - err = boost::str(boost::format(_("Amount for recipient %1% is too small to send after the fee has been deducted")) % i); - } - } else { - err = boost::str(boost::format(_("Amount for recipient %1% is too small")) % i); - } - - throw std::runtime_error(err); - } - - tx.vout.push_back(vout); - } - - // get coins - spendCoins.clear(); - - const auto& consensusParams = Params().GetConsensus(); - CAmount changeToMint = 0; - - if(required > 0) { - if (!wallet.GetCoinsToJoinSplit(required, spendCoins, changeToMint, coins, - consensusParams.nMaxLelantusInputPerTransaction, - consensusParams.nMaxValueLelantusSpendPerTransaction, coinControl)) { - throw InsufficientFunds(); - } - } - - if ((spendCoins.size()) > consensusParams.nMaxLelantusInputPerTransaction) - throw std::invalid_argument( - _("Number of inputs is bigger then limit.")); - - - CAmount input(0); - for (const auto &spend : spendCoins) { - input += spend.amount; - } - - changeToMint += (input - currentVout - fee - changeToMint - mint); - - if(changeToMint > consensusParams.nMaxValueLelantusMint) { - throw std::invalid_argument( - _("Value of change exceeds the limit")); - } - - // get outputs - mintCoins.clear(); - std::vector outputMints; - std::vector Cout; - { - CWalletDB walletdb(pwalletMain->strWalletFile); - pwalletMain->zwallet->ResetCount(walletdb); - } - GenerateMints(newMints, changeToMint, Cout, outputMints); - - // shuffle outputs to provide some privacy - std::vector> outputs; - outputs.reserve(outputMints.size()); - - for (auto& output : outputMints) { - outputs.push_back(std::ref(output)); - } - - std::shuffle(outputs.begin(), outputs.end(), std::random_device()); - - // replace outputs with shuffled one - size_t coinIdx = 0; - for (size_t i = 0; i < outputs.size(); i++) { - auto& output = outputs[i]; - - result.changes.insert(static_cast(tx.vout.size() + i)); - - CScript script; - if ((script = output.get().scriptPubKey).IsLelantusJMint()) { - GroupElement g; - std::vector enc; - lelantus::ParseLelantusJMintScript(script, g, enc); - - for (size_t i = coinIdx; i != Cout.size(); i++) { - if (Cout[i].getPublicCoin() == g) { - std::swap(Cout[i], Cout[coinIdx++]); - break; - } - } - } - } - - tx.vout.insert(tx.vout.end(), outputs.begin(), outputs.end()); - - // fill inputs - uint32_t sequence = CTxIn::SEQUENCE_FINAL; - tx.vin.emplace_back(COutPoint(), CScript(), sequence); - - if(outModifier) { - for(CTxOut & out : tx.vout) { - outModifier(out, *this); - } - } - - // clear vExtraPayload to calculate metadata hash correctly - tx.vExtraPayload.clear(); - - // set correct type of transaction (this affects metadata hash) - if (chainActive.Height() >= Params().GetConsensus().nLelantusV3PayloadStartBlock) { - tx.nVersion = 3; - tx.nType = TRANSACTION_LELANTUS; - } - - // now every fields is populated then we can sign transaction - uint256 sig = tx.GetHash(); - - CreateJoinSplit(sig, Cout, currentVout, fee, tx); - - // check fee - result.SetTx(MakeTransactionRef(tx)); - - if (GetTransactionWeight(tx) >= MAX_NEW_TX_WEIGHT) { - throw std::runtime_error(_("Transaction is too large (size limit: 100Kb). Select less inputs or consolidate your UTXOs")); - } - - // check fee - unsigned size = GetVirtualTransactionSize(tx); - CAmount feeNeeded = CWallet::GetMinimumFee(size, nTxConfirmTarget, mempool); - - // If we made it here and we aren't even able to meet the relay fee on the next pass, give up - // because we must be at the maximum allowed fee. - if (feeNeeded < minRelayTxFee.GetFee(size)) { - throw std::invalid_argument(_("Transaction too large for fee policy")); - } - - if (fee >= feeNeeded) { - break; - } - - fee = feeNeeded; - } - - if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { - // Lastly, ensure this tx will pass the mempool's chain limits - LockPoints lp; - CTxMemPoolEntry entry(MakeTransactionRef(tx), 0, 0, 0, 0, false, 0, lp); - CTxMemPool::setEntries setAncestors; - size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); - size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; - size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); - size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; - std::string errString; - if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, - nLimitDescendants, nLimitDescendantSize, errString)) { - throw std::runtime_error(_("Transaction has too long of a mempool chain")); - } - } - - return result; - -} - -void LelantusJoinSplitBuilder::GenerateMints(const std::vector& newMints, const CAmount& changeToMint, std::vector& Cout, std::vector& outputs) { - mintCoins.clear(); - Cout.clear(); - Cout.reserve(newMints.size() + 1); - CHDMint hdMint; - auto params = lelantus::Params::get_default(); - std::vector newMintsAndChange(newMints); - newMintsAndChange.push_back(changeToMint); - for (CAmount mintVal : newMintsAndChange) { - while (true) { - hdMint.SetNull(); - lelantus::PrivateCoin newCoin(params, mintVal); - newCoin.setVersion(LELANTUS_TX_VERSION_4); - CWalletDB walletdb(pwalletMain->strWalletFile); - - uint160 seedID; - mintWallet.GenerateLelantusMint(walletdb, newCoin, hdMint, seedID, boost::none, true); - - auto &pubCoin = newCoin.getPublicCoin(); - - if (!pubCoin.validate()) { - throw std::runtime_error("Unable to mint a lelantus coin."); - } - - // Create script for coin - CScript scriptSerializedCoin; - scriptSerializedCoin << OP_LELANTUSJMINT; - std::vector vch = pubCoin.getValue().getvch(); - scriptSerializedCoin.insert(scriptSerializedCoin.end(), vch.begin(), vch.end()); - - std::vector encryptedValue = pwalletMain->EncryptMintAmount(mintVal, pubCoin.getValue()); - scriptSerializedCoin.insert(scriptSerializedCoin.end(), encryptedValue.begin(), encryptedValue.end()); - - auto pubcoin = hdMint.GetPubcoinValue() + - lelantus::Params::get_default()->get_h1() * Scalar(hdMint.GetAmount()).negate(); - uint256 hashPub = primitives::GetPubCoinValueHash(pubcoin); - CDataStream ss(SER_GETHASH, 0); - ss << hashPub; - ss << seedID; - uint256 hashForRecover = Hash(ss.begin(), ss.end()); - // Check if there is a mint with same private data in chain, most likely Hd mint state corruption, - // If yes, try with new counter - GroupElement dummyValue; - if (lelantus::CLelantusState::GetState()->HasCoinTag(dummyValue, hashForRecover)) - continue; - - CDataStream serializedHash(SER_NETWORK, 0); - serializedHash << hashForRecover; - scriptSerializedCoin.insert(scriptSerializedCoin.end(), serializedHash.begin(), serializedHash.end()); - - Cout.emplace_back(newCoin); - outputs.push_back(CTxOut(0, scriptSerializedCoin)); - mintCoins.push_back(hdMint); - break; - } - } -} - -void LelantusJoinSplitBuilder::CreateJoinSplit( - const uint256& txHash, - const std::vector& Cout, - const uint64_t& Vout, - const uint64_t& fee, - CMutableTransaction& tx) { - - lelantus::CLelantusState* state = lelantus::CLelantusState::GetState(); - auto params = lelantus::Params::get_default(); - - std::vector> coins; - coins.reserve(spendCoins.size()); - std::map> anonymity_sets; - std::map groupBlockHashes; - int version = 0; - - // after nLelantusFixesStartBlock set new transaction version, - { - if (chainActive.Height() >= Params().GetConsensus().nLelantusV3PayloadStartBlock) - version = LELANTUS_TX_TPAYLOAD; - else - version = LELANTUS_TX_VERSION_4_5; - } - - std::vector> anonymity_set_hashes; - for (const auto &spend : spendCoins) { - // construct public part of the mint - lelantus::PublicCoin pub(spend.value); - // construct private part of the mint - lelantus::PrivateCoin priv(params, spend.amount); - priv.setVersion(version); - priv.setSerialNumber(spend.serialNumber); - priv.setRandomness(spend.randomness); - priv.setEcdsaSeckey(spend.ecdsaSecretKey); - priv.setPublicCoin(pub); - - // get coin group - int groupId; - int mintHeight; - std::tie(mintHeight, groupId) = state->GetMintedCoinHeightAndId(pub); - - if (groupId < 0) { - throw std::runtime_error(_("One of the lelantus coins has not been found in the chain!")); - } - - // Check if the coin is at overlapping parts of sets, use next set for proof creation if it is also in next set. - lelantus::CLelantusState::LelantusCoinGroupInfo nextCoinGroupInfo; - if (state->GetLatestCoinID() > groupId && state->GetCoinGroupInfo(groupId + 1, nextCoinGroupInfo)) { - if (nextCoinGroupInfo.firstBlock->nHeight <= mintHeight) - groupId += 1; - } - - coins.emplace_back(std::make_pair(priv, groupId)); - std::vector setHash; - if (anonymity_sets.count(groupId) == 0) { - std::vector set; - uint256 blockHash; - if (state->GetCoinSetForSpend( - &chainActive, - chainActive.Height() - (ZC_MINT_CONFIRMATIONS - 1), // required 1 confirmation for mint to spend - groupId, - blockHash, - set, - setHash) < 2) - throw std::runtime_error( - _("Has to have at least two mint coins with at least 1 confirmation in order to spend a coin")); - groupBlockHashes[groupId] = blockHash; - anonymity_sets[groupId] = set; - if (!setHash.empty()) - anonymity_set_hashes.push_back(setHash); - } - } - - std::sort(coins.begin(), coins.end(), CoinCompare()); - - lelantus::JoinSplit joinSplit(params, coins, anonymity_sets, anonymity_set_hashes, Vout, Cout, fee, groupBlockHashes, txHash, version); - - std::vector pCout; - pCout.reserve(Cout.size()); - for(const auto& coin : Cout) - pCout.emplace_back(coin.getPublicCoin()); - - if (!joinSplit.Verify(anonymity_sets, anonymity_set_hashes, pCout, Vout, txHash)) { - throw std::runtime_error(_("The joinsplit transaction failed to verify")); - } - - // construct spend script - CDataStream serialized(SER_NETWORK, PROTOCOL_VERSION); - serialized << joinSplit; - - CScript script; - - if (chainActive.Height() >= Params().GetConsensus().nLelantusV3PayloadStartBlock) { - script << OP_LELANTUSJOINSPLITPAYLOAD; - tx.nVersion = 3; - tx.nType = TRANSACTION_LELANTUS; - tx.vExtraPayload.assign(serialized.begin(), serialized.end()); - } - else { - script << OP_LELANTUSJOINSPLIT; - script.insert(script.end(), serialized.begin(), serialized.end()); - } - - tx.vin[0].scriptSig = script; -} diff --git a/src/wallet/lelantusjoinsplitbuilder.h b/src/wallet/lelantusjoinsplitbuilder.h deleted file mode 100644 index 22c8ba3d34..0000000000 --- a/src/wallet/lelantusjoinsplitbuilder.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef FIRO_WALLET_LELANTUSJOINSPLITBUILDER_H -#define FIRO_WALLET_LELANTUSJOINSPLITBUILDER_H - -#include "wallet.h" - -#include "../amount.h" -#include "../script/script.h" -#include "../primitives/transaction.h" - -#include "../hdmint/wallet.h" - - -class LelantusJoinSplitBuilder { -public: - LelantusJoinSplitBuilder(CWallet& wallet, CHDMintWallet& mintWallet, const CCoinControl *coinControl = nullptr); - ~LelantusJoinSplitBuilder(); - - CWalletTx Build( - const std::vector& recipients, - CAmount &fee, - const std::vector& newMintss, - std::function outModifier = nullptr); - -private: - void GenerateMints(const std::vector& newMints, const CAmount& changeToMint, std::vector& Cout, std::vector& outputs); - void CreateJoinSplit( - const uint256& txHash, - const std::vector& Cout, - const uint64_t& Vout, - const uint64_t& fee, - CMutableTransaction& tx); - -public: - std::vector spendCoins; - std::vector mintCoins; - - CWallet& wallet; - const CCoinControl *coinControl; - - CAmount fee = 0; - -private: - CHDMintWallet& mintWallet; -}; - - -#endif //FIRO_WALLET_LELANTUSJOINSPLITBUILDER_H diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 8ddd535777..cd0ea9e468 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -558,7 +558,6 @@ UniValue importwallet(const JSONRPCRequest& request) if(!masterKeyID.IsNull() && fHd){ // If change component in HD path is 2, this is a mint seed key. Add to mintpool. (Have to call after key addition) if(pwallet->mapKeyMetadata[keyid].nChange.first==2){ - pwallet->zwallet->RegenerateMintPoolEntry(walletdb, hdMasterKeyID, keyid, pwallet->mapKeyMetadata[keyid].nChild.first); fMintUpdate = true; } } @@ -577,8 +576,8 @@ UniValue importwallet(const JSONRPCRequest& request) pwallet->MarkDirty(); if(fMintUpdate){ - pwallet->zwallet->SyncWithChain(); - pwallet->zwallet->GetTracker().ListLelantusMints(false, false); + pwallet->ScanForWalletTransactions(chainActive.Genesis(), true); + pwallet->sparkWallet->ListSparkMints(); } if (!fGood) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index a132ec9a89..b3ba7b270a 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -10,7 +10,6 @@ #include "core_io.h" #include "init.h" #include "validation.h" -#include "lelantus.h" #include "llmq/quorums_instantsend.h" #include "llmq/quorums_chainlocks.h" #include "net.h" @@ -23,10 +22,8 @@ #include "utilmoneystr.h" #include "wallet.h" #include "walletdb.h" -#include "hdmint/tracker.h" #include "walletexcept.h" #include "masternode-payments.h" -#include "lelantusjoinsplitbuilder.h" #include "bip47/paymentchannel.h" #include "bip47/account.h" #include "wallet/coincontrol.h" @@ -64,17 +61,11 @@ bool EnsureWalletIsAvailable(CWallet * const pwallet, bool avoidException) return true; } -void EnsureLelantusWalletIsAvailable() -{ - if (!pwalletMain || !pwalletMain->zwallet) { - throw JSONRPCError(RPC_WALLET_ERROR, "lelantus mint/joinsplit is not allowed for legacy wallet"); - } -} void EnsureSparkWalletIsAvailable() { - if (!pwalletMain || !pwalletMain->zwallet) { - throw JSONRPCError(RPC_WALLET_ERROR, "lelantus mint/joinsplit is not allowed for legacy wallet"); + if (!pwalletMain || !pwalletMain->sparkWallet) { + throw JSONRPCError(RPC_WALLET_ERROR, "spark mint is not allowed for legacy wallet"); } } @@ -1012,8 +1003,7 @@ UniValue sendtoaddress(const JSONRPCRequest& request) std::list reservekeys; int nChangePosRet = -1; std::string strError; - - + // Debug logging LogPrintf("Attempting to create Spark mint transaction for %zu recipients\n", outputs.size()); for (size_t i = 0; i < outputs.size(); i++) { @@ -1376,6 +1366,7 @@ UniValue signmessage(const JSONRPCRequest& request) return EncodeBase64(&vchSig[0], vchSig.size()); } + UniValue signmessagewithsparkaddress(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); @@ -1454,52 +1445,6 @@ UniValue signmessagewithsparkaddress(const JSONRPCRequest& request) return HexStr(proofStream.begin(), proofStream.end()); } -UniValue proveprivatetxown(const JSONRPCRequest& request) -{ - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (request.fHelp || request.params.size() != 2) - throw std::runtime_error( - "proveprivatetxown \"txid\" \"message\"\n" - "\nCreated a proof by signing the message with private key of each spent coin." - + HelpRequiringPassphrase(pwallet) + "\n" - "\nArguments:\n" - "1. \"strTxId\" (string, required) Txid, in which we spend lelantus coins.\n" - "2. \"message\" (string, required) The message to create a signature of.\n" - "\nResult:\n" - "\"proof\" (string) The signatures of the message encoded in base 64\n" - "\nExamples:\n" - "\nUnlock the wallet for 30 seconds\n" - + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + - "\nCreate the signature\n" - + HelpExampleCli("proveprivatetxown", "\"34df0ec7bcc8a2bda2c0df41ac560172d974c56ffc9adc0e2377d0fc54b4e8f9 \" \"my message\"") + - "\nVerify the signature\n" - + HelpExampleCli("verifyprivatetxown", "\"34df0ec7bcc8a2bda2c0df41ac560172d974c56ffc9adc0e2377d0fc54b4e8f9 \" \"proof\" \"my message\"") + - "\nAs json rpc\n" - + HelpExampleRpc("proveprivatetxown", "\"34df0ec7bcc8a2bda2c0df41ac560172d974c56ffc9adc0e2377d0fc54b4e8f9 \", \"my message\"") - ); - - EnsureLelantusWalletIsAvailable(); - - LOCK2(cs_main, pwallet->cs_wallet); - EnsureWalletIsUnlocked(pwallet); - - std::string strTxId = request.params[0].get_str(); - std::string strMessage = request.params[1].get_str(); - - uint256 txid = uint256S(strTxId); - std::vector vchSig = pwallet->ProvePrivateTxOwn(txid, strMessage); - - if (vchSig.empty()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Something went wrong, may be you are not the owner of provided tx"); - - return EncodeBase64(&vchSig[0], vchSig.size()); -} - - UniValue getreceivedbyaddress(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); @@ -1695,7 +1640,7 @@ UniValue getprivatebalance(const JSONRPCRequest& request) throw std::runtime_error( "getprivatebalance\n" "\nReturns private balance.\n" - "Private balance is the sum of all confirmed sigma/lelantus mints which are created by the wallet.\n" + "Private balance is the sum of all confirmed spark mints which are created by the wallet.\n" "\nResult:\n" "amount (numeric) The confirmed private balance in " + CURRENCY_UNIT + ".\n" "\nExamples:\n" @@ -1704,10 +1649,10 @@ UniValue getprivatebalance(const JSONRPCRequest& request) + HelpExampleRpc("getprivatebalance", "") ); - EnsureLelantusWalletIsAvailable(); + EnsureSparkWalletIsAvailable(); LOCK2(cs_main, pwallet->cs_wallet); - return ValueFromAmount(pwallet->GetPrivateBalance().first + pwallet->sparkWallet->getAvailableBalance()); + return ValueFromAmount(pwallet->sparkWallet->getAvailableBalance()); } UniValue gettotalbalance(const JSONRPCRequest& request) @@ -1723,7 +1668,7 @@ UniValue gettotalbalance(const JSONRPCRequest& request) "gettotalbalance\n" "\nReturns total (transparent + private) balance.\n" "Transparent balance is the sum of coin amounts received as utxo.\n" - "Private balance is the sum of all confirmed sigma/lelantus/spark mints which are created by the wallet.\n" + "Private balance is the sum of all confirmed spark mints which are created by the wallet.\n" "\nResult:\n" "amount (numeric) The total balance in " + CURRENCY_UNIT + " for the wallet.\n" "\nExamples:\n" @@ -1732,12 +1677,10 @@ UniValue gettotalbalance(const JSONRPCRequest& request) + HelpExampleRpc("gettotalbalance", "") ); - EnsureLelantusWalletIsAvailable(); EnsureSparkWalletIsAvailable(); LOCK2(cs_main, pwallet->cs_wallet); - - return ValueFromAmount(pwallet->GetBalance() + pwallet->GetPrivateBalance().first + pwallet->sparkWallet->getAvailableBalance()); + return ValueFromAmount(pwallet->sparkWallet->getAvailableBalance()); } UniValue getunconfirmedbalance(const JSONRPCRequest &request) @@ -2908,14 +2851,7 @@ UniValue gettransaction(const JSONRPCRequest& request) CAmount nDebit = wtx.GetDebit(filter); CAmount nNet = nCredit - nDebit; CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0); - if (wtx.tx->vin[0].IsLelantusJoinSplit()) { - try { - nFee = (0 - lelantus::ParseLelantusJoinSplit(*wtx.tx)->getFee()); - } - catch (const std::exception &) { - // do nothing - } - } else if (wtx.tx->IsSparkSpend()) { + if (wtx.tx->IsSparkSpend()) { try { nFee = (0 - spark::ParseSparkSpend(*wtx.tx).getFee()); } @@ -3820,131 +3756,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) return result; } -UniValue regeneratemintpool(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (request.fHelp || request.params.size() > 0) - throw std::runtime_error( - "regeneratemintpool\n" - "\nIf issues exist with the keys that map to mintpool entries in the DB, this function corrects them.\n" - "\nExamples:\n" - + HelpExampleCli("regeneratemintpool", "") - + HelpExampleRpc("regeneratemintpool", "") - ); - - if (pwallet->IsLocked()) - throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, - "Error: Please enter the wallet passphrase with walletpassphrase first."); - - if (!pwallet->IsHDSeedAvailable() || !pwallet->zwallet) { - throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, - "Error: Can only regenerate mintpool on a HD-enabled wallet."); - } - - CWalletDB walletdb(pwallet->strWalletFile); - std::vector> listMintPool = walletdb.ListMintPool(); - std::vector> serialPubcoinPairs = walletdb.ListSerialPubcoinPairs(); - - // - std::pair nIndexes; - - uint256 oldHashSerial; - uint256 oldHashPubcoin; - - bool reindexRequired = false; - - for (auto& mintPoolPair : listMintPool){ - oldHashPubcoin = mintPoolPair.first; - bool hasSerial = pwallet->zwallet->GetSerialForPubcoin(serialPubcoinPairs, oldHashPubcoin, oldHashSerial); - - MintPoolEntry entry = mintPoolPair.second; - nIndexes = pwallet->zwallet->RegenerateMintPoolEntry(walletdb, std::get<0>(entry),std::get<1>(entry),std::get<2>(entry)); - - if(nIndexes.first != oldHashPubcoin){ - walletdb.EraseMintPoolPair(oldHashPubcoin); - reindexRequired = true; - } - - if(!hasSerial || nIndexes.second != oldHashSerial){ - walletdb.ErasePubcoin(oldHashSerial); - reindexRequired = true; - } - } - - if(reindexRequired) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Mintpool issue corrected. Please shutdown firo and restart with -reindex flag."); - - return true; -} - -UniValue listunspentlelantusmints(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (request.fHelp || request.params.size() > 2) { - throw std::runtime_error( - "listunspentsigmamints [minconf=1] [maxconf=9999999] \n" - "Returns array of unspent transaction outputs\n" - "with between minconf and maxconf (inclusive) confirmations.\n" - "Results are an array of Objects, each of which has:\n" - "{txid, vout, scriptPubKey, amount, confirmations}"); - } - - if (pwallet->IsLocked()) { - throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, - "Error: Please enter the wallet passphrase with walletpassphrase first."); - } - - EnsureLelantusWalletIsAvailable(); - - RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VARR)); - - int nMinDepth = 1; - if (request.params.size() > 0) - nMinDepth = request.params[0].get_int(); - - int nMaxDepth = 9999999; - if (request.params.size() > 1) - nMaxDepth = request.params[1].get_int(); - - UniValue results(UniValue::VARR); - std::vector vecOutputs; - assert(pwallet != NULL); - pwallet->ListAvailableLelantusMintCoins(vecOutputs, false); - LogPrintf("vecOutputs.size()=%zu\n", vecOutputs.size()); - BOOST_FOREACH(const COutput &out, vecOutputs) - { - if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) - continue; - - int64_t nValue = out.tx->tx->vout[out.i].nValue; - const CScript &pk = out.tx->tx->vout[out.i].scriptPubKey; - UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); - entry.push_back(Pair("vout", out.i)); - entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); - if (pk.IsPayToScriptHash()) { - CTxDestination address; - if (ExtractDestination(pk, address)) { - const CScriptID &hash = boost::get(address); - CScript redeemScript; - if (pwallet->GetCScript(hash, redeemScript)) - entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); - } - } - entry.push_back(Pair("amount", ValueFromAmount(nValue))); - entry.push_back(Pair("confirmations", out.nDepth)); - results.push_back(entry); - } - - return results; -} - UniValue listunspentsparkmints(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { @@ -5028,40 +4839,6 @@ UniValue transfersparkname(const JSONRPCRequest &request) { return HexStr(ownStream.begin(), ownStream.end()); } -UniValue lelantustospark(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (request.fHelp || request.params.size() > 0) { - throw std::runtime_error( - "lelantustospark \n" - "Takes all your lelantus mints, spends all to transparent layer, takes all that UTX's and mints to Spark"); - } - - if (!lelantus::IsLelantusGraceFulPeriod()) { - throw JSONRPCError(RPC_WALLET_ERROR, "Lelantus spends are not allowed anymore"); - } - - - EnsureWalletIsUnlocked(pwallet); - EnsureSparkWalletIsAvailable(); - - assert(pwallet != NULL); - std::string strFailReason = ""; - bool passed = false; - try { - passed = pwallet->LelantusToSpark(strFailReason); - } catch (const std::exception &) { - throw JSONRPCError(RPC_WALLET_ERROR, "Lelantus to Spark failed!"); - } - if (!passed || strFailReason != "") - throw JSONRPCError(RPC_WALLET_ERROR, "Lelantus to Spark failed. " + strFailReason); - - return NullUniValue; -} - UniValue identifysparkcoins(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); @@ -5146,464 +4923,6 @@ UniValue getsparkcoinaddr(const JSONRPCRequest& request) return results; } -UniValue mintlelantus(const JSONRPCRequest& request) -{ - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (request.fHelp || request.params.size() != 1) - throw std::runtime_error( - "mintlelantus amount\n" - + HelpRequiringPassphrase(pwallet) + "\n" - "\nArguments:\n" - "1. \"amount\" (numeric or string, required) The amount in " + CURRENCY_UNIT + " to mint, must be not less than 0.05\n" - "\nResult:\n" - "\"transactionid\" (string) The transaction id.\n" - "\nExamples:\n" - + HelpExampleCli("mintlelantus", "0.15") - + HelpExampleCli("mintlelantus", "100.9") - + HelpExampleRpc("mintlelantus", "0.15") - ); - - EnsureWalletIsUnlocked(pwallet); - EnsureLelantusWalletIsAvailable(); - - // Ensure Lelantus mints is already accepted by network so users will not lost their coins - // due to other nodes will treat it as garbage data. - if (!lelantus::IsLelantusAllowed()) { - throw JSONRPCError(RPC_WALLET_ERROR, "Lelantus is not active"); - } - - CAmount nAmount = AmountFromValue(request.params[0]); - LogPrintf("rpcWallet.mintlelantus() nAmount = %d \n", nAmount); - - std::vector> wtxAndFee; - std::vector mints; - std::string strError = pwallet->MintAndStoreLelantus(nAmount, wtxAndFee, mints); - - if (strError != "") - throw JSONRPCError(RPC_WALLET_ERROR, strError); - - UniValue result(UniValue::VARR); - for(const auto& wtx : wtxAndFee) { - result.push_back(wtx.first.GetHash().GetHex()); - } - - return result; -} - -UniValue autoMintlelantus(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (request.fHelp || request.params.size() != 0) - throw std::runtime_error( - "autoMintlelantus\n" - "This function automatically mints all unspent transparent funds to Lelantus.\n" - ); - - EnsureWalletIsUnlocked(pwallet); - EnsureLelantusWalletIsAvailable(); - - // Ensure Lelantus mints is already accepted by network so users will not lost their coins - // due to other nodes will treat it as garbage data. - if (!lelantus::IsLelantusAllowed()) { - throw JSONRPCError(RPC_WALLET_ERROR, "Lelantus is not active"); - } - - std::vector> wtxAndFee; - std::vector mints; - std::string strError = pwallet->MintAndStoreLelantus(0, wtxAndFee, mints, true); - - if (strError != "") - throw JSONRPCError(RPC_WALLET_ERROR, strError); - - UniValue result(UniValue::VARR); - for(const auto& wtx : wtxAndFee) { - result.push_back(wtx.first.GetHash().GetHex()); - } - - return result; -} - -UniValue joinsplit(const JSONRPCRequest& request) { - - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) - throw std::runtime_error( - "joinsplit {\"address\":amount,...} ([\"address\",...] )\n" - "\nSpend lelantus and mint in one transaction, you need at least provide one of 1-st or 3-rd arguments." - + HelpRequiringPassphrase(pwallet) + "\n" - "\nArguments:\n" - "1. \"amounts\" (string, optional) A json object with addresses and amounts\n" - " {\n" - " \"address\":amount (numeric or string) The Firo address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value\n" - " ,...\n" - " }\n" - "2. subtractfeefromamount (string, optional) A json array with addresses.\n" - " The fee will be equally deducted from the amount of each selected address.\n" - " Those recipients will receive less firos than you enter in their corresponding amount field.\n" - " If no addresses are specified here, the sender pays the fee.\n" - " [\n" - " \"address\" (string) Subtract fee from this address\n" - " ,...\n" - " ]\n" - "3. output mints (numeric, optional) A json object with amounts to mint\n" - " {\n" - " \"mint\"\n" - " ,...\n" - " }\n" - "\nResult:\n" - "\"transactionid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" - " the number of addresses.\n" - "\nExamples:\n" - "\nSend two amounts to two different addresses:\n" - + HelpExampleCli("joinsplit", "\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + - "\nSend two amounts to two different addresses and subtract fee from amount:\n" - + HelpExampleCli("joinsplit", "\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"\"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") - ); - - if (!lelantus::IsLelantusGraceFulPeriod()) { - throw JSONRPCError(RPC_WALLET_ERROR, "Lelantus spends are not allowed anymore"); - } - - EnsureLelantusWalletIsAvailable(); - - LOCK2(cs_main, pwallet->cs_wallet); - - - UniValue sendTo = request.params[0].get_obj(); - - std::unordered_set subtractFeeFromAmountSet; - UniValue subtractFeeFromAmount(UniValue::VARR); - if (request.params.size() > 1) { - try { - subtractFeeFromAmount = request.params[1].get_array(); - } catch (std::runtime_error const &) { - //may be empty - } - for (int i = subtractFeeFromAmount.size(); i--;) { - subtractFeeFromAmountSet.insert(subtractFeeFromAmount[i].get_str()); - } - } - - UniValue mintAmounts; - if(request.params.size() > 2) { - try { - mintAmounts = request.params[2].get_obj(); - } catch (std::runtime_error const &) { - //may be empty - } - } - - std::set setAddress; - std::vector vecSend; - std::vector vMints; - - FIRO_UNUSED CAmount totalAmount = 0; - - auto keys = sendTo.getKeys(); - std::vector mints = mintAmounts.empty() ? std::vector() : mintAmounts.getValues(); - - if(keys.empty() && mints.empty()) - throw JSONRPCError(RPC_TYPE_ERROR, "You have to provide at least public addressed or amount to mint"); - - for (const auto& strAddr : keys) { - CBitcoinAddress address(strAddr); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Firo address: " + strAddr); - - if (!setAddress.insert(address).second) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicated address: " + strAddr); - - CScript scriptPubKey = GetScriptForDestination(address.Get()); - CAmount nAmount = AmountFromValue(sendTo[strAddr]); - if (nAmount <= 0) { - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); - } - totalAmount += nAmount; - - bool fSubtractFeeFromAmount = - subtractFeeFromAmountSet.find(strAddr) != subtractFeeFromAmountSet.end(); - - vecSend.push_back({scriptPubKey, nAmount, fSubtractFeeFromAmount, {}, {}}); - } - - for(const auto& mint : mints) { - auto val = mint.get_int64(); - if (!lelantus::IsAvailableToMint(val) || val <= 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Amount to mint is invalid.\n"); - } - - vMints.push_back(val); - } - - EnsureWalletIsUnlocked(pwallet); - - CWalletTx wtx; - - try { - pwallet->JoinSplitLelantus(vecSend, vMints, wtx); - } - catch (const InsufficientFunds& e) { - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, e.what()); - } - catch (const std::exception& e) { - throw JSONRPCError(RPC_WALLET_ERROR, e.what()); - } - - return wtx.GetHash().GetHex(); -} - -UniValue resetlelantusmint(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (request.fHelp || request.params.size() != 0) - throw std::runtime_error( - "resetlelantusmint" - + HelpRequiringPassphrase(pwallet)); - - EnsureLelantusWalletIsAvailable(); - - std::vector listMints; - CWalletDB walletdb(pwallet->strWalletFile); - listMints = pwallet->zwallet->GetTracker().ListLelantusMints(false, false); - - BOOST_FOREACH(const CLelantusMintMeta& mint, listMints) { - CHDMint dMint; - if (!walletdb.ReadHDMint(mint.GetPubCoinValueHash(), true, dMint)) { - continue; - } - dMint.SetUsed(false); - dMint.SetHeight(-1); - pwallet->zwallet->GetTracker().AddLelantus(walletdb, dMint, true); - } - - return NullUniValue; -} - -UniValue listlelantusmints(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (request.fHelp || request.params.size() > 1) - throw std::runtime_error( - "listlelantusmints (false/true)\n" - "\nArguments:\n" - "1. (boolean, optional) false (default) to return real listlelantusmints. true to return every listlelantusmints.\n" - "\nResults are an array of Objects, each of which has:\n" - "{id, IsUsed, amount, value, serialNumber, nHeight, randomness}"); - - EnsureLelantusWalletIsAvailable(); - - bool fAllStatus = false; - if (request.params.size() > 0) { - fAllStatus = request.params[0].get_bool(); - } - - // Mint secret data encrypted in wallet - EnsureWalletIsUnlocked(pwallet); - - std::list listCoin; - CWalletDB walletdb(pwallet->strWalletFile); - listCoin = pwallet->zwallet->GetTracker().MintsAsLelantusEntries(false, false); - UniValue results(UniValue::VARR); - - BOOST_FOREACH(const CLelantusEntry &lelantusItem, listCoin) { - if ((fAllStatus || lelantusItem.amount != uint64_t(0)) && (lelantusItem.IsUsed || (lelantusItem.randomness != uint64_t(0) && lelantusItem.serialNumber != uint64_t(0)))) { - UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("id", lelantusItem.id)); - entry.push_back(Pair("isUsed", lelantusItem.IsUsed)); - entry.push_back(Pair("amount", lelantusItem.amount)); - entry.push_back(Pair("value", lelantusItem.value.GetHex())); - entry.push_back(Pair("serialNumber", lelantusItem.serialNumber.GetHex())); - entry.push_back(Pair("nHeight", lelantusItem.nHeight)); - entry.push_back(Pair("randomness", lelantusItem.randomness.GetHex())); - results.push_back(entry); - } - } - - return results; -} - -UniValue setlelantusmintstatus(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (request.fHelp || request.params.size() != 2) - throw std::runtime_error( - "setlelantusmintstatus \"coinserial\" (true/false)\n" - "Set lelantus mint IsUsed status to True or False\n" - "Results are an array of one or no Objects, each of which has:\n" - "{id, IsUsed, amount, value, serialNumber, nHeight, randomness}"); - - EnsureLelantusWalletIsAvailable(); - - Scalar coinSerial; - coinSerial.SetHex(request.params[0].get_str()); - - bool fStatus = true; - fStatus = request.params[1].get_bool(); - - EnsureWalletIsUnlocked(pwallet); - - std::vector listMints; - listMints = pwallet->zwallet->GetTracker().ListLelantusMints(false, false, false); - CWalletDB walletdb(pwallet->strWalletFile); - - UniValue results(UniValue::VARR); - - BOOST_FOREACH(const CLelantusMintMeta& mint, listMints) { - CLelantusEntry lelantusItem; - if(!pwallet->GetMint(mint.hashSerial, lelantusItem)) - continue; - - CHDMint dMint; - if (!walletdb.ReadHDMint(mint.GetPubCoinValueHash(), true, dMint)){ - continue; - } - - if (!lelantusItem.serialNumber.isZero()) { - LogPrintf("lelantusItem.serialNumber = %s\n", lelantusItem.serialNumber.GetHex()); - if (lelantusItem.serialNumber == coinSerial) { - LogPrintf("setmintlelantusstatus Found!\n"); - - const std::string& isUsedAmountStr = - fStatus - ? "Used (" + std::to_string((double)lelantusItem.amount / COIN) + " mint)" - : "New (" + std::to_string((double)lelantusItem.amount / COIN) + " mint)"; - pwallet->NotifyZerocoinChanged(pwallet, lelantusItem.value.GetHex(), isUsedAmountStr, CT_UPDATED); - - dMint.SetUsed(fStatus); - pwallet->zwallet->GetTracker().AddLelantus(walletdb, dMint, true); - - if (!fStatus) { - // erase lelantus spend entry - CLelantusSpendEntry spendEntry; - spendEntry.coinSerial = coinSerial; - walletdb.EraseLelantusSpendSerialEntry(spendEntry); - } - - UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("id", lelantusItem.id)); - entry.push_back(Pair("isUsed", fStatus)); - entry.push_back(Pair("amount", lelantusItem.amount)); - entry.push_back(Pair("value", lelantusItem.value.GetHex())); - entry.push_back(Pair("serialNumber", lelantusItem.serialNumber.GetHex())); - entry.push_back(Pair("nHeight", lelantusItem.nHeight)); - entry.push_back(Pair("randomness", lelantusItem.randomness.GetHex())); - results.push_back(entry); - break; - } - } - } - - return results; -} - -UniValue listlelantusjoinsplits(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) - throw std::runtime_error( - "listlelantusjoinsplits\n" - "Return up to \"count\" saved lelantus joinsplit transactions\n" - "\nArguments:\n" - "1. count (numeric) The number of transactions to return, <=0 means no limit\n" - "2. onlyunconfirmed (bool, optional, default=false) If true return only unconfirmed transactions\n" - "\nResult:\n" - "[\n" - " {\n" - " \"txid\": \"transactionid\", (string) The transaction hash\n" - " \"confirmations\": n, (numeric) The number of confirmations for the transaction\n" - " \"abandoned\": xxx, (bool) True if the transaction was already abandoned\n" - " \"joinsplits\": \n" - " [\n" - " {\n" - " \"spendid\": id, (numeric) Spend group id\n" - " \"serial\": \"s\", (string) Serial number of the coin\n" - " }\n" - " ]\n" - " }\n" - "]\n"); - - EnsureLelantusWalletIsAvailable(); - - int count = request.params[0].get_int(); - bool fOnlyUnconfirmed = request.params.size()>=2 && request.params[1].get_bool(); - - LOCK2(cs_main, pwallet->cs_wallet); - - UniValue ret(UniValue::VARR); - const CWallet::TxItems& txOrdered = pwallet->wtxOrdered; - - for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); - it != txOrdered.rend(); - ++it) { - CWalletTx *const pwtx = (*it).second.first; - - if (!pwtx || !pwtx->tx->IsLelantusJoinSplit()) - continue; - - UniValue entry(UniValue::VOBJ); - - int confirmations = pwtx->GetDepthInMainChain(); - if (confirmations > 0 && fOnlyUnconfirmed) - continue; - - entry.push_back(Pair("txid", pwtx->GetHash().GetHex())); - entry.push_back(Pair("confirmations", confirmations)); - entry.push_back(Pair("abandoned", pwtx->isAbandoned())); - - UniValue spends(UniValue::VARR); - std::unique_ptr joinsplit; - try { - joinsplit = lelantus::ParseLelantusJoinSplit(*pwtx->tx); - } catch (const std::exception &) { - continue; - } - - std::vector spentSerials = joinsplit->getCoinSerialNumbers(); - std::vector ids = joinsplit->getCoinGroupIds(); - - if(spentSerials.size() != ids.size()) { - continue; - } - - for(size_t i = 0; i < spentSerials.size(); i++) { - UniValue spendEntry(UniValue::VOBJ); - spendEntry.push_back(Pair("spendid", int64_t(ids[i]))); - spendEntry.push_back(Pair("serial", spentSerials[i].GetHex())); - spends.push_back(spendEntry); - } - - entry.push_back(Pair("spent_coins", spends)); - ret.push_back(entry); - - if (count > 0 && (int)ret.size() >= count) - break; - } - - return ret; -} UniValue removetxmempool(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); @@ -6116,54 +5435,6 @@ UniValue createrapaddress(const JSONRPCRequest& request) return result; } -UniValue setupchannel(const JSONRPCRequest& request) -{ - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (request.fHelp || request.params.size() != 1) - throw std::runtime_error( - "setupchannel \"RapAddress\"\n" - "\nSets up a payment channel for the RAP address. Sends a notification transaction to the RAP address notification address.\n" - "It __will__ use Lelantus facilities to send the notification tx. The tx cost is " + std::to_string(1.0 * bip47::NotificationTxValue / COIN ) + " for the JoinSplit tx + fees\n" - + HelpRequiringPassphrase(pwallet) + - "\nArguments:\n" - "1. \"RapAddress\" (string, required) The RAP address to send to.\n" - "\nResult:\n" - "\"txid\" (string) The notification transaction id.\n" - "\nExamples:\n" - + HelpExampleCli("setupchannel", "\"PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA\"") - ); - - bip47::CPaymentCode theirPcode(request.params[0].get_str()); - - if (!lelantus::IsLelantusAllowed()) { - throw JSONRPCError(RPC_WALLET_ERROR, "Lelantus is not active"); - } - - EnsureLelantusWalletIsAvailable(); - - LOCK2(cs_main, pwallet->cs_wallet); - - EnsureWalletIsUnlocked(pwallet); - - try { - CWalletTx wtx = pwallet->PrepareAndSendNotificationTx(theirPcode); - return wtx.GetHash().GetHex(); - - } - catch (InsufficientFunds const & e) - { - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, std::string(e.what())+" Please check your Lelantus balance is greater than " + std::to_string(1.0 * bip47::NotificationTxValue / COIN)); - } - catch (std::runtime_error const & e) - { - throw JSONRPCError(RPC_WALLET_ERROR, e.what()); - } -} - UniValue sendtorapaddress(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); @@ -6326,25 +5597,14 @@ static const CRPCCommand commands[] = { "wallet", "settxfee", &settxfee, true, {"amount"} }, { "wallet", "signmessage", &signmessage, true, {"address","message"} }, { "wallet", "signmessagewithsparkaddress", &signmessagewithsparkaddress, true, {"sparkaddress","message"} }, - { "wallet", "proveprivatetxown", &proveprivatetxown, true, {"txid","message"} }, { "wallet", "walletlock", &walletlock, true, {} }, { "wallet", "walletpassphrasechange", &walletpassphrasechange, true, {"oldpassphrase","newpassphrase"} }, { "wallet", "walletpassphrase", &walletpassphrase, true, {"passphrase","timeout"} }, { "wallet", "removeprunedfunds", &removeprunedfunds, true, {"txid"} }, - { "wallet", "listunspentlelantusmints", &listunspentlelantusmints, false, {} }, - { "wallet", "mintlelantus", &mintlelantus, false, {} }, - { "wallet", "autoMintlelantus", &autoMintlelantus, false, {} }, - { "wallet", "joinsplit", &joinsplit, false, {} }, - { "wallet", "resetlelantusmint", &resetlelantusmint, false, {} }, - { "wallet", "setlelantusmintstatus", &setlelantusmintstatus, false, {} }, - { "wallet", "listlelantusmints", &listlelantusmints, false, {} }, - { "wallet", "setmininput", &setmininput, false, {} }, - { "wallet", "regeneratemintpool", ®eneratemintpool, false, {} }, { "wallet", "removetxmempool", &removetxmempool, false, {} }, { "wallet", "removetxwallet", &removetxwallet, false, {} }, - { "wallet", "listlelantusjoinsplits", &listlelantusjoinsplits, false, {} }, //spark { "wallet", "listunspentsparkmints", &listunspentsparkmints, false, {} }, @@ -6362,7 +5622,6 @@ static const CRPCCommand commands[] = { "wallet", "spendspark", &spendspark, false, {} }, { "wallet", "sendspark", &sendspark, false, {} }, { "wallet", "sendsparkmany", &sendsparkmany, false, {"fromaccount","amounts","comment","subtractfeefrom"} }, - { "wallet", "lelantustospark", &lelantustospark, false, {} }, { "wallet", "identifysparkcoins", &identifysparkcoins, false, {} }, { "wallet", "getsparkcoinaddr", &getsparkcoinaddr, false, {} }, { "wallet", "registersparkname", ®istersparkname, false, {} }, @@ -6371,7 +5630,6 @@ static const CRPCCommand commands[] = //bip47 { "bip47", "createrapaddress", &createrapaddress, true, {} }, - { "bip47", "setupchannel", &setupchannel, true, {} }, { "bip47", "sendtorapaddress", &sendtorapaddress, true, {} }, { "bip47", "listrapaddresses", &listrapaddresses, true, {} }, { "bip47", "setusednumber", &setusednumber, true, {} } diff --git a/src/wallet/test/CMakeLists.txt b/src/wallet/test/CMakeLists.txt index 2b714929fa..e15a41f331 100644 --- a/src/wallet/test/CMakeLists.txt +++ b/src/wallet/test/CMakeLists.txt @@ -8,7 +8,6 @@ target_sources(test_firo PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/crypto_tests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/lelantus_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mnemonic_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/spark_wallet_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/txbuilder_tests.cpp diff --git a/src/wallet/test/lelantus_tests.cpp b/src/wallet/test/lelantus_tests.cpp deleted file mode 100644 index 5ad17cea8d..0000000000 --- a/src/wallet/test/lelantus_tests.cpp +++ /dev/null @@ -1,298 +0,0 @@ -#include "../../test/fixtures.h" -#include "../../validation.h" -#include "../../lelantus.h" -#include "../walletexcept.h" -#include - -#include "../wallet.h" - -#include - -BOOST_FIXTURE_TEST_SUITE(wallet_lelantus_tests, LelantusTestingSetup) - -BOOST_AUTO_TEST_CASE(create_mint_recipient) -{ - lelantus::PrivateCoin coin(params, 1); - CHDMint m; - - auto r = CWallet::CreateLelantusMintRecipient(coin, m); - - // payload is commentment and schnorr proof - size_t expectedSize = 1 // op code - + lelantus::PublicCoin().GetSerializeSize() - + lelantus::SchnorrProof().memoryRequired() - + 32; - - BOOST_CHECK(r.scriptPubKey.IsLelantusMint()); - BOOST_CHECK_EQUAL(expectedSize, r.scriptPubKey.size()); - - // assert HDMint - BOOST_CHECK_EQUAL(0, m.GetCount()); -} - -BOOST_AUTO_TEST_CASE(mint_and_store_lelantus) -{ - bool oldFRequireStandard = fRequireStandard; - fRequireStandard = true; // to verify mainnet can accept lelantus mint - pwalletMain->SetBroadcastTransactions(true); - - GenerateBlocks(110); - auto amount = 1 * COIN; - - std::vector> wtxAndFee; - std::vector mints; - auto result = pwalletMain->MintAndStoreLelantus(amount, wtxAndFee, mints); - - BOOST_CHECK_EQUAL("", result); - size_t mintAmount = 0; - for(const auto& wtx : wtxAndFee) { - auto tx = wtx.first.tx.get(); - - BOOST_CHECK(tx->IsLelantusMint()); - BOOST_CHECK(tx->IsLelantusTransaction()); - BOOST_CHECK(mempool.exists(tx->GetHash())); - - - for (auto const &out : tx->vout) { - if (out.scriptPubKey.IsLelantusMint()) { - mintAmount += out.nValue; - } - } - - // verify tx - CMutableTransaction mtx(*tx); - BOOST_CHECK(GenerateBlock({mtx})); - } - - BOOST_CHECK_EQUAL(amount, mintAmount); - - auto lelantusState = lelantus::CLelantusState::GetState(); - lelantusState->Reset(); - fRequireStandard = oldFRequireStandard; -} - -BOOST_AUTO_TEST_CASE(get_and_list_mints) -{ - GenerateBlocks(120); - std::vector confirmedAmounts = {1, 2 * COIN}; - std::vector unconfirmedAmounts = {10 * COIN}; - std::vector allAmounts(confirmedAmounts); - allAmounts.insert(allAmounts.end(), unconfirmedAmounts.begin(), unconfirmedAmounts.end()); - - // Generate all coins - std::vector txs; - auto mints = GenerateMints(allAmounts, txs); - GenerateBlock(std::vector(txs.begin(), txs.begin() + txs.size() - 1)); - - std::vector>> pubCoins; - pubCoins.reserve(mints.size() - 1); - for (size_t i = 0; i != mints.size() - 1; i++) { - pubCoins.emplace_back(mints[i].GetPubcoinValue(), std::make_pair(mints[i].GetAmount(), uint256())); - } - - pwalletMain->zwallet->GetTracker().UpdateMintStateFromBlock(pubCoins); - - auto extractAmountsFromOutputs = [](std::vector const &outs) -> std::vector { - std::vector amounts; - for (auto const &out : outs) { - amounts.push_back(out.tx->tx->vout[out.i].nValue); - } - - return amounts; - }; - - std::vector confirmedCoins, allCoins; - pwalletMain->ListAvailableLelantusMintCoins(confirmedCoins, true); - pwalletMain->ListAvailableLelantusMintCoins(allCoins, false); - auto confirmed = extractAmountsFromOutputs(confirmedCoins); - auto all = extractAmountsFromOutputs(allCoins); - - BOOST_CHECK(std::is_permutation(confirmed.begin(), confirmed.end(), confirmedAmounts.begin())); - BOOST_CHECK(std::is_permutation(all.begin(), all.end(), allAmounts.begin())); - - // get mints - CLelantusEntry entry; - BOOST_CHECK(pwalletMain->GetMint(mints.front().GetSerialHash(), entry)); - BOOST_CHECK(entry.value == mints.front().GetPubcoinValue()); - - uint256 fakeSerial; - std::fill(fakeSerial.begin(), fakeSerial.end(), 1); - BOOST_CHECK(!pwalletMain->GetMint(fakeSerial, entry)); -} - -BOOST_AUTO_TEST_CASE(mintlelantus_and_mint_all) -{ - // utils - auto countMintsBalance = [&]( - std::vector> const &wtxs, - bool includeFee = false) -> CAmount { - - CAmount s = 0; - for (auto const &w : wtxs) { - for (auto const &out : w.first.tx->vout) { - if (out.scriptPubKey.IsLelantusMint()) { - s += out.nValue; - } - } - - if (includeFee) { - s += w.second; - } - } - - return s; - }; - - auto getAvialableCoinForLMintBalance = [&]() -> CAmount { - std::vector>> valueAndUTXO; - pwalletMain->AvailableCoinsForLMint(valueAndUTXO, nullptr); - CAmount s = 0; - - for (auto const &v : valueAndUTXO) { - s += v.first; - } - - return s; - }; - - CScript externalScript; - { - uint160 seed; - GetRandBytes(seed.begin(), seed.size()); - - externalScript = GetScriptForDestination(CKeyID(seed)); - } - - auto generateBlocksPerScripts = [&](size_t blocks, size_t blocksPerScript) -> std::vector { - LOCK2(cs_main, pwalletMain->cs_wallet); - std::vector scripts; - while (blocks != 0) { - CPubKey key; - key = pwalletMain->GenerateNewKey(); - scripts.push_back(GetScriptForDestination(key.GetID())); - - auto blockCount = std::min(blocksPerScript, blocks); - - GenerateBlocks(blockCount, &scripts.back()); - - blocks -= blockCount; - } - - return scripts; - }; - - auto scripts = generateBlocksPerScripts(200, 10); - GenerateBlocks(100, &externalScript); - - std::vector> wtxAndFee; - std::vector hdMints; - - // Produce just one txs - auto result = pwalletMain->MintAndStoreLelantus(10 * COIN, wtxAndFee, hdMints); - BOOST_CHECK_EQUAL("", result); - BOOST_CHECK_EQUAL(1, wtxAndFee.size()); - BOOST_CHECK_EQUAL(10 * COIN, countMintsBalance(wtxAndFee)); - - // Produce more than one txs - wtxAndFee.clear(); - hdMints.clear(); - - result = pwalletMain->MintAndStoreLelantus(600 * COIN, wtxAndFee, hdMints); - BOOST_CHECK_EQUAL("", result); - BOOST_CHECK_GT(wtxAndFee.size(), 1); - BOOST_CHECK_EQUAL(600 * COIN, countMintsBalance(wtxAndFee)); - - // Mint all and each address contain no larger than mint limit - wtxAndFee.clear(); - hdMints.clear(); - - auto balance = getAvialableCoinForLMintBalance(); - BOOST_CHECK_GT(balance, 0); - - result = pwalletMain->MintAndStoreLelantus(0, wtxAndFee, hdMints, true); - BOOST_CHECK_EQUAL("", result); - BOOST_CHECK_GE(wtxAndFee.size(), scripts.size() - 2); - BOOST_CHECK_GT(balance, countMintsBalance(wtxAndFee)); - BOOST_CHECK_EQUAL(balance, countMintsBalance(wtxAndFee, true)); - BOOST_CHECK_EQUAL(0, getAvialableCoinForLMintBalance()); - - // Mint all and have address that contain balance larger mint limit per tx - scripts = generateBlocksPerScripts(500, 200); - GenerateBlocks(100, &externalScript); - - wtxAndFee.clear(); - hdMints.clear(); - - balance = getAvialableCoinForLMintBalance(); - BOOST_CHECK_GT(balance, 0); - - result = pwalletMain->MintAndStoreLelantus(0, wtxAndFee, hdMints, true); - BOOST_CHECK_EQUAL("", result); - BOOST_CHECK_GE(wtxAndFee.size(), scripts.size()); - BOOST_CHECK_GT(balance, countMintsBalance(wtxAndFee)); - BOOST_CHECK_EQUAL(balance, countMintsBalance(wtxAndFee, true)); - BOOST_CHECK_EQUAL(0, pwalletMain->GetBalance()); - - // Scripts of all changes should unique - std::set changeScripts; - for (auto const &wtx : wtxAndFee) { - for (auto const &out : wtx.first.tx->vout) { - if (!out.scriptPubKey.IsLelantusMint()) { - BOOST_CHECK(!changeScripts.count(out.scriptPubKey)); - changeScripts.insert(out.scriptPubKey); - } - } - } -} - -BOOST_AUTO_TEST_CASE(spend) -{ - fRequireStandard = true; // to verify mainnet can accept lelantus mint - pwalletMain->SetBroadcastTransactions(true); - GenerateBlocks(150); - - std::vector> wtxAndFee; - std::vector mints; - auto result = pwalletMain->MintAndStoreLelantus(10 * COIN, wtxAndFee, mints); - BOOST_CHECK_EQUAL("", result); - CMutableTransaction mutableTx(*(wtxAndFee[0].first.tx)); - GenerateBlock({mutableTx}, &script); - GenerateBlocks(5); - BOOST_CHECK_EQUAL(1, wtxAndFee.size()); - wtxAndFee.clear(); - mints.clear(); - - CWalletTx tx; - std::vector recipients; - - CPubKey pub; - { - LOCK(pwalletMain->cs_wallet); - pub = pwalletMain->GenerateNewKey(); - } - recipients.push_back(CRecipient{ - .scriptPubKey = GetScriptForDestination(pub.GetID()), - .nAmount = 5 * COIN, - .fSubtractFeeFromAmount = false - }); - - BOOST_CHECK_EXCEPTION( - pwalletMain->JoinSplitLelantus(recipients, {0}, tx), - InsufficientFunds, - [](const InsufficientFunds& e) { return e.what() == std::string("Insufficient funds"); }); - - result = pwalletMain->MintAndStoreLelantus(10 * COIN, wtxAndFee, mints); - BOOST_CHECK_EQUAL("", result); - mutableTx = (*(wtxAndFee[0].first.tx)); - GenerateBlock({mutableTx}, &script); - GenerateBlocks(6); - BOOST_CHECK_EQUAL(1, wtxAndFee.size()); - wtxAndFee.clear(); - mints.clear(); - - BOOST_CHECK_NO_THROW(pwalletMain->JoinSplitLelantus(recipients, {0}, tx)); - - lelantus::CLelantusState::GetState()->Reset(); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/txbuilder.cpp b/src/wallet/txbuilder.cpp index cb853516ba..57ad3d6646 100644 --- a/src/wallet/txbuilder.cpp +++ b/src/wallet/txbuilder.cpp @@ -102,16 +102,8 @@ CWalletTx TxBuilder::Build(const std::vector& recipients, CAmount& f assert(tx.nLockTime <= static_cast(chainActive.Height())); assert(tx.nLockTime < LOCKTIME_THRESHOLD); - // Start with no fee and loop until there is enough fee; - uint32_t nCountNextUse = 0; - if (pwalletMain->zwallet) { - nCountNextUse = pwalletMain->zwallet->GetCount(); - } + // Start with no fee and loop until there is enough fee for (fee = payTxFee.GetFeePerK();;) { - // In case of not enough fee, reset mint seed counter - if (pwalletMain->zwallet) { - pwalletMain->zwallet->SetCount(nCountNextUse); - } CAmount required = spend; tx.vin.clear(); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c9d71264da..ea4a506c0c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -10,7 +10,6 @@ #include "support/allocators/secure.h" #include "sync.h" #include "walletexcept.h" -#include "lelantusjoinsplitbuilder.h" #include "amount.h" #include "base58.h" #include "checkpoints.h" @@ -21,7 +20,6 @@ #include "key.h" #include "keystore.h" #include "validation.h" -#include "lelantus.h" #include "llmq/quorums_instantsend.h" #include "llmq/quorums_chainlocks.h" #include "net.h" @@ -39,15 +37,12 @@ #include "masternode-sync.h" #include "random.h" #include "init.h" -#include "hdmint/wallet.h" #include "rpc/protocol.h" #include "wallet/bip39.h" #include "crypto/hmac_sha512.h" #include "crypto/aes.h" -#include "hdmint/tracker.h" - #include "evo/deterministicmns.h" #include @@ -109,13 +104,6 @@ struct CompareByAmount } }; -static void EnsureMintWalletAvailable() -{ - if (!pwalletMain || !pwalletMain->zwallet) { - throw std::logic_error("Sigma feature requires HD wallet"); - } -} - static void EnsureSparkWalletAvailable() { if (!pwalletMain || !pwalletMain->sparkWallet) { @@ -772,22 +760,11 @@ bool CWallet::IsSpent(const uint256 &hash, unsigned int n) const if (script.IsZerocoinMint()) { return true; - } else if (zwallet && script.IsSigmaMint()) { + } else if (pwalletMain && script.IsSigmaMint()) { return true; - } else if (zwallet && (script.IsLelantusMint() || script.IsLelantusJMint())) { - secp_primitives::GroupElement pubcoin; - try { - lelantus::ParseLelantusMintScript(script, pubcoin); - } catch (std::invalid_argument &) { - return false; - } - uint256 hashPubcoin = primitives::GetPubCoinValueHash(pubcoin); - CLelantusMintMeta meta; - if(!zwallet->GetTracker().GetLelantusMetaFromPubcoin(hashPubcoin, meta)){ - return false; - } - return meta.isUsed; - } else if (zwallet && (script.IsSparkMint() || script.IsSparkSMint())) { + } else if (pwalletMain && (script.IsLelantusMint() || script.IsLelantusJMint())) { + return true; + } else if (pwalletMain && pwalletMain->sparkWallet && (script.IsSparkMint() || script.IsSparkSMint())) { std::vector serialContext; for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx *pcoin = &(*it).second; @@ -1455,35 +1432,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) } } - if (wtx.tx->IsLelantusJoinSplit()) { - // find out coin serial number - assert(wtx.tx->vin.size() == 1); - - std::unique_ptr joinsplit; - try { - joinsplit = lelantus::ParseLelantusJoinSplit(*wtx.tx); - } - catch (const std::exception &) { - continue; - } - - const std::vector& serials = joinsplit->getCoinSerialNumbers(); - - for (const auto& serial : serials) { - // mark corresponding mint as unspent - uint256 hashSerial = primitives::GetSerialHash(serial); - CLelantusMintMeta meta; - if(zwallet->GetTracker().GetMetaFromSerial(hashSerial, meta)){ - meta.isUsed = false; - zwallet->GetTracker().UpdateState(meta); - - // erase lelantus spend entry - CLelantusSpendEntry spendEntry; - spendEntry.coinSerial = serial; - walletdb.EraseLelantusSpendSerialEntry(spendEntry); - } - } - } else if (wtx.tx->IsSparkSpend()) { + if (wtx.tx->IsSparkSpend()) { std::vector lTags; try { spark::SpendTransaction spend = spark::ParseSparkSpend(*wtx.tx); @@ -1496,25 +1445,6 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) sparkWallet->AbandonSpends(lTags); } - if (wtx.tx->IsLelantusMint()) { - for (const CTxOut &txout: wtx.tx->vout) { - if (!txout.scriptPubKey.IsLelantusMint() && !txout.scriptPubKey.IsLelantusJMint()) - continue; - - try { - secp_primitives::GroupElement groupElement; - lelantus::ParseLelantusMintScript(txout.scriptPubKey, groupElement); - uint256 hashPubcoin = primitives::GetPubCoinValueHash(groupElement); - CLelantusMintMeta meta; - if (zwallet->GetTracker().GetLelantusMetaFromPubcoin(hashPubcoin, meta)) - zwallet->GetTracker().Archive(meta); - } - catch (std::invalid_argument &) { - continue; - } - } - } - if (wtx.tx->IsSparkTransaction()) { std::vector coins = spark::GetSparkMintCoins(*wtx.tx); sparkWallet->AbandonSparkMints(coins); @@ -1605,21 +1535,8 @@ isminetype CWallet::IsMine(const CTxIn &txin, const CTransaction& tx) const { LOCK(cs_wallet); - if (txin.IsZerocoinSpend() || txin.IsSigmaSpend()) { + if (txin.IsZerocoinSpend() || txin.IsSigmaSpend() || txin.IsLelantusJoinSplit()) { return ISMINE_NO; - } else if (txin.IsLelantusJoinSplit()) { - CWalletDB db(strWalletFile); - std::unique_ptr joinsplit; - try { - joinsplit = lelantus::ParseLelantusJoinSplit(tx); - } - catch (const std::exception &) { - return ISMINE_NO; - } - - if (db.HasLelantusSpendSerialEntry(joinsplit->getCoinSerialNumbers()[0])) { - return ISMINE_SPENDABLE; - } } else if (txin.IsZerocoinRemint()) { return ISMINE_NO; } else if (tx.IsSparkSpend()) { @@ -1654,34 +1571,10 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const CTransaction& tx, const ismin { LOCK(cs_wallet); - if (txin.IsZerocoinSpend() || txin.IsSigmaSpend()) { - // Reverting it to its pre-lelantus state. + if (txin.IsZerocoinSpend() || txin.IsSigmaSpend() || txin.IsLelantusJoinSplit()) { goto end; } else if (txin.IsZerocoinRemint()) { return 0; - } else if (txin.IsLelantusJoinSplit()) { - if (!(filter & ISMINE_SPENDABLE)) { - goto end; - } - - CWalletDB db(strWalletFile); - std::unique_ptr joinsplit; - try { - joinsplit = lelantus::ParseLelantusJoinSplit(tx); - } - catch (const std::exception &) { - goto end; - } - - CAmount amount = 0; - - const std::vector& serials = joinsplit->getCoinSerialNumbers(); - for (const auto& serial : serials) { - CLelantusSpendEntry lelantusSpend; - if(db.ReadLelantusSpendSerialEntry(serial, lelantusSpend)) - amount += lelantusSpend.amount; - } - return amount; } else if (tx.IsSparkSpend()) { if (!(filter & ISMINE_SPENDABLE)) { goto end; @@ -1719,16 +1612,7 @@ isminetype CWallet::IsMine(const CTxOut &txout) const { LOCK(cs_wallet); - if (txout.scriptPubKey.IsLelantusMint() || txout.scriptPubKey.IsLelantusJMint()) { - CWalletDB db(strWalletFile); - secp_primitives::GroupElement pub; - try { - lelantus::ParseLelantusMintScript(txout.scriptPubKey, pub); - } catch (std::invalid_argument &) { - return ISMINE_NO; - } - return db.HasHDMint(pub) ? ISMINE_SPENDABLE : ISMINE_NO; - } else if (txout.scriptPubKey.IsSparkMint() || txout.scriptPubKey.IsSparkSMint()) { + if (txout.scriptPubKey.IsSparkMint() || txout.scriptPubKey.IsSparkSMint()) { std::vector serialContext; for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx *pcoin = &(*it).second; @@ -1767,24 +1651,6 @@ CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) cons { if (!MoneyRange(txout.nValue)) throw std::runtime_error(std::string(__func__) + ": value out of range"); - if (txout.scriptPubKey.IsLelantusJMint()) { - if (!(filter & ISMINE_SPENDABLE)) - return 0; - CWalletDB db(strWalletFile); - secp_primitives::GroupElement pub; - try { - std::vector encryptedValue; - lelantus::ParseLelantusJMintScript(txout.scriptPubKey, pub, encryptedValue); - } catch (std::invalid_argument&) { - return ISMINE_NO; - } - uint256 hashPubcoin = primitives::GetPubCoinValueHash(pub); - CHDMint dMint; - if (db.ReadHDMint(hashPubcoin, true, dMint)) { - return dMint.GetAmount(); - } - return 0; - } if (txout.scriptPubKey.IsSparkSMint()) { if (!(filter & ISMINE_SPENDABLE)) @@ -2006,7 +1872,7 @@ void CWallet::GenerateNewMnemonic() if (!mnContainer.SetMnemonic(secureMnemonic, securePassphrase)) throw std::runtime_error(std::string(__func__) + ": SetMnemonic failed"); - newHdChain.masterKeyID = CKeyID(Hash160(mnContainer.seed.begin(), mnContainer.seed.end())); + newHdChain.masterKeyID = CKeyID(Hash160(mnContainer.seed.begin(), mnContainer.seed.end())); } if (!SetHDChain(newHdChain, false)) @@ -2198,14 +2064,7 @@ void CWalletTx::GetAmounts(std::list& listReceived, CAmount nDebit = GetDebit(filter); if (nDebit > 0) // debit>0 means we signed/sent this transaction { - if (tx->IsLelantusJoinSplit()) { - try { - nFee = lelantus::ParseLelantusJoinSplit(*tx)->getFee(); - } - catch (const std::exception &) { - // do nothing - } - } else if (tx->IsSparkSpend()) { + if (tx->IsSparkSpend()) { try { nFee = spark::ParseSparkSpend(*tx).getFee(); } @@ -2252,7 +2111,7 @@ void CWalletTx::GetAmounts(std::list& listReceived, } CAmount nValue; - if(txout.scriptPubKey.IsLelantusJMint() || txout.scriptPubKey.IsSparkSMint()) { + if(txout.scriptPubKey.IsSparkSMint()) { LOCK(pwalletMain->cs_wallet); nValue = pwallet->GetCredit(txout, ISMINE_SPENDABLE); } else { @@ -2824,100 +2683,6 @@ CAmount CWallet::GetBalance(bool fExcludeLocked) const return nTotal; } -std::pair CWallet::GetPrivateBalance() -{ - if (cachedLelantusBalance.first >= 0) - return {cachedLelantusBalance.first, cachedLelantusBalance.second}; - - std::pair balance = {0, 0}; - - auto zwallet = pwalletMain->zwallet.get(); - - if(!zwallet) - return balance; - - auto lelantusCoins = zwallet->GetTracker().ListLelantusMints(true, false, false); - for (auto const &c : lelantusCoins) { - - if (c.isUsed || c.isArchived || !c.isSeedCorrect) { - continue; - } - - auto conf = c.nHeight > 0 - ? chainActive.Height() - c.nHeight + 1 : 0; - - if (conf >= ZC_MINT_CONFIRMATIONS) { - balance.first += c.amount; - } else { - balance.second += c.amount; - } - } - cachedLelantusBalance.first = balance.first; - cachedLelantusBalance.second = balance.second; - - return balance; -} - -CRecipient CWallet::CreateLelantusMintRecipient( - lelantus::PrivateCoin& coin, - CHDMint& vDMint, - bool generate) -{ - EnsureMintWalletAvailable(); - - while (true) { - CWalletDB walletdb(pwalletMain->strWalletFile); - uint160 seedID; - if (generate) { - // Generate and store secrets deterministically in the following function. - pwalletMain->zwallet->GenerateLelantusMint(walletdb, coin, vDMint, seedID); - } - - // Get a copy of the 'public' portion of the coin. You should - // embed this into a Lelantus 'MINT' transaction along with a series of currency inputs - auto &pubCoin = coin.getPublicCoin(); - - if (!pubCoin.validate()) { - throw std::runtime_error("Unable to mint a lelantus coin."); - } - - // Create script for coin - CScript script; - // opcode is inserted as 1 byte according to file script/script.h - script << OP_LELANTUSMINT; - - // and this one will write the size in different byte lengths depending on the length of vector. If vector size is <0.4c, which is 76, will write the size of vector in just 1 byte. In our case the size is always 34, so must write that 34 in 1 byte. - std::vector vch = pubCoin.getValue().getvch(); - script.insert(script.end(), vch.begin(), vch.end()); //this uses 34 byte - - // generating schnorr proof - CDataStream serializedSchnorrProof(SER_NETWORK, PROTOCOL_VERSION); - lelantus::GenerateMintSchnorrProof(coin, serializedSchnorrProof); - script.insert(script.end(), serializedSchnorrProof.begin(), serializedSchnorrProof.end()); //this uses 98 byte - - auto pubcoin = vDMint.GetPubcoinValue() + - lelantus::Params::get_default()->get_h1() * Scalar(vDMint.GetAmount()).negate(); - uint256 hashPub = primitives::GetPubCoinValueHash(pubcoin); - CDataStream ss(SER_GETHASH, 0); - ss << hashPub; - ss << seedID; - uint256 hashForRecover = Hash(ss.begin(), ss.end()); - - // Check if there is a mint with same private data in chain, most likely Hd mint state corruption, - // If yes, try with new counter - GroupElement dummyValue; - if (lelantus::CLelantusState::GetState()->HasCoinTag(dummyValue, hashForRecover)) - continue; - - CDataStream serializedHash(SER_NETWORK, 0); - serializedHash << hashForRecover; - script.insert(script.end(), serializedHash.begin(), serializedHash.end()); - - // overall Lelantus mint script size is 1 + 34 + 98 + 32 = 165 byte - return {script, CAmount(coin.getV()), false, {}, {}}; - } -} - std::list CWallet::GetAvailableSparkCoins(const CCoinControl *coinControl) const { EnsureSparkWalletAvailable(); @@ -2968,84 +2733,6 @@ CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth, cons return balance; } -std::list CWallet::GetAvailableLelantusCoins(const CCoinControl *coinControl, bool includeUnsafe, bool forEstimation) const { - EnsureMintWalletAvailable(); - - LOCK2(cs_main, cs_wallet); - CWalletDB walletdb(strWalletFile); - std::list coins; - std::vector vecMints = zwallet->GetTracker().ListLelantusMints(true, true, false); - for (const CLelantusMintMeta& mint : vecMints) { - CLelantusEntry entry; - GetMint(mint.hashSerial, entry, forEstimation); - if(entry.amount != 0) // ignore 0 mints which where created to increase privacy - coins.push_back(entry); - } - - std::set lockedCoins = setLockedCoins; - - // Filter out coins which are not confirmed, I.E. do not have at least 2 blocks - // above them, after they were minted. - // Also filter out used coins. - // Finally filter out coins that have not been selected from CoinControl should that be used - coins.remove_if([lockedCoins, coinControl, includeUnsafe](const CLelantusEntry& coin) { - lelantus::CLelantusState* state = lelantus::CLelantusState::GetState(); - if (coin.IsUsed) - return true; - - COutPoint outPoint; - lelantus::PublicCoin pubCoin(coin.value); - lelantus::GetOutPoint(outPoint, pubCoin); - - if(lockedCoins.count(outPoint) > 0){ - return true; - } - - if(coinControl != NULL){ - if(coinControl->HasSelected()){ - if(!coinControl->IsSelected(outPoint)){ - return true; - } - } - } - - int coinHeight, coinId; - std::tie(coinHeight, coinId) = state->GetMintedCoinHeightAndId(lelantus::PublicCoin(coin.value)); - - // Check group size - uint256 hashOut; - std::vector coinOuts; - std::vector setHash; - state->GetCoinSetForSpend( - &chainActive, - chainActive.Height() - (ZC_MINT_CONFIRMATIONS - 1), // required 1 confirmation for mint to spend - coinId, - hashOut, - coinOuts, - setHash - ); - - if (!includeUnsafe && coinOuts.size() < 2) { - return true; - } - - if (coinHeight == -1) { - // Coin still in the mempool. - return true; - } - - if (coinHeight + (ZC_MINT_CONFIRMATIONS - 1) > chainActive.Height()) { - // Remove the coin from the candidates list, since it does not have the - // required number of confirmations. - return true; - } - - return false; - }); - - return coins; -} - std::vector GetAESKey(const secp_primitives::GroupElement& pubcoin) { uint32_t keyPath = primitives::GetPubCoinValueHash(pubcoin).GetFirstUint32(); CKey secret; @@ -3095,154 +2782,6 @@ static CAmount CalculateCoinsBalance(Iterator begin, Iterator end) { return balance; } -template -static CAmount CalculateLelantusCoinsBalance(Iterator begin, Iterator end) { - CAmount balance(0); - for (auto start = begin; start != end; start++) { - balance += start->amount; - } - return balance; -} - -bool CWallet::GetCoinsToJoinSplit( - CAmount required, - std::vector& coinsToSpend_out, - CAmount& changeToMint, - std::list coins, - const size_t coinsToSpendLimit, - const CAmount amountToSpendLimit, - const CCoinControl *coinControl) const -{ - - EnsureMintWalletAvailable(); - const Consensus::Params &consensusParams = Params().GetConsensus(); - - if (required > consensusParams.nMaxValueLelantusSpendPerTransaction) { - throw std::invalid_argument(_("The required amount exceeds spend limit")); - } - - CAmount availableBalance = CalculateLelantusCoinsBalance(coins.begin(), coins.end()); - - if (required > availableBalance) { - throw InsufficientFunds(); - } - - // sort by biggest amount. if it is same amount we will prefer the older block - auto comparer = [](const CLelantusEntry& a, const CLelantusEntry& b) -> bool { - return a.amount != b.amount ? a.amount > b.amount : a.nHeight < b.nHeight; - }; - coins.sort(comparer); - - CAmount spend_val(0); - - std::list coinsToSpend; - - // If coinControl, want to use all inputs - bool coinControlUsed = false; - if(coinControl != NULL) { - if(coinControl->HasSelected()) { - auto coinIt = coins.rbegin(); - for (; coinIt != coins.rend(); coinIt++) { - spend_val += coinIt->amount; - } - coinControlUsed = true; - coinsToSpend.insert(coinsToSpend.begin(), coins.begin(), coins.end()); - } - } - - if(!coinControlUsed) { - while (spend_val < required) { - if(coins.empty()) - break; - - CLelantusEntry choosen; - CAmount need = required - spend_val; - - auto itr = coins.begin(); - if(need >= itr->amount) { - choosen = *itr; - coins.erase(itr); - } else { - for (auto coinIt = coins.rbegin(); coinIt != coins.rend(); coinIt++) { - auto nextItr = coinIt; - nextItr++; - - if (coinIt->amount >= need && (nextItr == coins.rend() || nextItr->amount != coinIt->amount)) { - choosen = *coinIt; - coins.erase(std::next(coinIt).base()); - break; - } - } - } - - spend_val += choosen.amount; - coinsToSpend.push_back(choosen); - } - } - - // sort by group id ay ascending order. it is mandatory for creting proper joinsplit - auto idComparer = [](const CLelantusEntry& a, const CLelantusEntry& b) -> bool { - return a.id < b.id; - }; - coinsToSpend.sort(idComparer); - - changeToMint = spend_val - required; - coinsToSpend_out.insert(coinsToSpend_out.begin(), coinsToSpend.begin(), coinsToSpend.end()); - - return true; -} - -std::vector CWallet::ProvePrivateTxOwn(const uint256& txid, const std::string& message) const { - std::vector result; - if (!mapWallet.count(txid)) - return result; - - const CWalletTx& wtx = mapWallet.at(txid); - - if (wtx.tx->IsLelantusJoinSplit()) { - CHashWriter ss(SER_GETHASH, 0); - ss << strLelantusMessageMagic; - ss << message; - - std::unique_ptr joinsplit; - try { - joinsplit = lelantus::ParseLelantusJoinSplit(*wtx.tx); - } catch (const std::exception&) { - return result; - } - const auto& serials = joinsplit->getCoinSerialNumbers(); - uint32_t count = 0; - result.resize(serials.size() * 64); - - for (const auto& serial : serials) { - CLelantusEntry mint; - uint256 hashSerial = primitives::GetSerialHash(serial); - std::vector ecdsaSecretKey; - ecdsaSecretKey = mint.ecdsaSecretKey; - - ss << count; - uint256 metahash = ss.GetHash(); - secp256k1_ecdsa_signature sig; - if (1 != secp256k1_ecdsa_sign( - OpenSSLContext::get_context(), &sig, - metahash.begin(), &ecdsaSecretKey[0], NULL, NULL)) { - return std::vector(); - } - if (1 != secp256k1_ecdsa_signature_serialize_compact( - OpenSSLContext::get_context(), &result[count * 64], &sig)) { - return std::vector(); - } - - count++; - } - } else if (wtx.tx->IsCoinBase()) { - throw std::runtime_error("This is a coinbase transaction and not a private transaction"); - } else { - throw std::runtime_error("Currently this operation is allowed only for Lelantus transactions"); - } - - return result; -} bool CWallet::TryGetBalances(CAmount& balance, CAmount& unconfirmedBalance, @@ -3430,7 +2969,7 @@ void CWallet::AvailableCoins(std::vector &vCoins, bool fOnlyConfirmed, if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && (!IsLockedCoin((*it).first, i) || nCoinType == CoinType::ONLY_1000) && - (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue || ((pcoin->tx->vout[i].scriptPubKey.IsLelantusJMint() || pcoin->tx->vout[i].scriptPubKey.IsSparkSMint()) && GetCredit(pcoin->tx->vout[i], ISMINE_SPENDABLE) > 0)) && + (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue || ((pcoin->tx->vout[i].scriptPubKey.IsSparkSMint()) && GetCredit(pcoin->tx->vout[i], ISMINE_SPENDABLE) > 0)) && (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))) { vCoins.push_back(COutput(pcoin, i, nDepth, ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || @@ -3533,63 +3072,6 @@ bool CWallet::GetVinAndKeysFromOutput(COutput out, CTxIn &txinRet, CPubKey &pubK return true; } -void CWallet::ListAvailableLelantusMintCoins(std::vector &vCoins, bool fOnlyConfirmed) const { - EnsureMintWalletAvailable(); - - vCoins.clear(); - LOCK2(cs_main, cs_wallet); - std::list listOwnCoins; - CWalletDB walletdb(pwalletMain->strWalletFile); - listOwnCoins = zwallet->GetTracker().MintsAsLelantusEntries(true, false); - LogPrintf("listOwnCoins.size()=%zu\n", listOwnCoins.size()); - for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { - const CWalletTx *pcoin = &(*it).second; -// LogPrintf("pcoin=%s\n", pcoin->GetHash().ToString()); - if (!CheckFinalTx(*pcoin)) { - LogPrintf("!CheckFinalTx(*pcoin)=%d\n", !CheckFinalTx(*pcoin)); - continue; - } - - if (fOnlyConfirmed && !pcoin->IsTrusted()) { - LogPrintf("fOnlyConfirmed = %d, !pcoin->IsTrusted() = %d\n", fOnlyConfirmed, !pcoin->IsTrusted()); - continue; - } - - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) { - LogPrintf("Not trusted\n"); - continue; - } - - int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < 0) { - LogPrintf("nDepth=%d\n", nDepth); - continue; - } - LogPrintf("pcoin->tx->vout.size()=%zu\n", pcoin->tx->vout.size()); - - for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { - if (pcoin->tx->vout[i].scriptPubKey.IsLelantusMint() || pcoin->tx->vout[i].scriptPubKey.IsLelantusJMint()) { - CTxOut txout = pcoin->tx->vout[i]; - secp_primitives::GroupElement pubCoin; - try { - lelantus::ParseLelantusMintScript(txout.scriptPubKey, pubCoin); - } catch (std::invalid_argument &) { - continue; - } - LogPrintf("Pubcoin=%s\n", pubCoin.tostring()); - // CHECKING PROCESS - BOOST_FOREACH(const CLelantusEntry& ownCoinItem, listOwnCoins) { - if (ownCoinItem.value == pubCoin && ownCoinItem.IsUsed == false && - !ownCoinItem.randomness.isZero() && !ownCoinItem.serialNumber.isZero()) { - vCoins.push_back(COutput(pcoin, i, nDepth, true, true)); - LogPrintf("-->OK\n"); - } - } - } - } - } -} - static void ApproximateBestSubset(std::vector > >vValue, const CAmount& nTotalLower, const CAmount& nTargetValue, std::vector& vfBest, CAmount& nBest, int iterations = 1000) { @@ -3742,6 +3224,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin return true; } + bool CWallet::SelectCoins(const std::vector& vAvailableCoins, const CAmount& nTargetValue, std::set >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl, bool fForUseInInstantSend) const { std::vector vCoins(vAvailableCoins); @@ -4331,498 +3814,36 @@ bool CWallet::EraseFromWallet(uint256 hash) { * @param sign Whether to sign the transaction immediately * @return true if transaction creation succeeded, false otherwise */ -bool CWallet::CreateLelantusMintTransactions( - CAmount valueToMint, - std::vector>& wtxAndFee, - CAmount& nAllFeeRet, - std::vector& dMints, - std::list& reservekeys, - int& nChangePosInOut, - std::string& strFailReason, - const CCoinControl *coinControl, - bool autoMintAll, - bool sign) -{ - const auto& lelantusParams = lelantus::Params::get_default(); - int nChangePosRequest = nChangePosInOut; +std::string CWallet::MintAndStoreSpark( + const std::vector& outputs, + std::vector>& wtxAndFee, + bool subtractFeeFromAmount, + bool fSplit, + bool autoMintAll, + bool fAskFee, + const CCoinControl *coinControl) { + std::string strError; - // Create transaction template - CWalletTx wtxNew; - wtxNew.fTimeReceivedIsTxTime = true; - wtxNew.BindWallet(this); + EnsureSparkWalletAvailable(); - CMutableTransaction txNew; - int nHeight = 0; - { - LOCK(cs_main); - nHeight = chainActive.Height(); + if (IsLocked()) { + strError = _("Error: Wallet locked, unable to create transaction!"); + LogPrintf("MintSpark() : %s", strError); + return strError; } - txNew.nLockTime = nHeight; - assert(txNew.nLockTime <= static_cast(nHeight)); - assert(txNew.nLockTime < LOCKTIME_THRESHOLD); - - { - LOCK2(cs_main, cs_wallet); - { - std::list cacheWtxs; - std::vector>> valueAndUTXO; - AvailableCoinsForLMint(valueAndUTXO, coinControl); + uint64_t value = 0; + for (auto& output : outputs) + value += output.v; - Shuffle(valueAndUTXO.begin(), valueAndUTXO.end(), FastRandomContext()); - { - CWalletDB walletdb(pwalletMain->strWalletFile); - pwalletMain->zwallet->ResetCount(walletdb); - } - while (!valueAndUTXO.empty()) { + if (cmp::greater((value + payTxFee.GetFeePerK()), GetBalance())) + return _("Insufficient funds"); - // initialize - CWalletTx wtx = wtxNew; - CMutableTransaction tx = txNew; + LogPrintf("payTxFee.GetFeePerK()=%d\n", payTxFee.GetFeePerK()); + int64_t nFeeRequired = 0; - reservekeys.emplace_back(this); - auto &reservekey = reservekeys.back(); - - if (GetRandInt(10) == 0) - tx.nLockTime = std::max(0, (int) tx.nLockTime - GetRandInt(100)); - - CHDMint dMint; - - CAmount nFeeRet = 0; - LogPrintf("nFeeRet=%d\n", nFeeRet); - - auto itr = valueAndUTXO.begin(); - - CAmount valueToMintInTx = std::min( - ::Params().GetConsensus().nMaxValueLelantusMint, - itr->first); - - if (!autoMintAll) { - valueToMintInTx = std::min(valueToMintInTx, valueToMint); - } - - CAmount nValueToSelect, mintedValue; - - std::set> setCoins; - bool skipCoin = false; - // Start with no fee and loop until there is enough fee - while (true) { - mintedValue = valueToMintInTx; - nValueToSelect = mintedValue + nFeeRet; - - // if have no enough coins in this group then subtract fee from mint - if (nValueToSelect > itr->first) { - mintedValue -= nFeeRet; - nValueToSelect = mintedValue + nFeeRet; - } - - if (!MoneyRange(mintedValue) || mintedValue == 0) { - valueAndUTXO.erase(itr); - skipCoin = true; - break; - } - - nChangePosInOut = nChangePosRequest; - tx.vin.clear(); - tx.vout.clear(); - - wtx.fFromMe = true; - wtx.changes.clear(); - - setCoins.clear(); - - // create recipient using random private coin to mock script sig - lelantus::PrivateCoin privCoin(lelantusParams, mintedValue); - auto recipient = CWallet::CreateLelantusMintRecipient(privCoin, dMint, false); - - double dPriority = 0; - - // vout to create mint - CTxOut txout(recipient.nAmount, recipient.scriptPubKey); - - if (txout.IsDust(::minRelayTxFee)) { - strFailReason = _("Transaction amount too small"); - return false; - } - - tx.vout.push_back(txout); - - // Choose coins to use - - CAmount nValueIn = 0; - if (!SelectCoins(itr->second, nValueToSelect, setCoins, nValueIn, coinControl)) { - - if (nValueIn < nValueToSelect) { - strFailReason = _("Insufficient funds"); - } - return false; - } - - for (auto const &pcoin : setCoins) { - CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; - //The coin age after the next block (depth+1) is used instead of the current, - //reflecting an assumption the user would accept a bit more delay for - //a chance at a free transaction. - //But mempool inputs might still be in the mempool, so their age stays 0 - int age = pcoin.first->GetDepthInMainChain(); - assert(age >= 0); - if (age != 0) - age += 1; - dPriority += (double) nCredit * age; - } - - CAmount nChange = nValueIn - nValueToSelect; - - if (nChange > 0) { - // Fill a vout to ourself - // TODO: pass in scriptChange instead of reservekey so - // change transaction isn't always pay-to-bitcoin-address - CScript scriptChange; - - // coin control: send change to custom address - if (coinControl && !boost::get(&coinControl->destChange)) - scriptChange = GetScriptForDestination(coinControl->destChange); - - // send change to one of the specified change addresses - else if (IsArgSet("-change") && mapMultiArgs.at("-change").size() > 0) { - CBitcoinAddress address( - mapMultiArgs.at("-change")[GetRandInt(mapMultiArgs.at("-change").size())]); - CKeyID keyID; - if (!address.GetKeyID(keyID)) { - strFailReason = _("Bad change address"); - return false; - } - scriptChange = GetScriptForDestination(keyID); - } - - // no coin control: send change to newly generated address - else { - // Note: We use a new key here to keep it from being obvious which side is the change. - // The drawback is that by not reusing a previous key, the change may be lost if a - // backup is restored, if the backup doesn't have the new private key for the change. - // If we reused the old key, it would be possible to add code to look for and - // rediscover unknown transactions that were written with keys of ours to recover - // post-backup change. - - // Reserve a new key pair from key pool - CPubKey vchPubKey; - bool ret; - ret = reservekey.GetReservedKey(vchPubKey); - if (!ret) { - strFailReason = _("Keypool ran out, please call keypoolrefill first"); - return false; - } - - scriptChange = GetScriptForDestination(vchPubKey.GetID()); - } - - CTxOut newTxOut(nChange, scriptChange); - - // Never create dust outputs; if we would, just - // add the dust to the fee. - if (newTxOut.IsDust(::minRelayTxFee)) { - nChangePosInOut = -1; - nFeeRet += nChange; - reservekey.ReturnKey(); - } else { - - if (nChangePosInOut == -1) { - - // Insert change txn at random position: - nChangePosInOut = GetRandInt(tx.vout.size() + 1); - } else if ((unsigned int) nChangePosInOut > tx.vout.size()) { - - strFailReason = _("Change index out of range"); - return false; - } - - std::vector::iterator position = tx.vout.begin() + nChangePosInOut; - tx.vout.insert(position, newTxOut); - wtx.changes.insert(static_cast(nChangePosInOut)); - } - } else { - reservekey.ReturnKey(); - } - - // Fill vin - // - // Note how the sequence number is set to max()-1 so that the - // nLockTime set above actually works. - for (const auto &coin : setCoins) { - tx.vin.push_back(CTxIn( - coin.first->GetHash(), - coin.second, - CScript(), - std::numeric_limits::max() - 1)); - } - - // Fill in dummy signatures for fee calculation. - if (!DummySignTx(tx, setCoins)) { - strFailReason = _("Signing transaction failed"); - return false; - } - - unsigned int nBytes = GetVirtualTransactionSize(tx); - - // Limit size - CTransaction txConst(tx); - if (GetTransactionWeight(txConst) >= MAX_STANDARD_TX_WEIGHT) { - strFailReason = _("Transaction is too large (size limit: 100Kb). Select less inputs or consolidate your UTXOs"); - return false; - } - dPriority = txConst.ComputePriority(dPriority, nBytes); - - // Remove scriptSigs to eliminate the fee calculation dummy signatures - for (auto &vin : tx.vin) { - vin.scriptSig = CScript(); - vin.scriptWitness.SetNull(); - } - - // Can we complete this as a free transaction? - if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) { - // Not enough fee: enough priority? - double dPriorityNeeded = mempool.estimateSmartPriority(nTxConfirmTarget); - // Require at least hard-coded AllowFree. - if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) - break; - } - CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool); - - if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { - nFeeNeeded = coinControl->nMinimumTotalFee; - } - - if (coinControl && coinControl->fOverrideFeeRate) - nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); - - // If we made it here and we aren't even able to meet the relay fee on the next pass, give up - // because we must be at the maximum allowed fee. - if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) { - strFailReason = _("Transaction too large for fee policy"); - return false; - } - - if (cmp::greater_equal(nFeeRet, nFeeNeeded)) { - for (auto &usedCoin : setCoins) { - for (auto coin = itr->second.begin(); coin != itr->second.end(); coin++) { - if (usedCoin.first == coin->tx && cmp::equal(usedCoin.second, coin->i)) { - itr->first -= coin->tx->tx->vout[coin->i].nValue; - itr->second.erase(coin); - break; - } - } - } - - if (itr->second.empty()) { - valueAndUTXO.erase(itr); - } - - // Generate hdMint - recipient = CWallet::CreateLelantusMintRecipient(privCoin, dMint); - - // vout to mint - txout = CTxOut(recipient.nAmount, recipient.scriptPubKey); - LogPrintf("txout: %s\n", txout.ToString()); - - for (size_t i = 0; i != tx.vout.size(); i++) { - if (tx.vout[i].scriptPubKey.IsLelantusMint()) { - tx.vout[i] = txout; - } - } - - break; // Done, enough fee included. - } - - // Include more fee and try again. - nFeeRet = nFeeNeeded; - continue; - } - - if(skipCoin) - continue; - - if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { - // Lastly, ensure this tx will pass the mempool's chain limits - LockPoints lp; - CTxMemPoolEntry entry(MakeTransactionRef(tx), 0, 0, 0, 0, false, 0, lp); - CTxMemPool::setEntries setAncestors; - size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); - size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; - size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); - size_t nLimitDescendantSize = - GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; - std::string errString; - if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, - nLimitDescendants, nLimitDescendantSize, errString)) { - strFailReason = _("Transaction has too long of a mempool chain"); - return false; - } - } - - // Sign - int nIn = 0; - CTransaction txNewConst(tx); - for (const auto &coin : setCoins) { - bool signSuccess = false; - const CScript &scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey; - SignatureData sigdata; - if (sign) - signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, - coin.first->tx->vout[coin.second].nValue, - SIGHASH_ALL), scriptPubKey, - sigdata); - else - signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata); - - if (!signSuccess) { - strFailReason = _("Signing transaction failed"); - return false; - } else { - UpdateTransaction(tx, nIn, sigdata); - } - nIn++; - } - - wtx.SetTx(MakeTransactionRef(std::move(tx))); - - wtxAndFee.push_back(std::make_pair(wtx, nFeeRet)); - - if (nChangePosInOut >= 0) { - // Cache wtx to somewhere because COutput use pointer of it. - cacheWtxs.push_back(wtx); - auto &wtx = cacheWtxs.back(); - - COutput out(&wtx, nChangePosInOut, wtx.GetDepthInMainChain(false), true, true); - auto val = wtx.tx->vout[nChangePosInOut].nValue; - - bool added = false; - for (auto &utxos : valueAndUTXO) { - auto const &o = utxos.second.front(); - if (o.tx->tx->vout[o.i].scriptPubKey == wtx.tx->vout[nChangePosInOut].scriptPubKey) { - utxos.first += val; - utxos.second.push_back(out); - - added = true; - } - } - - if (!added) { - valueAndUTXO.push_back({val, {out}}); - } - } - - nAllFeeRet += nFeeRet; - dMints.push_back(dMint); - if(!autoMintAll) { - valueToMint -= mintedValue; - if (valueToMint == 0) - break; - } - } - } - } - - if (!autoMintAll && valueToMint > 0) { - return false; - } - - return true; -} - -std::string CWallet::MintAndStoreLelantus(const CAmount& value, - std::vector>& wtxAndFee, - std::vector& mints, - bool autoMintAll, - bool fAskFee, - const CCoinControl *coinControl) { - std::string strError; - - EnsureMintWalletAvailable(); - - if (IsLocked()) { - strError = _("Error: Wallet locked, unable to create transaction!"); - LogPrintf("MintLelantus() : %s", strError); - return strError; - } - - - if ((value + payTxFee.GetFeePerK()) > GetBalance()) - return _("Insufficient funds"); - - LogPrintf("payTxFee.GetFeePerK()=%d\n", payTxFee.GetFeePerK()); - int64_t nFeeRequired = 0; - - int nChangePosRet = -1; - - std::vector dMints; - std::list reservekeys; - if (!CreateLelantusMintTransactions(value, wtxAndFee, nFeeRequired, dMints, reservekeys, nChangePosRet, strError, coinControl, autoMintAll)) { - return strError; - } - - if (fAskFee && !uiInterface.ThreadSafeAskFee(nFeeRequired)){ - LogPrintf("MintLelantus: returning aborted..\n"); - return "ABORTED"; - } - - CValidationState state; - CWalletDB walletdb(pwalletMain->strWalletFile); - - auto reservekey = reservekeys.begin(); - for(size_t i = 0; i < wtxAndFee.size(); i++) { - if (!CommitTransaction(wtxAndFee[i].first, *reservekey++, g_connman.get(), state)) { - return _( - "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); - } else { - LogPrintf("CommitTransaction success!\n"); - } - - //update mints with full transaction hash and then database them - CHDMint dMintTmp = dMints[i]; - mints.push_back(dMints[i]); - dMintTmp.SetTxHash(wtxAndFee[i].first.GetHash()); - zwallet->GetTracker().AddLelantus(walletdb, dMintTmp, true); - NotifyZerocoinChanged(this, - dMintTmp.GetPubcoinValue().GetHex(), - "New (" + std::to_string(dMintTmp.GetAmount()) + " mint)", - CT_NEW); - } - // Update nCountNextUse in HDMint wallet database - zwallet->UpdateCountDB(walletdb); - - return ""; -} - -std::string CWallet::MintAndStoreSpark( - const std::vector& outputs, - std::vector>& wtxAndFee, - bool subtractFeeFromAmount, - bool fSplit, - bool autoMintAll, - bool fAskFee, - const CCoinControl *coinControl) { - std::string strError; - - EnsureSparkWalletAvailable(); - - if (IsLocked()) { - strError = _("Error: Wallet locked, unable to create transaction!"); - LogPrintf("MintSpark() : %s", strError); - return strError; - } - - uint64_t value = 0; - for (auto& output : outputs) - value += output.v; - - if (cmp::greater((value + payTxFee.GetFeePerK()), GetBalance())) - return _("Insufficient funds"); - - LogPrintf("payTxFee.GetFeePerK()=%d\n", payTxFee.GetFeePerK()); - int64_t nFeeRequired = 0; - - int nChangePosRet = -1; + int nChangePosRet = -1; std::list reservekeys; if (!sparkWallet->CreateSparkMintTransactions(outputs, wtxAndFee, nFeeRequired, reservekeys, nChangePosRet, subtractFeeFromAmount, strError, fSplit, coinControl, autoMintAll)) { @@ -4848,44 +3869,6 @@ std::string CWallet::MintAndStoreSpark( return ""; } -std::vector CWallet::JoinSplitLelantus(const std::vector& recipients, const std::vector& newMints, CWalletTx& result, const CCoinControl *coinControl) { - // create transaction - std::vector spendCoins; //spends - std::vector mintCoins; // new mints - CAmount fee; - result = CreateLelantusJoinSplitTransaction(recipients, fee, newMints, spendCoins, mintCoins, coinControl); - - CommitLelantusTransaction(result, spendCoins, mintCoins); - - return spendCoins; -} - -CWalletTx CWallet::CreateLelantusJoinSplitTransaction( - const std::vector& recipients, - CAmount &fee, - const std::vector& newMints, - std::vector& spendCoins, - std::vector& mintCoins, - const CCoinControl *coinControl, - std::function modifier) -{ - // sanity check - EnsureMintWalletAvailable(); - - if (IsLocked()) { - throw std::runtime_error(_("Wallet locked")); - } - - // create transaction - LelantusJoinSplitBuilder builder(*this, *zwallet, coinControl); - - CWalletTx tx = builder.Build(recipients, fee, newMints, modifier); - spendCoins = builder.spendCoins; - mintCoins = builder.mintCoins; - - return tx; -} - CWalletTx CWallet::CreateSparkSpendTransaction( const std::vector& recipients, const std::vector>& privateRecipients, @@ -4893,7 +3876,7 @@ CWalletTx CWallet::CreateSparkSpendTransaction( const CCoinControl *coinControl) { // sanity check - EnsureMintWalletAvailable(); + EnsureSparkWalletAvailable(); if (IsLocked()) { throw std::runtime_error(_("Wallet locked")); @@ -4909,7 +3892,7 @@ CWalletTx CWallet::CreateSparkNameTransaction( const CCoinControl *coinControl) { // sanity check - EnsureMintWalletAvailable(); + EnsureSparkWalletAvailable(); if (IsLocked()) { throw std::runtime_error(_("Wallet locked")); @@ -4946,223 +3929,6 @@ CWalletTx CWallet::SpendAndStoreSpark( return result; } -bool CWallet::LelantusToSpark(std::string& strFailReason) { - std::list coins = GetAvailableLelantusCoins(); - CScript scriptChange; - { - // Reserve a new key pair from key pool - CPubKey vchPubKey; - bool ret; - ret = CReserveKey(this).GetReservedKey(vchPubKey); - if (!ret) - { - strFailReason = _("Keypool ran out, please call keypoolrefill first"); - return false; - } - - scriptChange = GetScriptForDestination(vchPubKey.GetID()); - } - - while (coins.size() > 0) { - std::size_t selectedNum = 0; - CCoinControl coinControl; - CAmount spendValue = 0; - while (true) { - COutPoint outPoint; - if (coins.size() > 0) { - auto coin = coins.begin(); - lelantus::GetOutPoint(outPoint, coin->value); - coinControl.Select(outPoint); - spendValue += coin->amount; - selectedNum++; - coins.erase(coin); - } else - break; - - if ((spendValue + coins.begin()->amount) > Params().GetConsensus().nMaxValueLelantusSpendPerTransaction) - break; - - if (selectedNum == Params().GetConsensus().nMaxLelantusInputPerTransaction) - break; - } - CRecipient recipient = {scriptChange, spendValue, true, {}, {}}; - - CWalletTx result; - JoinSplitLelantus({recipient}, {}, result, &coinControl); - coinControl.UnSelectAll(); - - uint32_t i = 0; - for (; i < result.tx->vout.size(); ++i) { - if (result.tx->vout[i].scriptPubKey == recipient.scriptPubKey) - break; - } - - COutPoint outPoint(result.GetHash(), i); - coinControl.Select(outPoint); - std::vector> wtxAndFee; - MintAndStoreSpark({}, wtxAndFee, true, true, false, false, &coinControl); - } - - return true; -} - - -std::pair CWallet::EstimateJoinSplitFee( - CAmount required, - bool subtractFeeFromAmount, - std::list coins, - const CCoinControl *coinControl) { - CAmount fee; - unsigned size; - std::vector spendCoins; - - for (fee = payTxFee.GetFeePerK();;) { - CAmount currentRequired = required; - - if (!subtractFeeFromAmount) - currentRequired += fee; - - spendCoins.clear(); - const auto &consensusParams = Params().GetConsensus(); - CAmount changeToMint = 0; - - try { - if (currentRequired > 0) { - if (!this->GetCoinsToJoinSplit(currentRequired, spendCoins, changeToMint, coins, - consensusParams.nMaxLelantusInputPerTransaction, - consensusParams.nMaxValueLelantusSpendPerTransaction, coinControl)) { - return std::make_pair(0, 0); - } - } - } catch (std::runtime_error const &) { - } - - // 1054 is constant part, mainly Schnorr and Range proofs, 2560 is for each sigma/aux data - // 179 other parts of tx, assuming 1 utxo and 1 jmint - size = 1054 + 2560 * (spendCoins.size()) + 179; - CAmount feeNeeded = CWallet::GetMinimumFee(size, nTxConfirmTarget, mempool); - - if (fee >= feeNeeded) { - break; - } - - fee = feeNeeded; - - if(subtractFeeFromAmount) - break; - } - - return std::make_pair(fee, size); -} - -bool CWallet::CommitLelantusTransaction(CWalletTx& wtxNew, std::vector& spendCoins, std::vector& mintCoins) { - EnsureMintWalletAvailable(); - - // commit - try { - CValidationState state; - CReserveKey reserveKey(this); - CommitTransaction(wtxNew, reserveKey, g_connman.get(), state); - } catch (const std::exception &) { - auto error = _( - "Error: The transaction was rejected! This might happen if some of " - "the coins in your wallet were already spent, such as if you used " - "a copy of wallet.dat and coins were spent in the copy but not " - "marked as spent here." - ); - - std::throw_with_nested(std::runtime_error(error)); - } - - // mark selected coins as used - lelantus::CLelantusState* lelantusState = lelantus::CLelantusState::GetState(); - CWalletDB db(strWalletFile); - - for (auto& coin : spendCoins) { - // get coin id & height - int height, id; - - std::tie(height, id) = lelantusState->GetMintedCoinHeightAndId(lelantus::PublicCoin(coin.value)); - - // add CLelantusSpendEntry - CLelantusSpendEntry spend; - - spend.coinSerial = coin.serialNumber; - spend.hashTx = wtxNew.GetHash(); - spend.pubCoin = coin.value; - spend.id = id; - spend.amount = coin.amount; - - if (!db.WriteLelantusSpendSerialEntry(spend)) { - throw std::runtime_error(_("Failed to write coin serial number into wallet")); - } - - //Set spent mint as used in memory - uint256 hashPubcoin = primitives::GetPubCoinValueHash(coin.value); - zwallet->GetTracker().SetLelantusPubcoinUsed(hashPubcoin, wtxNew.GetHash()); - CLelantusMintMeta metaCheck; - zwallet->GetTracker().GetLelantusMetaFromPubcoin(hashPubcoin, metaCheck); - if (!metaCheck.isUsed) { - std::string strError = "Error, mint with pubcoin hash " + hashPubcoin.GetHex() + " did not get marked as used"; - LogPrintf("SpendLelantus() : %s\n", strError.c_str()); - } - - //Set spent mint as used in DB - zwallet->GetTracker().UpdateState(metaCheck); - - // update CLelantusEntry - coin.IsUsed = true; - coin.id = id; - coin.nHeight = height; - - // raise event - NotifyZerocoinChanged( - this, - coin.value.GetHex(), - "Used (" + std::to_string(coin.amount) + " mint)", - CT_UPDATED); - } - - for (auto& coin : mintCoins) { - coin.SetTxHash(wtxNew.GetHash()); - zwallet->GetTracker().AddLelantus(db, coin, true); - - // raise event - NotifyZerocoinChanged(this, - coin.GetPubcoinValue().GetHex(), - "New (" + std::to_string(coin.GetAmount()) + " mint)", - CT_NEW); - } - - // Update nCountNextUse in HDMint wallet database - zwallet->UpdateCountDB(db); - - return true; -} - -bool CWallet::GetMint(const uint256& hashSerial, CLelantusEntry& mint, bool forEstimation) const -{ - EnsureMintWalletAvailable(); - - if (IsLocked() && !forEstimation) { - return false; - } - - CLelantusMintMeta meta; - if(!zwallet->GetTracker().GetMetaFromSerial(hashSerial, meta)) - return error("%s: serialhash %s is not in tracker", __func__, hashSerial.GetHex()); - - CWalletDB walletdb(strWalletFile); - - CHDMint dMint; - if (!walletdb.ReadHDMint(meta.GetPubCoinValueHash(), true, dMint)) - return error("%s: failed to read deterministic Lelantus mint", __func__); - if (!zwallet->RegenerateMint(walletdb, dMint, mint, forEstimation)) - return error("%s: failed to generate Lelantus mint", __func__); - return true; - -} - void CWallet::ListAccountCreditDebit(const std::string& strAccount, std::list& entries) { CWalletDB walletdb(strWalletFile); return walletdb.ListAccountCreditDebit(strAccount, entries); @@ -5332,18 +4098,6 @@ DBErrors CWallet::ZapWalletTx(std::vector& vWtx) return DB_LOAD_OK; } -DBErrors CWallet::ZapLelantusMints() { - if (!fFileBacked) - return DB_LOAD_OK; - DBErrors nZapLelantusMintRet = CWalletDB(strWalletFile, "cr+").ZapLelantusMints(this); - if (nZapLelantusMintRet != DB_LOAD_OK){ - LogPrintf("Failed to remove Lelantus mints from CWalletDB"); - return nZapLelantusMintRet; - } - - return DB_LOAD_OK; -} - DBErrors CWallet::ZapSparkMints() { if (!fFileBacked) return DB_LOAD_OK; @@ -6039,7 +4793,6 @@ std::string CWallet::GetWalletHelpString(bool showDebug) std::string strUsage = HelpMessageGroup(_("Wallet options:")); strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); strUsage += HelpMessageOpt("-keypool=", strprintf(_("Set key pool size to (default: %u)"), DEFAULT_KEYPOOL_SIZE)); - strUsage += HelpMessageOpt("-mintpoolsize=", strprintf(_("Set mint pool size to (default: %u)"), DEFAULT_MINTPOOL_SIZE)); strUsage += HelpMessageOpt("-fallbackfee=", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE))); strUsage += HelpMessageOpt("-mintxfee=", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), @@ -6058,7 +4811,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-mnemonic=", _("User defined mnemonic for HD wallet (bip39). Only has effect during wallet creation/first start (default: randomly generated)")); strUsage += HelpMessageOpt("-mnemonicpassphrase=", _("User defined mnemonic passphrase for HD wallet (BIP39). Only has effect during wallet creation/first start (default: empty string)")); strUsage += HelpMessageOpt("-hdseed=", _("User defined seed for HD wallet (should be in hex). Only has effect during wallet creation/first start (default: randomly generated)")); - strUsage += HelpMessageOpt("-batching", _("In case of sync/reindex verifies sigma/lelantus proofs with batch verification, default: true")); + strUsage += HelpMessageOpt("-batching", _("In case of sync/reindex verifies privacy (Spark) proofs with batch verification, default: true")); strUsage += HelpMessageOpt("-mobile", _("Use this argument when you want to keep additional data in block index for mobile api, default: false")); strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (default: %u)"), DEFAULT_WALLET_RBF)); strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); @@ -6088,9 +4841,8 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) uiInterface.InitMessage(_("Zapping all Sigma mints from wallet...")); CWallet *tempWallet = new CWallet(walletFile); - DBErrors nZapLelantusMintRet = tempWallet->ZapLelantusMints(); DBErrors nZapSparkMintRet = tempWallet->ZapSparkMints(); - if (nZapLelantusMintRet != DB_LOAD_OK || nZapSparkMintRet != DB_LOAD_OK) { + if (nZapSparkMintRet != DB_LOAD_OK) { InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile)); return NULL; } @@ -6219,8 +4971,6 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); if (pwalletMain->IsHDSeedAvailable()) { - walletInstance->zwallet = std::make_unique(pwalletMain->strWalletFile); - // if it is first run, we need to generate the full key set for spark, if not we are loading spark wallet from db walletInstance->sparkWallet = std::make_unique(pwalletMain->strWalletFile); @@ -6543,71 +5293,6 @@ bip47::CPaymentCode CWallet::GeneratePcode(std::string const & label) return newAcc.getMyPcode(); } -CWalletTx CWallet::PrepareAndSendNotificationTx(bip47::CPaymentCode const & theirPcode) -{ - bip47::CPaymentChannel pchannel = SetupPchannel(theirPcode); - - CWalletTx wtxNew; - - if (GetBroadcastTransactions() && !g_connman) { - throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - } - - CBitcoinAddress const notifAddr = pchannel.getTheirPcode().getNotificationAddress(); - - std::vector recipients; - std::vector newMints; - - CRecipient receiver; - receiver.scriptPubKey = GetScriptForDestination(notifAddr.Get()); - receiver.nAmount = bip47::NotificationTxValue; - receiver.fSubtractFeeFromAmount = false; - - recipients.emplace_back(receiver); - CScript opReturnScript = CScript() << OP_RETURN << std::vector(80); // Passing empty array to calc fees - recipients.push_back({opReturnScript, 0, false, {}, {}}); - - auto throwSigma = - [](){throw std::runtime_error(std::string("There are unspent Sigma coins in your wallet. Using Sigma coins for BIP47 is not supported. Please spend your Sigma coins before establishing a BIP47 channel."));}; - - try { - std::vector spendCoins; - std::vector mintCoins; - CAmount fee; - - wtxNew = CreateLelantusJoinSplitTransaction(recipients, fee, newMints, spendCoins, mintCoins, nullptr, - [&pchannel, &throwSigma](CTxOut & out, LelantusJoinSplitBuilder const & builder) { - if(out.scriptPubKey[0] == OP_RETURN) { - CKey spendPrivKey; - if (builder.spendCoins.empty()) - throwSigma(); - spendPrivKey.Set(builder.spendCoins[0].ecdsaSecretKey.begin(), builder.spendCoins[0].ecdsaSecretKey.end(), false); - CDataStream ds(SER_NETWORK, 0); - ds << builder.spendCoins[0].serialNumber; - bip47::Bytes const pcode = pchannel.getMaskedPayload((unsigned char const *)ds.vch.data(), ds.vch.size(), spendPrivKey); - out.scriptPubKey = CScript() << OP_RETURN << pcode; - } - }); - - if (spendCoins.empty()) - throw std::runtime_error(std::string("Cannot create a Lelantus spend to address: " + notifAddr.ToString()).c_str()); - - CommitLelantusTransaction(wtxNew, spendCoins, mintCoins); - LogBip47("Paymentcode %s was sent to notification address: %s\n", pchannel.getMyPcode().toString().c_str(), notifAddr.ToString().c_str() ); - } - catch (const InsufficientFunds& e) - { - throw e; - } - catch (const std::exception& e) - { - throw WalletError(e.what()); - } - - SetNotificationTxId(theirPcode, wtxNew.GetHash()); - return wtxNew; -} - std::vector CWallet::ListPcodes() { std::vector result; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 623a425173..1590edaedf 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -7,7 +7,6 @@ #define BITCOIN_WALLET_WALLET_H #include "amount.h" -#include "../liblelantus/coin.h" #include "libspark/keys.h" #include "streams.h" #include "tinyformat.h" @@ -27,9 +26,6 @@ #include "firo_params.h" #include "univalue.h" -#include "hdmint/tracker.h" -#include "hdmint/wallet.h" - #include "primitives/mint_spend.h" #include "bip47/paymentcode.h" @@ -641,8 +637,6 @@ class CAccountingEntry std::vector _ssExtra; }; -class LelantusJoinSplitBuilder; - /**Open unlock wallet window**/ //static boost::signals2::signal UnlockWallet; @@ -760,8 +754,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface MasterKeyMap mapMasterKeys; unsigned int nMasterKeyMaxID; - std::unique_ptr zwallet; - std::unique_ptr sparkWallet; std::atomic fUnlockRequested; @@ -799,7 +791,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface fAnonymizableTallyCachedNonDenom = false; vecAnonymizableTallyCached.clear(); vecAnonymizableTallyCachedNonDenom.clear(); - zwallet = NULL; bip47wallet.reset(); } @@ -822,8 +813,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface std::set setLockedCoins; - std::pair cachedLelantusBalance = {-1, -1}; - const CWalletTx* GetWalletTx(const uint256& hash) const; //! check whether we are allowed to upgrade (or already support) to the named feature @@ -937,7 +926,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override; std::vector ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman); CAmount GetBalance(bool fExcludeLocked = false) const; - std::pair GetPrivateBalance(); bool TryGetBalances(CAmount& balance, CAmount& unconfirmedBalance, CAmount& newImmatureBalance, CAmount& mintableBalance) const; CAmount GetUnconfirmedBalance() const; CAmount GetImmatureBalance() const; @@ -946,15 +934,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface CAmount GetImmatureWatchOnlyBalance() const; CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account, bool fAddLocked = false) const; - static CRecipient CreateLelantusMintRecipient( - lelantus::PrivateCoin& coin, - CHDMint& vDMint, - bool generate = true); - - // Returns a list of unspent and verified coins, I.E. coins which are ready - // to be spent. - std::list GetAvailableLelantusCoins(const CCoinControl *coinControl = NULL, bool includeUnsafe = false, bool forEstimation = false) const; - // Returns the list of pairs of coins and meta data for that coin, std::list GetAvailableSparkCoins(const CCoinControl *coinControl = NULL) const; @@ -970,17 +949,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface * \returns true, if it was possible to spend exactly required(rounded up to 0.1 firo) amount using coins we have. */ - bool GetCoinsToJoinSplit( - CAmount required, - std::vector& coinsToSpend_out, - CAmount& changeToMint, - std::list coins, - const size_t coinsToSpendLimit = SIZE_MAX, - const CAmount amountToSpendLimit = MAX_MONEY, - const CCoinControl *coinControl = NULL) const; - - std::vector ProvePrivateTxOwn(const uint256& txid, const std::string& message) const; - /** * Insert additional inputs into the transaction by * calling CreateTransaction(); @@ -998,13 +966,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /** * Add Mint and Spend functions */ - void ListAvailableLelantusMintCoins(std::vector &vCoins, bool fOnlyConfirmed) const; - - bool CreateLelantusMintTransactions(CAmount valueToMint, std::vector>& wtxAndFee, - CAmount& nAllFeeRet, std::vector& dMints, - std::list& reservekeys, int& nChangePosInOut, - std::string& strFailReason, const CCoinControl *coinControl, bool autoMintAll = false, bool sign = true); - std::pair GetSparkBalance(); bool IsSparkAddressMine(const std::string& address); @@ -1020,28 +981,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface const CCoinControl *coinControl, bool autoMintAll = false); - - CWalletTx CreateLelantusJoinSplitTransaction( - const std::vector& recipients, - CAmount& fee, - const std::vector& newMints, - std::vector& spendCoins, - std::vector& mintCoins, - const CCoinControl *coinControl = NULL, - std::function modifier = nullptr); - - bool CommitLelantusTransaction(CWalletTx& wtxNew, std::vector& spendCoins, std::vector& mintCoins); std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, bool fAskFee=false); std::string SendMoneyToDestination(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew, bool fAskFee=false); - std::string MintAndStoreLelantus( - const CAmount& value, - std::vector>& wtxAndFee, - std::vector& mints, - bool autoMintAll = false, - bool fAskFee = false, - const CCoinControl *coinControl = NULL); - std::string MintAndStoreSpark( const std::vector& outputs, std::vector>& wtxAndFee, @@ -1069,14 +1011,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface CAmount &fee, const CCoinControl *coinControl = NULL); - bool LelantusToSpark(std::string& strFailReason); - - std::vector JoinSplitLelantus(const std::vector& recipients, const std::vector& newMints, CWalletTx& result, const CCoinControl *coinControl = NULL); - - std::pair EstimateJoinSplitFee(CAmount required, bool subtractFeeFromAmount, std::list coins, const CCoinControl *coinControl); - - bool GetMint(const uint256& hashSerial, CLelantusEntry& mint, bool forEstimation = false) const; - bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state); @@ -1153,8 +1087,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface DBErrors ZapWalletTx(std::vector& vWtx); DBErrors ZapSelectTx(std::vector& vHashIn, std::vector& vHashOut); - // Remove all Lelantus HDMint objects from WalletDB - DBErrors ZapLelantusMints(); // Remove all Spark Mint objects from WalletDB DBErrors ZapSparkMints(); @@ -1238,10 +1170,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface */ boost::signals2::signal NotifyTransactionChanged; - /** - * sigma/lelantus entry changed. - * @note called with lock cs_wallet held. - */ + boost::signals2::signal NotifyZerocoinChanged; @@ -1322,9 +1251,6 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface /* Generates and strores a new payment code for receiving*/ bip47::CPaymentCode GeneratePcode(std::string const & label); - /*Prepares and sends a notification tx using Lelantus facilities*/ - CWalletTx PrepareAndSendNotificationTx(bip47::CPaymentCode const & theirPcode); - /* Lists all receiving pcodes as tuples of (pcode, label, notification address) */ std::vector ListPcodes(); @@ -1458,6 +1384,4 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins return true; } -CWalletTx PrepareAndSendNotificationTx(CWallet* pwallet, bip47::CPaymentCode const & theirPcode); - #endif // BITCOIN_WALLET_WALLET_H diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 7a34e38d5a..3d3dd1e054 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -271,61 +271,10 @@ void CWalletDB::ListAccountCreditDebit(const std::string& strAccount, std::list< pcursor->close(); } -bool CWalletDB::WriteLelantusSpendSerialEntry(const CLelantusSpendEntry& lelantusSpend) { - return Write(std::make_pair(std::string("lelantus_spend"), lelantusSpend.coinSerial), lelantusSpend, true); -} - -bool CWalletDB::HasLelantusSpendSerialEntry(const secp_primitives::Scalar& serial) { - return Exists(std::make_pair(std::string("lelantus_spend"), serial)); -} - -bool CWalletDB::EraseLelantusSpendSerialEntry(const CLelantusSpendEntry& lelantusSpend) { - return Erase(std::make_pair(std::string("lelantus_spend"), lelantusSpend.coinSerial)); -} - -bool CWalletDB::ReadLelantusSpendSerialEntry(const secp_primitives::Scalar& serial, CLelantusSpendEntry& lelantusSpend) { - return Read(std::make_pair(std::string("lelantus_spend"), serial), lelantusSpend); -} - bool CWalletDB::WriteCalculatedZCBlock(int height) { return Write(std::string("calculatedzcblock"), height); } -void CWalletDB::ListLelantusSpendSerial(std::list & listLelantusSpendSerial) { - Dbc *pcursor = GetCursor(); - if (!pcursor) - throw std::runtime_error("CWalletDB::ListLelantusSpendSerial() : cannot create DB cursor"); - bool setRange = true; - while (true) { - // Read next record - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - if (setRange) - ssKey << std::make_pair(std::string("lelantus_spend"), secp_primitives::Scalar()); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = ReadAtCursor(pcursor, ssKey, ssValue, setRange); - setRange = false; - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) { - pcursor->close(); - throw std::runtime_error("CWalletDB::ListLelantusSpendSerial() : error scanning DB"); - } - - // Unserialize - std::string strType; - ssKey >> strType; - if (strType != "lelantus_spend") - break; - Scalar value; - ssKey >> value; - CLelantusSpendEntry lelantusSpendItem; - ssValue >> lelantusSpendItem; - listLelantusSpendSerial.push_back(lelantusSpendItem); - } - - pcursor->close(); -} - DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet) { LOCK(pwallet->cs_wallet); @@ -402,36 +351,6 @@ DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet) return DB_LOAD_OK; } -bool CWalletDB::WriteHDMint(const uint256& hashPubcoin, const CHDMint& dMint, bool isLelantus) -{ - std::string name; - if(!isLelantus) - name = "hdmint"; - else - name = "hdmint_lelantus"; - return Write(std::make_pair(name, hashPubcoin), dMint, true); -} - -bool CWalletDB::ReadHDMint(const uint256& hashPubcoin, bool isLelantus, CHDMint& dMint) -{ - std::string name; - if(!isLelantus) - name = "hdmint"; - else - name = "hdmint_lelantus"; - return Read(std::make_pair(name, hashPubcoin), dMint); -} - -bool CWalletDB::EraseHDMint(const CHDMint& dMint) { - nWalletDBUpdateCounter++; - uint256 hash = dMint.GetPubCoinHash(); - return Erase(std::make_pair(std::string("hdmint"), hash)) || Erase(std::make_pair(std::string("hdmint_lelantus"), hash)); -} - -bool CWalletDB::HasHDMint(const secp_primitives::GroupElement& pub) { - return Exists(std::make_pair(std::string("hdmint"), primitives::GetPubCoinValueHash(pub))) || Exists(std::make_pair(std::string("hdmint_lelantus"), primitives::GetPubCoinValueHash(pub))); -} - bool CWalletDB::WritePubcoinHashes(const uint256& fullHash, const uint256& reducedHash) { return Write(std::make_pair(std::string("pubhash"), fullHash), reducedHash, true); } @@ -984,20 +903,6 @@ DBErrors CWalletDB::ZapSelectTx(CWallet* pwallet, std::vector& vTxHashI return DB_LOAD_OK; } -DBErrors CWalletDB::ZapLelantusMints(CWallet *pwallet) { - // get list of HD Mints - std::list lelantusHDMints = ListHDMints(true); - - // erase each HD Mint - BOOST_FOREACH(CHDMint & hdMint, lelantusHDMints) - { - if (!EraseHDMint(hdMint)) - return DB_CORRUPT; - } - - return DB_LOAD_OK; -} - DBErrors CWalletDB::ZapSparkMints(CWallet *pwallet) { // get list of spark Mints std::unordered_map sparkMints = ListSparkMints(); @@ -1449,143 +1354,6 @@ bool CWalletDB::ReadMintPoolPair(const uint256& hashPubcoin, uint160& hashSeedMa return true; } -//! list of MintPoolEntry objects mapped with pubCoin hash, returned as pairs -std::vector> CWalletDB::ListMintPool() -{ - std::vector> listPool; - Dbc* pcursor = GetCursor(); - if (!pcursor) - throw std::runtime_error(std::string(__func__)+" : cannot create DB cursor"); - bool setRange = true; - for (;;) - { - // Read next record - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - if (setRange) - ssKey << std::make_pair(std::string("mintpool"), ArithToUint256(arith_uint256(0))); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = ReadAtCursor(pcursor, ssKey, ssValue, setRange); - setRange = false; - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - { - pcursor->close(); - throw std::runtime_error(std::string(__func__)+" : error scanning DB"); - } - - // Unserialize - - try { - std::string strType; - ssKey >> strType; - if (strType != "mintpool") - break; - - uint256 hashPubcoin; - ssKey >> hashPubcoin; - - uint160 hashSeedMaster; - ssValue >> hashSeedMaster; - - CKeyID seedId; - ssValue >> seedId; - - int32_t nCount; - ssValue >> nCount; - - MintPoolEntry mintPoolEntry(hashSeedMaster, seedId, nCount); - - listPool.push_back(std::make_pair(hashPubcoin, mintPoolEntry)); - } catch (std::ios_base::failure const &) { - // There maybe some old entries that don't conform to the latest version. Just skipping those. - } - } - - pcursor->close(); - - return listPool; -} - -std::list CWalletDB::ListHDMints(bool isLelantus) -{ - std::list listMints; - Dbc* pcursor = GetCursor(); - if (!pcursor) - throw std::runtime_error(std::string(__func__)+" : cannot create DB cursor"); - - std::string mintName; - - if(isLelantus) - mintName = "hdmint_lelantus"; - else - mintName = "hdmint"; - - bool setRange = true; - for (;;) - { - // Read next record - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - if (setRange) - ssKey << std::make_pair(mintName, ArithToUint256(arith_uint256(0))); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = ReadAtCursor(pcursor, ssKey, ssValue, setRange); - setRange = false; - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - { - pcursor->close(); - throw std::runtime_error(std::string(__func__)+" : error scanning DB"); - } - - // Unserialize - std::string strType; - ssKey >> strType; - if (strType != mintName) - break; - - uint256 hashPubcoin; - ssKey >> hashPubcoin; - - CHDMint mint; - ssValue >> mint; - - listMints.emplace_back(mint); - } - - pcursor->close(); - return listMints; -} - -bool CWalletDB::ArchiveDeterministicOrphan(const CHDMint& dMint) -{ - if (!Write(std::make_pair(std::string("dzco"), dMint.GetPubCoinHash()), dMint)) - return error("%s: write failed", __func__); - - if (!Erase(std::make_pair(std::string("hdmint"), dMint.GetPubCoinHash()))) - return error("%s: failed to erase", __func__); - - if (!Erase(std::make_pair(std::string("hdmint_lelantus"), dMint.GetPubCoinHash()))) - return error("%s: failed to erase lelantus", __func__); - - return true; -} - -bool CWalletDB::UnarchiveHDMint(const uint256& hashPubcoin, bool isLelantus, CHDMint& dMint) -{ - if (!Read(std::make_pair(std::string("dzco"), hashPubcoin), dMint)) - return error("%s: failed to retrieve deterministic mint from archive", __func__); - - if (!WriteHDMint(hashPubcoin, dMint, isLelantus)) - return error("%s: failed to write deterministic mint", __func__); - - if (!Erase(std::make_pair(std::string("dzco"), dMint.GetPubCoinHash()))) - return error("%s : failed to erase archived deterministic mint", __func__); - - return true; -} - void CWalletDB::IncrementUpdateCounter() { nWalletDBUpdateCounter++; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 01ac024713..89749e2b73 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -14,8 +14,6 @@ #include "streams.h" #include "key.h" -#include "hdmint/hdmint.h" -#include "hdmint/mintpool.h" #include "../secp256k1/include/GroupElement.h" #include "../secp256k1/include/Scalar.h" #include "../libspark/keys.h" @@ -238,12 +236,6 @@ class CWalletDB : public CDB CAmount GetAccountCreditDebit(const std::string& strAccount); void ListAccountCreditDebit(const std::string& strAccount, std::list& acentries); - void ListLelantusSpendSerial(std::list& listLelantusSpendSerial); - bool WriteLelantusSpendSerialEntry(const CLelantusSpendEntry& lelantusSpend); - bool ReadLelantusSpendSerialEntry(const secp_primitives::Scalar& serial, CLelantusSpendEntry& lelantusSpend); - bool HasLelantusSpendSerialEntry(const secp_primitives::Scalar& serial); - bool EraseLelantusSpendSerialEntry(const CLelantusSpendEntry& lelantusSpend); - bool ReadCalculatedZCBlock(int& height); bool WriteCalculatedZCBlock(int height); @@ -252,7 +244,6 @@ class CWalletDB : public CDB DBErrors FindWalletTx(CWallet* pwallet, std::vector& vTxHash, std::vector& vWtx); DBErrors ZapWalletTx(CWallet* pwallet, std::vector& vWtx); DBErrors ZapSelectTx(CWallet* pwallet, std::vector& vHashIn, std::vector& vHashOut); - DBErrors ZapLelantusMints(CWallet *pwallet); DBErrors ZapSparkMints(CWallet *pwallet); static bool Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys); static bool Recover(CDBEnv& dbenv, const std::string& filename); @@ -269,19 +260,10 @@ class CWalletDB : public CDB bool readFullViewKey(spark::FullViewKey& viewKey); bool writeFullViewKey(const spark::FullViewKey& viewKey); - bool ArchiveDeterministicOrphan(const CHDMint& dMint); - bool UnarchiveHDMint(const uint256& hashPubcoin, bool isLelantus, CHDMint& dMint); - - bool WriteHDMint(const uint256& hashPubcoin, const CHDMint& dMint, bool isLelantus); - bool ReadHDMint(const uint256& hashPubcoin, bool isLelantus, CHDMint& dMint); - bool EraseHDMint(const CHDMint& dMint); - bool HasHDMint(const secp_primitives::GroupElement& pub); - bool WritePubcoinHashes(const uint256& fullHash, const uint256& reducedHash); bool ReadPubcoinHashes(const uint256& fullHash, uint256& reducedHash); bool ErasePubcoinHashes(const uint256& fullHash); - std::list ListHDMints(bool isLelantus); bool WritePubcoin(const uint256& hashSerial, const GroupElement& hashPubcoin); bool ReadPubcoin(const uint256& hashSerial, GroupElement& hashPubcoin); bool ErasePubcoin(const uint256& hashSerial); @@ -289,7 +271,6 @@ class CWalletDB : public CDB bool EraseMintPoolPair(const uint256& hashPubcoin); bool WriteMintPoolPair(const uint256& hashPubcoin, const std::tuple& hashSeedMintPool); bool ReadMintPoolPair(const uint256& hashPubcoin, uint160& hashSeedMaster, CKeyID& seedId, int32_t& nCount); - std::vector> ListMintPool(); std::unordered_map ListSparkMints(); bool WriteSparkOutputTx(const CScript& scriptPubKey, const CSparkOutputTx& output); From b107f2aeadcabd4b111ad2af16df09e6a40ddc9e Mon Sep 17 00:00:00 2001 From: levoncrypto Date: Tue, 24 Mar 2026 18:38:03 +0400 Subject: [PATCH 02/12] fix CodeRabbit review --- README.md | 2 +- src/chain.h | 1 - src/chainparams.cpp | 8 ++++---- src/libspark/coin.h | 17 ++++++++++------- src/llmq/quorums_instantsend.cpp | 6 +++--- src/spark/state.cpp | 2 +- src/validation.cpp | 13 ++++++++----- src/wallet/rpcdump.cpp | 12 ++++++------ src/wallet/rpcwallet.cpp | 12 +++++++----- 9 files changed, 40 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 51c5228b69..548c231d9e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [![GitHub last-commit](https://img.shields.io/github/last-commit/firoorg/firo)](https://github.com/firoorg/firo/commits/master) ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/firoorg/firo?utm_source=oss&utm_medium=github&utm_campaign=firoorg%2Ffiro&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews) -[Firo](https://firo.org) formerly known as Zcoin, is a privacy focused cryptocurrency that utilizes the [Spark protocol](https://eprint.iacr.org/2021/1173) which supports high anonymity sets without requiring trusted setup and relying on standard cryptographic assumptions. +[Firo](https://firo.org) formerly known as Zcoin, is a privacy-focused cryptocurrency that utilizes the [Spark protocol](https://eprint.iacr.org/2021/1173) which supports high anonymity sets without requiring trusted setup and relying on standard cryptographic assumptions. Firo also utilises [Dandelion++](https://arxiv.org/abs/1805.11060) to obscure the originating IP of transactions without relying on any external services such as Tor/i2P. diff --git a/src/chain.h b/src/chain.h index 1c043b36d9..76a04b9a05 100644 --- a/src/chain.h +++ b/src/chain.h @@ -22,7 +22,6 @@ #include "coin_containers.h" #include "streams.h" #include "sparkname.h" -#include "libspark/coin.h" #include #include diff --git a/src/chainparams.cpp b/src/chainparams.cpp index fac4265e39..d432cbbfee 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -744,11 +744,11 @@ class CTestNetParams : public CChainParams { consensus.nRestartSigmaWithBlacklistCheck = INT_MAX; consensus.nOldSigmaBanBlock = 1; - consensus.nLelantusStartBlock = 1; - consensus.nLelantusFixesStartBlock = 1; + consensus.nLelantusStartBlock = ZC_LELANTUS_TESTNET_STARTING_BLOCK; + consensus.nLelantusFixesStartBlock = ZC_LELANTUS_TESTNET_FIXES_START_BLOCK; consensus.nSparkStartBlock = SPARK_TESTNET_START_BLOCK; - consensus.nLelantusGracefulPeriod = 0; - consensus.nSigmaEndBlock = 1; + consensus.nLelantusGracefulPeriod = LELANTUS_TESTNET_GRACEFUL_PERIOD; + consensus.nSigmaEndBlock = ZC_SIGMA_TESTNET_END_BLOCK; consensus.nZerocoinV2MintMempoolGracefulPeriod = ZC_V2_MINT_TESTNET_GRACEFUL_MEMPOOL_PERIOD; consensus.nZerocoinV2MintGracefulPeriod = ZC_V2_MINT_TESTNET_GRACEFUL_PERIOD; consensus.nZerocoinV2SpendMempoolGracefulPeriod = ZC_V2_SPEND_TESTNET_GRACEFUL_MEMPOOL_PERIOD; diff --git a/src/libspark/coin.h b/src/libspark/coin.h index eb16bd6e3f..7339b0497e 100644 --- a/src/libspark/coin.h +++ b/src/libspark/coin.h @@ -174,26 +174,29 @@ class PublicCoin { PublicCoin() {} template inline void Serialize(Stream& s) const { - constexpr int size = GroupElement::memoryRequired(); + constexpr int size = secp_primitives::GroupElement::memoryRequired(); unsigned char buffer[size + sizeof(int32_t)]; value.serialize(buffer); - std::memcpy(buffer + size, &denomination, sizeof(denomination)); + int32_t denom32 = static_cast(static_cast(denomination)); + std::memcpy(buffer + size, &denom32, sizeof(denom32)); char* b = (char*)buffer; s.write(b, size + sizeof(int32_t)); } template inline void Unserialize(Stream& s) { - constexpr int size = GroupElement::memoryRequired(); + constexpr int size = secp_primitives::GroupElement::memoryRequired(); unsigned char buffer[size + sizeof(int32_t)]; char* b = (char*)buffer; s.read(b, size + sizeof(int32_t)); value.deserialize(buffer); - std::memcpy(&denomination, buffer + size, sizeof(denomination)); + int32_t denom32; + std::memcpy(&denom32, buffer + size, sizeof(denom32)); + denomination = static_cast(static_cast(denom32)); } private: - GroupElement value; + secp_primitives::GroupElement value; CoinDenomination denomination; }; @@ -218,7 +221,7 @@ struct CSpendCoinInfo { }; struct CScalarHash { - std::size_t operator ()(const Scalar& bn) const noexcept { + std::size_t operator ()(const secp_primitives::Scalar& bn) const noexcept { std::vector bnData(bn.memoryRequired()); bn.serialize(&bnData[0]); unsigned char hash[CSHA256::OUTPUT_SIZE]; @@ -230,7 +233,7 @@ struct CScalarHash { } }; -using spend_info_container = std::unordered_map; +using spend_info_container = std::unordered_map; } diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp index 2fb025a551..afe7ae5b56 100644 --- a/src/llmq/quorums_instantsend.cpp +++ b/src/llmq/quorums_instantsend.cpp @@ -491,10 +491,10 @@ bool CInstantSendManager::CheckCanLock(const CTransaction& tx, bool printDebug, } if (tx.IsSparkSpend()) { + LOCK(cs_main); for (CTxIn const & in : tx.vin) { GroupElement lTag; lTag.deserialize(&in.scriptSig.front()); - LOCK(cs_main); if (spark::CSparkState::GetState()->IsUsedLTag(lTag)) return false; } @@ -1013,7 +1013,7 @@ void CInstantSendManager::SyncTransaction(const CTransaction& tx_, const CBlockI return; } - CTransaction const & tx{(tx_.IsLelantusJoinSplit() || tx_.IsSparkSpend()) ? isutils::AdaptPrivateTx(tx_) : tx_}; + CTransaction const & tx{tx_.IsSparkSpend() ? isutils::AdaptPrivateTx(tx_) : tx_}; bool inMempool = mempool.get(tx.GetHash()) != nullptr; bool isDisconnect = pindex && posInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK; @@ -1522,7 +1522,7 @@ CInstantSendLockPtr CInstantSendManager::GetConflictingLock(const CTransaction& return nullptr; } - CTransaction const & tx{(tx_.IsLelantusJoinSplit() || tx_.IsSparkSpend()) ? isutils::AdaptPrivateTx(tx_) : tx_}; + CTransaction const & tx{tx_.IsSparkSpend() ? isutils::AdaptPrivateTx(tx_) : tx_}; LOCK(cs); for (const auto& in : tx.vin) { diff --git a/src/spark/state.cpp b/src/spark/state.cpp index 32c44a83b5..313342584e 100644 --- a/src/spark/state.cpp +++ b/src/spark/state.cpp @@ -473,7 +473,7 @@ void DisconnectTipSpark(CBlock& block, CBlockIndex *pindexDelete) { bool CheckSparkBlock(CValidationState &state, const CBlock& block, int nBlockHeight) { auto& consensus = ::Params().GetConsensus(); - size_t blockSpendsValue = 0; + CAmount blockSpendsValue = 0; for (const auto& tx : block.vtx) { auto txSpendsValue = GetSpendTransparentAmount(*tx); diff --git a/src/validation.cpp b/src/validation.cpp index 0c2c48e63e..847489ef62 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -693,12 +693,15 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, bool fChe } if (tx.IsSigmaSpend() || tx.IsSigmaMint()) { - if (!isVerifyDB && ::Params().GetConsensus().nLelantusStartBlock > 1 && nHeight >= (::Params().GetConsensus().nLelantusStartBlock + 5)) + if (!isVerifyDB && nHeight >= params.nLelantusStartBlock) return state.DoS(1, error("Sigma already is not available, start using Lelantus.")); } if (tx.IsLelantusJoinSplit() || tx.IsLelantusMint()) { - if (!isVerifyDB && !IsInitialBlockDownload() && nHeight >= (::Params().GetConsensus().nSparkStartBlock + 5)) + const int lelantusDisabledHeight = params.nLelantusGracefulPeriod > 0 + ? params.nLelantusGracefulPeriod + : params.nSparkStartBlock; + if (!isVerifyDB && nHeight >= lelantusDisabledHeight) return state.DoS(1, error("Lelantus already is not available, start using Spark.")); } @@ -4194,12 +4197,12 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P if (nSigOps * WITNESS_SCALE_FACTOR > MAX_BLOCK_SIGOPS_COST) return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount"); - if (fCheckPOW && fCheckMerkleRoot) - block.fChecked = true; - if (!spark::CheckSparkBlock(state, block, nHeight)) return false; + if (fCheckPOW && fCheckMerkleRoot) + block.fChecked = true; + return true; } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index cd0ea9e468..8da9c2bc0b 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -571,14 +571,14 @@ UniValue importwallet(const JSONRPCRequest& request) CBlockIndex *pindex = chainActive.FindEarliestAtLeast(nTimeBegin - 7200); - LogPrintf("Rescanning last %i blocks\n", pindex ? chainActive.Height() - pindex->nHeight + 1 : 0); - pwallet->ScanForWalletTransactions(pindex); - pwallet->MarkDirty(); - - if(fMintUpdate){ + if (fMintUpdate) { + LogPrintf("Rescanning full chain (legacy HD mint key path)\n"); pwallet->ScanForWalletTransactions(chainActive.Genesis(), true); - pwallet->sparkWallet->ListSparkMints(); + } else { + LogPrintf("Rescanning last %i blocks\n", pindex ? chainActive.Height() - pindex->nHeight + 1 : 0); + pwallet->ScanForWalletTransactions(pindex); } + pwallet->MarkDirty(); if (!fGood) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding some keys to wallet"); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b3ba7b270a..34ce58e6f6 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -65,7 +65,7 @@ bool EnsureWalletIsAvailable(CWallet * const pwallet, bool avoidException) void EnsureSparkWalletIsAvailable() { if (!pwalletMain || !pwalletMain->sparkWallet) { - throw JSONRPCError(RPC_WALLET_ERROR, "spark mint is not allowed for legacy wallet"); + throw JSONRPCError(RPC_WALLET_ERROR, "Spark wallet is not available for this wallet (legacy or disabled)"); } } @@ -1666,9 +1666,9 @@ UniValue gettotalbalance(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 0) throw std::runtime_error( "gettotalbalance\n" - "\nReturns total (transparent + private) balance.\n" - "Transparent balance is the sum of coin amounts received as utxo.\n" - "Private balance is the sum of all confirmed spark mints which are created by the wallet.\n" + "\nReturns total (transparent + Spark private) balance.\n" + "Transparent balance is the sum of UTXO amounts.\n" + "Private balance is the sum of confirmed Spark funds tracked by the wallet.\n" "\nResult:\n" "amount (numeric) The total balance in " + CURRENCY_UNIT + " for the wallet.\n" "\nExamples:\n" @@ -1680,7 +1680,9 @@ UniValue gettotalbalance(const JSONRPCRequest& request) EnsureSparkWalletIsAvailable(); LOCK2(cs_main, pwallet->cs_wallet); - return ValueFromAmount(pwallet->sparkWallet->getAvailableBalance()); + const CAmount transparent = pwallet->GetBalance(); + const CAmount spark = pwallet->sparkWallet->getAvailableBalance(); + return ValueFromAmount(transparent + spark); } UniValue getunconfirmedbalance(const JSONRPCRequest &request) From 588c4ef27f8297c5a498b505ad004c285ca7af5d Mon Sep 17 00:00:00 2001 From: levoncrypto Date: Tue, 24 Mar 2026 19:42:57 +0400 Subject: [PATCH 03/12] Second fix of CodeRabbit comments --- src/chainparams.cpp | 17 +--- src/consensus/params.h | 3 - src/llmq/quorums_instantsend.cpp | 4 + src/spark/sparkwallet.cpp | 81 +++++++++++-------- src/spark/threadpool.h | 130 +++++++++++++++++++++++++++++++ src/wallet/rpcdump.cpp | 2 +- src/wallet/rpcwallet.cpp | 8 +- 7 files changed, 191 insertions(+), 54 deletions(-) create mode 100644 src/spark/threadpool.h diff --git a/src/chainparams.cpp b/src/chainparams.cpp index d432cbbfee..d7b58e774f 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -485,9 +485,6 @@ class CMainParams : public CChainParams { // Bip39 consensus.nMnemonicBlock = 222400; - // moving lelantus data to v3 payload - consensus.nLelantusV3PayloadStartBlock = 401580; - // ProgPow consensus.nPPSwitchTime = 1635228000; // Tue Oct 26 2021 06:00:00 GMT+0000 consensus.nPPBlockNumber = 419264; @@ -801,9 +798,6 @@ class CTestNetParams : public CChainParams { // Bip39 consensus.nMnemonicBlock = 1; - // Lelantus strip - consensus.nLelantusV3PayloadStartBlock = 1; - // ProgPow consensus.nPPSwitchTime = 1630069200; // August 27 2021, 13:00 UTC consensus.nPPBlockNumber = 37305; @@ -1020,7 +1014,7 @@ class CDevNetParams : public CChainParams { consensus.nLelantusFixesStartBlock = 1; consensus.nSparkStartBlock = 1500; - consensus.nLelantusGracefulPeriod = 0; + consensus.nLelantusGracefulPeriod = 6000; consensus.nSigmaEndBlock = 3600; consensus.nMaxSigmaInputPerBlock = ZC_SIGMA_INPUT_LIMIT_PER_BLOCK; consensus.nMaxValueSigmaSpendPerBlock = ZC_SIGMA_VALUE_SPEND_LIMIT_PER_BLOCK; @@ -1059,9 +1053,6 @@ class CDevNetParams : public CChainParams { // Bip39 consensus.nMnemonicBlock = 1; - // moving lelantus data to v3 payload - consensus.nLelantusV3PayloadStartBlock = 1; - // ProgPow consensus.nPPSwitchTime = 1631261566; // immediately after network start consensus.nPPBlockNumber = 1; @@ -1270,12 +1261,11 @@ class CRegTestParams : public CChainParams { consensus.nStartSigmaBlacklist = INT_MAX; consensus.nRestartSigmaWithBlacklistCheck = INT_MAX; consensus.nOldSigmaBanBlock = 1; - // Lelantus stripped (like Sigma): both "start" at 1, only Spark active (Sigma end=1, Lelantus grace=0) consensus.nLelantusStartBlock = 1; consensus.nLelantusFixesStartBlock = 1; consensus.nSparkStartBlock = 100; consensus.nExchangeAddressStartBlock = 1000; - consensus.nLelantusGracefulPeriod = 0; + consensus.nLelantusGracefulPeriod = 600; consensus.nSigmaEndBlock = 1; consensus.nZerocoinV2MintMempoolGracefulPeriod = 1; consensus.nZerocoinV2MintGracefulPeriod = 1; @@ -1317,9 +1307,6 @@ class CRegTestParams : public CChainParams { // Bip39 consensus.nMnemonicBlock = 0; - // Lelantus strip: no v3 payload boundary (same as testnet/devnet) - consensus.nLelantusV3PayloadStartBlock = 1; - // ProgPow // this can be overridden with either -ppswitchtime or -ppswitchtimefromnow flags consensus.nPPSwitchTime = INT_MAX; diff --git a/src/consensus/params.h b/src/consensus/params.h index f38aaf1d7b..82edf1532c 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -430,9 +430,6 @@ struct Params { /** block to start reorg depth enforcement */ int nMaxReorgDepthEnforcementBlock; - /** move lelantus data to v3 payload since this block */ - int nLelantusV3PayloadStartBlock; - /** whitelisted transactions */ std::set txidWhitelist; diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp index afe7ae5b56..22372feed8 100644 --- a/src/llmq/quorums_instantsend.cpp +++ b/src/llmq/quorums_instantsend.cpp @@ -490,6 +490,10 @@ bool CInstantSendManager::CheckCanLock(const CTransaction& tx, bool printDebug, return false; } + if (tx.IsLelantusJoinSplit()) { + return false; + } + if (tx.IsSparkSpend()) { LOCK(cs_main); for (CTxIn const & in : tx.vin) { diff --git a/src/spark/sparkwallet.cpp b/src/spark/sparkwallet.cpp index 443968f8df..4f617b2c2c 100644 --- a/src/spark/sparkwallet.cpp +++ b/src/spark/sparkwallet.cpp @@ -1,4 +1,5 @@ #include "sparkwallet.h" +#include "threadpool.h" #include "state.h" #include "../wallet/wallet.h" #include "../wallet/coincontrol.h" @@ -12,6 +13,7 @@ #include "../chain.h" #include #include +#include const uint32_t DEFAULT_SPARK_NCOUNT = 1; @@ -82,16 +84,23 @@ CSparkWallet::CSparkWallet(const std::string& strWalletFile) { } } - threadPool = nullptr; + + unsigned nThreads = std::thread::hardware_concurrency(); + threadPool = new ParallelOpThreadPool(static_cast(nThreads)); if (fWalletJustUnlocked) pwalletMain->Lock(); } CSparkWallet::~CSparkWallet() { + delete (ParallelOpThreadPool*)threadPool; + threadPool = nullptr; } void CSparkWallet::FinishTasks() { + if (threadPool) { + ((ParallelOpThreadPool*)threadPool)->Shutdown(); + } spark::ShutdownSparkState(); } @@ -511,32 +520,36 @@ void CSparkWallet::UpdateSpendState(const GroupElement& lTag, const uint256& txH } void CSparkWallet::UpdateSpendStateFromMempool(const std::vector& lTags, const uint256& txHash, bool fUpdateMint) { - LOCK(cs_spark_wallet); - for (const auto& lTag : lTags) { - uint256 lTagHash = primitives::GetLTagHash(lTag); - if (coinMeta.count(lTagHash)) { - UpdateSpendState(lTag, lTagHash, txHash, fUpdateMint); + ((ParallelOpThreadPool*)threadPool)->PostTask([=]() { + LOCK(cs_spark_wallet); + for (const auto& lTag : lTags) { + uint256 lTagHash = primitives::GetLTagHash(lTag); + if (coinMeta.count(lTagHash)) { + UpdateSpendState(lTag, lTagHash, txHash, fUpdateMint); + } } - } + }); } void CSparkWallet::UpdateSpendStateFromBlock(const CBlock& block) { - const auto& transactions = block.vtx; - LOCK(cs_spark_wallet); - for (const auto& tx : transactions) { - if (tx->IsSparkSpend()) { - try { - spark::SpendTransaction spend = spark::ParseSparkSpend(*tx); - const auto& txLTags = spend.getUsedLTags(); - for (const auto& txLTag : txLTags) { - uint256 txHash = tx->GetHash(); - uint256 lTagHash = primitives::GetLTagHash(txLTag); - UpdateSpendState(txLTag, lTagHash, txHash); + std::vector vtxCopy = block.vtx; + ((ParallelOpThreadPool*)threadPool)->PostTask([=]() { + LOCK(cs_spark_wallet); + for (const auto& tx : vtxCopy) { + if (tx->IsSparkSpend()) { + try { + spark::SpendTransaction spend = spark::ParseSparkSpend(*tx); + const auto& txLTags = spend.getUsedLTags(); + for (const auto& txLTag : txLTags) { + uint256 txHash = tx->GetHash(); + uint256 lTagHash = primitives::GetLTagHash(txLTag); + UpdateSpendState(txLTag, lTagHash, txHash); + } + } catch (const std::exception &) { } - } catch (const std::exception &) { } } - } + }); } bool CSparkWallet::isMine(spark::Coin coin) const { @@ -655,22 +668,26 @@ void CSparkWallet::UpdateMintState(const std::vector& coins, const } void CSparkWallet::UpdateMintStateFromMempool(const std::vector& coins, const uint256& txHash) { - LOCK(cs_spark_wallet); - CWalletDB walletdb(strWalletFile); - UpdateMintState(coins, txHash, walletdb); + ((ParallelOpThreadPool*)threadPool)->PostTask([=]() mutable { + LOCK(cs_spark_wallet); + CWalletDB walletdb(strWalletFile); + UpdateMintState(coins, txHash, walletdb); + }); } void CSparkWallet::UpdateMintStateFromBlock(const CBlock& block) { - const auto& transactions = block.vtx; - LOCK(cs_spark_wallet); - CWalletDB walletdb(strWalletFile); - for (const auto& tx : transactions) { - if (tx->IsSparkTransaction()) { - auto coins = spark::GetSparkMintCoins(*tx); - uint256 txHash = tx->GetHash(); - UpdateMintState(coins, txHash, walletdb); + std::vector vtxCopy = block.vtx; + ((ParallelOpThreadPool*)threadPool)->PostTask([=]() mutable { + LOCK(cs_spark_wallet); + CWalletDB walletdb(strWalletFile); + for (const auto& tx : vtxCopy) { + if (tx->IsSparkTransaction()) { + auto coins = spark::GetSparkMintCoins(*tx); + uint256 txHash = tx->GetHash(); + UpdateMintState(coins, txHash, walletdb); + } } - } + }); } void CSparkWallet::RemoveSparkMints(const std::vector& mints) { diff --git a/src/spark/threadpool.h b/src/spark/threadpool.h new file mode 100644 index 0000000000..3db83a7d36 --- /dev/null +++ b/src/spark/threadpool.h @@ -0,0 +1,130 @@ +// Copyright (c) 2022 The Firo Core Developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef FIRO_SPARK_THREADPOOL_H +#define FIRO_SPARK_THREADPOOL_H + +#include +#include +#include +#include +#include +#include + +#define BOOST_THREAD_PROVIDES_FUTURE + +#include +#include +#include +#include +#include +#include + +// Number of seconds before thread shuts down if idle +constexpr static int secondsBeforeThreadShutdown = 60; + +// Simple thread pool for Spark wallet background tasks (moved from liblelantus with Lelantus strip). + +template +class ParallelOpThreadPool { +private: + std::list threads; + std::queue> task_queue; + boost::mutex task_queue_mutex; + boost::condition_variable task_queue_condition; + + bool shutdown; + size_t const number_of_threads; + + void ThreadProc() { + for (;;) { + boost::packaged_task job; + { + boost::unique_lock lock(task_queue_mutex); + + task_queue_condition.wait_for(lock, boost::chrono::seconds(secondsBeforeThreadShutdown), + [this] { return !task_queue.empty() || shutdown; }); + if (task_queue.empty()) { + boost::thread::id currentId = boost::this_thread::get_id(); + auto pThread = std::find_if(threads.begin(), threads.end(), + [currentId](const boost::thread &t) { return t.get_id() == currentId; }); + if (pThread != threads.end()) { + pThread->detach(); + threads.erase(pThread); + } + break; + } + job = std::move(task_queue.front()); + task_queue.pop(); + } + job(); + } + } + + void StartThreads() { + while (threads.size() < number_of_threads) { + threads.emplace_back([this]() { ThreadProc(); }); + } + } + +public: + ParallelOpThreadPool(std::size_t thread_number) : shutdown(false), number_of_threads(thread_number) {} + + ~ParallelOpThreadPool() { + Shutdown(); + } + + boost::future PostTask(std::function task) { + boost::packaged_task packagedTask(task); + boost::future ret = packagedTask.get_future(); + + boost::mutex::scoped_lock lock(task_queue_mutex); + + if (threads.size() < number_of_threads) + StartThreads(); + + task_queue.emplace(std::move(packagedTask)); + task_queue_condition.notify_one(); + + return ret; + } + + int GetNumberOfThreads() const { + return number_of_threads; + } + + void Shutdown() { + std::list threadsToJoin; + + { + boost::mutex::scoped_lock lock(task_queue_mutex); + shutdown = true; + task_queue_condition.notify_all(); + + threadsToJoin.swap(threads); + } + + for (boost::thread &t: threadsToJoin) + t.join(); + } + + bool IsPoolShutdown() { + boost::mutex::scoped_lock lock(task_queue_mutex); + return shutdown; + } + + std::size_t GetPendingTaskCount() { + boost::mutex::scoped_lock lock(task_queue_mutex); + return task_queue.size(); + } +}; + +class DoNotDisturb { +private: + boost::this_thread::disable_interruption dnd; +public: + DoNotDisturb() {} +}; + +#endif // FIRO_SPARK_THREADPOOL_H diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 8da9c2bc0b..71e3c6b165 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -576,7 +576,7 @@ UniValue importwallet(const JSONRPCRequest& request) pwallet->ScanForWalletTransactions(chainActive.Genesis(), true); } else { LogPrintf("Rescanning last %i blocks\n", pindex ? chainActive.Height() - pindex->nHeight + 1 : 0); - pwallet->ScanForWalletTransactions(pindex); + pwallet->ScanForWalletTransactions(pindex, true); } pwallet->MarkDirty(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 34ce58e6f6..e99bff2e8f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1668,7 +1668,7 @@ UniValue gettotalbalance(const JSONRPCRequest& request) "gettotalbalance\n" "\nReturns total (transparent + Spark private) balance.\n" "Transparent balance is the sum of UTXO amounts.\n" - "Private balance is the sum of confirmed Spark funds tracked by the wallet.\n" + "Private balance is confirmed Spark funds when Spark wallet is enabled; otherwise 0.\n" "\nResult:\n" "amount (numeric) The total balance in " + CURRENCY_UNIT + " for the wallet.\n" "\nExamples:\n" @@ -1677,11 +1677,13 @@ UniValue gettotalbalance(const JSONRPCRequest& request) + HelpExampleRpc("gettotalbalance", "") ); - EnsureSparkWalletIsAvailable(); LOCK2(cs_main, pwallet->cs_wallet); const CAmount transparent = pwallet->GetBalance(); - const CAmount spark = pwallet->sparkWallet->getAvailableBalance(); + CAmount spark = 0; + if (pwallet->sparkWallet) { + spark = pwallet->sparkWallet->getAvailableBalance(); + } return ValueFromAmount(transparent + spark); } From aca1bad211196d73f6dd127075fe36cafa1cd255 Mon Sep 17 00:00:00 2001 From: levoncrypto Date: Tue, 24 Mar 2026 20:07:07 +0400 Subject: [PATCH 04/12] CodeRabbit fixes (importwallet, InstantSend Spark inputs) --- qa/pull-tester/rpc-tests.py | 1 - src/llmq/quorums_instantsend.cpp | 2 ++ src/wallet/rpcdump.cpp | 24 ++++++++---------------- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index f6cb7cd3c9..0d21ec34a8 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -187,7 +187,6 @@ #, 'dip4-coinbasemerkleroots.py' # bip47 - 'bip47-sendreceive.py', 'bip47-walletrestore.py', 'sendtoaddress.py', diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp index 22372feed8..02ae2bab05 100644 --- a/src/llmq/quorums_instantsend.cpp +++ b/src/llmq/quorums_instantsend.cpp @@ -497,6 +497,8 @@ bool CInstantSendManager::CheckCanLock(const CTransaction& tx, bool printDebug, if (tx.IsSparkSpend()) { LOCK(cs_main); for (CTxIn const & in : tx.vin) { + if (in.scriptSig.empty()) + return false; GroupElement lTag; lTag.deserialize(&in.scriptSig.front()); if (spark::CSparkState::GetState()->IsUsedLTag(lTag)) diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 71e3c6b165..25499bbd63 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -477,8 +477,6 @@ UniValue importwallet(const JSONRPCRequest& request) bool fGood = true; - bool fMintUpdate = false; - int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); file.seekg(0, file.beg); @@ -547,6 +545,12 @@ UniValue importwallet(const JSONRPCRequest& request) pwallet->mapKeyMetadata[keyid].hdKeypath = hdKeypath; pwallet->mapKeyMetadata[keyid].hdMasterKeyID = hdMasterKeyID; pwallet->mapKeyMetadata[keyid].ParseComponents(); + if (pwallet->mapKeyMetadata[keyid].nChange.first == 2) { + throw JSONRPCError(RPC_WALLET_ERROR, + "This dump contains legacy Lelantus mint-seed keys (HD change=2). " + "This release cannot recover funds from those keys. " + ); + } } } @@ -554,13 +558,6 @@ UniValue importwallet(const JSONRPCRequest& request) fGood = false; continue; } - - if(!masterKeyID.IsNull() && fHd){ - // If change component in HD path is 2, this is a mint seed key. Add to mintpool. (Have to call after key addition) - if(pwallet->mapKeyMetadata[keyid].nChange.first==2){ - fMintUpdate = true; - } - } if (fLabel) pwallet->SetAddressBook(keyid, strLabel, "receive"); nTimeBegin = std::min(nTimeBegin, nTime); @@ -571,13 +568,8 @@ UniValue importwallet(const JSONRPCRequest& request) CBlockIndex *pindex = chainActive.FindEarliestAtLeast(nTimeBegin - 7200); - if (fMintUpdate) { - LogPrintf("Rescanning full chain (legacy HD mint key path)\n"); - pwallet->ScanForWalletTransactions(chainActive.Genesis(), true); - } else { - LogPrintf("Rescanning last %i blocks\n", pindex ? chainActive.Height() - pindex->nHeight + 1 : 0); - pwallet->ScanForWalletTransactions(pindex, true); - } + LogPrintf("Rescanning last %i blocks\n", pindex ? chainActive.Height() - pindex->nHeight + 1 : 0); + pwallet->ScanForWalletTransactions(pindex, true); pwallet->MarkDirty(); if (!fGood) From 5b5611a28c9b0a4249293f063ff626201a946d81 Mon Sep 17 00:00:00 2001 From: levoncrypto Date: Tue, 24 Mar 2026 21:46:50 +0400 Subject: [PATCH 05/12] Minor fixes --- src/llmq/quorums_instantsend.cpp | 19 ++++++++++--------- src/wallet/rpcdump.cpp | 8 +------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp index 02ae2bab05..b7adc721b4 100644 --- a/src/llmq/quorums_instantsend.cpp +++ b/src/llmq/quorums_instantsend.cpp @@ -494,15 +494,16 @@ bool CInstantSendManager::CheckCanLock(const CTransaction& tx, bool printDebug, return false; } - if (tx.IsSparkSpend()) { - LOCK(cs_main); - for (CTxIn const & in : tx.vin) { - if (in.scriptSig.empty()) - return false; - GroupElement lTag; - lTag.deserialize(&in.scriptSig.front()); - if (spark::CSparkState::GetState()->IsUsedLTag(lTag)) - return false; + if (tx.nType == isutils::INSTANTSEND_ADAPTED_TX) { + if (tx.IsSparkSpend()) { + for (CTxIn const & in : tx.vin) { + if (in.scriptSig.empty()) + return false; + GroupElement lTag; + lTag.deserialize(&in.scriptSig.front()); + if (spark::CSparkState::GetState()->IsUsedLTag(lTag)) + return false; + } } return true; } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 25499bbd63..6d2ad1871f 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -545,12 +545,6 @@ UniValue importwallet(const JSONRPCRequest& request) pwallet->mapKeyMetadata[keyid].hdKeypath = hdKeypath; pwallet->mapKeyMetadata[keyid].hdMasterKeyID = hdMasterKeyID; pwallet->mapKeyMetadata[keyid].ParseComponents(); - if (pwallet->mapKeyMetadata[keyid].nChange.first == 2) { - throw JSONRPCError(RPC_WALLET_ERROR, - "This dump contains legacy Lelantus mint-seed keys (HD change=2). " - "This release cannot recover funds from those keys. " - ); - } } } @@ -569,7 +563,7 @@ UniValue importwallet(const JSONRPCRequest& request) CBlockIndex *pindex = chainActive.FindEarliestAtLeast(nTimeBegin - 7200); LogPrintf("Rescanning last %i blocks\n", pindex ? chainActive.Height() - pindex->nHeight + 1 : 0); - pwallet->ScanForWalletTransactions(pindex, true); + pwallet->ScanForWalletTransactions(pindex); pwallet->MarkDirty(); if (!fGood) From f070e6e9944e10aa1e554ddd1aca5b870497e175 Mon Sep 17 00:00:00 2001 From: levoncrypto Date: Tue, 24 Mar 2026 21:59:17 +0400 Subject: [PATCH 06/12] Fix LTag check for Spark IS adapted tx --- src/llmq/quorums_instantsend.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp index b7adc721b4..9538833399 100644 --- a/src/llmq/quorums_instantsend.cpp +++ b/src/llmq/quorums_instantsend.cpp @@ -495,15 +495,13 @@ bool CInstantSendManager::CheckCanLock(const CTransaction& tx, bool printDebug, } if (tx.nType == isutils::INSTANTSEND_ADAPTED_TX) { - if (tx.IsSparkSpend()) { - for (CTxIn const & in : tx.vin) { - if (in.scriptSig.empty()) - return false; - GroupElement lTag; - lTag.deserialize(&in.scriptSig.front()); - if (spark::CSparkState::GetState()->IsUsedLTag(lTag)) - return false; - } + for (CTxIn const & in : tx.vin) { + if (in.scriptSig.empty()) + return false; + GroupElement lTag; + lTag.deserialize(&in.scriptSig.front()); + if (spark::CSparkState::GetState()->IsUsedLTag(lTag)) + return false; } return true; } From fba6ac9a4d92eea17a69feb00b9ffcd0100c4b22 Mon Sep 17 00:00:00 2001 From: levoncrypto Date: Tue, 24 Mar 2026 22:20:06 +0400 Subject: [PATCH 07/12] Fix for test after Lelantus strip (spark_mint.py) --- qa/rpc-tests/spark_mint.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/qa/rpc-tests/spark_mint.py b/qa/rpc-tests/spark_mint.py index e57da982ba..598e0a99f1 100755 --- a/qa/rpc-tests/spark_mint.py +++ b/qa/rpc-tests/spark_mint.py @@ -3,17 +3,15 @@ from test_framework.util import assert_equal, assert_raises_message, JSONRPCException class SparkMintTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 - self.setup_clean_chain = False + self.setup_clean_chain = True def run_test(self): assert_raises_message( JSONRPCException, "Spark is not activated yet", self.nodes[0].mintspark, 1) - self.nodes[0].generate(501) # generate coins From 9f0b130ed9f6d1cb1806420a64ec6f2b7c4aff30 Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 Date: Fri, 17 Apr 2026 06:18:39 +0400 Subject: [PATCH 08/12] Some unneeded changes reverted, minor fixes done. --- src/qt/forms/optionsdialog.ui | 49 ++++++ src/qt/optionsdialog.cpp | 39 ++++- src/qt/optionsmodel.cpp | 46 ++++- src/qt/optionsmodel.h | 5 + src/qt/overviewpage.cpp | 15 ++ src/qt/overviewpage.h | 1 + src/qt/walletmodeltransaction.h | 7 - src/spark/state.cpp | 70 +++++++- src/spark/threadpool.h | 29 +-- src/validation.cpp | 15 +- src/wallet/CMakeLists.txt | 1 - src/wallet/test/CMakeLists.txt | 1 - src/wallet/test/txbuilder_tests.cpp | 238 ------------------------- src/wallet/txbuilder.cpp | 262 ---------------------------- src/wallet/txbuilder.h | 48 ----- 15 files changed, 238 insertions(+), 588 deletions(-) delete mode 100644 src/wallet/test/txbuilder_tests.cpp delete mode 100644 src/wallet/txbuilder.cpp delete mode 100644 src/wallet/txbuilder.h diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index b7aeb0821b..b3de4370d3 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -163,6 +163,55 @@ + + + + Restore Spark and wallet transaction data following a full reindex (deletes Spark mint records from the wallet, then reindexes the chain and reapplies wallet transactions). This can take several hours. + + + &Reindex Spark wallet data + + + + + + + + + + Spark + + + + + + When enabled, the wallet can prompt to anonymize transparent funds using Spark. + + + Enable &auto-anonymize features + + + + + + + Split outputs when minting to Spark for better privacy. + + + Enable &splitting when minting + + + + + + + Show the Spark manual anonymize controls in the overview. + + + Enable Spark &manual-anonymize page + + + diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 6c7d0c3d84..f5a7fce8f2 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -19,6 +19,7 @@ #ifdef ENABLE_WALLET #include "wallet/wallet.h" // for CWallet::GetRequiredFee() +#include "spark/state.h" #endif #include @@ -164,6 +165,9 @@ void OptionsDialog::setModel(OptionsModel *_model) connect(ui->threadsScriptVerif, qOverload(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning); /* Wallet */ connect(ui->spendZeroConfChange, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); +#ifdef ENABLE_WALLET + connect(ui->reindexSpark, &QCheckBox::clicked, this, &OptionsDialog::handleEnabledZapChanged); +#endif /* Network */ connect(ui->allowIncoming, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); connect(ui->connectSocks, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); @@ -182,7 +186,20 @@ void OptionsDialog::setMapper() /* Wallet */ mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange); +#ifdef ENABLE_WALLET + mapper->addMapping(ui->reindexSpark, OptionsModel::ReindexSpark); +#endif mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures); + mapper->addMapping(ui->autoAnonymize, OptionsModel::AutoAnonymize); + mapper->addMapping(ui->fSplit, OptionsModel::Split); + mapper->addMapping(ui->sparkPage, OptionsModel::SparkPage); +#ifdef ENABLE_WALLET + if (!spark::IsSparkAllowed()) { + ui->sparkGroupBox->setVisible(false); + } +#else + ui->sparkGroupBox->setVisible(false); +#endif /* Network */ mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); mapper->addMapping(ui->allowIncoming, OptionsModel::Listen); @@ -255,10 +272,24 @@ void OptionsDialog::on_hideTrayIcon_stateChanged(int fState) ui->minimizeToTray->setEnabled(true); } } -void OptionsDialog::handleEnabledZapChanged(){ - QMessageBox msgBox; - - clearStatusLabel(); +void OptionsDialog::handleEnabledZapChanged() +{ +#ifdef ENABLE_WALLET + if (ui->reindexSpark->isChecked()) { + QMessageBox::StandardButton retval = QMessageBox::warning(this, tr("Confirm Spark reindex"), + tr("Warning: On restart, this setting will wipe your transaction list, reindex the blockchain, and restore wallet data from your seed. Spark mint records are cleared and rebuilt from the chain. This will likely take a few hours. Are you sure?"), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel); + if (retval == QMessageBox::Cancel) { + ui->reindexSpark->setChecked(false); + } else { + showRestartWarning(); + } + } else +#endif + { + clearStatusLabel(); + } } void OptionsDialog::showRestartWarning(bool fPersistent) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 1e8f7cca07..57e59727c8 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -13,7 +13,7 @@ #include "amount.h" #include "init.h" -#include "validation.h" // For DEFAULT_SCRIPTCHECK_THREADS +#include "validation.h" // For DEFAULT_SCRIPTCHECK_THREADS and DEFAULT_ZAP_WALLET #include "net.h" #include "netbase.h" #include "txdb.h" // for -dbcache defaults @@ -89,6 +89,14 @@ void OptionsModel::Init(bool resetSettings) settings.setValue("fSplit", true); fSplit = settings.value("fSplit", true).toBool(); + if (!settings.contains("fSparkPage")) { + if (settings.contains("fLelantusPage")) + settings.setValue("fSparkPage", settings.value("fLelantusPage")); + else + settings.setValue("fSparkPage", false); + } + fSparkPage = settings.value("fSparkPage", false).toBool(); + // These are shared with the core or have a command-line parameter // and we want command-line parameters to overwrite the GUI settings. // @@ -118,6 +126,23 @@ void OptionsModel::Init(bool resetSettings) if (!SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool())) addOverriddenOption("-spendzeroconfchange"); + if (!settings.contains("bReindexSpark")) { + if (settings.contains("bReindexLelantus")) + settings.setValue("bReindexSpark", settings.value("bReindexLelantus")); + else + settings.setValue("bReindexSpark", DEFAULT_ZAP_WALLET); + } + bool reindexSpark = settings.value("bReindexSpark").toBool(); + if (reindexSpark) { + if (!SoftSetBoolArg("-zapwalletmints", true)) + addOverriddenOption("-zapwalletmints"); + if (!SoftSetBoolArg("-reindex", true)) + addOverriddenOption("-reindex"); + if (!SoftSetArg("-zapwallettxes", std::string("1"))) + addOverriddenOption("-zapwallettxes"); + } + settings.setValue("bReindexSpark", false); + #endif // Network @@ -262,6 +287,12 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return fAutoAnonymize; case Split: return fSplit; +#ifdef ENABLE_WALLET + case ReindexSpark: + return settings.value("bReindexSpark"); +#endif + case SparkPage: + return fSparkPage; case DatabaseCache: return settings.value("nDatabaseCache"); case ThreadsScriptVerif: @@ -408,6 +439,19 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in fSplit = value.toBool(); settings.setValue("fSplit", fSplit); break; +#ifdef ENABLE_WALLET + case ReindexSpark: + if (settings.value("bReindexSpark") != value) { + settings.setValue("bReindexSpark", value); + setRestartRequired(true); + } + break; +#endif + case SparkPage: + fSparkPage = value.toBool(); + settings.setValue("fSparkPage", fSparkPage); + Q_EMIT sparkPageChanged(fSparkPage); + break; case DatabaseCache: if (settings.value("nDatabaseCache") != value) { settings.setValue("nDatabaseCache", value); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 4715af8932..206e206586 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -50,6 +50,8 @@ class OptionsModel : public QAbstractListModel AutoAnonymize, // bool Split, // bool enableRapAddresses, // bool + ReindexSpark, // bool (wallet: zap Spark mints + reindex; QSettings bReindexSpark) + SparkPage, // bool (show Spark manual-anonymize UI; QSettings fSparkPage) OptionIDRowCount, }; @@ -73,6 +75,7 @@ class OptionsModel : public QAbstractListModel const QString& getOverriddenByCommandLine() { return strOverriddenByCommandLine; } bool getAutoAnonymize() { return fAutoAnonymize; } bool getfSplit() { return fSplit; } + bool getSparkPage() { return fSparkPage; } /* Restart flag helper */ void setRestartRequired(bool fRequired); @@ -89,6 +92,7 @@ class OptionsModel : public QAbstractListModel bool fCoinControlFeatures; bool fAutoAnonymize; bool fSplit; + bool fSparkPage; bool fenableRapAddresses; /* settings that were overridden by command-line */ @@ -104,6 +108,7 @@ class OptionsModel : public QAbstractListModel void coinControlFeaturesChanged(bool); void enableRapAddressesChanged(bool); void autoAnonymizeChanged(bool); + void sparkPageChanged(bool); void hideTrayIconChanged(bool); }; diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 83111e4f33..84260c4cd1 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -10,6 +10,7 @@ #include "guiconstants.h" #include "guiutil.h" #include "sparkmodel.h" +#include "spark/state.h" #include "optionsmodel.h" #include "platformstyle.h" #include "transactionfilterproxy.h" @@ -296,6 +297,7 @@ void OverviewPage::setBalance( ui->labelAnonymizable->setText(BitcoinUnits::formatWithUnit(unit, anonymizableBalance, false, BitcoinUnits::separatorAlways)); auto wallet = walletModel->getWallet(); + updateSparkAnonymizeRowVisibility(); ui->anonymizeButton->setEnabled(wallet && wallet->sparkWallet && spark::IsSparkAllowed() && anonymizableBalance > 0); // only show immature (newly mined) balance if it's non-zero, so as not to complicate things @@ -369,9 +371,11 @@ void OverviewPage::setWalletModel(WalletModel *model) connect(model, &WalletModel::balanceChanged, this, &OverviewPage::setBalance); connect(model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &OverviewPage::updateDisplayUnit); + connect(model->getOptionsModel(), &OptionsModel::sparkPageChanged, this, &OverviewPage::updateSparkAnonymizeRowVisibility); updateWatchOnlyLabels(model->haveWatchOnly()); connect(model, &WalletModel::notifyWatchonlyChanged, this, &OverviewPage::updateWatchOnlyLabels); + updateSparkAnonymizeRowVisibility(); } // update the display unit, to not use the default ("BTC") @@ -452,4 +456,15 @@ void OverviewPage::adjustTextSize(int width, int height){ ui->labelPrivate->setFont(labelFont); ui->label_4->setFont(labelFont); +} + +void OverviewPage::updateSparkAnonymizeRowVisibility() +{ + if (!walletModel || !walletModel->getOptionsModel()) { + return; + } + const bool show = spark::IsSparkAllowed() && walletModel->getOptionsModel()->getSparkPage(); + ui->labelAnonymizableText->setVisible(show); + ui->labelAnonymizable->setVisible(show); + ui->anonymizeButton->setVisible(show); } \ No newline at end of file diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index e68c183a97..7073b40d15 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -98,6 +98,7 @@ private Q_SLOTS: void updateAlerts(const QString &warnings); void updateWatchOnlyLabels(bool showWatchOnly); void handleOutOfSyncWarningClicks(); + void updateSparkAnonymizeRowVisibility(); }; #endif // BITCOIN_QT_OVERVIEWPAGE_H diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index f948bf55de..64922efada 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -5,9 +5,6 @@ #ifndef BITCOIN_QT_WALLETMODELTRANSACTION_H #define BITCOIN_QT_WALLETMODELTRANSACTION_H -#include "../primitives/mint_spend.h" -#include "spark/state.h" - #include "walletmodel.h" #include @@ -45,10 +42,6 @@ class WalletModelTransaction CWalletTx *walletTransaction; CReserveKey *keyChange; CAmount fee; - - // spark transaction - std::vector spendCoins; - std::vector mintCoins; }; #endif // BITCOIN_QT_WALLETMODELTRANSACTION_H diff --git a/src/spark/state.cpp b/src/spark/state.cpp index 313342584e..eb89967259 100644 --- a/src/spark/state.cpp +++ b/src/spark/state.cpp @@ -1,10 +1,11 @@ +#include "threadpool.h" #include "state.h" #include "compat_layer.h" #include "sparkname.h" #include "../validation.h" #include "../batchproof_container.h" - +#include #include namespace spark { @@ -15,12 +16,17 @@ struct ProofCheckState { // result of the check (if fChecked is true) bool fResult; + + // if this is non-null, then the proof is being checked right now + std::shared_ptr> checkInProgress; }; // map from transaction hash to the state of checking its proofs static std::map gCheckedSparkSpendTransactions; static CCriticalSection cs_checkedSparkSpendTransactions; +static ParallelOpThreadPool gCheckProofThreadPool(std::min(boost::thread::hardware_concurrency(), 4u)); + static CSparkState sparkState; static bool CheckLTag( @@ -97,7 +103,7 @@ unsigned char GetNetworkType() { } /* - * Util funtions + * Util functions */ size_t CountCoinInBlock(CBlockIndex *index, int id) { return index->sparkMintedCoins.count(id) > 0 @@ -787,10 +793,61 @@ bool CheckSparkSpendTransaction( fChecked = true; } } + // If the check is in progress and we are doing a stateful check, we need to wait + else if (checkState.checkInProgress && fStatefulSigmaCheck) { + // wait for the check to complete + auto future = checkState.checkInProgress; + cs_checkedSparkSpendTransactions.unlock(); + bool result = future->get(); + cs_checkedSparkSpendTransactions.lock(); + + // Entry may have been erased by DisconnectTipSpark during the unlock window + auto it = gCheckedSparkSpendTransactions.find(hashTx); + if (it == gCheckedSparkSpendTransactions.end()) { + fRecheckNeeded = true; + continue; + } + ProofCheckState& checkStateAfterWait = it->second; + + checkStateAfterWait.fChecked = true; + checkStateAfterWait.fResult = result; + checkStateAfterWait.checkInProgress = nullptr; + + if (!result) { + // unfortunately, it's possible that the proof was checked and failed + // because the anonymity set was incomplete at the time of checking. We need + // to recheck the proof again + fRecheckNeeded = true; + gCheckedSparkSpendTransactions.erase(hashTx); + } + else + fChecked = true; + } + } + else if (!fStatefulSigmaCheck && !gCheckProofThreadPool.IsPoolShutdown()) { + // not an urgent check, put the proof into the thread pool for verification + // don't post a request if there are too many tasks already + if (gCheckProofThreadPool.GetPendingTaskCount() < (std::size_t)gCheckProofThreadPool.GetNumberOfThreads()/2) { + auto future = gCheckProofThreadPool.PostTask([spend, cover_sets]() mutable { + try { + bool result = spark::SpendTransaction::verify(*spend, cover_sets); + spend.reset(); + cover_sets.clear(); + return result; + } catch (const std::exception &) { + return false; + } + }); + auto &checkState = gCheckedSparkSpendTransactions[hashTx]; + checkState.fChecked = false; + checkState.fResult = false; + checkState.checkInProgress = std::make_shared>(std::move(future)); + scheduledAsync = true; + } } } while (fRecheckNeeded); - + if (fChecked) { // if we are here, then the proof was already checked and it passed passVerify = true; @@ -799,10 +856,6 @@ bool CheckSparkSpendTransaction( if (fStatefulSigmaCheck) { // we need the answer now, so verify and execute passVerify = spark::SpendTransaction::verify(*spend, cover_sets); - { - LOCK(cs_checkedSparkSpendTransactions); - gCheckedSparkSpendTransactions[hashTx] = {true, passVerify}; - } } else { if (scheduledAsync) { @@ -836,7 +889,7 @@ bool CheckSparkSpendTransaction( if (!(sparkTxInfo && sparkTxInfo->spTransactions.count(hashTx) > 0)) { for (size_t i = 0; i < lTags.size(); ++i) { if (!CheckLTag(state, sparkTxInfo, lTags[i], nHeight, false)) { - LogPrintf("CheckSparkSpendTransaction: lTAg check failed, ltag=%s\n", lTags[i]); + LogPrintf("CheckSparkSpendTransaction: lTag check failed, ltag=%s\n", lTags[i]); return false; } } @@ -966,6 +1019,7 @@ bool CheckSparkTransaction( } void ShutdownSparkState() { + gCheckProofThreadPool.Shutdown(); } uint256 GetTxHashFromCoin(const spark::Coin& coin) { diff --git a/src/spark/threadpool.h b/src/spark/threadpool.h index 3db83a7d36..8dcac043c4 100644 --- a/src/spark/threadpool.h +++ b/src/spark/threadpool.h @@ -1,9 +1,8 @@ -// Copyright (c) 2022 The Firo Core Developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) The Firo Core Developers +// Thread pool for Spark wallet background tasks (same implementation as historical liblelantus/threadpool.h). -#ifndef FIRO_SPARK_THREADPOOL_H -#define FIRO_SPARK_THREADPOOL_H +#ifndef THREADPOOL_H +#define THREADPOOL_H #include #include @@ -24,7 +23,7 @@ // Number of seconds before thread shuts down if idle constexpr static int secondsBeforeThreadShutdown = 60; -// Simple thread pool for Spark wallet background tasks (moved from liblelantus with Lelantus strip). +// Simple thread pool class for using multiple cores efficiently template class ParallelOpThreadPool { @@ -46,6 +45,8 @@ class ParallelOpThreadPool { task_queue_condition.wait_for(lock, boost::chrono::seconds(secondsBeforeThreadShutdown), [this] { return !task_queue.empty() || shutdown; }); if (task_queue.empty()) { + // Either timeout or shutdown. If it's a timeout we need to delete ourself from the thread list and detach the thread + // In case of shutdown thread list will be empty and destructor will wait for this thread completion boost::thread::id currentId = boost::this_thread::get_id(); auto pThread = std::find_if(threads.begin(), threads.end(), [currentId](const boost::thread &t) { return t.get_id() == currentId; }); @@ -63,9 +64,10 @@ class ParallelOpThreadPool { } void StartThreads() { - while (threads.size() < number_of_threads) { - threads.emplace_back([this]() { ThreadProc(); }); - } + // should be called with mutex acquired + // start missing threads + while (threads.size() < number_of_threads) + threads.emplace_back(std::bind(&ParallelOpThreadPool::ThreadProc, this)); } public: @@ -75,12 +77,14 @@ class ParallelOpThreadPool { Shutdown(); } + // Post a task to the thread pool and return a future to wait for its completion boost::future PostTask(std::function task) { boost::packaged_task packagedTask(task); boost::future ret = packagedTask.get_future(); boost::mutex::scoped_lock lock(task_queue_mutex); + // lazy start threads on first request or after shutdown if (threads.size() < number_of_threads) StartThreads(); @@ -102,9 +106,11 @@ class ParallelOpThreadPool { shutdown = true; task_queue_condition.notify_all(); + // move the list to separate variable to wait for the shutdown process to complete threadsToJoin.swap(threads); } + // wait for all the threads for (boost::thread &t: threadsToJoin) t.join(); } @@ -120,6 +126,8 @@ class ParallelOpThreadPool { } }; + +// helper class to put thread interruption on pause class DoNotDisturb { private: boost::this_thread::disable_interruption dnd; @@ -127,4 +135,5 @@ class DoNotDisturb { DoNotDisturb() {} }; -#endif // FIRO_SPARK_THREADPOOL_H + +#endif diff --git a/src/validation.cpp b/src/validation.cpp index 847489ef62..ea68979e1f 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -687,33 +687,32 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, bool fChe } const auto ¶ms = ::Params().GetConsensus(); + // Use nTxHeight (not nHeight) so mempool calls with nHeight==INT_MAX use chain tip like + // nExchangeAddressStartBlock checks above — same idea as Sigma/Lelantus mempool gates. if (tx.IsZerocoinSpend() || tx.IsZerocoinMint()) { - if (!isVerifyDB && nHeight >= params.nDisableZerocoinStartBlock) + if (!isVerifyDB && nTxHeight >= params.nDisableZerocoinStartBlock) return state.DoS(1, error("Zerocoin is disabled at this point")); } if (tx.IsSigmaSpend() || tx.IsSigmaMint()) { - if (!isVerifyDB && nHeight >= params.nLelantusStartBlock) + if (!isVerifyDB && nTxHeight >= params.nLelantusStartBlock) return state.DoS(1, error("Sigma already is not available, start using Lelantus.")); } if (tx.IsLelantusJoinSplit() || tx.IsLelantusMint()) { - const int lelantusDisabledHeight = params.nLelantusGracefulPeriod > 0 - ? params.nLelantusGracefulPeriod - : params.nSparkStartBlock; - if (!isVerifyDB && nHeight >= lelantusDisabledHeight) + if (!isVerifyDB && nTxHeight >= params.nLelantusGracefulPeriod) return state.DoS(1, error("Lelantus already is not available, start using Spark.")); } if (tx.IsZerocoinRemint()) { - if (!isVerifyDB && (nHeight < params.nSigmaStartBlock || nHeight >= params.nSigmaStartBlock + params.nZerocoinToSigmaRemintWindowSize)) + if (!isVerifyDB && (nTxHeight < params.nSigmaStartBlock || nTxHeight >= params.nSigmaStartBlock + params.nZerocoinToSigmaRemintWindowSize)) // we allow transactions of remint type only during specific window return false; } } bool isInWhitelist = Params().GetConsensus().txidWhitelist.count(tx.GetHash()) > 0; - if (nHeight >= ::Params().GetConsensus().nStartBlacklist && !isInWhitelist) { + if (nTxHeight >= ::Params().GetConsensus().nStartBlacklist && !isInWhitelist) { for (const auto& vin : tx.vin) { if (txid_blacklist.count(vin.prevout.hash.GetHex()) > 0) { return state.DoS(100, error("Spending this tx is temporarily disabled"), diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 8ff481b344..d585589319 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -23,7 +23,6 @@ add_library(firo_wallet STATIC EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/db.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rpcdump.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rpcwallet.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/txbuilder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/walletexcept.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wallet.cpp ${CMAKE_CURRENT_SOURCE_DIR}/walletdb.cpp diff --git a/src/wallet/test/CMakeLists.txt b/src/wallet/test/CMakeLists.txt index e15a41f331..3f1344abc1 100644 --- a/src/wallet/test/CMakeLists.txt +++ b/src/wallet/test/CMakeLists.txt @@ -10,7 +10,6 @@ target_sources(test_firo ${CMAKE_CURRENT_SOURCE_DIR}/crypto_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mnemonic_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/spark_wallet_tests.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/txbuilder_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wallet_test_fixture.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wallet_tests.cpp ) diff --git a/src/wallet/test/txbuilder_tests.cpp b/src/wallet/test/txbuilder_tests.cpp deleted file mode 100644 index 3f97f4cc45..0000000000 --- a/src/wallet/test/txbuilder_tests.cpp +++ /dev/null @@ -1,238 +0,0 @@ -#include "../txbuilder.h" -#include "../../amount.h" -#include "../../random.h" - -#include "wallet_test_fixture.h" - -#include - -#include -#include - -static const CBitcoinAddress randomAddr1("aHEog3QYDGa8wH4Go9igKLDFkpaMsi3btq"); -static const CBitcoinAddress randomAddr2("aLTSv7QbTZbkgorYEhbNx2gH4hGYNLsoGv"); - -class TestInputSigner : public InputSigner -{ -public: - CScript signature; - -public: - TestInputSigner() - { - } - - explicit TestInputSigner(const CScript& sig, const COutPoint& outputParam = COutPoint(), uint32_t seq = CTxIn::SEQUENCE_FINAL) : - InputSigner(outputParam, seq), - signature(sig) - { - } - - CScript Sign(const CMutableTransaction& tx, const uint256& sig) override - { - return signature; - } -}; - -class TestTxBuilder : public TxBuilder -{ -public: - std::vector amountsRequested; - std::vector changesRequested; - std::vector> adjustFeeRequested; - - std::function>& signers, CAmount required)> getInputs; - std::function& outputs, CAmount amount, CWalletDB& walletdb)> getChanges; - std::function adjustFee; - -public: - explicit TestTxBuilder(CWallet& walletParam) : TxBuilder(walletParam) - { - } - -protected: - CAmount GetInputs(std::vector>& signers, CAmount required) override - { - amountsRequested.push_back(required); - - return getInputs ? getInputs(signers, required) : required; - } - - CAmount GetChanges(std::vector& outputs, CAmount amount, CWalletDB& walletdb) override - { - changesRequested.push_back(amount); - - return getChanges ? getChanges(outputs, amount, walletdb) : amount; - } - - CAmount AdjustFee(CAmount needed, unsigned txSize) override - { - adjustFeeRequested.push_back(std::make_pair(needed, txSize)); - - return adjustFee ? adjustFee(needed, txSize) : TxBuilder::AdjustFee(needed, txSize); - } -}; - -BOOST_FIXTURE_TEST_SUITE(wallet_txbuilder_tests, WalletTestingSetup) - -BOOST_AUTO_TEST_CASE(build_with_empty_recipients) -{ - TestTxBuilder builder(*pwalletMain); - CAmount fee; - CWalletDB walletdb(pwalletMain->strWalletFile); - bool fChangeAddedToFee; - BOOST_CHECK_EXCEPTION( - builder.Build({}, fee, fChangeAddedToFee, walletdb), - std::invalid_argument, - [](const std::invalid_argument& e) { return e.what() == std::string("No recipients"); } - ); -} - -BOOST_AUTO_TEST_CASE(build_with_some_recipients_have_negative_amount) -{ - TestTxBuilder builder(*pwalletMain); - CAmount fee; - - std::vector recipients = { - {.scriptPubKey = GetScriptForDestination(randomAddr1.Get()), .nAmount = 10, .fSubtractFeeFromAmount = false}, - {.scriptPubKey = GetScriptForDestination(randomAddr2.Get()), .nAmount = -5, .fSubtractFeeFromAmount = false} - }; - CWalletDB walletdb(pwalletMain->strWalletFile); - bool fChangeAddedToFee; - BOOST_CHECK_EXCEPTION( - builder.Build(recipients, fee, fChangeAddedToFee, walletdb), - std::invalid_argument, - [](const std::invalid_argument& e) { return e.what() == std::string("Recipient 1 has invalid amount"); } - ); -} - -BOOST_AUTO_TEST_CASE(build_with_some_recipients_have_amount_exceed_limit) -{ - TestTxBuilder builder(*pwalletMain); - CAmount fee; - - std::vector recipients = { - {.scriptPubKey = GetScriptForDestination(randomAddr1.Get()), .nAmount = MAX_MONEY + 1, .fSubtractFeeFromAmount = false}, - {.scriptPubKey = GetScriptForDestination(randomAddr2.Get()), .nAmount = 1, .fSubtractFeeFromAmount = false} - }; - - CWalletDB walletdb(pwalletMain->strWalletFile); - bool fChangeAddedToFee; - BOOST_CHECK_EXCEPTION( - builder.Build(recipients, fee, fChangeAddedToFee, walletdb), - std::invalid_argument, - [](const std::invalid_argument& e) { return e.what() == std::string("Recipient 0 has invalid amount"); } - ); -} - -BOOST_AUTO_TEST_CASE(build_with_no_subtract_fee) -{ - TestTxBuilder builder(*pwalletMain); - CAmount fee; - - std::vector recipients = { - {.scriptPubKey = GetScriptForDestination(randomAddr1.Get()), .nAmount = 10, .fSubtractFeeFromAmount = false}, - {.scriptPubKey = GetScriptForDestination(randomAddr2.Get()), .nAmount = 20, .fSubtractFeeFromAmount = false} - }; - CWalletDB walletdb(pwalletMain->strWalletFile); - bool fChangeAddedToFee; - auto tx = builder.Build(recipients, fee, fChangeAddedToFee, walletdb); - - BOOST_CHECK_GT(fee, 0); - BOOST_CHECK_GT(builder.amountsRequested.size(), 0); - BOOST_CHECK_EQUAL(builder.amountsRequested.back(), 30 + fee); - - BOOST_CHECK_EQUAL(tx.tx->vout.size(), 2); - BOOST_CHECK(tx.tx->vout[0].scriptPubKey == GetScriptForDestination(randomAddr1.Get())); - BOOST_CHECK_EQUAL(tx.tx->vout[0].nValue, 10); - BOOST_CHECK(tx.tx->vout[1].scriptPubKey == GetScriptForDestination(randomAddr2.Get())); - BOOST_CHECK_EQUAL(tx.tx->vout[1].nValue, 20); -} - -BOOST_AUTO_TEST_CASE(build_with_subtract_fee) -{ - TestTxBuilder builder(*pwalletMain); - CAmount fee; - - std::vector recipients = { - {.scriptPubKey = GetScriptForDestination(randomAddr1.Get()), .nAmount = 10, .fSubtractFeeFromAmount = true}, - {.scriptPubKey = GetScriptForDestination(randomAddr2.Get()), .nAmount = 20, .fSubtractFeeFromAmount = true} - }; - - CWalletDB walletdb(pwalletMain->strWalletFile); - bool fChangeAddedToFee; - auto tx = builder.Build(recipients, fee, fChangeAddedToFee, walletdb); - - BOOST_CHECK_GT(fee, 0); - BOOST_CHECK_GT(builder.amountsRequested.size(), 0); - BOOST_CHECK_EQUAL(builder.amountsRequested.back(), 30); - - BOOST_CHECK_EQUAL(tx.tx->vout.size(), 2); - BOOST_CHECK(tx.tx->vout[0].scriptPubKey == GetScriptForDestination(randomAddr1.Get())); - BOOST_CHECK_EQUAL(tx.tx->vout[0].nValue, 10 - (fee / 2 + fee % 2)); - BOOST_CHECK(tx.tx->vout[1].scriptPubKey == GetScriptForDestination(randomAddr2.Get())); - BOOST_CHECK_EQUAL(tx.tx->vout[1].nValue, 20 - fee / 2); -} - -BOOST_AUTO_TEST_CASE(build_with_changes) -{ - TestTxBuilder builder(*pwalletMain); - CAmount fee; - CScript in1, in2; - COutPoint out1(GetRandHash(), 0), out2(GetRandHash(), 1); - - in1 << std::vector({ 0x21, 0xe3, 0xad, 0x9a, 0xec, 0x5b, 0x70, 0xcb, 0x4c, 0xc1, 0xd8, 0xe2, 0x95, 0x27, 0xe3, 0x7c }); - in2 << std::vector({ 0xac, 0xd9, 0x86, 0x7d, 0xd7, 0x6e, 0xc1, 0xb7, 0x9d, 0xde, 0xdc, 0xbd, 0x91, 0xc1, 0x8e, 0xed }); - - builder.getInputs = [&in1, &in2, &out1, &out2](std::vector>& signers, CAmount required) { - signers.push_back(std::unique_ptr(new TestInputSigner(in1, out1, 1))); - signers.push_back(std::unique_ptr(new TestInputSigner(in2, out2, 2))); - return required + 5; - }; - - builder.getChanges = [](std::vector& outputs, CAmount amount, CWalletDB& walletdb) { - outputs.emplace_back(amount - 1, CScript()); - return 1; - }; - - std::vector recipients = { - {.scriptPubKey = GetScriptForDestination(randomAddr1.Get()), .nAmount = 10, .fSubtractFeeFromAmount = false}, - {.scriptPubKey = GetScriptForDestination(randomAddr2.Get()), .nAmount = 20, .fSubtractFeeFromAmount = false} - }; - CWalletDB walletdb(pwalletMain->strWalletFile); - bool fChangeAddedToFee; - auto tx = builder.Build(recipients, fee, fChangeAddedToFee, walletdb); - - BOOST_CHECK_GT(fee, 0); - BOOST_CHECK_GT(builder.amountsRequested.size(), 0); - BOOST_CHECK_EQUAL(builder.amountsRequested.back(), 30 + fee - 1); - - BOOST_CHECK_GT(builder.changesRequested.size(), 0); - - for (auto& call : builder.changesRequested) { - BOOST_CHECK_EQUAL(call, 5); - } - - BOOST_CHECK_GT(builder.adjustFeeRequested.size(), 0); - - for (auto& call : builder.adjustFeeRequested) { - BOOST_CHECK_GT(call.first, 0); - BOOST_CHECK_GT(call.second, 0); - } - - BOOST_CHECK_EQUAL(tx.tx->vin.size(), 2); - BOOST_CHECK(tx.tx->vin[0].scriptSig == in1); - BOOST_CHECK(tx.tx->vin[0].prevout == out1); - BOOST_CHECK_EQUAL(tx.tx->vin[0].nSequence, 1); - BOOST_CHECK(tx.tx->vin[1].scriptSig == in2); - BOOST_CHECK(tx.tx->vin[1].prevout == out2); - BOOST_CHECK_EQUAL(tx.tx->vin[1].nSequence, 2); - - BOOST_CHECK_EQUAL(tx.tx->vout.size(), 3); - BOOST_CHECK(std::find_if(tx.tx->vout.begin(), tx.tx->vout.end(), [](const CTxOut& o) { return o.nValue == 4; }) != tx.tx->vout.end()); - BOOST_CHECK(std::find_if(tx.tx->vout.begin(), tx.tx->vout.end(), [](const CTxOut& o) { return o.nValue == 10; }) != tx.tx->vout.end()); - BOOST_CHECK(std::find_if(tx.tx->vout.begin(), tx.tx->vout.end(), [](const CTxOut& o) { return o.nValue == 20; }) != tx.tx->vout.end()); -} - -BOOST_AUTO_TEST_SUITE_END() - diff --git a/src/wallet/txbuilder.cpp b/src/wallet/txbuilder.cpp deleted file mode 100644 index 57ad3d6646..0000000000 --- a/src/wallet/txbuilder.cpp +++ /dev/null @@ -1,262 +0,0 @@ -#include "txbuilder.h" - -#include "../amount.h" -#include "../validation.h" -#include "../policy/policy.h" -#include "../random.h" -#include "../script/script.h" -#include "../txmempool.h" -#include "../uint256.h" -#include "../util.h" - -#include - -#include -#include -#include -#include - -#include -#include - -InputSigner::InputSigner() : InputSigner(COutPoint()) -{ -} - -InputSigner::InputSigner(const COutPoint& output, uint32_t seq) : output(output), sequence(seq) -{ -} - -InputSigner::~InputSigner() -{ -} - -TxBuilder::TxBuilder(CWallet& wallet) noexcept : wallet(wallet) -{ -} - -TxBuilder::~TxBuilder() -{ -} - -CWalletTx TxBuilder::Build(const std::vector& recipients, CAmount& fee, bool& fChangeAddedToFee, CWalletDB& walletdb) -{ - if (recipients.empty()) { - throw std::invalid_argument(_("No recipients")); - } - - // calculate total value to spend - CAmount spend = 0; - unsigned recipientsToSubtractFee = 0; - - for (size_t i = 0; i < recipients.size(); i++) { - auto& recipient = recipients[i]; - - if (!MoneyRange(recipient.nAmount)) { - throw std::invalid_argument(boost::str(boost::format(_("Recipient %1% has invalid amount")) % i)); - } - - spend += recipient.nAmount; - - if (recipient.fSubtractFeeFromAmount) { - recipientsToSubtractFee++; - } - } - - CWalletTx result; - CMutableTransaction tx; - - result.fTimeReceivedIsTxTime = true; - result.BindWallet(&wallet); - - // Discourage fee sniping. - // - // For a large miner the value of the transactions in the best block and - // the mempool can exceed the cost of deliberately attempting to mine two - // blocks to orphan the current best block. By setting nLockTime such that - // only the next block can include the transaction, we discourage this - // practice as the height restricted and limited blocksize gives miners - // considering fee sniping fewer options for pulling off this attack. - // - // A simple way to think about this is from the wallet's point of view we - // always want the blockchain to move forward. By setting nLockTime this - // way we're basically making the statement that we only want this - // transaction to appear in the next block; we don't want to potentially - // encourage reorgs by allowing transactions to appear at lower heights - // than the next block in forks of the best chain. - // - // Of course, the subsidy is high enough, and transaction volume low - // enough, that fee sniping isn't a problem yet, but by implementing a fix - // now we ensure code won't be written that makes assumptions about - // nLockTime that preclude a fix later. - tx.nLockTime = chainActive.Height(); - - // Secondly occasionally randomly pick a nLockTime even further back, so - // that transactions that are delayed after signing for whatever reason, - // e.g. high-latency mix networks and some CoinJoin implementations, have - // better privacy. - if (GetRandInt(10) == 0) { - tx.nLockTime = std::max(0, static_cast(tx.nLockTime) - GetRandInt(100)); - } - - assert(tx.nLockTime <= static_cast(chainActive.Height())); - assert(tx.nLockTime < LOCKTIME_THRESHOLD); - - // Start with no fee and loop until there is enough fee - for (fee = payTxFee.GetFeePerK();;) { - CAmount required = spend; - - tx.vin.clear(); - tx.vout.clear(); - - result.fFromMe = true; - result.changes.clear(); - - // If no any recipients to subtract fee then the sender need to pay by themself. - if (!recipientsToSubtractFee) { - required += fee; - } - - // fill outputs - bool remainderSubtracted = false; - - for (size_t i = 0; i < recipients.size(); i++) { - auto& recipient = recipients[i]; - CTxOut vout(recipient.nAmount, recipient.scriptPubKey); - - if (recipient.fSubtractFeeFromAmount) { - // Subtract fee equally from each selected recipient. - vout.nValue -= fee / recipientsToSubtractFee; - - if (!remainderSubtracted) { - // First receiver pays the remainder not divisible by output count. - vout.nValue -= fee % recipientsToSubtractFee; - remainderSubtracted = true; - } - } - - if (vout.IsDust(minRelayTxFee)) { - std::string err; - - if (recipient.fSubtractFeeFromAmount && fee > 0) { - if (vout.nValue < 0) { - err = boost::str(boost::format(_("Amount for recipient %1% is too small to pay the fee")) % i); - } else { - err = boost::str(boost::format(_("Amount for recipient %1% is too small to send after the fee has been deducted")) % i); - } - } else { - err = boost::str(boost::format(_("Amount for recipient %1% is too small")) % i); - } - - throw std::invalid_argument(err); - } - - tx.vout.push_back(vout); - } - - // get inputs - std::vector> signers; - CAmount total = GetInputs(signers, required); - - // add changes - CAmount change = total - required; - - if (change > 0) { - // get changes outputs - std::vector changes; - CAmount addToFee = GetChanges(changes, change, walletdb); - if(addToFee > 0) - fChangeAddedToFee = true; - fee += addToFee; - - // shuffle changes to provide some privacy - std::vector, bool>> outputs; - outputs.reserve(tx.vout.size() + changes.size()); - - for (auto& output : tx.vout) { - outputs.push_back(std::make_pair(std::ref(output), false)); - } - - for (auto& output : changes) { - outputs.push_back(std::make_pair(std::ref(output), true)); - } - - std::shuffle(outputs.begin(), outputs.end(), std::random_device()); - - // replace outputs with shuffled one - std::vector shuffled; - shuffled.reserve(outputs.size()); - - for (size_t i = 0; i < outputs.size(); i++) { - auto& output = outputs[i]; - - shuffled.push_back(output.first); - - if (output.second) { - result.changes.insert(static_cast(i)); - } - } - - tx.vout = std::move(shuffled); - } - - // fill inputs - for (auto& signer : signers) { - tx.vin.emplace_back(signer->output, CScript(), signer->sequence); - } - - // now every fields is populated then we can sign transaction - uint256 sig = tx.GetHash(); - - for (size_t i = 0; i < tx.vin.size(); i++) { - tx.vin[i].scriptSig = signers[i]->Sign(tx, sig); - } - - // check fee - result.SetTx(MakeTransactionRef(tx)); - - if (GetTransactionWeight(tx) >= MAX_STANDARD_TX_WEIGHT) { - throw std::runtime_error(_("Transaction is too large (size limit: 100Kb). Select less inputs or consolidate your UTXOs")); - } - - // check fee - unsigned size = GetVirtualTransactionSize(tx); - CAmount feeNeeded = CWallet::GetMinimumFee(size, nTxConfirmTarget, mempool); - feeNeeded = AdjustFee(feeNeeded, size); - - // If we made it here and we aren't even able to meet the relay fee on the next pass, give up - // because we must be at the maximum allowed fee. - if (feeNeeded < minRelayTxFee.GetFee(size)) { - throw std::runtime_error(_("Transaction too large for fee policy")); - } - - if (fee >= feeNeeded) { - break; - } - - fee = feeNeeded; - } - - if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { - // Lastly, ensure this tx will pass the mempool's chain limits - LockPoints lp; - CTxMemPoolEntry entry(MakeTransactionRef(tx), 0, 0, 0, 0, false, 0, lp); - CTxMemPool::setEntries setAncestors; - size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); - size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000; - size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); - size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000; - std::string errString; - if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, - nLimitDescendants, nLimitDescendantSize, errString)) { - throw std::runtime_error(_("Transaction has too long of a mempool chain")); - } - } - - return result; -} - -CAmount TxBuilder::AdjustFee(CAmount needed, unsigned txSize) -{ - return needed; -} diff --git a/src/wallet/txbuilder.h b/src/wallet/txbuilder.h deleted file mode 100644 index 7266904a18..0000000000 --- a/src/wallet/txbuilder.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef FIRO_WALLET_TXBUILDER_H -#define FIRO_WALLET_TXBUILDER_H - -#include "wallet.h" - -#include "../amount.h" -#include "../script/script.h" -#include "../primitives/transaction.h" -#include "../uint256.h" - -#include -#include - -#include - -class InputSigner -{ -public: - COutPoint output; - uint32_t sequence; - -public: - InputSigner(); - explicit InputSigner(const COutPoint& output, uint32_t seq = CTxIn::SEQUENCE_FINAL); - virtual ~InputSigner(); - - virtual CScript Sign(const CMutableTransaction& tx, const uint256& sig) = 0; -}; - -class TxBuilder -{ -public: - CWallet& wallet; - const CCoinControl *coinControl; - -public: - explicit TxBuilder(CWallet& wallet) noexcept; - virtual ~TxBuilder(); - - CWalletTx Build(const std::vector& recipients, CAmount& fee, bool& fChangeAddedToFee, CWalletDB& walletdb); - -protected: - virtual CAmount GetInputs(std::vector>& signers, CAmount required) = 0; - virtual CAmount GetChanges(std::vector& outputs, CAmount amount, CWalletDB& walletdb) = 0; - virtual CAmount AdjustFee(CAmount needed, unsigned txSize); -}; - -#endif From b570722dffe6693383ee20de88a9640a1d3b130e Mon Sep 17 00:00:00 2001 From: levoncrypto Date: Thu, 30 Apr 2026 15:12:42 +0400 Subject: [PATCH 09/12] build fix: fix wallet model header include dependencies --- src/qt/walletmodel.h | 5 +++-- src/qt/walletmodeltransaction.cpp | 3 ++- src/qt/walletmodeltransaction.h | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 3154047320..b14f6f045a 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -5,9 +5,8 @@ #ifndef BITCOIN_QT_WALLETMODEL_H #define BITCOIN_QT_WALLETMODEL_H -#include "walletmodeltransaction.h" - #include "support/allocators/secure.h" +#include "walletmodeltransaction.h" #ifdef ENABLE_WALLET #include "wallet/walletdb.h" #include "wallet/wallet.h" @@ -35,6 +34,8 @@ class COutput; class CPubKey; class CWallet; class uint256; +class CSparkNameTxData; +class CValidationState; QT_BEGIN_NAMESPACE class QTimer; diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index 71c995a678..be7c14164f 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -2,9 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "walletmodeltransaction.h" +#include "walletmodel.h" #include "policy/policy.h" +#include "spark/state.h" #include "wallet/wallet.h" WalletModelTransaction::WalletModelTransaction(const QList &_recipients) : diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index 64922efada..17c0a85a6a 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -5,9 +5,9 @@ #ifndef BITCOIN_QT_WALLETMODELTRANSACTION_H #define BITCOIN_QT_WALLETMODELTRANSACTION_H -#include "walletmodel.h" +#include "amount.h" -#include +#include class SendCoinsRecipient; From 2016e4c8f65c60088e212bc8e90a8650d8c3c689 Mon Sep 17 00:00:00 2001 From: levoncrypto Date: Thu, 30 Apr 2026 15:51:49 +0400 Subject: [PATCH 10/12] Restore TRANSACTION_LELANTUS and remove unnecessary parameter --- src/bloom.cpp | 1 + src/init.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bloom.cpp b/src/bloom.cpp index bf1c315c9d..e41103db7f 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -223,6 +223,7 @@ bool CBloomFilter::CheckSpecialTransactionMatchesAndUpdate(const CTransaction &t } case(TRANSACTION_COINBASE): case(TRANSACTION_QUORUM_COMMITMENT): + case(TRANSACTION_LELANTUS): case (TRANSACTION_SPORK): // No aditional checks for this transaction types return false; diff --git a/src/init.cpp b/src/init.cpp index 98e9272724..90a25955eb 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1816,10 +1816,9 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) CBlockIndex *tip = chainActive.Tip(); if (tip) { int lastBlockIndexVersion = pblocktree->GetBlockIndexVersion(*tip->phashBlock); - bool lelantusReindex = false; bool evoReindex = (tip->nHeight >= chainparams.GetConsensus().nEvoSporkStartBlock && lastBlockIndexVersion < EVOSPORK_MIN_VERSION); - if (lelantusReindex || evoReindex) { + if (evoReindex) { strLoadError = _( "Block index is outdated, reindex required\n"); break; From 8a1efdcfb38598d87aba667edf1f5d2d09b6c572 Mon Sep 17 00:00:00 2001 From: levoncrypto Date: Sat, 2 May 2026 16:56:01 +0400 Subject: [PATCH 11/12] LOCK(cs_main) for Spark LTag conflict check in InstantSend --- src/llmq/quorums_instantsend.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp index 9538833399..3a802356de 100644 --- a/src/llmq/quorums_instantsend.cpp +++ b/src/llmq/quorums_instantsend.cpp @@ -495,6 +495,7 @@ bool CInstantSendManager::CheckCanLock(const CTransaction& tx, bool printDebug, } if (tx.nType == isutils::INSTANTSEND_ADAPTED_TX) { + LOCK(cs_main); for (CTxIn const & in : tx.vin) { if (in.scriptSig.empty()) return false; From 89456e21cc1bfa4daf687f53770bf9bf84cda0ad Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 Date: Mon, 4 May 2026 01:37:17 +0400 Subject: [PATCH 12/12] Add one more sanity check --- src/libspark/chaum.cpp | 2 +- src/libspark/test/chaum_test.cpp | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/libspark/chaum.cpp b/src/libspark/chaum.cpp index ee4d563ee7..3defe64524 100644 --- a/src/libspark/chaum.cpp +++ b/src/libspark/chaum.cpp @@ -90,7 +90,7 @@ bool Chaum::verify( ) { // Check proof semantics std::size_t n = S.size(); - if (!(T.size() == n && proof.A2.size() == n && proof.t1.size() == n)) { + if (n == 0 || !(T.size() == n && proof.A2.size() == n && proof.t1.size() == n)) { throw std::invalid_argument("Bad Chaum semantics!"); } for (std::size_t i = 0; i < n; i++) { diff --git a/src/libspark/test/chaum_test.cpp b/src/libspark/test/chaum_test.cpp index 26281438bd..52caccaddc 100644 --- a/src/libspark/test/chaum_test.cpp +++ b/src/libspark/test/chaum_test.cpp @@ -175,6 +175,23 @@ BOOST_AUTO_TEST_CASE(bad_proofs) BOOST_CHECK(!(chaum.verify(mu, S, T, evil_proof))); } +BOOST_AUTO_TEST_CASE(empty_input_vectors_rejected) +{ + GroupElement F, G, H, U; + F.randomize(); + G.randomize(); + H.randomize(); + U.randomize(); + Scalar mu; + mu.randomize(); + std::vector S; + std::vector T; + ChaumProof proof; + Chaum chaum(F, G, H, U); + BOOST_CHECK_THROW(chaum.verify(mu, S, T, proof), std::invalid_argument); + } + + BOOST_AUTO_TEST_SUITE_END() }