Merge branch 'FSM' of https://github.com/jl777/komodo into jl777-FSM

xo
This commit is contained in:
blackjok3r
2019-05-02 20:17:10 +08:00
33 changed files with 901 additions and 191 deletions

View File

@@ -1,9 +1,10 @@
package=libcurl
$(package)_version=7.54.0
$(package)_version=7.64.1
$(package)_dependencies=openssl
$(package)_download_path=https://curl.haxx.se/download
$(package)_file_name=curl-$($(package)_version).tar.gz
$(package)_sha256_hash=a84b635941c74e26cce69dd817489bec687eb1f230e7d1897fc5b5f108b59adf
$(package)_config_opts_linux=--disable-shared --enable-static --prefix=$(host_prefix)
$(package)_sha256_hash=432d3f466644b9416bc5b649d344116a753aeaa520c8beaf024a90cba9d3d35d
$(package)_config_opts_linux=--disable-shared --enable-static --prefix=$(host_prefix) --host=x86_64-unknown-linux-gnu
$(package)_config_opts_mingw32=--enable-mingw --disable-shared --enable-static --prefix=$(host_prefix) --host=x86_64-w64-mingw32
$(package)_config_opts_darwin=--disable-shared --enable-static --prefix=$(host_prefix)
$(package)_cflags_darwin=-mmacosx-version-min=10.9
@@ -15,14 +16,23 @@ define $(package)_set_vars
endef
endif
define $(package)_config_cmds
$($(package)_conf_tool) $($(package)_config_opts)
ifeq ($(build_os),linux)
define $(package)_set_vars
$(package)_config_env=LD_LIBRARY_PATH="$(host_prefix)/lib" PKG_CONFIG_LIBDIR="$(host_prefix)/lib/pkgconfig" CPPFLAGS="-I$(host_prefix)/include" LDFLAGS="-L$(host_prefix)/lib"
endef
endif
define $(package)_config_cmds
echo '=== config for $(package):' && \
echo '$($(package)_config_env) $($(package)_conf_tool) $($(package)_config_opts)' && \
echo '=== ' && \
$($(package)_config_env) $($(package)_conf_tool) $($(package)_config_opts)
endef
ifeq ($(build_os),darwin)
define $(package)_build_cmds
$(MAKE) CPPFLAGS='-fPIC' CFLAGS='-mmacosx-version-min=10.9'
$(MAKE) CPPFLAGS="-I$(host_prefix)/include -fPIC" CFLAGS='-mmacosx-version-min=10.9'
endef
else
define $(package)_build_cmds
@@ -31,5 +41,6 @@ endef
endif
define $(package)_stage_cmds
echo 'Staging dir: $($(package)_staging_dir)$(host_prefix)/' && \
$(MAKE) DESTDIR=$($(package)_staging_dir) install
endef

View File

@@ -3,6 +3,7 @@ $(package)_version=1.1.1a
$(package)_download_path=https://www.openssl.org/source
$(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=fc20130f8b7cbd2fb918b2f14e2f429e109c31ddd0fb38fc5d71d9ffed3f9f41
$(package)_patches=ssl_fix.patch
define $(package)_set_vars
$(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)"
@@ -22,7 +23,6 @@ $(package)_config_opts+=no-comp
$(package)_config_opts+=no-crypto-mdebug
$(package)_config_opts+=no-crypto-mdebug-backtrace
$(package)_config_opts+=no-ct
$(package)_config_opts+=no-des
$(package)_config_opts+=no-dgram
$(package)_config_opts+=no-dsa
$(package)_config_opts+=no-dso
@@ -58,15 +58,12 @@ $(package)_config_opts+=no-scrypt
$(package)_config_opts+=no-sctp
$(package)_config_opts+=no-seed
$(package)_config_opts+=no-shared
$(package)_config_opts+=no-sock
$(package)_config_opts+=no-srp
$(package)_config_opts+=no-srtp
$(package)_config_opts+=no-ssl
$(package)_config_opts+=no-ssl3
$(package)_config_opts+=no-ssl3-method
$(package)_config_opts+=no-ssl-trace
$(package)_config_opts+=no-stdio
$(package)_config_opts+=no-tls
$(package)_config_opts+=no-tls1
$(package)_config_opts+=no-tls1-method
$(package)_config_opts+=no-ts
@@ -93,7 +90,8 @@ endef
define $(package)_preprocess_cmds
sed -i.old 's/built on: $date/built on: not available/' util/mkbuildinf.pl && \
sed -i.old "s|\"engines\", \"apps\", \"test\"|\"engines\"|" Configure
sed -i.old "s|\"engines\", \"apps\", \"test\"|\"engines\"|" Configure && \
patch -p1 < $($(package)_patch_dir)/ssl_fix.patch
endef
define $(package)_config_cmds

View File

@@ -47,7 +47,7 @@ native_packages := native_ccache
wallet_packages=bdb
ifeq ($(host_os),linux)
packages := boost openssl libevent zeromq $(zcash_packages) googletest #googlemock
packages := boost openssl libevent zeromq $(zcash_packages) googletest libcurl #googlemock
else
packages := boost openssl libevent zeromq $(zcash_packages) libcurl googletest #googlemock
endif

View File

@@ -0,0 +1,273 @@
From f725fe5b4b6504df08e30f5194d321c3025e2336 Mon Sep 17 00:00:00 2001
From: Matt Caswell <matt@openssl.org>
Date: Tue, 20 Nov 2018 15:32:55 +0000
Subject: [PATCH] Fix a RUN_ONCE bug
We have a number of instances where there are multiple "init" functions for
a single CRYPTO_ONCE variable, e.g. to load config automatically or to not
load config automatically. Unfortunately the RUN_ONCE mechanism was not
correctly giving the right return value where an alternative init function
was being used.
Reviewed-by: Tim Hudson <tjh@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/7983)
---
crypto/init.c | 38 +++++++++-----
include/internal/thread_once.h | 92 ++++++++++++++++++++++++++++++++++
ssl/ssl_init.c | 6 ++-
3 files changed, 121 insertions(+), 15 deletions(-)
diff --git a/crypto/init.c b/crypto/init.c
index 209d1a483da..f20a12f069a 100644
--- a/crypto/init.c
+++ b/crypto/init.c
@@ -177,12 +177,6 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_load_crypto_nodelete)
static CRYPTO_ONCE load_crypto_strings = CRYPTO_ONCE_STATIC_INIT;
static int load_crypto_strings_inited = 0;
-DEFINE_RUN_ONCE_STATIC(ossl_init_no_load_crypto_strings)
-{
- /* Do nothing in this case */
- return 1;
-}
-
DEFINE_RUN_ONCE_STATIC(ossl_init_load_crypto_strings)
{
int ret = 1;
@@ -201,6 +195,13 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_load_crypto_strings)
return ret;
}
+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_load_crypto_strings,
+ ossl_init_load_crypto_strings)
+{
+ /* Do nothing in this case */
+ return 1;
+}
+
static CRYPTO_ONCE add_all_ciphers = CRYPTO_ONCE_STATIC_INIT;
DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_ciphers)
{
@@ -218,6 +219,13 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_ciphers)
return 1;
}
+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_add_all_ciphers,
+ ossl_init_add_all_ciphers)
+{
+ /* Do nothing */
+ return 1;
+}
+
static CRYPTO_ONCE add_all_digests = CRYPTO_ONCE_STATIC_INIT;
DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_digests)
{
@@ -235,7 +243,8 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_digests)
return 1;
}
-DEFINE_RUN_ONCE_STATIC(ossl_init_no_add_algs)
+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_add_all_digests,
+ ossl_init_add_all_digests)
{
/* Do nothing */
return 1;
@@ -255,7 +264,7 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_config)
config_inited = 1;
return 1;
}
-DEFINE_RUN_ONCE_STATIC(ossl_init_no_config)
+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_config, ossl_init_config)
{
#ifdef OPENSSL_INIT_DEBUG
fprintf(stderr,
@@ -595,8 +604,9 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
return 0;
if ((opts & OPENSSL_INIT_NO_LOAD_CRYPTO_STRINGS)
- && !RUN_ONCE(&load_crypto_strings,
- ossl_init_no_load_crypto_strings))
+ && !RUN_ONCE_ALT(&load_crypto_strings,
+ ossl_init_no_load_crypto_strings,
+ ossl_init_load_crypto_strings))
return 0;
if ((opts & OPENSSL_INIT_LOAD_CRYPTO_STRINGS)
@@ -604,7 +614,8 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
return 0;
if ((opts & OPENSSL_INIT_NO_ADD_ALL_CIPHERS)
- && !RUN_ONCE(&add_all_ciphers, ossl_init_no_add_algs))
+ && !RUN_ONCE_ALT(&add_all_ciphers, ossl_init_no_add_all_ciphers,
+ ossl_init_add_all_ciphers))
return 0;
if ((opts & OPENSSL_INIT_ADD_ALL_CIPHERS)
@@ -612,7 +623,8 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
return 0;
if ((opts & OPENSSL_INIT_NO_ADD_ALL_DIGESTS)
- && !RUN_ONCE(&add_all_digests, ossl_init_no_add_algs))
+ && !RUN_ONCE_ALT(&add_all_digests, ossl_init_no_add_all_digests,
+ ossl_init_add_all_digests))
return 0;
if ((opts & OPENSSL_INIT_ADD_ALL_DIGESTS)
@@ -624,7 +636,7 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings)
return 0;
if ((opts & OPENSSL_INIT_NO_LOAD_CONFIG)
- && !RUN_ONCE(&config, ossl_init_no_config))
+ && !RUN_ONCE_ALT(&config, ossl_init_no_config, ossl_init_config))
return 0;
if (opts & OPENSSL_INIT_LOAD_CONFIG) {
diff --git a/include/internal/thread_once.h b/include/internal/thread_once.h
index 224244353ab..e268a959ef3 100644
--- a/include/internal/thread_once.h
+++ b/include/internal/thread_once.h
@@ -9,6 +9,20 @@
#include <openssl/crypto.h>
+/*
+ * DEFINE_RUN_ONCE: Define an initialiser function that should be run exactly
+ * once. It takes no arguments and returns and int result (1 for success or
+ * 0 for failure). Typical usage might be:
+ *
+ * DEFINE_RUN_ONCE(myinitfunc)
+ * {
+ * do_some_initialisation();
+ * if (init_is_successful())
+ * return 1;
+ *
+ * return 0;
+ * }
+ */
#define DEFINE_RUN_ONCE(init) \
static int init(void); \
int init##_ossl_ret_ = 0; \
@@ -17,10 +31,30 @@
init##_ossl_ret_ = init(); \
} \
static int init(void)
+
+/*
+ * DECLARE_RUN_ONCE: Declare an initialiser function that should be run exactly
+ * once that has been defined in another file via DEFINE_RUN_ONCE().
+ */
#define DECLARE_RUN_ONCE(init) \
extern int init##_ossl_ret_; \
void init##_ossl_(void);
+/*
+ * DEFINE_RUN_ONCE_STATIC: Define an initialiser function that should be run
+ * exactly once. This function will be declared as static within the file. It
+ * takes no arguments and returns and int result (1 for success or 0 for
+ * failure). Typical usage might be:
+ *
+ * DEFINE_RUN_ONCE_STATIC(myinitfunc)
+ * {
+ * do_some_initialisation();
+ * if (init_is_successful())
+ * return 1;
+ *
+ * return 0;
+ * }
+ */
#define DEFINE_RUN_ONCE_STATIC(init) \
static int init(void); \
static int init##_ossl_ret_ = 0; \
@@ -30,6 +64,46 @@
} \
static int init(void)
+/*
+ * DEFINE_RUN_ONCE_STATIC_ALT: Define an alternative initialiser function. This
+ * function will be declared as static within the file. It takes no arguments
+ * and returns an int result (1 for success or 0 for failure). An alternative
+ * initialiser function is expected to be associated with a primary initialiser
+ * function defined via DEFINE_ONCE_STATIC where both functions use the same
+ * CRYPTO_ONCE object to synchronise. Where an alternative initialiser function
+ * is used only one of the primary or the alternative initialiser function will
+ * ever be called - and that function will be called exactly once. Definitition
+ * of an alternative initialiser function MUST occur AFTER the definition of the
+ * primary initialiser function.
+ *
+ * Typical usage might be:
+ *
+ * DEFINE_RUN_ONCE_STATIC(myinitfunc)
+ * {
+ * do_some_initialisation();
+ * if (init_is_successful())
+ * return 1;
+ *
+ * return 0;
+ * }
+ *
+ * DEFINE_RUN_ONCE_STATIC_ALT(myaltinitfunc, myinitfunc)
+ * {
+ * do_some_alternative_initialisation();
+ * if (init_is_successful())
+ * return 1;
+ *
+ * return 0;
+ * }
+ */
+#define DEFINE_RUN_ONCE_STATIC_ALT(initalt, init) \
+ static int initalt(void); \
+ static void initalt##_ossl_(void) \
+ { \
+ init##_ossl_ret_ = initalt(); \
+ } \
+ static int initalt(void)
+
/*
* RUN_ONCE - use CRYPTO_THREAD_run_once, and check if the init succeeded
* @once: pointer to static object of type CRYPTO_ONCE
@@ -43,3 +117,21 @@
*/
#define RUN_ONCE(once, init) \
(CRYPTO_THREAD_run_once(once, init##_ossl_) ? init##_ossl_ret_ : 0)
+
+/*
+ * RUN_ONCE_ALT - use CRYPTO_THREAD_run_once, to run an alternative initialiser
+ * function and check if that initialisation succeeded
+ * @once: pointer to static object of type CRYPTO_ONCE
+ * @initalt: alternative initialiser function name that was previously given to
+ * DEFINE_RUN_ONCE_STATIC_ALT. This function must return 1 for
+ * success or 0 for failure.
+ * @init: primary initialiser function name that was previously given to
+ * DEFINE_RUN_ONCE_STATIC. This function must return 1 for success or
+ * 0 for failure.
+ *
+ * The return value is 1 on success (*) or 0 in case of error.
+ *
+ * (*) by convention, since the init function must return 1 on success.
+ */
+#define RUN_ONCE_ALT(once, initalt, init) \
+ (CRYPTO_THREAD_run_once(once, initalt##_ossl_) ? init##_ossl_ret_ : 0)
diff --git a/ssl/ssl_init.c b/ssl/ssl_init.c
index c0ccb9304a6..96526472c57 100644
--- a/ssl/ssl_init.c
+++ b/ssl/ssl_init.c
@@ -134,7 +134,8 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_load_ssl_strings)
return 1;
}
-DEFINE_RUN_ONCE_STATIC(ossl_init_no_load_ssl_strings)
+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_load_ssl_strings,
+ ossl_init_load_ssl_strings)
{
/* Do nothing in this case */
return 1;
@@ -207,7 +208,8 @@ int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS * settings)
return 0;
if ((opts & OPENSSL_INIT_NO_LOAD_SSL_STRINGS)
- && !RUN_ONCE(&ssl_strings, ossl_init_no_load_ssl_strings))
+ && !RUN_ONCE_ALT(&ssl_strings, ossl_init_no_load_ssl_strings,
+ ossl_init_load_ssl_strings))
return 0;
if ((opts & OPENSSL_INIT_LOAD_SSL_STRINGS)

View File

@@ -65,6 +65,7 @@ testScripts=(
'disablewallet.py'
'zcjoinsplit.py'
'zcjoinsplitdoublespend.py'
'ivk_import_export.py'
'zkey_import_export.py'
'reorg_limit.py'
'getblocktemplate.py'

137
qa/rpc-tests/ivk_import_export.py Executable file
View File

@@ -0,0 +1,137 @@
#!/usr/bin/env python2
# Copyright (c) 2019 Bartlomiej Lisiecki
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_greater_than, start_nodes,\
initialize_chain_clean, connect_nodes_bi, wait_and_assert_operationid_status
import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)
fee = Decimal('0.0001') # constant (but can be changed within reason)
class IVKImportExportTest (BitcoinTestFramework):
def setup_chain(self):
print("Initializing test directory "+self.options.tmpdir)
initialize_chain_clean(self.options.tmpdir, 4)
def setup_network(self, split=False):
self.nodes = start_nodes(4, self.options.tmpdir, [[
'-nuparams=5ba81b19:101', # Overwinter
'-nuparams=76b809bb:102', # Sapling
]] * 4)
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,0,2)
connect_nodes_bi(self.nodes,0,3)
self.is_network_split=False
self.sync_all()
def run_test(self):
[alice, bob, charlie, miner] = self.nodes
# the sender loses 'amount' plus fee; to_addr receives exactly 'amount'
def z_send(from_node, from_addr, to_addr, amount):
global fee
opid = from_node.z_sendmany(from_addr,
[{"address": to_addr, "amount": Decimal(amount)}], 1, fee)
wait_and_assert_operationid_status(from_node, opid)
self.sync_all()
miner.generate(1)
self.sync_all()
def verify_utxos(node, amts, zaddr):
amts.sort(reverse=True)
txs = node.z_listreceivedbyaddress(zaddr)
def cmp_confirmations_high_to_low(a, b):
return cmp(b["amount"], a["amount"])
txs.sort(cmp_confirmations_high_to_low)
print("Sorted txs", txs)
print("amts", amts)
try:
assert_equal(amts, [tx["amount"] for tx in txs])
except AssertionError:
logging.error(
'Expected amounts: %r; txs: %r',
amts, txs)
raise
def get_private_balance(node):
balance = node.z_gettotalbalance()
return balance['private']
def find_imported_zaddr(node, import_zaddr):
zaddrs = node.z_listaddresses()
assert(import_zaddr in zaddrs)
return import_zaddr
# activate sapling
alice.generate(102)
self.sync_all()
# sanity-check the test harness
assert_equal(self.nodes[0].getblockcount(), 102)
# shield alice's coinbase funds to her zaddr
alice_zaddr = alice.z_getnewaddress('sapling')
res = alice.z_shieldcoinbase("*", alice_zaddr)
wait_and_assert_operationid_status(alice, res['opid'])
self.sync_all()
miner.generate(1)
self.sync_all()
# the amounts of each txn embodied which generates a single utxo:
amounts = map(Decimal, ['2.3', '3.7', '0.1', '0.5', '1.0', '0.19'])
# internal test consistency assertion:
assert_greater_than(
get_private_balance(alice),
reduce(Decimal.__add__, amounts))
# now get a pristine z-address for receiving transfers:
bob_zaddr = bob.z_getnewaddress('sapling')
verify_utxos(bob, [], bob_zaddr)
logging.info("sending pre-export txns...")
for amount in amounts[0:2]:
z_send(alice, alice_zaddr, bob_zaddr, amount)
logging.info("exporting ivk from bob...")
bob_ivk = bob.z_exportviewingkey(bob_zaddr)
logging.info("sending post-export txns...")
for amount in amounts[2:4]:
z_send(alice, alice_zaddr, bob_zaddr, amount)
verify_utxos(bob, amounts[:4], bob_zaddr)
logging.info("importing bob_ivk into charlie...")
# we need to pass bob_zaddr since it's a sapling address
charlie.z_importviewingkey(bob_ivk, 'yes', 0, bob_zaddr)
# z_importkey should have rescanned for new key, so this should pass:
verify_utxos(charlie, amounts[:4], bob_zaddr)
# verify idempotent behavior:
charlie.z_importviewingkey(bob_ivk, 'yes', 0, bob_zaddr)
verify_utxos(charlie, amounts[:4], bob_zaddr)
logging.info("Sending post-import txns...")
for amount in amounts[4:]:
z_send(alice, alice_zaddr, bob_zaddr, amount)
verify_utxos(bob, amounts, bob_zaddr)
verify_utxos(charlie, amounts, bob_zaddr)
if __name__ == '__main__':
IVKImportExportTest().main()

View File

@@ -226,8 +226,11 @@ bool priv2addr(char *coinaddr,uint8_t buf33[33],uint8_t priv32[32]);
CPubKey buf2pk(uint8_t *buf33);
void endiancpy(uint8_t *dest,uint8_t *src,int32_t len);
uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv,int32_t entropyvout,int32_t usevout);
CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue,CPubKey pk, const std::vector<std::vector<unsigned char>>* vData = NULL);
CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk,CPubKey pk2, const std::vector<std::vector<unsigned char>>* vData = NULL);
CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue,CPubKey pk, std::vector<std::vector<unsigned char>>* vData = NULL);
CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk,CPubKey pk2, std::vector<std::vector<unsigned char>>* vData = NULL);
int32_t has_opret(const CTransaction &tx, uint8_t evalcode);
CScript getCCopret(const CScript &scriptPubKey);
bool makeCCopret(CScript &opret, std::vector<std::vector<unsigned char>> &vData);
CC *MakeCCcond1(uint8_t evalcode,CPubKey pk);
CC *MakeCCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2);
CC* GetCryptoCondition(CScript const& scriptSig);

View File

@@ -58,35 +58,70 @@ CC *MakeCCcond1(uint8_t evalcode,CPubKey pk)
return CCNewThreshold(2, {condCC, Sig});
}
CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue, CPubKey pk, const std::vector<std::vector<unsigned char>>* vData)
int32_t has_opret(const CTransaction &tx, uint8_t evalcode)
{
int i = 0;
for ( auto vout : tx.vout )
{
if ( vout.scriptPubKey[0] == OP_RETURN && vout.scriptPubKey[1] == evalcode )
return i;
i++;
}
return 0;
}
CScript getCCopret(const CScript &scriptPubKey)
{
std::vector<std::vector<unsigned char>> vParams = std::vector<std::vector<unsigned char>>();
CScript dummy; CScript opret;
if ( scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) )
{
//opret << E_MARSHAL(ss << vParams[0]);
opret = CScript(vParams[0].begin()+6, vParams[0].end());
}
//fprintf(stderr, "params_size.%li parmas_hexstr.%s\n", vParams.size(), HexStr(vParams[0].begin(),vParams[0].end()).c_str());
//opret = CScript(vParams[0].begin(), vParams[0].end());
return opret;
}
bool makeCCopret(CScript &opret, std::vector<std::vector<unsigned char>> &vData)
{
if ( opret.empty() )
return false;
vData.push_back(std::vector<unsigned char>(opret.begin(), opret.end()));
return true;
}
CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue, CPubKey pk, std::vector<std::vector<unsigned char>>* vData)
{
CTxOut vout;
CC *payoutCond = MakeCCcond1(evalcode,pk);
vout = CTxOut(nValue,CCPubKey(payoutCond));
if ( vData )
{
std::vector<std::vector<unsigned char>> vtmpData = std::vector<std::vector<unsigned char>>(vData->begin(), vData->end());
//std::vector<std::vector<unsigned char>> vtmpData = std::vector<std::vector<unsigned char>>(vData->begin(), vData->end());
std::vector<CPubKey> vPubKeys = std::vector<CPubKey>();
vPubKeys.push_back(pk);
COptCCParams ccp = COptCCParams(COptCCParams::VERSION, evalcode, 1, 1, vPubKeys, vtmpData);
//vPubKeys.push_back(pk);
COptCCParams ccp = COptCCParams(COptCCParams::VERSION, evalcode, 1, 1, vPubKeys, ( * vData));
vout.scriptPubKey << ccp.AsVector() << OP_DROP;
}
cc_free(payoutCond);
return(vout);
}
CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk1,CPubKey pk2, const std::vector<std::vector<unsigned char>>* vData)
CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk1,CPubKey pk2, std::vector<std::vector<unsigned char>>* vData)
{
CTxOut vout;
CC *payoutCond = MakeCCcond1of2(evalcode,pk1,pk2);
vout = CTxOut(nValue,CCPubKey(payoutCond));
if ( vData )
{
std::vector<std::vector<unsigned char>> vtmpData = std::vector<std::vector<unsigned char>>(vData->begin(), vData->end());
//std::vector<std::vector<unsigned char>> vtmpData = std::vector<std::vector<unsigned char>>(vData->begin(), vData->end());
std::vector<CPubKey> vPubKeys = std::vector<CPubKey>();
vPubKeys.push_back(pk1);
vPubKeys.push_back(pk2);
COptCCParams ccp = COptCCParams(COptCCParams::VERSION, evalcode, 1, 2, vPubKeys, vtmpData);
// skip pubkeys. These need to maybe be optional and we need some way to get them out that is easy!
//vPubKeys.push_back(pk1);
//vPubKeys.push_back(pk2);
COptCCParams ccp = COptCCParams(COptCCParams::VERSION, evalcode, 1, 2, vPubKeys, ( * vData));
vout.scriptPubKey << ccp.AsVector() << OP_DROP;
}
cc_free(payoutCond);
@@ -114,8 +149,12 @@ bool IsCCInput(CScript const& scriptSig)
bool CheckTxFee(const CTransaction &tx, uint64_t txfee, uint32_t height, uint64_t blocktime)
{
LOCK(mempool.cs);
CCoinsView dummy;
CCoinsViewCache view(&dummy);
int64_t interest; uint64_t valuein;
CCoinsViewCache &view = *pcoinsTip;
CCoinsViewMemPool viewMemPool(pcoinsTip, mempool);
view.SetBackend(viewMemPool);
valuein = view.GetValueIn(height,&interest,tx,blocktime);
if ( valuein-tx.GetValueOut() > txfee )
{
@@ -584,7 +623,7 @@ bool komodo_txnotarizedconfirmed(uint256 txid)
fprintf(stderr,"komodo_txnotarizedconfirmed no hashBlock for txid %s\n",txid.ToString().c_str());
return(0);
}
else if ( (pindex= mapBlockIndex[hashBlock]) == 0 || (txheight= pindex->GetHeight()) <= 0 )
else if ( (pindex= komodo_blockindex(hashBlock)) == 0 || (txheight= pindex->GetHeight()) <= 0 )
{
fprintf(stderr,"komodo_txnotarizedconfirmed no txheight.%d %p for txid %s\n",txheight,pindex,txid.ToString().c_str());
return(0);

View File

@@ -250,10 +250,8 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
return eval->Invalid("channelopen is not yet confirmed(notarised)!");
else if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
return eval->Invalid("vin.0 is normal for channelpayment!");
else if ( IsCCInput(tx.vin[1].scriptSig) == 0 )
return eval->Invalid("vin.1 is CC for channelpayment!");
else if ( IsCCInput(tx.vin[2].scriptSig) == 0 )
return eval->Invalid("vin.2 is CC for channelpayment!");
else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 )
return eval->Invalid("vin."+std::to_string(tx.vin.size()-2)+" is CC for channelpayment!");
else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 )
return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelpayment!");
else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 )
@@ -283,8 +281,8 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
return eval->Invalid("invalid previous tx OP_RETURN data!");
else if ( ConstrainVout(tx.vout[0],1,channeladdress,(p1-param2)*payment)==0 )
return eval->Invalid("vout.0 is CC or invalid CC change amount for channelpayment!");
else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || prevTx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE)
return eval->Invalid("vin.2 is CC marker or invalid marker amount for channelpayment!");
else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker or invalid marker amount for channelpayment!");
else if (param1+param2!=p1)
return eval->Invalid("invalid payment depth!");
else if (tx.vout[3].nValue > prevTx.vout[0].nValue)
@@ -313,10 +311,8 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
return eval->Invalid("channelopen is not yet confirmed(notarised)!");
else if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
return eval->Invalid("vin.0 is normal for channelclose!");
else if ( IsCCInput(tx.vin[1].scriptSig) == 0 )
return eval->Invalid("vin.1 is CC for channelclose!");
else if ( IsCCInput(tx.vin[2].scriptSig) == 0 )
return eval->Invalid("vin.2 is CC for channelclose!");
else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 )
return eval->Invalid("vin."+std::to_string(tx.vin.size()-2)+" is CC for channelclose!");
else if ( ConstrainVout(tx.vout[0],1,channeladdress,0)==0 )
return eval->Invalid("vout.0 is CC for channelclose!");
else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 )
@@ -329,8 +325,8 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
{
if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0)
return eval->Invalid("invalid previous tx OP_RETURN data!");
else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || prevTx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE)
return eval->Invalid("vin.2 is CC marker or invalid marker amount for channelclose!");
else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker or invalid marker amount for channelclose!");
else if (tx.vout[0].nValue != prevTx.vout[0].nValue)
return eval->Invalid("invalid CC amount, amount must match funds in channel");
}
@@ -358,10 +354,8 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
return eval->Invalid("channelClose is not yet confirmed(notarised)!");
else if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
return eval->Invalid("vin.0 is normal for channelrefund!");
else if ( IsCCInput(tx.vin[1].scriptSig) == 0 )
return eval->Invalid("vin.1 is CC for channelrefund!");
else if ( IsCCInput(tx.vin[2].scriptSig) == 0 )
return eval->Invalid("vin.2 is CC for channelrefund!");
else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 )
return eval->Invalid("vin."+std::to_string(tx.vin.size()-2)+" CC for channelrefund!");
else if ( ConstrainVout(tx.vout[0],1,srcmarker,CC_MARKER_VALUE)==0 )
return eval->Invalid("vout.0 is CC marker to srcpub or invalid amount for channelrefund!");
else if ( ConstrainVout(tx.vout[1],1,destmarker,CC_MARKER_VALUE)==0 )
@@ -376,8 +370,8 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
{
if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0)
return eval->Invalid("invalid previous tx OP_RETURN data!");
else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || prevTx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE)
return eval->Invalid("vin.2 is CC marker or invalid marker amount for channelrefund!");
else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker or invalid marker amount for channelrefund!");
else if (tx.vout[2].nValue != prevTx.vout[0].nValue)
return eval->Invalid("invalid amount, refund amount and funds in channel must match!");
}

View File

@@ -67,38 +67,26 @@ UniValue custom_func1(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
// make op_return payload as normal.
CScript opret = custom_opret('1',mypk);
std::vector<std::vector<unsigned char>> vData = std::vector<std::vector<unsigned char>>();
vData.push_back(std::vector<unsigned char>(opret.begin(), opret.end()));
// make vout0 with op_return included as payload.
mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,mypk,&vData));
fprintf(stderr, "vout size2.%li\n", mtx.vout.size());
rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,CScript());
return(custom_rawtxresult(result,rawtx,broadcastflag));
if ( makeCCopret(opret, vData) )
{
// make vout0 with op_return included as payload.
mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,mypk,&vData));
fprintf(stderr, "vout size2.%li\n", mtx.vout.size());
rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,CScript());
return(custom_rawtxresult(result,rawtx,broadcastflag));
}
}
return(result);
}
bool has_opret(const CTransaction &tx, uint8_t evalcode)
{
for ( auto vout : tx.vout )
{
if ( vout.scriptPubKey[0] == OP_RETURN && vout.scriptPubKey[1] == evalcode )
return true;
}
return false;
}
bool custom_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx)
{
char expectedaddress[64]; CPubKey pk;
CScript opret; int32_t numvout;
if ( !has_opret(tx, EVAL_CUSTOM) )
if ( has_opret(tx, EVAL_CUSTOM) == 0 )
{
std::vector<std::vector<unsigned char>> vParams = std::vector<std::vector<unsigned char>>();
CScript dummy;
if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) && vParams.size() == 1 )
{
opret << E_MARSHAL(ss << vParams[0]);
}
opret = getCCopret(tx.vout[0].scriptPubKey);
numvout = 1;
}
else

View File

@@ -592,7 +592,7 @@ void addmultisigaddress(char *refcoin,char *acname,int32_t M, char *pubkeys)
cJSON *getinputarray(int64_t *totalp,cJSON *unspents,int64_t required)
{
cJSON *vin,*item,*vins = cJSON_CreateArray(); int32_t i,n,v; int64_t satoshis; bits256 txid;
cJSON *vin,*item,*vins = cJSON_CreateArray(); int32_t i,j=0,n,v; int64_t satoshis; bits256 txid;
*totalp = 0;
if ( (n= cJSON_GetArraySize(unspents)) > 0 )
{
@@ -610,7 +610,10 @@ cJSON *getinputarray(int64_t *totalp,cJSON *unspents,int64_t required)
jaddi(vins,vin);
*totalp += satoshis;
if ( (*totalp) >= required )
break;
{
if (j<3) j++;
else break;
}
}
}
}
@@ -910,10 +913,11 @@ void update_gatewayspending(int8_t type,char *refcoin,char *acname,char *bindtxi
processed++;
}
free(rawtx);
} else fprintf(stderr,"couldnt create rawtx\n");
} else fprintf(stderr,"couldnt create rawtx\n");
}
else
{
rawtx=0;
lasttxid = jbits256(item,"last_txid");
if ( lasttxid.txid==withdrawtxid.txid)
{
@@ -921,24 +925,27 @@ void update_gatewayspending(int8_t type,char *refcoin,char *acname,char *bindtxi
}
else rawtx=jstr(item,"hex");
K=jint(item,"number_of_signs");
if ( rawtx!=0 && (clijson=addsignature(refcoin,"",rawtx,M)) != 0 )
if (rawtx!=0)
{
if ( is_cJSON_True(jobj(clijson,"complete")) != 0 )
{
txid=gatewayscompletesigning(type,refcoin,acname,lasttxid,jstr(clijson,"hex"));
if (txid.txid!=zeroid.txid) fprintf(stderr,"### SIGNING withdraw %s %dof%d\n",bits256_str(str,withdrawtxid),K+1,N);
else fprintf(stderr,"### SIGNING error broadcasting tx on %s\n",acname);
}
else if ( jint(clijson,"partialtx") != 0 )
if ((clijson=addsignature(refcoin,"",rawtx,M)) != 0 )
{
txid=gatewayspartialsign(type,refcoin,acname,lasttxid,jstr(clijson,"hex"));
if (txid.txid!=zeroid.txid) fprintf(stderr,"### SIGNING withdraw %s %d/%dof%d\n",bits256_str(str,withdrawtxid),K+1,M,N);
else fprintf(stderr,"### SIGNING error broadcasting tx on %s\n",acname);
}
free_json(clijson);
processed++;
if ( lasttxid.txid==withdrawtxid.txid) free(rawtx);
}
if ( is_cJSON_True(jobj(clijson,"complete")) != 0 )
{
txid=gatewayscompletesigning(type,refcoin,acname,lasttxid,jstr(clijson,"hex"));
if (txid.txid!=zeroid.txid) fprintf(stderr,"### SIGNING withdraw %s %dof%d\n",bits256_str(str,withdrawtxid),K+1,N);
else fprintf(stderr,"### SIGNING error broadcasting tx on %s\n",acname);
}
else if ( jint(clijson,"partialtx") != 0 )
{
txid=gatewayspartialsign(type,refcoin,acname,lasttxid,jstr(clijson,"hex"));
if (txid.txid!=zeroid.txid) fprintf(stderr,"### SIGNING withdraw %s %d/%dof%d\n",bits256_str(str,withdrawtxid),K+1,M,N);
else fprintf(stderr,"### SIGNING error broadcasting tx on %s\n",acname);
}
free_json(clijson);
processed++;
if ( lasttxid.txid==withdrawtxid.txid) free(rawtx);
}
} else fprintf(stderr,"couldnt create rawtx or find previous partial signed tx\n");
}
}
}

View File

@@ -628,20 +628,25 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
return eval->Invalid("deposit amount greater then bind total supply");
else if (komodo_txnotarizedconfirmed(deposittxid) == false)
return eval->Invalid("gatewaysdeposit tx is not yet confirmed(notarised)!");
else if (myGetTransaction(tx.vin[2].prevout.hash,tmptx,hashblock) == 0)
return eval->Invalid("invalid gatewaysdeposittxid!");
else if (IsCCInput(tx.vin[0].scriptSig) != 0)
return eval->Invalid("vin.0 is normal for gatewaysclaim!");
else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || myGetTransaction(tx.vin[tx.vin.size()-1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
else if (tx.vin.size()>2)
{
i=1;
while (i<=tx.vin.size()-2)
{
if (IsCCInput(tx.vin[i].scriptSig)==0) return eval->Invalid("vin."+std::to_string(i)+" is CC for gatewaysclaim!");
i++;
}
}
else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker for gatewaysclaim or invalid marker amount!");
else if (_GetCCaddress(destaddr,EVAL_TOKENS,pubkey)==0 || ConstrainVout(tx.vout[0],1,destaddr,amount)==0)
return eval->Invalid("invalid vout tokens to destpub for gatewaysclaim!");
else if (numvouts>2 && (myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || ConstrainVout(tx.vout[1],1,gatewaystokensaddr,tmptx.vout[tx.vin[1].prevout.n].nValue-amount)==0))
else if (numvouts>2 && tx.vout[1].scriptPubKey.IsPayToCryptoCondition() && (myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || ConstrainVout(tx.vout[1],1,gatewaystokensaddr,tmptx.vout[tx.vin[1].prevout.n].nValue-amount)==0))
return eval->Invalid("invalid CC change vout for gatewaysclaim!");
else if (amount!=tmpamount)
return eval->Invalid("claimed amount different then deposit amount");
else if (tx.vout[0].nValue!=amount)
return eval->Invalid("claim amount not matching amount in opret");
return eval->Invalid("claimed amount different then deposit amount");
else if (pubkey!=tmppubkey)
return eval->Invalid("claim destination pubkey different than in deposit tx");
else
@@ -697,8 +702,6 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
return eval->Invalid("invalid marker vout for gatewaysWithdraw!");
else if ( ConstrainVout(tmptx.vout[1],1,gatewaystokensaddr,amount)==0)
return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!");
else if (tmptx.vout[1].nValue!=amount)
return eval->Invalid("amount in opret not matching tx tokens amount!");
else if (komodo_txnotarizedconfirmed(withdrawtxid) == false)
return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!");
else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0)
@@ -953,7 +956,7 @@ std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t
LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl);
return("");
}
if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3) > 0 )
if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,2) > 0 )
{
if (AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, totalsupply, 64)>0)
{
@@ -1032,7 +1035,7 @@ std::string GatewaysDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::
LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl);
return("");
}
if ( AddNormalinputs(mtx,mypk,txfee+2*CC_MARKER_VALUE,4) > 0 )
if ( AddNormalinputs(mtx,mypk,txfee+2*CC_MARKER_VALUE,3) > 0 )
{
mtx.vout.push_back(MakeCC1vout(cp->evalcode,CC_MARKER_VALUE,destpub));
mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(CCtxidaddr(txidaddr,cointxid))) << OP_CHECKSIG));
@@ -1104,7 +1107,7 @@ std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,ui
LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl);
return("");
}
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
if ( AddNormalinputs(mtx,mypk,txfee,1) > 0 )
{
if ((inputs=AddGatewaysInputs(cp, mtx, gatewayspk, bindtxid, amount, 60)) > 0)
{
@@ -1182,7 +1185,7 @@ std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin
}
}
}
if( AddNormalinputs(mtx, mypk, txfee+CC_MARKER_VALUE, 4) > 0 )
if( AddNormalinputs(mtx, mypk, txfee+CC_MARKER_VALUE, 2) > 0 )
{
if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, amount, 60)) > 0)
{
@@ -1294,7 +1297,7 @@ std::string GatewaysPartialSign(uint64_t txfee,uint256 lasttxid,std::string refc
return("");
}
}
if (AddNormalinputs(mtx,mypk,txfee,3)!=0)
if (AddNormalinputs(mtx,mypk,txfee,1)!=0)
{
mtx.vin.push_back(CTxIn(tx.GetHash(),0,CScript()));
mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CC_MARKER_VALUE,gatewayspk));
@@ -1393,7 +1396,7 @@ std::string GatewaysCompleteSigning(uint64_t txfee,uint256 lasttxid,std::string
return("");
}
}
if (AddNormalinputs(mtx,mypk,txfee,3)!=0)
if (AddNormalinputs(mtx,mypk,txfee,1)!=0)
{
mtx.vin.push_back(CTxIn(lasttxid,0,CScript()));
mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CC_MARKER_VALUE,gatewayspk));
@@ -1458,7 +1461,7 @@ std::string GatewaysMarkDone(uint64_t txfee,uint256 completetxid,std::string ref
LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl);
return("");
}
if (AddNormalinputs(mtx,mypk,txfee,3)!=0)
if (AddNormalinputs(mtx,mypk,txfee,1)!=0)
{
mtx.vin.push_back(CTxIn(completetxid,0,CScript()));
mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));

View File

@@ -16,6 +16,12 @@
#include "CCPayments.h"
/*
-earlytxid is not an -ac_param, so it doesnt affect the chain magics
extra data after the normal CCvout is whatever data we want and can represent whatever we want
so -ac_script=<payments CC vout + useearlytxid>
in the validation if you see the useearlytxid in the opreturn data or extra data, you use the earlytxid as the txid that specifies the payment
0) txidopret <- allocation, scriptPubKey, opret
1) create <- locked_blocks, minrelease, list of txidopret
@@ -193,11 +199,10 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
{
if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 )
return(eval->Invalid("negative values"));
if ( !CheckTxFee(tx, PAYMENTS_TXFEE, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime) )
return eval->Invalid("txfee is too high");
Paymentspk = GetUnspendable(cp,0);
//fprintf(stderr, "lockedblocks.%i minrelease.%i totalallocations.%i txidopret1.%s txidopret2.%s\n",lockedblocks, minrelease, totalallocations, txidoprets[0].ToString().c_str(), txidoprets[1].ToString().c_str() );
if ( !CheckTxFee(tx, PAYMENTS_TXFEE+1, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime) )
return eval->Invalid("txfee is too high");
// Get all the script pubkeys and allocations
std::vector<int64_t> allocations;
std::vector<CScript> scriptPubKeys;
@@ -212,6 +217,20 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
allocations.push_back(allocation);
//fprintf(stderr, "i.%i scriptpubkey.%s allocation.%li\n",i,scriptPubKeys[i].ToString().c_str(),allocation);
checkallocations += allocation;
// if we have an op_return to pay to need to check it exists and is paying the correct opret.
if ( !opret.empty() )
{
if ( !fHasOpret )
{
fprintf(stderr, "missing opret.%s in payments release.\n",HexStr(opret.begin(), opret.end()).c_str());
return(eval->Invalid("missing opret in payments release"));
}
else if ( CScript(opret.begin(),opret.end()) != tx.vout[tx.vout.size()-1].scriptPubKey )
{
fprintf(stderr, "opret.%s vs opret.%s\n",HexStr(opret.begin(), opret.end()).c_str(), HexStr(tx.vout[tx.vout.size()-1].scriptPubKey.begin(), tx.vout[tx.vout.size()-1].scriptPubKey.end()).c_str());
return(eval->Invalid("pays incorrect opret"));
}
}
}
i++;
}
@@ -247,7 +266,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
int64_t test = allocations[n];
test *= checkamount;
test /= totalallocations;
if ( test != tx.vout[i].nValue )
if ( test != tx.vout[i].nValue && test != tx.vout[i].nValue-1 )
{
fprintf(stderr, "vout.%i test.%li vs nVlaue.%li\n",i, test, tx.vout[i].nValue);
return(eval->Invalid("amounts do not match"));
@@ -277,18 +296,28 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
Getscriptaddress(destaddr,txin.vout[vin.prevout.n].scriptPubKey);
if ( strcmp(destaddr,coinaddr) != 0 )
{
std::vector<uint8_t> scriptPubKey,opret; uint256 checktxid;
if ( txin.vout.size() < 2 || DecodePaymentsFundOpRet(txin.vout[txin.vout.size()-1].scriptPubKey,checktxid) != 'F' || checktxid != createtxid )
CScript opret; uint256 checktxid; int32_t opret_ind;
if ( (opret_ind= has_opret(txin, EVAL_PAYMENTS)) == 0 )
{
// get op_return from CCvout
opret = getCCopret(txin.vout[0].scriptPubKey);
}
else
{
// get op_return from the op_return
opret = txin.vout[opret_ind].scriptPubKey;
} // else return(eval->Invalid("vin has wrong amount of vouts")); // dont think this is needed?
if ( DecodePaymentsFundOpRet(opret,checktxid) != 'F' || checktxid != createtxid )
{
fprintf(stderr, "vin.%i is not a payments CC vout: txid.%s\n", i, txin.GetHash().ToString().c_str());
return(eval->Invalid("vin is not paymentsCC type"));
} //else fprintf(stderr, "vin.%i opret type txid.%s\n", i, txin.GetHash().ToString().c_str());
}
}
// check the chain depth vs locked blcoks requirement.
CBlockIndex* pblockindex = mapBlockIndex[blockhash];
if ( pblockindex->GetHeight() > ht-lockedblocks )
// check the chain depth vs locked blocks requirement.
CBlockIndex* pblockindex = komodo_blockindex(blockhash);
if ( pblockindex == 0 || pblockindex->GetHeight() > ht-lockedblocks )
{
fprintf(stderr, "vin.%i is not elegible to be spent yet height.%i vs elegible_ht.%i\n", i, pblockindex->GetHeight(), ht-lockedblocks);
fprintf(stderr, "vin.%i is not elegible to be spent yet height.%i vs elegible_ht.%i\n", i, pblockindex!=0?pblockindex->GetHeight():0, ht-lockedblocks);
return(eval->Invalid("vin not elegible"));
}
} else return(eval->Invalid("cant get vin transaction"));
@@ -340,8 +369,18 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP
}
if ( iter == 0 )
{
std::vector<uint8_t> scriptPubKey,opret;
if ( myGetTransaction(txid,tx,hashBlock) == 0 || tx.vout.size() < 2 || DecodePaymentsFundOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,checktxid) != 'F' || checktxid != createtxid )
CScript opret; uint256 checktxid; int32_t opret_ind;
if ( (opret_ind= has_opret(vintx, EVAL_PAYMENTS)) == 0 )
{
// get op_return from CCvout
opret = getCCopret(vintx.vout[0].scriptPubKey);
}
else
{
// get op_return from the op_return
opret = vintx.vout[opret_ind].scriptPubKey;
}
if ( myGetTransaction(txid,tx,hashBlock) == 0 || DecodePaymentsFundOpRet(opret,checktxid) != 'F' || checktxid != createtxid )
{
fprintf(stderr,"bad opret %s vs %s\n",checktxid.GetHex().c_str(),createtxid.GetHex().c_str());
continue;
@@ -524,7 +563,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr)
newamount += (PAYMENTS_TXFEE - mtx.vout[i+1].nValue);
mtx.vout[i+1].nValue = PAYMENTS_TXFEE;
}
}
}
if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,newamount+2*PAYMENTS_TXFEE,CC_MAXVINS/2,createtxid,latestheight)) >= newamount+2*PAYMENTS_TXFEE )
{
std::string rawtx;
@@ -603,10 +642,16 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr)
}
else
{
mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk));
opret = EncodePaymentsFundOpRet(txid);
fprintf(stderr, "opret.%s\n", HexStr(opret.begin(), opret.end()).c_str());
std::vector<std::vector<unsigned char>> vData = std::vector<std::vector<unsigned char>>();
if ( makeCCopret(opret, vData) )
{
mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk,&vData));
fprintf(stderr, "params_size.%li parmas_hexstr.%s\n", vData.size(), HexStr(vData[0].begin(),vData[0].end()).c_str());
}
}
rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,opret);
rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,CScript());
if ( params != 0 )
free_json(params);
return(payments_rawtxresult(result,rawtx,1));
@@ -630,24 +675,34 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr)
UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); CPubKey mypk; std::string rawtx;
std::vector<uint8_t> scriptPubKey,opret; int32_t allocation,n,retval0,retval1=0;
std::vector<uint8_t> scriptPubKey,opret; int32_t n,retval0,retval1=0; int64_t allocation;
cJSON *params = payments_reparse(&n,jsonstr);
mypk = pubkey2pk(Mypubkey());
if ( params != 0 && n > 1 && n <= 3 )
{
allocation = juint(jitem(params,0),0);
allocation = (int64_t)jint(jitem(params,0),0);
retval0 = payments_parsehexdata(scriptPubKey,jitem(params,1),0);
if ( n == 3 )
retval1 = payments_parsehexdata(opret,jitem(params,2),0);
if ( allocation > 0 && retval0 == 0 && retval1 == 0 && AddNormalinputs(mtx,mypk,PAYMENTS_TXFEE,10) > 0 )
CScript test = CScript(scriptPubKey.begin(),scriptPubKey.end());
txnouttype whichType;
if (!::IsStandard(test, whichType))
{
rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsTxidOpRet(allocation,scriptPubKey,opret));
if ( params != 0 )
free_json(params);
return(payments_rawtxresult(result,rawtx,1));
result.push_back(Pair("result","error"));
result.push_back(Pair("error","scriptPubkey is not valid payment."));
}
else
{
if ( n == 3 )
retval1 = payments_parsehexdata(opret,jitem(params,2),0);
if ( allocation > 0 && retval0 == 0 && retval1 == 0 && AddNormalinputs(mtx,mypk,PAYMENTS_TXFEE*2,10) > 0 )
{
rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsTxidOpRet(allocation,scriptPubKey,opret));
if ( params != 0 )
free_json(params);
return(payments_rawtxresult(result,rawtx,1));
}
result.push_back(Pair("result","error"));
result.push_back(Pair("error","invalid params or cant find txfee"));
}
result.push_back(Pair("result","error"));
result.push_back(Pair("error","invalid params or cant find txfee"));
}
else
{
@@ -867,7 +922,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr)
funds = CCaddress_balance(fundsaddr,1);
result.push_back(Pair(fundsaddr,ValueFromAmount(funds)));
GetCCaddress(cp,fundsopretaddr,Paymentspk);
fundsopret = CCaddress_balance(fundsopretaddr,1);
fundsopret = CCaddress_balance(fundsopretaddr,1); // Shows balance for ALL payments plans, not just the one asked for!
result.push_back(Pair(fundsopretaddr,ValueFromAmount(fundsopret)));
result.push_back(Pair("totalfunds",ValueFromAmount(funds+fundsopret)));
result.push_back(Pair("result","success"));

View File

@@ -289,6 +289,8 @@ void *chainparams_commandline(void *ptr)
mainParams.SetNValue(ASSETCHAINS_NK[0]);
mainParams.SetKValue(ASSETCHAINS_NK[1]);
}
if ( KOMODO_TESTNODE != 0 )
mainParams.SetMiningRequiresPeers(false);
if ( ASSETCHAINS_RPCPORT == 0 )
ASSETCHAINS_RPCPORT = ASSETCHAINS_P2PPORT + 1;
mainParams.pchMessageStart[0] = ASSETCHAINS_MAGIC & 0xff;

View File

@@ -121,6 +121,7 @@ public:
void SetCheckpointData(CCheckpointData checkpointData);
void SetNValue(uint64_t n) { nEquihashN = n; }
void SetKValue(uint64_t k) { nEquihashK = k; }
void SetMiningRequiresPeers(bool flag) { fMiningRequiresPeers = flag; }
//void setnonce(uint32_t nonce) { memcpy(&genesis.nNonce,&nonce,sizeof(nonce)); }
//void settimestamp(uint32_t timestamp) { genesis.nTime = timestamp; }

View File

@@ -44,5 +44,19 @@ TEST(Keys, EncodeAndDecodeSapling)
auto addr2 = boost::get<libzcash::SaplingPaymentAddress>(paymentaddr2);
EXPECT_EQ(addr, addr2);
}
{
auto ivk = sk.ToXFVK().fvk.in_viewing_key();
std::string ivk_string = EncodeViewingKey(ivk);
EXPECT_EQ(
ivk_string.substr(0, 5),
Params().Bech32HRP(CChainParams::SAPLING_INCOMING_VIEWING_KEY));
auto viewing_key = DecodeViewingKey(ivk_string);
EXPECT_TRUE(IsValidViewingKey(viewing_key));
auto ivk2 = boost::get<libzcash::SaplingIncomingViewingKey>(&viewing_key);
ASSERT_TRUE(ivk2 != nullptr);
EXPECT_EQ(*ivk2, ivk);
}
}
}

View File

@@ -128,6 +128,19 @@ public:
return ret;
}
std::string operator()(const libzcash::SaplingIncomingViewingKey& vk) const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << vk;
std::vector<unsigned char> serkey(ss.begin(), ss.end());
std::vector<unsigned char> data;
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, serkey.begin(), serkey.end());
std::string ret = bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_INCOMING_VIEWING_KEY), data);
memory_cleanse(serkey.data(), serkey.size());
memory_cleanse(data.data(), data.size());
return ret;
}
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
};
@@ -175,6 +188,7 @@ public:
// perform ceiling division to get the number of 5-bit clusters.
const size_t ConvertedSaplingPaymentAddressSize = ((32 + 11) * 8 + 4) / 5;
const size_t ConvertedSaplingExtendedSpendingKeySize = (ZIP32_XSK_SIZE * 8 + 4) / 5;
const size_t ConvertedSaplingIncomingViewingKeySize = (32 * 8 + 4) / 5;
} // namespace
CKey DecodeSecret(const std::string& str)
@@ -346,7 +360,19 @@ libzcash::ViewingKey DecodeViewingKey(const std::string& str)
return ret;
}
}
memory_cleanse(data.data(), data.size());
data.clear();
auto bech = bech32::Decode(str);
if(bech.first == Params().Bech32HRP(CChainParams::SAPLING_INCOMING_VIEWING_KEY) &&
bech.second.size() == ConvertedSaplingIncomingViewingKeySize) {
// Bech32 decoding
data.reserve((bech.second.size() * 5) / 8);
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) {
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
libzcash::SaplingIncomingViewingKey ret;
ss >> ret;
return ret;
}
}
return libzcash::InvalidEncoding();
}

View File

@@ -75,7 +75,8 @@ int tx_height( const uint256 &hash ){
nHeight = it->second->GetHeight();
//fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight);
} else {
fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() );
// Unconfirmed xtns
//fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() );
}
return nHeight;
}
@@ -206,8 +207,11 @@ try_again:
if ( strncmp(url,"https",5) == 0 )
{
curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYPEER,0);
curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYHOST,0);
/* printf("[ Decker ] SSL: %s\n", curl_version()); */
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
//curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L); // this is useful for debug, but seems crash on libcurl/7.64.1 OpenSSL/1.1.1b zlib/1.2.8 librtmp/2.3
}
if ( userpass != 0 )
curl_easy_setopt(curl_handle,CURLOPT_USERPWD, userpass);
@@ -1091,6 +1095,17 @@ int32_t komodo_blockheight(uint256 hash)
return(0);
}
uint32_t komodo_blocktime(uint256 hash)
{
BlockMap::const_iterator it; CBlockIndex *pindex = 0;
if ( (it = mapBlockIndex.find(hash)) != mapBlockIndex.end() )
{
if ( (pindex= it->second) != 0 )
return(pindex->nTime);
}
return(0);
}
int32_t komodo_checkpoint(int32_t *notarized_heightp,int32_t nHeight,uint256 hash)
{
int32_t notarized_height,MoMdepth; uint256 MoM,notarized_hash,notarized_desttxid; CBlockIndex *notary,*pindex;

View File

@@ -78,6 +78,7 @@ extern std::string DONATION_PUBKEY;
extern uint8_t ASSETCHAINS_PRIVATE;
extern int32_t USE_EXTERNAL_PUBKEY;
extern char NOTARYADDRS[64][64];
extern int32_t KOMODO_TESTNODE;
int tx_height( const uint256 &hash );
extern std::vector<std::string> vWhiteListAddress;
void komodo_netevent(std::vector<uint8_t> payload);
@@ -94,6 +95,7 @@ char *bitcoin_address(char *coinaddr,uint8_t addrtype,uint8_t *pubkey_or_rmd160,
int32_t komodo_minerids(uint8_t *minerids,int32_t height,int32_t width);
int32_t komodo_kvsearch(uint256 *refpubkeyp,int32_t current_height,uint32_t *flagsp,int32_t *heightp,uint8_t value[IGUANA_MAXSCRIPTSIZE],uint8_t *key,int32_t keylen);
uint32_t komodo_blocktime(uint256 hash);
int32_t komodo_longestchain();
int32_t komodo_dpowconfs(int32_t height,int32_t numconfs);
int8_t komodo_segid(int32_t nocache,int32_t height);

View File

@@ -109,6 +109,8 @@ uint64_t PENDING_KOMODO_TX;
extern int32_t KOMODO_LOADINGBLOCKS;
unsigned int MAX_BLOCK_SIGOPS = 20000;
int32_t KOMODO_TESTNODE;
struct komodo_kv *KOMODO_KV;
pthread_mutex_t KOMODO_KV_mutex,KOMODO_CC_mutex;

View File

@@ -1710,6 +1710,7 @@ void komodo_args(char *argv0)
DONATION_PUBKEY = GetArg("-donation", "");
NOTARY_PUBKEY = GetArg("-pubkey", "");
KOMODO_DEALERNODE = GetArg("-dealer",0);
KOMODO_TESTNODE = GetArg("-testnode",0);
if ( strlen(NOTARY_PUBKEY.c_str()) == 66 )
{
decode_hex(NOTARY_PUBKEY33,33,(char *)NOTARY_PUBKEY.c_str());

View File

@@ -6420,8 +6420,8 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
nLoaded++;
if (state.IsError())
break;
} else if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex[hash]->GetHeight() % 1000 == 0) {
LogPrintf("Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->GetHeight());
} else if (hash != chainparams.GetConsensus().hashGenesisBlock && komodo_blockheight(hash) % 1000 == 0) {
LogPrintf("Block Import: already had block %s at height %d\n", hash.ToString(), komodo_blockheight(hash));
}
// Recursively process earlier encountered successors of this block

View File

@@ -217,7 +217,8 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue&
{
if (rawproof.size() > 0)
{
std::string sourceSymbol(rawproof.begin(), rawproof.end());
std::string sourceSymbol;
E_UNMARSHAL(rawproof, ss >> sourceSymbol);
in.push_back(Pair("address", "IMP-" + sourceSymbol + "-" + burnTx.GetHash().ToString()));
}
}

View File

@@ -24,6 +24,7 @@ int32_t ASSETCHAINS_LWMAPOS = 0;
int32_t VERUS_BLOCK_POSUNITS = 1000;
int32_t ASSETCHAINS_OVERWINTER = 227520;
int32_t ASSETCHAINS_SAPLING = 227520;
int32_t KOMODO_TESTNODE = 0;
unsigned int MAX_BLOCK_SIGOPS = 20000;

View File

@@ -46,6 +46,7 @@
#include <thread>
#include "paymentdisclosuredb.h"
int32_t komodo_blockheight(uint256 hash);
using namespace libzcash;
@@ -677,7 +678,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
if (mapBlockIndex.find(wtx.hashBlock) == mapBlockIndex.end()) {
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("mapBlockIndex does not contain block hash %s", wtx.hashBlock.ToString()));
}
wtxHeight = mapBlockIndex[wtx.hashBlock]->GetHeight();
wtxHeight = komodo_blockheight(wtx.hashBlock);
wtxDepth = wtx.GetDepthInMainChain();
}
LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n",

View File

@@ -55,6 +55,7 @@ using namespace libzcash;
extern char ASSETCHAINS_SYMBOL[65];
int32_t komodo_dpowconfs(int32_t height,int32_t numconfs);
int32_t komodo_blockheight(uint256 hash);
int tx_height( const uint256 &hash );
extern UniValue signrawtransaction(const UniValue& params, bool fHelp);
extern UniValue sendrawtransaction(const UniValue& params, bool fHelp);
@@ -836,7 +837,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
if (mapBlockIndex.find(wtx.hashBlock) == mapBlockIndex.end()) {
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("mapBlockIndex does not contain block hash %s", wtx.hashBlock.ToString()));
}
wtxHeight = mapBlockIndex[wtx.hashBlock]->GetHeight();
wtxHeight = komodo_blockheight(wtx.hashBlock);
wtxDepth = wtx.GetDepthInMainChain();
}
LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n",

View File

@@ -526,6 +526,59 @@ TEST(WalletTests, FindMySaplingNotes) {
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
TEST(WalletTests, FindMySaplingNotesWithIvkOnly) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
auto consensusParams = Params().GetConsensus();
TestWallet wallet;
// Generate dummy Sapling address
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto sk = libzcash::SaplingExtendedSpendingKey::Master(seed);
auto addr = sk.DefaultAddress();
auto expsk = sk.expsk;
auto fvk = expsk.full_viewing_key();
auto pk = sk.DefaultAddress();
auto ivk = fvk.in_viewing_key();
// Generate dummy Sapling note
libzcash::SaplingNote note(pk, 50000);
auto cm = note.cm().get();
SaplingMerkleTree tree;
tree.append(cm);
auto anchor = tree.root();
auto witness = tree.witness();
// Generate transaction
auto builder = TransactionBuilder(consensusParams, 1);
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
auto tx = maybe_tx.get();
// No Sapling notes can be found in tx which does not belong to the wallet
CWalletTx wtx {&wallet, tx};
ASSERT_FALSE(wallet.HaveSaplingSpendingKey(fvk));
ASSERT_FALSE(wallet.HaveSaplingIncomingViewingKey(addr));
auto noteMap = wallet.FindMySaplingNotes(wtx).first;
EXPECT_EQ(0, noteMap.size());
// Add ivk to wallet, so Sapling notes can be found
ASSERT_TRUE(wallet.AddSaplingIncomingViewingKey(ivk, addr));
ASSERT_FALSE(wallet.HaveSaplingSpendingKey(fvk));
ASSERT_TRUE(wallet.HaveSaplingIncomingViewingKey(addr));
noteMap = wallet.FindMySaplingNotes(wtx).first;
EXPECT_EQ(2, noteMap.size());
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
TEST(WalletTests, FindMySproutNotes) {
CWallet wallet;

View File

@@ -82,6 +82,9 @@ TEST(wallet_zkeys_tests, StoreAndLoadSaplingZkeys) {
EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(sk.DefaultAddress()));
EXPECT_TRUE(wallet.HaveSaplingIncomingViewingKey(dpa));
// verify that resets nTimeFirstKey, since there is no birthday info for watch-only keys
EXPECT_EQ(wallet.nTimeFirstKey, 1);
// Load a third key into the wallet
auto sk2 = m.Derive(1);
ASSERT_TRUE(wallet.LoadSaplingZKey(sk2));

View File

@@ -694,7 +694,7 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp)
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() < 1 || params.size() > 3)
if (fHelp || params.size() < 1 || params.size() > 4)
throw runtime_error(
"z_importviewingkey \"vkey\" ( rescan startHeight )\n"
"\nAdds a viewing key (as returned by z_exportviewingkey) to your wallet.\n"
@@ -702,6 +702,7 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp)
"1. \"vkey\" (string, required) The viewing key (see z_exportviewingkey)\n"
"2. rescan (string, optional, default=\"whenkeyisnew\") Rescan the wallet for transactions - can be \"yes\", \"no\" or \"whenkeyisnew\"\n"
"3. startHeight (numeric, optional, default=0) Block height to start rescan from\n"
"4. zaddr (string, optional, default=\"\") zaddr in case of importing viewing key for Sapling\n"
"\nNote: This call can take minutes to complete if rescan is true.\n"
"\nExamples:\n"
"\nImport a viewing key\n"
@@ -712,6 +713,8 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp)
+ HelpExampleCli("z_importviewingkey", "\"vkey\" whenkeyisnew 30000") +
"\nRe-import the viewing key with longer partial rescan\n"
+ HelpExampleCli("z_importviewingkey", "\"vkey\" yes 20000") +
"\nImport the viewing key for Sapling address\n"
+ HelpExampleCli("z_importviewingkey", "\"vkey\" no 0 \"zaddr\"") +
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("z_importviewingkey", "\"vkey\", \"no\"")
);
@@ -751,14 +754,34 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp)
if (!IsValidViewingKey(viewingkey)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid viewing key");
}
// TODO: Add Sapling support. For now, return an error to the user.
if (boost::get<libzcash::SproutViewingKey>(&viewingkey) == nullptr) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Currently, only Sprout viewing keys are supported");
}
auto vkey = boost::get<libzcash::SproutViewingKey>(viewingkey);
auto addr = vkey.address();
{
if (boost::get<libzcash::SproutViewingKey>(&viewingkey) == nullptr) {
if (params.size() < 4) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Missing zaddr for Sapling viewing key.");
}
string strAddress = params[3].get_str();
auto address = DecodePaymentAddress(strAddress);
if (!IsValidPaymentAddress(address)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr");
}
auto addr = boost::get<libzcash::SaplingPaymentAddress>(address);
auto ivk = boost::get<libzcash::SaplingIncomingViewingKey>(viewingkey);
if (pwalletMain->HaveSaplingIncomingViewingKey(addr)) {
if (fIgnoreExistingKey) {
return NullUniValue;
}
} else {
pwalletMain->MarkDirty();
if (!pwalletMain->AddSaplingIncomingViewingKey(ivk, addr)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding viewing key to wallet");
}
}
} else {
auto vkey = boost::get<libzcash::SproutViewingKey>(viewingkey);
auto addr = vkey.address();
if (pwalletMain->HaveSproutSpendingKey(addr)) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this viewing key");
}
@@ -775,13 +798,12 @@ UniValue z_importviewingkey(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding viewing key to wallet");
}
}
// We want to scan for transactions and notes
if (fRescan) {
pwalletMain->ScanForWalletTransactions(chainActive[nRescanHeight], true);
}
}
// We want to scan for transactions and notes
if (fRescan) {
pwalletMain->ScanForWalletTransactions(chainActive[nRescanHeight], true);
}
return NullUniValue;
}
@@ -853,12 +875,17 @@ UniValue z_exportviewingkey(const UniValue& params, bool fHelp)
if (!IsValidPaymentAddress(address)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr");
}
// TODO: Add Sapling support. For now, return an error to the user.
if (boost::get<libzcash::SproutPaymentAddress>(&address) == nullptr) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Currently, only Sprout zaddrs are supported");
}
auto addr = boost::get<libzcash::SproutPaymentAddress>(address);
if (boost::get<libzcash::SproutPaymentAddress>(&address) == nullptr) {
auto addr = boost::get<libzcash::SaplingPaymentAddress>(address);
libzcash::SaplingIncomingViewingKey ivk;
if(!pwalletMain->GetSaplingIncomingViewingKey(addr, ivk)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet does not hold viewing key for this zaddr");
}
return EncodeViewingKey(ivk);
}
auto addr = boost::get<libzcash::SproutPaymentAddress>(address);
libzcash::SproutViewingKey vk;
if (!pwalletMain->GetSproutViewingKey(addr, vk)) {
libzcash::SproutSpendingKey k;

View File

@@ -122,10 +122,10 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
entry.push_back(Pair("generated", true));
if (confirms > 0)
{
entry.push_back(Pair("confirmations", komodo_dpowconfs((int32_t)mapBlockIndex[wtx.hashBlock]->GetHeight(),confirms)));
entry.push_back(Pair("confirmations", komodo_dpowconfs((int32_t)komodo_blockheight(wtx.hashBlock),confirms)));
entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
entry.push_back(Pair("blockindex", wtx.nIndex));
entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime()));
entry.push_back(Pair("blocktime", (uint64_t)komodo_blocktime(wtx.hashBlock)));
entry.push_back(Pair("expiryheight", (int64_t)wtx.nExpiryHeight));
} else entry.push_back(Pair("confirmations", confirms));
uint256 hash = wtx.GetHash();
@@ -1624,7 +1624,7 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts)
tallyitem& item = mapTally[address];
item.nAmount += txout.nValue; // komodo_interest?
item.nConf = min(item.nConf, nDepth);
item.nHeight = mapBlockIndex[wtx.hashBlock]->GetHeight();
item.nHeight = komodo_blockheight(wtx.hashBlock);
item.txids.push_back(wtx.GetHash());
if (mine & ISMINE_WATCH_ONLY)
item.fIsWatchonly = true;
@@ -3915,7 +3915,8 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
}
// Visitor to support Sprout and Sapling addrs
if (!boost::apply_visitor(PaymentAddressBelongsToWallet(pwalletMain), zaddr)) {
if (!boost::apply_visitor(PaymentAddressBelongsToWallet(pwalletMain), zaddr) &&
!boost::apply_visitor(IncomingViewingKeyBelongsToWallet(pwalletMain), zaddr)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found.");
}

View File

@@ -185,6 +185,7 @@ bool CWallet::AddSaplingZKey(
return false;
}
nTimeFirstKey = 1; // No birthday information for viewing keys.
if (!fFileBacked) {
return true;
}
@@ -1541,26 +1542,29 @@ void CWallet::UpdateSaplingNullifierNoteMapWithTx(CWalletTx& wtx) {
}
else {
uint64_t position = nd.witnesses.front().position();
SaplingFullViewingKey fvk = mapSaplingFullViewingKeys.at(nd.ivk);
OutputDescription output = wtx.vShieldedOutput[op.n];
auto optPlaintext = SaplingNotePlaintext::decrypt(output.encCiphertext, nd.ivk, output.ephemeralKey, output.cm);
if (!optPlaintext) {
// An item in mapSaplingNoteData must have already been successfully decrypted,
// otherwise the item would not exist in the first place.
assert(false);
// Skip if we only have incoming viewing key
if (mapSaplingFullViewingKeys.count(nd.ivk) != 0) {
SaplingFullViewingKey fvk = mapSaplingFullViewingKeys.at(nd.ivk);
OutputDescription output = wtx.vShieldedOutput[op.n];
auto optPlaintext = SaplingNotePlaintext::decrypt(output.encCiphertext, nd.ivk, output.ephemeralKey, output.cm);
if (!optPlaintext) {
// An item in mapSaplingNoteData must have already been successfully decrypted,
// otherwise the item would not exist in the first place.
assert(false);
}
auto optNote = optPlaintext.get().note(nd.ivk);
if (!optNote) {
assert(false);
}
auto optNullifier = optNote.get().nullifier(fvk, position);
if (!optNullifier) {
// This should not happen. If it does, maybe the position has been corrupted or miscalculated?
assert(false);
}
uint256 nullifier = optNullifier.get();
mapSaplingNullifiersToNotes[nullifier] = op;
item.second.nullifier = nullifier;
}
auto optNote = optPlaintext.get().note(nd.ivk);
if (!optNote) {
assert(false);
}
auto optNullifier = optNote.get().nullifier(fvk, position);
if (!optNullifier) {
// This should not happen. If it does, maybe the position has been corrupted or miscalculated?
assert(false);
}
uint256 nullifier = optNullifier.get();
mapSaplingNullifiersToNotes[nullifier] = op;
item.second.nullifier = nullifier;
}
}
}
@@ -1991,23 +1995,40 @@ std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap> CWallet::FindMySap
// Protocol Spec: 4.19 Block Chain Scanning (Sapling)
for (uint32_t i = 0; i < tx.vShieldedOutput.size(); ++i) {
const OutputDescription output = tx.vShieldedOutput[i];
bool found = false;
for (auto it = mapSaplingFullViewingKeys.begin(); it != mapSaplingFullViewingKeys.end(); ++it) {
SaplingIncomingViewingKey ivk = it->first;
auto result = SaplingNotePlaintext::decrypt(output.encCiphertext, ivk, output.ephemeralKey, output.cm);
if (!result) {
continue;
if (result) {
auto address = ivk.address(result.get().d);
if (address && mapSaplingIncomingViewingKeys.count(address.get()) == 0) {
viewingKeysToAdd[address.get()] = ivk;
}
// We don't cache the nullifier here as computing it requires knowledge of the note position
// in the commitment tree, which can only be determined when the transaction has been mined.
SaplingOutPoint op {hash, i};
SaplingNoteData nd;
nd.ivk = ivk;
noteData.insert(std::make_pair(op, nd));
found = true;
break;
}
auto address = ivk.address(result.get().d);
if (address && mapSaplingIncomingViewingKeys.count(address.get()) == 0) {
viewingKeysToAdd[address.get()] = ivk;
}
if (!found) {
for (auto it = mapSaplingIncomingViewingKeys.begin(); it != mapSaplingIncomingViewingKeys.end(); ++it) {
SaplingIncomingViewingKey ivk = it-> second;
auto result = SaplingNotePlaintext::decrypt(output.encCiphertext, ivk, output.ephemeralKey, output.cm);
if (!result) {
continue;
}
// We don't cache the nullifier here as computing it requires knowledge of the note position
// in the commitment tree, which can only be determined when the transaction has been mined.
SaplingOutPoint op {hash, i};
SaplingNoteData nd;
nd.ivk = ivk;
noteData.insert(std::make_pair(op, nd));
break;
}
// We don't cache the nullifier here as computing it requires knowledge of the note position
// in the commitment tree, which can only be determined when the transaction has been mined.
SaplingOutPoint op {hash, i};
SaplingNoteData nd;
nd.ivk = ivk;
noteData.insert(std::make_pair(op, nd));
break;
}
}
@@ -5071,6 +5092,22 @@ void CWallet::GetFilteredNotes(
// Shielded key and address generalizations
//
bool IncomingViewingKeyBelongsToWallet::operator()(const libzcash::SproutPaymentAddress &zaddr) const
{
return m_wallet->HaveSproutViewingKey(zaddr);
}
bool IncomingViewingKeyBelongsToWallet::operator()(const libzcash::SaplingPaymentAddress &zaddr) const
{
libzcash::SaplingIncomingViewingKey ivk;
return m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk);
}
bool IncomingViewingKeyBelongsToWallet::operator()(const libzcash::InvalidEncoding& no) const
{
return false;
}
bool PaymentAddressBelongsToWallet::operator()(const libzcash::SproutPaymentAddress &zaddr) const
{
return m_wallet->HaveSproutSpendingKey(zaddr) || m_wallet->HaveSproutViewingKey(zaddr);

View File

@@ -1392,6 +1392,19 @@ public:
bool operator()(const libzcash::InvalidEncoding& no) const;
};
class IncomingViewingKeyBelongsToWallet : public boost::static_visitor<bool>
{
private:
CWallet *m_wallet;
public:
IncomingViewingKeyBelongsToWallet(CWallet *wallet) : m_wallet(wallet) {}
bool operator()(const libzcash::SproutPaymentAddress &zaddr) const;
bool operator()(const libzcash::SaplingPaymentAddress &zaddr) const;
bool operator()(const libzcash::InvalidEncoding& no) const;
};
class HaveSpendingKeyForPaymentAddress : public boost::static_visitor<bool>
{
private:

View File

@@ -219,7 +219,7 @@ public:
};
typedef boost::variant<InvalidEncoding, SproutPaymentAddress, SaplingPaymentAddress> PaymentAddress;
typedef boost::variant<InvalidEncoding, SproutViewingKey> ViewingKey;
typedef boost::variant<InvalidEncoding, SproutViewingKey, SaplingIncomingViewingKey> ViewingKey;
}