diff --git a/qa/rpc-tests/src/nspv_client_test.py b/qa/rpc-tests/src/nspv_client_test.py new file mode 100644 index 000000000..fbf0e5360 --- /dev/null +++ b/qa/rpc-tests/src/nspv_client_test.py @@ -0,0 +1,114 @@ +import sys +sys.path.append('../../src/tui') + +from lib import tuilib +import unittest + +''' +specify chain ticker (daemon should be up), wif which will be imported and address to which you want to broadcast +added 1 second sleep after each case to surely not face the nSPV server limitation (1 call/second) +''' + +wif = '' +dest_address = 'RMjy5VkHFJkXTJDTJ3XX1zVzukP48sKyva' +amount = '0.1' +chain = 'ILN' + +rpc_proxy = tuilib.def_credentials(chain) + + +class TestNspvClient(unittest.TestCase): + + def test_nspv_getinfo(self): + print("testing nspv_getinfo") + result = rpc_proxy.nspv_getinfo() + self.assertEqual(result["result"], "success") + self.assertGreater(result["height"], 2689) + time.sleep(1) + + def test_nspv_notarizations(self): + print("testing nspv_notarizations") + result = rpc_proxy.nspv_notarizations("2000") + self.assertEqual(result["result"], "success") + self.assertEqual(result["prev"]["notarized_height"], 1998) + self.assertEqual(result["next"]["notarized_height"], 2002) + time.sleep(1) + + def test_nspv_hdrsproof(self): + print("testing nspv_hdrsproof") + result = rpc_proxy.nspv_hdrsproof("2000", "2100") + self.assertEqual(result["result"], "success") + self.assertEqual(result["numhdrs"], 101) + time.sleep(1) + + def test_nspv_login(self): + print("testing nspv_login") + result = rpc_proxy.nspv_login(wif) + self.assertEqual(result["result"], "success") + self.assertEqual(result["status"], "wif will expire in 777 seconds") + time.sleep(1) + + def test_nspv_listunspent(self): + print("testing nspv_listunspent") + result = rpc_proxy.nspv_listunspent() + self.assertEqual(result["result"], "success") + time.sleep(1) + result = rpc_proxy.nspv_listunspent("RQ1mvCUcziWzRwE8Ugtex29VjoFjRzxQJT") + self.assertEqual(result["result"], "error") + + def test_nspv_spend(self): + print("testing nspv_spend") + result = rpc_proxy.nspv_login(wif) + result = rpc_proxy.nspv_spend(dest_address, amount) + self.assertEqual(result["result"], "success") + self.assertEqual(result["vout"][0]["valueZat"], 10000000) + time.sleep(1) + + def test_nspv_broadcast(self): + print("testing nspv_broadcast") + result = rpc_proxy.nspv_login(wif) + broadcast_hex = rpc_proxy.nspv_spend(dest_address, amount)["hex"] + time.sleep(1) + result = rpc_proxy.nspv_broadcast(broadcast_hex) + self.assertEqual(result["result"], "success") + self.assertEqual(result["retcode"], 1) + self.assertEqual(result["expected"], result["broadcast"]) + print("Broadcast txid: " + result["broadcast"]) + time.sleep(1) + + def test_nspv_logout(self): + print("testing nspv_logout") + rpc_proxy.nspv_login(wif) + time.sleep(1) + rpc_proxy.nspv_logout() + time.sleep(1) + result = rpc_proxy.nspv_spend(dest_address, amount) + self.assertEqual(result["result"], "error") + self.assertEqual(result["error"], "wif expired") + time.sleep(1) + + def test_nspv_spentinfo(self): + print("testing nspv_spent_info") + result = rpc_proxy.nspv_spentinfo("67ffe0eaecd6081de04675c492a59090b573ee78955c4e8a85b8ac0be0e8e418", "1") + self.assertEqual(result["result"], "success") + self.assertEqual(result["spentheight"], 2681) + time.sleep(1) + + def test_nspv_txproof(self): + print("testing nspv_txproof") + result = rpc_proxy.nspv_txproof("67ffe0eaecd6081de04675c492a59090b573ee78955c4e8a85b8ac0be0e8e418", "2673") + self.assertEqual(result["txid"], "67ffe0eaecd6081de04675c492a59090b573ee78955c4e8a85b8ac0be0e8e418") + time.sleep(1) + + def test_nspv_login_timout(self): + print("testing auto-logout in 777 seconds") + rpc_proxy.nspv_login(wif) + time.sleep(777) + result = rpc_proxy.nspv_spend(dest_address, amount) + self.assertEqual(result["result"], "error") + self.assertEqual(result["error"], "wif expired") + time.sleep(1) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 68e0da8b3..ff329a0d6 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -77,7 +77,8 @@ void WaitForShutdown(boost::thread_group* threadGroup) //fprintf(stderr,"call passport iteration\n"); if ( ASSETCHAINS_SYMBOL[0] == 0 ) { - komodo_passport_iteration(); + if ( KOMODO_NSPV == 0 ) + komodo_passport_iteration(); for (i=0; i<10; i++) { fShutdown = ShutdownRequested(); diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 1d8cf669e..c4ef6c52c 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -174,7 +174,7 @@ static int32_t ignorevin; bool myGetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock); int32_t is_hexstr(char *str,int32_t n); bool myAddtomempool(CTransaction &tx, CValidationState *pstate = NULL, bool fSkipExpiry = false); -int32_t CCgettxout(uint256 txid,int32_t vout,int32_t mempoolflag,int32_t lockflag); +int64_t CCgettxout(uint256 txid,int32_t vout,int32_t mempoolflag,int32_t lockflag); bool myIsutxo_spentinmempool(uint256 &spenttxid,int32_t &spentvini,uint256 txid,int32_t vout); bool mytxid_inmempool(uint256 txid); int32_t myIsutxo_spent(uint256 &spenttxid,uint256 txid,int32_t vout); @@ -289,6 +289,7 @@ void SetCCtxids(std::vector > &addressIndex int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs); int64_t AddNormalinputs2(CMutableTransaction &mtx,int64_t total,int32_t maxinputs); int64_t CCutxovalue(char *coinaddr,uint256 utxotxid,int32_t utxovout,int32_t CCflag); +bool NSPV_SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey); // curve25519 and sha256 bits256 curve25519_shared(bits256 privkey,bits256 otherpub); diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index 7435defbf..a3624fd67 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -150,8 +150,16 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran utxovout = mtx.vin[i].prevout.n; if ( vintx.vout[utxovout].scriptPubKey.IsPayToCryptoCondition() == 0 ) { - if ( SignTx(mtx,i,vintx.vout[utxovout].nValue,vintx.vout[utxovout].scriptPubKey) == 0 ) - fprintf(stderr,"signing error for vini.%d of %llx\n",i,(long long)vinimask); + if ( KOMODO_NSPV == 0 ) + { + if ( SignTx(mtx,i,vintx.vout[utxovout].nValue,vintx.vout[utxovout].scriptPubKey) == 0 ) + fprintf(stderr,"signing error for vini.%d of %llx\n",i,(long long)vinimask); + } + else + { + if ( NSPV_SignTx(mtx,i,vintx.vout[utxovout].nValue,vintx.vout[utxovout].scriptPubKey) == 0 ) + fprintf(stderr,"NSPV signing error for vini.%d of %llx\n",i,(long long)vinimask); + } } else { @@ -357,7 +365,7 @@ int64_t CCutxovalue(char *coinaddr,uint256 utxotxid,int32_t utxovout,int32_t CCf return(0); } -int32_t CCgettxout(uint256 txid,int32_t vout,int32_t mempoolflag,int32_t lockflag) +int64_t CCgettxout(uint256 txid,int32_t vout,int32_t mempoolflag,int32_t lockflag) { CCoins coins; //fprintf(stderr,"CCgettxoud %s/v%d\n",txid.GetHex().c_str(),vout); @@ -510,6 +518,10 @@ int32_t CC_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t * int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs) { int32_t abovei,belowi,ind,vout,i,n = 0; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; uint256 txid,hashBlock; std::vector vecOutputs; CTransaction tx; struct CC_utxo *utxos,*up; + if ( KOMODO_NSPV != 0 ) + { + //return(NSPV_addinputs(struct NSPV_utxoresp *used,CMutableTransaction &mtx,int64_t total,int32_t maxinputs,struct NSPV_utxoresp *ptr,int32_t num)); + } #ifdef ENABLE_WALLET assert(pwalletMain != NULL); const CKeyStore& keystore = *pwalletMain; @@ -605,6 +617,10 @@ int64_t AddNormalinputs2(CMutableTransaction &mtx,int64_t total,int32_t maxinput { int32_t abovei,belowi,ind,vout,i,n = 0; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; char coinaddr[64]; uint256 txid,hashBlock; CTransaction tx; struct CC_utxo *utxos,*up; std::vector > unspentOutputs; + if ( KOMODO_NSPV != 0 ) + { + //return(NSPV_addinputs(struct NSPV_utxoresp *used,CMutableTransaction &mtx,int64_t total,int32_t maxinputs,struct NSPV_utxoresp *ptr,int32_t num)); + } utxos = (struct CC_utxo *)calloc(CC_MAXVINS,sizeof(*utxos)); if ( maxinputs > CC_MAXVINS ) maxinputs = CC_MAXVINS; diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index 5de5ed0db..3c67b4068 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -437,9 +437,15 @@ std::vector Mypubkey() return(pubkey); } +extern CKey NSPV_key; bool Myprivkey(uint8_t myprivkey[]) { char coinaddr[64],checkaddr[64]; std::string strAddress; char *dest; int32_t i,n; CBitcoinAddress address; CKeyID keyID; CKey vchSecret; uint8_t buf33[33]; + if ( KOMODO_NSPV != 0 ) + { + NSPV_key.SetKey32(myprivkey); + return true; + } if ( Getscriptaddress(coinaddr,CScript() << Mypubkey() << OP_CHECKSIG) != 0 ) { n = (int32_t)strlen(coinaddr); diff --git a/src/gtest/test_checktransaction.cpp b/src/gtest/test_checktransaction.cpp index c6f66cc64..0efac93db 100644 --- a/src/gtest/test_checktransaction.cpp +++ b/src/gtest/test_checktransaction.cpp @@ -166,7 +166,7 @@ TEST(checktransaction_tests, BadTxnsOversize) { // ... but fails contextual ones! EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-oversize", false)).Times(1); - EXPECT_FALSE(ContextualCheckTransaction(tx, state, 1, 100)); + EXPECT_FALSE(ContextualCheckTransaction(0,tx, state, 1, 100)); } { @@ -188,7 +188,7 @@ TEST(checktransaction_tests, BadTxnsOversize) { MockCValidationState state; EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state)); - EXPECT_TRUE(ContextualCheckTransaction(tx, state, 1, 100)); + EXPECT_TRUE(ContextualCheckTransaction(0,tx, state, 1, 100)); // Revert to default UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); @@ -508,9 +508,9 @@ TEST(checktransaction_tests, bad_txns_invalid_joinsplit_signature) { MockCValidationState state; // during initial block download, DoS ban score should be zero, else 100 EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1); - ContextualCheckTransaction(tx, state, 0, 100, []() { return true; }); + ContextualCheckTransaction(0,tx, state, 0, 100, []() { return true; }); EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1); - ContextualCheckTransaction(tx, state, 0, 100, []() { return false; }); + ContextualCheckTransaction(0,tx, state, 0, 100, []() { return false; }); } TEST(checktransaction_tests, non_canonical_ed25519_signature) { @@ -522,7 +522,7 @@ TEST(checktransaction_tests, non_canonical_ed25519_signature) { { CTransaction tx(mtx); MockCValidationState state; - EXPECT_TRUE(ContextualCheckTransaction(tx, state, 0, 100)); + EXPECT_TRUE(ContextualCheckTransaction(0,tx, state, 0, 100)); } // Copied from libsodium/crypto_sign/ed25519/ref10/open.c @@ -544,9 +544,9 @@ TEST(checktransaction_tests, non_canonical_ed25519_signature) { MockCValidationState state; // during initial block download, DoS ban score should be zero, else 100 EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1); - ContextualCheckTransaction(tx, state, 0, 100, []() { return true; }); + ContextualCheckTransaction(0,tx, state, 0, 100, []() { return true; }); EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1); - ContextualCheckTransaction(tx, state, 0, 100, []() { return false; }); + ContextualCheckTransaction(0,tx, state, 0, 100, []() { return false; }); } TEST(checktransaction_tests, OverwinterConstructors) { @@ -801,7 +801,7 @@ TEST(checktransaction_tests, OverwinterVersionNumberHigh) { UNSAFE_CTransaction tx(mtx); MockCValidationState state; EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-overwinter-version-too-high", false)).Times(1); - ContextualCheckTransaction(tx, state, 1, 100); + ContextualCheckTransaction(0,tx, state, 1, 100); // Revert to default UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); @@ -837,9 +837,9 @@ TEST(checktransaction_tests, OverwinterNotActive) { MockCValidationState state; // during initial block download, DoS ban score should be zero, else 100 EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1); - ContextualCheckTransaction(tx, state, 1, 100, []() { return true; }); + ContextualCheckTransaction(0,tx, state, 1, 100, []() { return true; }); EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1); - ContextualCheckTransaction(tx, state, 1, 100, []() { return false; }); + ContextualCheckTransaction(0,tx, state, 1, 100, []() { return false; }); } // This tests a transaction without the fOverwintered flag set, against the Overwinter consensus rule set. @@ -856,7 +856,7 @@ TEST(checktransaction_tests, OverwinterFlagNotSet) { CTransaction tx(mtx); MockCValidationState state; EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-flag-not-set", false)).Times(1); - ContextualCheckTransaction(tx, state, 1, 100); + ContextualCheckTransaction(0,tx, state, 1, 100); // Revert to default UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); diff --git a/src/gtest/test_transaction_builder.cpp b/src/gtest/test_transaction_builder.cpp index 05a8cb601..a04e30b0e 100644 --- a/src/gtest/test_transaction_builder.cpp +++ b/src/gtest/test_transaction_builder.cpp @@ -51,7 +51,7 @@ TEST(TransactionBuilder, Invoke) EXPECT_EQ(tx1.valueBalance, -40000); CValidationState state; - EXPECT_TRUE(ContextualCheckTransaction(tx1, state, 2, 0)); + EXPECT_TRUE(ContextualCheckTransaction(0,tx1, state, 2, 0)); EXPECT_EQ(state.GetRejectReason(), ""); // Prepare to spend the note that was just created @@ -85,7 +85,7 @@ TEST(TransactionBuilder, Invoke) EXPECT_EQ(tx2.vShieldedOutput.size(), 2); EXPECT_EQ(tx2.valueBalance, 10000); - EXPECT_TRUE(ContextualCheckTransaction(tx2, state, 3, 0)); + EXPECT_TRUE(ContextualCheckTransaction(0,tx2, state, 3, 0)); EXPECT_EQ(state.GetRejectReason(), ""); // Revert to default diff --git a/src/init.cpp b/src/init.cpp index 107e3e476..738d94377 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -90,12 +90,10 @@ using namespace std; +#include "komodo_defs.h" extern void ThreadSendAlert(); extern bool komodo_dailysnapshot(int32_t height); extern int32_t KOMODO_LOADINGBLOCKS; -extern bool VERUS_MINTBLOCKS; -extern char ASSETCHAINS_SYMBOL[]; -extern int32_t KOMODO_SNAPSHOT_INTERVAL; ZCJoinSplit* pzcashParams = NULL; @@ -978,13 +976,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Make sure enough file descriptors are available int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1); nMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); + //fprintf(stderr,"nMaxConnections %d\n",nMaxConnections); nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0); int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); + //fprintf(stderr,"nMaxConnections %d FD_SETSIZE.%d nBind.%d expr.%d \n",nMaxConnections,FD_SETSIZE,nBind,(int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)); if (nFD < MIN_CORE_FILEDESCRIPTORS) return InitError(_("Not enough file descriptors available.")); if (nFD - MIN_CORE_FILEDESCRIPTORS < nMaxConnections) nMaxConnections = nFD - MIN_CORE_FILEDESCRIPTORS; - + fprintf(stderr,"nMaxConnections %d\n",nMaxConnections); // if using block pruning, then disable txindex // also disable the wallet (for now, until SPV support is implemented in wallet) if (GetArg("-prune", 0)) { @@ -1066,6 +1066,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) RegisterAllCoreRPCCommands(tableRPC); #ifdef ENABLE_WALLET bool fDisableWallet = GetBoolArg("-disablewallet", false); + if ( KOMODO_NSPV != 0 ) + { + fDisableWallet = true; + nLocalServices = 0; + } if (!fDisableWallet) RegisterWalletRPCCommands(tableRPC); #endif @@ -1299,9 +1304,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) libsnark::inhibit_profiling_info = true; libsnark::inhibit_profiling_counters = true; - // Initialize Zcash circuit parameters - ZC_LoadParams(chainparams); - + if ( KOMODO_NSPV == 0 ) + { + // Initialize Zcash circuit parameters + ZC_LoadParams(chainparams); + } /* Start the RPC server already. It will be started in "warmup" mode * and not really process calls already (but it will signify connections * that the server is there and will be ready later). Warmup mode will @@ -1478,6 +1485,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } #endif + if ( KOMODO_NSPV != 0 ) + { + std::vector vImportFiles; + threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); + StartNode(threadGroup, scheduler); + pcoinsTip = new CCoinsViewCache(pcoinscatcher); + InitBlockIndex(); + SetRPCWarmupFinished(); + uiInterface.InitMessage(_("Done loading")); + return !fRequestShutdown; + } // ********************************************************* Step 7: load block chain fReindex = GetBoolArg("-reindex", false); @@ -1890,7 +1908,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) PruneAndFlush(); } } - + if ( GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX) != 0 ) + nLocalServices |= NODE_ADDRINDEX; + if ( GetBoolArg("-spentindex", DEFAULT_SPENTINDEX) != 0 ) + nLocalServices |= NODE_SPENTINDEX; +fprintf(stderr,"nLocalServices %llx %d, %d\n",(long long)nLocalServices,GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX),GetBoolArg("-spentindex", DEFAULT_SPENTINDEX)); // ********************************************************* Step 10: import blocks if (mapArgs.count("-blocknotify")) diff --git a/src/komodo_defs.h b/src/komodo_defs.h index 5074b4303..06ed750a5 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -271,7 +271,7 @@ extern uint64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_FOUNDERS_REWARD; extern uint64_t ASSETCHAINS_TIMELOCKGTE; extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH,ASSETCHAINS_EQUIHASH,KOMODO_INITDONE; -extern int32_t KOMODO_MININGTHREADS,KOMODO_LONGESTCHAIN,ASSETCHAINS_SEED,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,KOMODO_ON_DEMAND,KOMODO_PASSPORT_INITDONE,ASSETCHAINS_STAKED; +extern int32_t KOMODO_MININGTHREADS,KOMODO_LONGESTCHAIN,ASSETCHAINS_SEED,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,KOMODO_ON_DEMAND,KOMODO_PASSPORT_INITDONE,ASSETCHAINS_STAKED,KOMODO_NSPV; extern uint64_t ASSETCHAINS_COMMISSION, ASSETCHAINS_LASTERA,ASSETCHAINS_CBOPRET; extern bool VERUS_MINTBLOCKS; extern uint64_t ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_NONCEMASK[],ASSETCHAINS_NK[2]; @@ -335,6 +335,8 @@ int64_t komodo_pricemult(int32_t ind); int32_t komodo_priceget(int64_t *buf64,int32_t ind,int32_t height,int32_t numblocks); uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 hash,int32_t n,int32_t checkheight,uint64_t checkvalue,int32_t tipheight); int32_t komodo_currentheight(); +int32_t komodo_notarized_bracket(struct notarized_checkpoint *nps[2],int32_t height); +uint256 Parseuint256(const char *hexstr); #endif diff --git a/src/komodo_globals.h b/src/komodo_globals.h index 9ffa147ea..85fcfcccf 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -46,7 +46,7 @@ struct komodo_state KOMODO_STATES[34]; int COINBASE_MATURITY = _COINBASE_MATURITY;//100; unsigned int WITNESS_CACHE_SIZE = _COINBASE_MATURITY+10; uint256 KOMODO_EARLYTXID; -int32_t KOMODO_MININGTHREADS = -1,IS_KOMODO_NOTARY,IS_STAKED_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAINS_SEED,KOMODO_ON_DEMAND,KOMODO_EXTERNAL_NOTARIES,KOMODO_PASSPORT_INITDONE,KOMODO_PAX,KOMODO_EXCHANGEWALLET,KOMODO_REWIND,STAKED_ERA,KOMODO_CONNECTING = -1,KOMODO_DEALERNODE,KOMODO_EXTRASATOSHI,ASSETCHAINS_FOUNDERS,ASSETCHAINS_CBMATURITY; +int32_t KOMODO_MININGTHREADS = -1,IS_KOMODO_NOTARY,IS_STAKED_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAINS_SEED,KOMODO_ON_DEMAND,KOMODO_EXTERNAL_NOTARIES,KOMODO_PASSPORT_INITDONE,KOMODO_PAX,KOMODO_EXCHANGEWALLET,KOMODO_REWIND,STAKED_ERA,KOMODO_CONNECTING = -1,KOMODO_DEALERNODE,KOMODO_EXTRASATOSHI,ASSETCHAINS_FOUNDERS,ASSETCHAINS_CBMATURITY,KOMODO_NSPV; int32_t KOMODO_INSYNC,KOMODO_LASTMINED,prevKOMODO_LASTMINED,KOMODO_CCACTIVATE,JUMBLR_PAUSE = 1; std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY,ASSETCHAINS_SCRIPTPUB,NOTARY_ADDRESS,ASSETCHAINS_SELFIMPORT,ASSETCHAINS_CCLIB; uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEYHASH[20],ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE,ASSETCHAINS_TXPOW,ASSETCHAINS_MARMARA; diff --git a/src/komodo_nSPV.h b/src/komodo_nSPV.h new file mode 100644 index 000000000..cba34c290 --- /dev/null +++ b/src/komodo_nSPV.h @@ -0,0 +1,498 @@ + +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +// todo: + +// headers "sync" make sure it connects to prior blocks to notarization. use getinfo hdrht to get missing hdrs + +// interest calculations are currently just using what is returned, it should calculate it from scratch + +// bruteforce sig -> NN pubkey validator +// CC signing +// make sure to sanity check all vector lengths on receipt +// make sure no files are updated (this is to allow nSPV=1 and later nSPV=0 without affecting database) + +#ifndef KOMODO_NSPV_H +#define KOMODO_NSPV_H + +#define NSPV_SKIPFULLVALIDATION 1 // changing to 0 issues 26 remote gettransaction, per utxo! need to change to bruteforcer +#define NSPV_POLLITERS 10 +#define NSPV_POLLMICROS 100777 +#define NSPV_MAXVINS 64 +#define NSPV_AUTOLOGOUT 777 +#define NSPV_BRANCHID 0x76b809bb + +// nSPV defines and struct definitions with serialization and purge functions + +#define NSPV_INFO 0x00 +#define NSPV_INFORESP 0x01 +#define NSPV_UTXOS 0x02 +#define NSPV_UTXOSRESP 0x03 +#define NSPV_NTZS 0x04 +#define NSPV_NTZSRESP 0x05 +#define NSPV_NTZSPROOF 0x06 +#define NSPV_NTZSPROOFRESP 0x07 +#define NSPV_TXPROOF 0x08 +#define NSPV_TXPROOFRESP 0x09 +#define NSPV_SPENTINFO 0x0a +#define NSPV_SPENTINFORESP 0x0b +#define NSPV_BROADCAST 0x0c +#define NSPV_BROADCASTRESP 0x0d + +int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int32_t height,CTransaction &tx); + +int32_t iguana_rwbuf(int32_t rwflag,uint8_t *serialized,uint16_t len,uint8_t *buf) +{ + if ( rwflag != 0 ) + memcpy(serialized,buf,len); + else memcpy(buf,serialized,len); + return(len); +} + +struct NSPV_equihdr +{ + int32_t nVersion; + uint256 hashPrevBlock; + uint256 hashMerkleRoot; + uint256 hashFinalSaplingRoot; + uint32_t nTime; + uint32_t nBits; + uint256 nNonce; + uint8_t nSolution[1344]; +}; + +int32_t NSPV_rwequihdr(int32_t rwflag,uint8_t *serialized,struct NSPV_equihdr *ptr) +{ + int32_t len = 0; + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nVersion),&ptr->nVersion); + len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->hashPrevBlock),(uint8_t *)&ptr->hashPrevBlock); + len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->hashMerkleRoot),(uint8_t *)&ptr->hashMerkleRoot); + len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->hashFinalSaplingRoot),(uint8_t *)&ptr->hashFinalSaplingRoot); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nTime),&ptr->nTime); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nBits),&ptr->nBits); + len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->nNonce),(uint8_t *)&ptr->nNonce); + len += iguana_rwbuf(rwflag,&serialized[len],sizeof(ptr->nSolution),ptr->nSolution); + return(len); +} + +int32_t iguana_rwequihdrvec(int32_t rwflag,uint8_t *serialized,uint16_t *vecsizep,struct NSPV_equihdr **ptrp) +{ + int32_t i,vsize,len = 0; + len += iguana_rwnum(rwflag,&serialized[len],sizeof(*vecsizep),vecsizep); + if ( (vsize= *vecsizep) != 0 ) + { + //fprintf(stderr,"vsize.%d ptrp.%p alloc %ld\n",vsize,*ptrp,sizeof(struct NSPV_equihdr)*vsize); + if ( *ptrp == 0 ) + *ptrp = (struct NSPV_equihdr *)calloc(sizeof(struct NSPV_equihdr),vsize); // relies on uint16_t being "small" to prevent mem exhaustion + for (i=0; itxid),(uint8_t *)&ptr->txid); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->satoshis),&ptr->satoshis); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->extradata),&ptr->extradata); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->vout),&ptr->vout); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); + return(len); +} + +struct NSPV_utxosresp +{ + struct NSPV_utxoresp *utxos; + char coinaddr[64]; + int64_t total,interest; + int32_t nodeheight; + uint16_t numutxos,pad16; +}; + +int32_t NSPV_rwutxosresp(int32_t rwflag,uint8_t *serialized,struct NSPV_utxosresp *ptr) // check mempool +{ + int32_t i,len = 0; + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->numutxos),&ptr->numutxos); + if ( ptr->numutxos != 0 ) + { + if ( ptr->utxos == 0 ) + ptr->utxos = (struct NSPV_utxoresp *)calloc(sizeof(*ptr->utxos),ptr->numutxos); // relies on uint16_t being "small" to prevent mem exhaustion + for (i=0; inumutxos; i++) + len += NSPV_rwutxoresp(rwflag,&serialized[len],&ptr->utxos[i]); + } + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->total),&ptr->total); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->interest),&ptr->interest); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nodeheight),&ptr->nodeheight); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad16),&ptr->pad16); + if ( rwflag != 0 ) + { + memcpy(&serialized[len],ptr->coinaddr,sizeof(ptr->coinaddr)); + len += sizeof(ptr->coinaddr); + } + else + { + memcpy(ptr->coinaddr,&serialized[len],sizeof(ptr->coinaddr)); + len += sizeof(ptr->coinaddr); + } + return(len); +} + +void NSPV_utxosresp_purge(struct NSPV_utxosresp *ptr) +{ + if ( ptr != 0 ) + { + if ( ptr->utxos != 0 ) + free(ptr->utxos); + memset(ptr,0,sizeof(*ptr)); + } +} + +struct NSPV_ntz +{ + uint256 blockhash,txid,othertxid; + int32_t height,txidheight; +}; + +int32_t NSPV_rwntz(int32_t rwflag,uint8_t *serialized,struct NSPV_ntz *ptr) +{ + int32_t len = 0; + len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->blockhash),(uint8_t *)&ptr->blockhash); + len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); + len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->othertxid),(uint8_t *)&ptr->othertxid); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->txidheight),&ptr->txidheight); + return(len); +} + +struct NSPV_ntzsresp +{ + struct NSPV_ntz prevntz,nextntz; +}; + +int32_t NSPV_rwntzsresp(int32_t rwflag,uint8_t *serialized,struct NSPV_ntzsresp *ptr) +{ + int32_t len = 0; + len += NSPV_rwntz(rwflag,&serialized[len],&ptr->prevntz); + len += NSPV_rwntz(rwflag,&serialized[len],&ptr->nextntz); + return(len); +} + +void NSPV_ntzsresp_purge(struct NSPV_ntzsresp *ptr) +{ + if ( ptr != 0 ) + memset(ptr,0,sizeof(*ptr)); +} + +struct NSPV_inforesp +{ + struct NSPV_ntz notarization; + uint256 blockhash; + int32_t height,hdrheight; + struct NSPV_equihdr H; +}; + +int32_t NSPV_rwinforesp(int32_t rwflag,uint8_t *serialized,struct NSPV_inforesp *ptr) +{ + int32_t len = 0; + len += NSPV_rwntz(rwflag,&serialized[len],&ptr->notarization); + len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->blockhash),(uint8_t *)&ptr->blockhash); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->hdrheight),&ptr->hdrheight); + len += NSPV_rwequihdr(rwflag,&serialized[len],&ptr->H); + //fprintf(stderr,"hdr rwlen.%d\n",len); + return(len); +} + +void NSPV_inforesp_purge(struct NSPV_inforesp *ptr) +{ + if ( ptr != 0 ) + memset(ptr,0,sizeof(*ptr)); +} + +struct NSPV_txproof +{ + uint256 txid; + int64_t unspentvalue; + int32_t height,vout,pad; + uint16_t txlen,txprooflen; + uint8_t *tx,*txproof; +}; + +int32_t NSPV_rwtxproof(int32_t rwflag,uint8_t *serialized,struct NSPV_txproof *ptr) +{ + int32_t len = 0; + len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->unspentvalue),&ptr->unspentvalue); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->vout),&ptr->vout); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad),&ptr->pad); + len += iguana_rwuint8vec(rwflag,&serialized[len],&ptr->txlen,&ptr->tx); + len += iguana_rwuint8vec(rwflag,&serialized[len],&ptr->txprooflen,&ptr->txproof); + return(len); +} + +void NSPV_txproof_purge(struct NSPV_txproof *ptr) +{ + if ( ptr != 0 ) + { + if ( ptr->tx != 0 ) + free(ptr->tx); + if ( ptr->txproof != 0 ) + free(ptr->txproof); + memset(ptr,0,sizeof(*ptr)); + } +} + +struct NSPV_ntzproofshared +{ + struct NSPV_equihdr *hdrs; + int32_t prevht,nextht,pad32; + uint16_t numhdrs,pad16; +}; + +int32_t NSPV_rwntzproofshared(int32_t rwflag,uint8_t *serialized,struct NSPV_ntzproofshared *ptr) +{ + int32_t len = 0; + len += iguana_rwequihdrvec(rwflag,&serialized[len],&ptr->numhdrs,&ptr->hdrs); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->prevht),&ptr->prevht); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nextht),&ptr->nextht); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad32),&ptr->pad32); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad16),&ptr->pad16); + //fprintf(stderr,"rwcommon prev.%d next.%d\n",ptr->prevht,ptr->nextht); + return(len); +} + +struct NSPV_ntzsproofresp +{ + struct NSPV_ntzproofshared common; + uint256 prevtxid,nexttxid; + int32_t pad32,prevtxidht,nexttxidht; + uint16_t prevtxlen,nexttxlen; + uint8_t *prevntz,*nextntz; +}; + +int32_t NSPV_rwntzsproofresp(int32_t rwflag,uint8_t *serialized,struct NSPV_ntzsproofresp *ptr) +{ + int32_t len = 0; + len += NSPV_rwntzproofshared(rwflag,&serialized[len],&ptr->common); + len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->prevtxid),(uint8_t *)&ptr->prevtxid); + len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->nexttxid),(uint8_t *)&ptr->nexttxid); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad32),&ptr->pad32); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->prevtxidht),&ptr->prevtxidht); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nexttxidht),&ptr->nexttxidht); + len += iguana_rwuint8vec(rwflag,&serialized[len],&ptr->prevtxlen,&ptr->prevntz); + len += iguana_rwuint8vec(rwflag,&serialized[len],&ptr->nexttxlen,&ptr->nextntz); + return(len); +} + +void NSPV_ntzsproofresp_purge(struct NSPV_ntzsproofresp *ptr) +{ + if ( ptr != 0 ) + { + if ( ptr->common.hdrs != 0 ) + free(ptr->common.hdrs); + if ( ptr->prevntz != 0 ) + free(ptr->prevntz); + if ( ptr->nextntz != 0 ) + free(ptr->nextntz); + memset(ptr,0,sizeof(*ptr)); + } +} + +struct NSPV_MMRproof +{ + struct NSPV_ntzproofshared common; + // tbd +}; + +struct NSPV_spentinfo +{ + struct NSPV_txproof spent; + uint256 txid; + int32_t vout,spentvini; +}; + +int32_t NSPV_rwspentinfo(int32_t rwflag,uint8_t *serialized,struct NSPV_spentinfo *ptr) // check mempool +{ + int32_t len = 0; + len += NSPV_rwtxproof(rwflag,&serialized[len],&ptr->spent); + len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->vout),&ptr->vout); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->spentvini),&ptr->spentvini); + return(len); +} + +void NSPV_spentinfo_purge(struct NSPV_spentinfo *ptr) +{ + if ( ptr != 0 ) + { + NSPV_txproof_purge(&ptr->spent); + memset(ptr,0,sizeof(*ptr)); + } +} + +struct NSPV_broadcastresp +{ + uint256 txid; + int32_t retcode; +}; + +int32_t NSPV_rwbroadcastresp(int32_t rwflag,uint8_t *serialized,struct NSPV_broadcastresp *ptr) +{ + int32_t len = 0; + len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->retcode),&ptr->retcode); + return(len); +} + +void NSPV_broadcast_purge(struct NSPV_broadcastresp *ptr) +{ + if ( ptr != 0 ) + memset(ptr,0,sizeof(*ptr)); +} + +// useful utility functions + +uint256 NSPV_doublesha256(uint8_t *data,int32_t datalen) +{ + bits256 _hash; uint256 hash; int32_t i; + _hash = bits256_doublesha256(0,data,datalen); + for (i=0; i<32; i++) + ((uint8_t *)&hash)[i] = _hash.bytes[31 - i]; + return(hash); +} + +uint256 NSPV_hdrhash(struct NSPV_equihdr *hdr) +{ + CBlockHeader block; + block.nVersion = hdr->nVersion; + block.hashPrevBlock = hdr->hashPrevBlock; + block.hashMerkleRoot = hdr->hashMerkleRoot; + block.hashFinalSaplingRoot = hdr->hashFinalSaplingRoot; + block.nTime = hdr->nTime; + block.nBits = hdr->nBits; + block.nNonce = hdr->nNonce; + block.nSolution.resize(sizeof(hdr->nSolution)); + memcpy(&block.nSolution[0],hdr->nSolution,sizeof(hdr->nSolution)); + return(block.GetHash()); +} + +int32_t NSPV_txextract(CTransaction &tx,uint8_t *data,int32_t datalen) +{ + std::vector rawdata; + rawdata.resize(datalen); + memcpy(&rawdata[0],data,datalen); + if ( DecodeHexTx(tx,HexStr(rawdata)) != 0 ) + return(0); + else return(-1); +} + +/* + NSPV_notariescount is the slowest process during full validation as it requires looking up 13 transactions. + one way that would be 10000x faster would be to bruteforce validate the signatures in each vin, against all 64 pubkeys! for a valid tx, that is on average 13*32 secp256k1/sapling verify operations, which is much faster than even a single network request. + Unfortunately, due to the complexity of calculating the hash to sign for a tx, this bruteforcing would require determining what type of signature method and having sapling vs legacy methods of calculating the txhash. + It could be that the fullnode side could calculate this and send it back to the superlite side as any hash that would validate 13 different ways has to be the valid txhash. + However, since the vouts being spent by the notaries are highly constrained p2pk vouts, the txhash can be deduced if a specific notary pubkey is indeed the signer + */ + +int32_t NSPV_notariescount(CTransaction tx,uint8_t elected[64][33]) +{ + uint8_t *script; CTransaction vintx; int32_t i,j,utxovout,scriptlen,numsigs = 0; + for (i=0; i opret,uint256 txid) +{ + uint256 desttxid; int32_t i; + iguana_rwnum(0,&opret[32],sizeof(*heightp),heightp); + for (i=0; i<32; i++) + ((uint8_t *)blockhashp)[i] = opret[i]; + for (i=0; i<32; i++) + ((uint8_t *)&desttxid)[i] = opret[4 + 32 + i]; + if ( 0 && *heightp != 2690 ) + fprintf(stderr," ntzht.%d %s <- txid.%s size.%d\n",*heightp,(*blockhashp).GetHex().c_str(),(txid).GetHex().c_str(),(int32_t)opret.size()); + return(desttxid); +} + +int32_t NSPV_notarizationextract(int32_t verifyntz,int32_t *ntzheightp,uint256 *blockhashp,uint256 *desttxidp,CTransaction tx) +{ + int32_t numsigs=0; uint8_t elected[64][33]; char *symbol; std::vector opret; + if ( tx.vout.size() >= 2 ) + { + symbol = (ASSETCHAINS_SYMBOL[0] == 0) ? (char *)"KMD" : ASSETCHAINS_SYMBOL; + GetOpReturnData(tx.vout[1].scriptPubKey,opret); + if ( opret.size() >= 32*2+4 ) + { + *desttxidp = NSPV_opretextract(ntzheightp,blockhashp,symbol,opret,tx.GetHash()); + komodo_notaries(elected,*ntzheightp,0); + if ( verifyntz != 0 && (numsigs= NSPV_notariescount(tx,elected)) < 12 ) + { + fprintf(stderr,"numsigs.%d error\n",numsigs); + return(-3); + } + return(0); + } + else + { + fprintf(stderr,"opretsize.%d error\n",(int32_t)opret.size()); + return(-2); + } + } else return(-1); +} +#endif // KOMODO_NSPV_H diff --git a/src/komodo_nSPV_fullnode.h b/src/komodo_nSPV_fullnode.h new file mode 100644 index 000000000..494c511e1 --- /dev/null +++ b/src/komodo_nSPV_fullnode.h @@ -0,0 +1,510 @@ + +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#ifndef KOMODO_NSPVFULLNODE_H +#define KOMODO_NSPVFULLNODE_H + +// NSPV_get... functions need to return the exact serialized length, which is the size of the structure minus size of pointers, plus size of allocated data + +#include "notarisationdb.h" + +struct NSPV_ntzargs +{ + uint256 txid,desttxid,blockhash; + int32_t txidht,ntzheight; +}; + +int32_t NSPV_notarization_find(struct NSPV_ntzargs *args,int32_t height,int32_t dir) +{ + int32_t ntzheight = 0; uint256 hashBlock; CTransaction tx; Notarisation nota; char *symbol; std::vector opret; + symbol = (ASSETCHAINS_SYMBOL[0] == 0) ? (char *)"KMD" : ASSETCHAINS_SYMBOL; + memset(args,0,sizeof(*args)); + if ( dir > 0 ) + height += 10; + if ( (args->txidht= ScanNotarisationsDB(height,symbol,1440,nota)) == 0 ) + return(-1); + args->txid = nota.first; + if ( !GetTransaction(args->txid,tx,hashBlock,false) || tx.vout.size() < 2 ) + return(-2); + GetOpReturnData(tx.vout[1].scriptPubKey,opret); + if ( opret.size() >= 32*2+4 ) + args->desttxid = NSPV_opretextract(&args->ntzheight,&args->blockhash,symbol,opret,args->txid); + return(args->ntzheight); +} + +int32_t NSPV_notarized_bracket(struct NSPV_ntzargs *prev,struct NSPV_ntzargs *next,int32_t height) +{ + uint256 bhash; int32_t txidht,ntzht,nextht,i=0; + memset(prev,0,sizeof(*prev)); + memset(next,0,sizeof(*next)); + if ( (ntzht= NSPV_notarization_find(prev,height,-1)) < 0 || ntzht > height || ntzht == 0 ) + return(-1); + txidht = height+1; + while ( (ntzht= NSPV_notarization_find(next,txidht,1)) < height ) + { + nextht = next->txidht + 10*i; +//fprintf(stderr,"found forward ntz, but ntzht.%d vs height.%d, txidht.%d -> nextht.%d\n",next->ntzheight,height,txidht,nextht); + memset(next,0,sizeof(*next)); + txidht = nextht; + if ( ntzht <= 0 ) + break; + if ( i++ > 10 ) + break; + } + return(0); +} + +int32_t NSPV_ntzextract(struct NSPV_ntz *ptr,uint256 ntztxid,int32_t txidht,uint256 desttxid,int32_t ntzheight) +{ + ptr->blockhash = *chainActive[ntzheight]->phashBlock; + ptr->height = ntzheight; + ptr->txidheight = txidht; + ptr->othertxid = desttxid; + ptr->txid = ntztxid; + return(0); +} + +int32_t NSPV_getntzsresp(struct NSPV_ntzsresp *ptr,int32_t height) +{ + struct NSPV_ntzargs prev,next; + if ( height < chainActive.LastTip()->GetHeight() ) + height++; + if ( NSPV_notarized_bracket(&prev,&next,height) == 0 ) + { + if ( prev.ntzheight != 0 ) + { + if ( NSPV_ntzextract(&ptr->prevntz,prev.txid,prev.txidht,prev.desttxid,prev.ntzheight) < 0 ) + return(-1); + } + if ( next.ntzheight != 0 ) + { + if ( NSPV_ntzextract(&ptr->nextntz,next.txid,next.txidht,next.desttxid,next.ntzheight) < 0 ) + return(-1); + } + } + return(sizeof(*ptr)); +} + +int32_t NSPV_setequihdr(struct NSPV_equihdr *hdr,int32_t height) +{ + CBlockIndex *pindex; + if ( (pindex= komodo_chainactive(height)) != 0 ) + { + hdr->nVersion = pindex->nVersion; + if ( pindex->pprev == 0 ) + return(-1); + hdr->hashPrevBlock = pindex->pprev->GetBlockHash(); + hdr->hashMerkleRoot = pindex->hashMerkleRoot; + hdr->hashFinalSaplingRoot = pindex->hashFinalSaplingRoot; + hdr->nTime = pindex->nTime; + hdr->nBits = pindex->nBits; + hdr->nNonce = pindex->nNonce; + memcpy(hdr->nSolution,&pindex->nSolution[0],sizeof(hdr->nSolution)); + return(sizeof(*hdr)); + } + return(-1); +} + +int32_t NSPV_getinfo(struct NSPV_inforesp *ptr,int32_t reqheight) +{ + int32_t prevMoMheight,len = 0; CBlockIndex *pindex; struct NSPV_ntzsresp pair; + if ( (pindex= chainActive.LastTip()) != 0 ) + { + ptr->height = pindex->GetHeight(); + ptr->blockhash = pindex->GetBlockHash(); + if ( NSPV_getntzsresp(&pair,ptr->height-1) < 0 ) + return(-1); + ptr->notarization = pair.prevntz; + if ( reqheight == 0 ) + reqheight = ptr->height; + ptr->hdrheight = reqheight; + if ( NSPV_setequihdr(&ptr->H,reqheight) < 0 ) + return(-1); + return(sizeof(*ptr)); + } else return(-1); +} + +int32_t NSPV_getaddressutxos(struct NSPV_utxosresp *ptr,char *coinaddr) // check mempool +{ + int64_t total = 0,interest=0; uint32_t locktime; int32_t tipheight,maxlen,txheight,n = 0,len = 0; + std::vector > unspentOutputs; + SetCCunspents(unspentOutputs,coinaddr,false); + maxlen = MAX_BLOCK_SIZE(tipheight) - 512; + maxlen /= sizeof(*ptr->utxos); + strncpy(ptr->coinaddr,coinaddr,sizeof(ptr->coinaddr)-1); + if ( (ptr->numutxos= (int32_t)unspentOutputs.size()) >= 0 && ptr->numutxos < maxlen ) + { + tipheight = chainActive.LastTip()->GetHeight(); + ptr->nodeheight = tipheight; + ptr->utxos = (struct NSPV_utxoresp *)calloc(ptr->numutxos,sizeof(*ptr->utxos)); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + ptr->utxos[n].txid = it->first.txhash; + ptr->utxos[n].vout = (int32_t)it->first.index; + ptr->utxos[n].satoshis = it->second.satoshis; + ptr->utxos[n].height = it->second.blockHeight; + if ( ASSETCHAINS_SYMBOL[0] == 0 && it->second.satoshis >= 10*COIN ) + { + ptr->utxos[n].extradata = komodo_accrued_interest(&txheight,&locktime,ptr->utxos[n].txid,ptr->utxos[n].vout,ptr->utxos[n].height,ptr->utxos[n].satoshis,tipheight); + interest += ptr->utxos[n].extradata; + } + total += it->second.satoshis; + n++; + } + if ( len < maxlen ) + { + len = (int32_t)(sizeof(*ptr) + sizeof(*ptr->utxos)*ptr->numutxos - sizeof(ptr->utxos)); + //fprintf(stderr,"getaddressutxos for %s -> n.%d:%d total %.8f interest %.8f len.%d\n",coinaddr,n,ptr->numutxos,dstr(total),dstr(interest),len); + if ( n == ptr->numutxos ) + { + ptr->total = total; + ptr->interest = interest; + return(len); + } + } + } + if ( ptr->utxos != 0 ) + free(ptr->utxos); + memset(ptr,0,sizeof(*ptr)); + return(0); +} + +uint8_t *NSPV_getrawtx(CTransaction &tx,uint256 &hashBlock,uint16_t *txlenp,uint256 txid) +{ + uint8_t *rawtx = 0; + *txlenp = 0; + { + LOCK(cs_main); + if (!GetTransaction(txid, tx, hashBlock, false)) + return(0); + string strHex = EncodeHexTx(tx); + *txlenp = (int32_t)strHex.size() >> 1; + if ( *txlenp > 0 ) + { + rawtx = (uint8_t *)calloc(1,*txlenp); + decode_hex(rawtx,*txlenp,(char *)strHex.c_str()); + } + } + return(rawtx); +} + +int32_t NSPV_sendrawtransaction(struct NSPV_broadcastresp *ptr,uint8_t *data,int32_t n) +{ + CTransaction tx; + ptr->retcode = 0; + if ( NSPV_txextract(tx,data,n) == 0 ) + { + LOCK(cs_main); + ptr->txid = tx.GetHash(); + fprintf(stderr,"addmempool transaction %s\n",ptr->txid.GetHex().c_str()); + if ( myAddtomempool(tx) != 0 ) + ptr->retcode = 1; + else ptr->retcode = 0; + //fprintf(stderr,"relay transaction %s retcode.%d\n",ptr->txid.GetHex().c_str(),ptr->retcode); + RelayTransaction(tx); + } else ptr->retcode = -1; + return(sizeof(*ptr)); +} + +int32_t NSPV_gettxproof(struct NSPV_txproof *ptr,int32_t vout,uint256 txid,int32_t height) +{ + int32_t flag = 0,len = 0; CTransaction _tx; uint256 hashBlock; CBlock block; CBlockIndex *pindex; + if ( (ptr->tx= NSPV_getrawtx(_tx,hashBlock,&ptr->txlen,txid)) == 0 ) + return(-1); + ptr->txid = txid; + ptr->vout = vout; + ptr->height = height; + if ( height != 0 && (pindex= komodo_chainactive(height)) != 0 && komodo_blockload(block,pindex) == 0 ) + { + BOOST_FOREACH(const CTransaction&tx, block.vtx) + { + if ( tx.GetHash() == txid ) + { + flag = 1; + break; + } + } + if ( flag != 0 ) + { + set setTxids; + CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); + setTxids.insert(txid); + CMerkleBlock mb(block, setTxids); + ssMB << mb; + std::vector proof(ssMB.begin(), ssMB.end()); + ptr->txprooflen = (int32_t)proof.size(); + //fprintf(stderr,"%s txproof.(%s)\n",txid.GetHex().c_str(),HexStr(proof).c_str()); + if ( ptr->txprooflen > 0 ) + { + ptr->txproof = (uint8_t *)calloc(1,ptr->txprooflen); + memcpy(ptr->txproof,&proof[0],ptr->txprooflen); + } + //fprintf(stderr,"gettxproof slen.%d\n",(int32_t)(sizeof(*ptr) - sizeof(ptr->tx) - sizeof(ptr->txproof) + ptr->txlen + ptr->txprooflen)); + } + } + ptr->unspentvalue = CCgettxout(txid,vout,1,1); + return(sizeof(*ptr) - sizeof(ptr->tx) - sizeof(ptr->txproof) + ptr->txlen + ptr->txprooflen); +} + +int32_t NSPV_getntzsproofresp(struct NSPV_ntzsproofresp *ptr,uint256 prevntztxid,uint256 nextntztxid) +{ + int32_t i; uint256 hashBlock,bhash0,bhash1,desttxid0,desttxid1; CTransaction tx; + ptr->prevtxid = prevntztxid; + ptr->prevntz = NSPV_getrawtx(tx,hashBlock,&ptr->prevtxlen,ptr->prevtxid); + ptr->prevtxidht = komodo_blockheight(hashBlock); + if ( NSPV_notarizationextract(0,&ptr->common.prevht,&bhash0,&desttxid0,tx) < 0 ) + return(-2); + else if ( komodo_blockheight(bhash0) != ptr->common.prevht ) + return(-3); + + ptr->nexttxid = nextntztxid; + ptr->nextntz = NSPV_getrawtx(tx,hashBlock,&ptr->nexttxlen,ptr->nexttxid); + ptr->nexttxidht = komodo_blockheight(hashBlock); + if ( NSPV_notarizationextract(0,&ptr->common.nextht,&bhash1,&desttxid1,tx) < 0 ) + return(-5); + else if ( komodo_blockheight(bhash1) != ptr->common.nextht ) + return(-6); + + else if ( ptr->common.prevht > ptr->common.nextht || (ptr->common.nextht - ptr->common.prevht) > 1440 ) + { + fprintf(stderr,"illegal prevht.%d nextht.%d\n",ptr->common.prevht,ptr->common.nextht); + return(-7); + } + //fprintf(stderr,"%s -> prevht.%d, %s -> nexht.%d\n",ptr->prevtxid.GetHex().c_str(),ptr->common.prevht,ptr->nexttxid.GetHex().c_str(),ptr->common.nextht); + ptr->common.numhdrs = (ptr->common.nextht - ptr->common.prevht + 1); + ptr->common.hdrs = (struct NSPV_equihdr *)calloc(ptr->common.numhdrs,sizeof(*ptr->common.hdrs)); + //fprintf(stderr,"prev.%d next.%d allocate numhdrs.%d\n",prevht,nextht,ptr->common.numhdrs); + for (i=0; icommon.numhdrs; i++) + { + //hashBlock = NSPV_hdrhash(&ptr->common.hdrs[i]); + //fprintf(stderr,"hdr[%d] %s\n",prevht+i,hashBlock.GetHex().c_str()); + if ( NSPV_setequihdr(&ptr->common.hdrs[i],ptr->common.prevht+i) < 0 ) + { + fprintf(stderr,"error setting hdr.%d\n",ptr->common.prevht+i); + free(ptr->common.hdrs); + ptr->common.hdrs = 0; + return(-1); + } + } + return(sizeof(*ptr) + sizeof(*ptr->common.hdrs)*ptr->common.numhdrs - sizeof(ptr->common.hdrs) - sizeof(ptr->prevntz) - sizeof(ptr->nextntz) + ptr->prevtxlen + ptr->nexttxlen); +} + +int32_t NSPV_getspentinfo(struct NSPV_spentinfo *ptr,uint256 txid,int32_t vout) +{ + int32_t len = 0; + ptr->txid = txid; + ptr->vout = vout; + ptr->spentvini = -1; + len = (int32_t)(sizeof(*ptr) - sizeof(ptr->spent.tx) - sizeof(ptr->spent.txproof)); + if ( CCgetspenttxid(ptr->spent.txid,ptr->spentvini,ptr->spent.height,txid,vout) == 0 ) + { + if ( NSPV_gettxproof(&ptr->spent,0,ptr->spent.txid,ptr->spent.height) > 0 ) + len += ptr->spent.txlen + ptr->spent.txprooflen; + else + { + NSPV_txproof_purge(&ptr->spent); + return(-1); + } + } + return(len); +} + +void komodo_nSPVreq(CNode *pfrom,std::vector request) // received a request +{ + int32_t len,slen,ind,reqheight; std::vector response; uint32_t timestamp = (uint32_t)time(NULL); + if ( (len= request.size()) > 0 ) + { + if ( (ind= request[0]>>1) >= sizeof(pfrom->prevtimes)/sizeof(*pfrom->prevtimes) ) + ind = (int32_t)(sizeof(pfrom->prevtimes)/sizeof(*pfrom->prevtimes)) - 1; + if ( pfrom->prevtimes[ind] > timestamp ) + pfrom->prevtimes[ind] = 0; + if ( request[0] == NSPV_INFO ) // info + { + //fprintf(stderr,"check info %u vs %u, ind.%d\n",timestamp,pfrom->prevtimes[ind],ind); + if ( timestamp > pfrom->prevtimes[ind] ) + { + struct NSPV_inforesp I; + if ( len == 1+sizeof(reqheight) ) + iguana_rwnum(0,&request[1],sizeof(reqheight),&reqheight); + else reqheight = 0; + //fprintf(stderr,"request height.%d\n",reqheight); + memset(&I,0,sizeof(I)); + if ( (slen= NSPV_getinfo(&I,reqheight)) > 0 ) + { + response.resize(1 + slen); + response[0] = NSPV_INFORESP; + //fprintf(stderr,"slen.%d\n",slen); + if ( NSPV_rwinforesp(1,&response[1],&I) == slen ) + { + pfrom->PushMessage("nSPV",response); + pfrom->prevtimes[ind] = timestamp; + } + NSPV_inforesp_purge(&I); + } + } + } + else if ( request[0] == NSPV_UTXOS ) + { + //fprintf(stderr,"utxos: %u > %u, ind.%d, len.%d\n",timestamp,pfrom->prevtimes[ind],ind,len); + if ( timestamp > pfrom->prevtimes[ind] ) + { + struct NSPV_utxosresp U; char coinaddr[64]; + if ( len < 64 && request[1] == len-2 ) + { + memcpy(coinaddr,&request[2],request[1]); + coinaddr[request[1]] = 0; + memset(&U,0,sizeof(U)); + if ( (slen= NSPV_getaddressutxos(&U,coinaddr)) > 0 ) + { + response.resize(1 + slen); + response[0] = NSPV_UTXOSRESP; + if ( NSPV_rwutxosresp(1,&response[1],&U) == slen ) + { + pfrom->PushMessage("nSPV",response); + pfrom->prevtimes[ind] = timestamp; + } + NSPV_utxosresp_purge(&U); + } + } + } + } + else if ( request[0] == NSPV_NTZS ) + { + if ( timestamp > pfrom->prevtimes[ind] ) + { + struct NSPV_ntzsresp N; int32_t height; + if ( len == 1+sizeof(height) ) + { + iguana_rwnum(0,&request[1],sizeof(height),&height); + memset(&N,0,sizeof(N)); + if ( (slen= NSPV_getntzsresp(&N,height)) > 0 ) + { + response.resize(1 + slen); + response[0] = NSPV_NTZSRESP; + if ( NSPV_rwntzsresp(1,&response[1],&N) == slen ) + { + pfrom->PushMessage("nSPV",response); + pfrom->prevtimes[ind] = timestamp; + } + NSPV_ntzsresp_purge(&N); + } + } + } + } + else if ( request[0] == NSPV_NTZSPROOF ) + { + if ( timestamp > pfrom->prevtimes[ind] ) + { + struct NSPV_ntzsproofresp P; uint256 prevntz,nextntz; + if ( len == 1+sizeof(prevntz)+sizeof(nextntz) ) + { + iguana_rwbignum(0,&request[1],sizeof(prevntz),(uint8_t *)&prevntz); + iguana_rwbignum(0,&request[1+sizeof(prevntz)],sizeof(nextntz),(uint8_t *)&nextntz); + memset(&P,0,sizeof(P)); + //fprintf(stderr,"msg prev.%s next.%s\n",prevntz.GetHex().c_str(),nextntz.GetHex().c_str()); + if ( (slen= NSPV_getntzsproofresp(&P,prevntz,nextntz)) > 0 ) + { + response.resize(1 + slen); + response[0] = NSPV_NTZSPROOFRESP; + if ( NSPV_rwntzsproofresp(1,&response[1],&P) == slen ) + { + pfrom->PushMessage("nSPV",response); + pfrom->prevtimes[ind] = timestamp; + } + NSPV_ntzsproofresp_purge(&P); + } + } + } + } + else if ( request[0] == NSPV_TXPROOF ) + { + if ( timestamp > pfrom->prevtimes[ind] ) + { + struct NSPV_txproof P; uint256 txid; int32_t height,vout; + if ( len == 1+sizeof(txid)+sizeof(height)+sizeof(vout) ) + { + iguana_rwnum(0,&request[1],sizeof(height),&height); + iguana_rwnum(0,&request[1+sizeof(height)],sizeof(vout),&vout); + iguana_rwbignum(0,&request[1+sizeof(height)+sizeof(vout)],sizeof(txid),(uint8_t *)&txid); + //fprintf(stderr,"got txid %s/v%d ht.%d\n",txid.GetHex().c_str(),vout,height); + memset(&P,0,sizeof(P)); + if ( (slen= NSPV_gettxproof(&P,vout,txid,height)) > 0 ) + { + response.resize(1 + slen); + response[0] = NSPV_TXPROOFRESP; + if ( NSPV_rwtxproof(1,&response[1],&P) == slen ) + { + pfrom->PushMessage("nSPV",response); + pfrom->prevtimes[ind] = timestamp; + } + NSPV_txproof_purge(&P); + } + } + } + } + else if ( request[0] == NSPV_SPENTINFO ) + { + if ( timestamp > pfrom->prevtimes[ind] ) + { + struct NSPV_spentinfo S; int32_t vout; uint256 txid; + if ( len == 1+sizeof(txid)+sizeof(vout) ) + { + iguana_rwnum(0,&request[1],sizeof(vout),&vout); + iguana_rwbignum(0,&request[1+sizeof(vout)],sizeof(txid),(uint8_t *)&txid); + memset(&S,0,sizeof(S)); + if ( (slen= NSPV_getspentinfo(&S,txid,vout)) > 0 ) + { + response.resize(1 + slen); + response[0] = NSPV_SPENTINFORESP; + if ( NSPV_rwspentinfo(1,&response[1],&S) == slen ) + { + pfrom->PushMessage("nSPV",response); + pfrom->prevtimes[ind] = timestamp; + } + NSPV_spentinfo_purge(&S); + } + } + } + } + else if ( request[0] == NSPV_BROADCAST ) + { + if ( timestamp > pfrom->prevtimes[ind] ) + { + struct NSPV_broadcastresp B; uint16_t n,offset; uint256 txid; + if ( len > 1+sizeof(txid)+sizeof(n) ) + { + iguana_rwbignum(0,&request[1],sizeof(txid),(uint8_t *)&txid); + iguana_rwnum(0,&request[1+sizeof(txid)],sizeof(n),&n); + memset(&B,0,sizeof(B)); + offset = 1 + sizeof(txid) + sizeof(n); + if ( request.size() == offset+n && (slen= NSPV_sendrawtransaction(&B,&request[offset],n)) > 0 ) + { + response.resize(1 + slen); + response[0] = NSPV_BROADCASTRESP; + if ( NSPV_rwbroadcastresp(1,&response[1],&B) == slen ) + { + pfrom->PushMessage("nSPV",response); + pfrom->prevtimes[ind] = timestamp; + } + NSPV_broadcast_purge(&B); + } + } + } + } + } +} + +#endif // KOMODO_NSPVFULLNODE_H diff --git a/src/komodo_nSPV_superlite.h b/src/komodo_nSPV_superlite.h new file mode 100644 index 000000000..813390c01 --- /dev/null +++ b/src/komodo_nSPV_superlite.h @@ -0,0 +1,645 @@ + +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#ifndef KOMODO_NSPVSUPERLITE_H +#define KOMODO_NSPVSUPERLITE_H + +// nSPV client. VERY simplistic "single threaded" networking model. for production GUI best to multithread, etc. +// no caching, no optimizations, no reducing the number of ntzsproofs needed by detecting overlaps, etc. +// advantage is that it is simpler to implement and understand to create a design for a more performant version + + +CAmount AmountFromValue(const UniValue& value); +int32_t bitcoin_base58decode(uint8_t *data,char *coinaddr); + +uint32_t NSPV_lastinfo,NSPV_logintime; +CKey NSPV_key; +char NSPV_wifstr[64],NSPV_pubkeystr[67]; +std::string NSPV_address; +struct NSPV_inforesp NSPV_inforesult; +struct NSPV_utxosresp NSPV_utxosresult; +struct NSPV_spentinfo NSPV_spentresult; +struct NSPV_ntzsresp NSPV_ntzsresult; +struct NSPV_ntzsproofresp NSPV_ntzsproofresult; +struct NSPV_txproof NSPV_txproofresult; +struct NSPV_broadcastresp NSPV_broadcastresult; + + +// komodo_nSPVresp is called from async message processing + +void komodo_nSPVresp(CNode *pfrom,std::vector response) // received a response +{ + struct NSPV_inforesp I; int32_t len; uint32_t timestamp = (uint32_t)time(NULL); + if ( (len= response.size()) > 0 ) + { + switch ( response[0] ) + { + case NSPV_INFORESP: + //fprintf(stderr,"got info response %u size.%d height.%d\n",timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status + I = NSPV_inforesult; + NSPV_inforesp_purge(&NSPV_inforesult); + NSPV_rwinforesp(0,&response[1],&NSPV_inforesult); + if ( NSPV_inforesult.height < I.height ) + { + fprintf(stderr,"got old info response %u size.%d height.%d\n",timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status + NSPV_inforesp_purge(&NSPV_inforesult); + NSPV_inforesult = I; + } + else if ( NSPV_inforesult.height > I.height ) + NSPV_lastinfo = timestamp - ASSETCHAINS_BLOCKTIME/4; + break; + case NSPV_UTXOSRESP: + NSPV_utxosresp_purge(&NSPV_utxosresult); + NSPV_rwutxosresp(0,&response[1],&NSPV_utxosresult); + fprintf(stderr,"got utxos response %u size.%d\n",timestamp,(int32_t)response.size()); // update utxos list + break; + case NSPV_NTZSRESP: + NSPV_ntzsresp_purge(&NSPV_ntzsresult); + NSPV_rwntzsresp(0,&response[1],&NSPV_ntzsresult); + fprintf(stderr,"got ntzs response %u size.%d %s prev.%d, %s next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsresult.prevntz.txid.GetHex().c_str(),NSPV_ntzsresult.prevntz.height,NSPV_ntzsresult.nextntz.txid.GetHex().c_str(),NSPV_ntzsresult.nextntz.height); + break; + case NSPV_NTZSPROOFRESP: + NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresult); + NSPV_rwntzsproofresp(0,&response[1],&NSPV_ntzsproofresult); + fprintf(stderr,"got ntzproof response %u size.%d prev.%d next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsproofresult.common.prevht,NSPV_ntzsproofresult.common.nextht); + break; + case NSPV_TXPROOFRESP: + NSPV_txproof_purge(&NSPV_txproofresult); + NSPV_rwtxproof(0,&response[1],&NSPV_txproofresult); + fprintf(stderr,"got txproof response %u size.%d %s ht.%d\n",timestamp,(int32_t)response.size(),NSPV_txproofresult.txid.GetHex().c_str(),NSPV_txproofresult.height); + break; + case NSPV_SPENTINFORESP: + NSPV_spentinfo_purge(&NSPV_spentresult); + NSPV_rwspentinfo(0,&response[1],&NSPV_spentresult); + fprintf(stderr,"got spentinfo response %u size.%d\n",timestamp,(int32_t)response.size()); + break; + case NSPV_BROADCASTRESP: + NSPV_broadcast_purge(&NSPV_broadcastresult); + NSPV_rwbroadcastresp(0,&response[1],&NSPV_broadcastresult); + fprintf(stderr,"got broadcast response %u size.%d %s retcode.%d\n",timestamp,(int32_t)response.size(),NSPV_broadcastresult.txid.GetHex().c_str(),NSPV_broadcastresult.retcode); + break; + default: fprintf(stderr,"unexpected response %02x size.%d at %u\n",response[0],(int32_t)response.size(),timestamp); + break; + } + } +} + +// superlite message issuing + +CNode *NSPV_req(CNode *pnode,uint8_t *msg,int32_t len,uint64_t mask,int32_t ind) +{ + int32_t n,flag = 0; CNode *pnodes[64]; uint32_t timestamp = (uint32_t)time(NULL); + if ( pnode == 0 ) + { + memset(pnodes,0,sizeof(pnodes)); + LOCK(cs_vNodes); + n = 0; + BOOST_FOREACH(CNode *ptr,vNodes) + { + if ( ptr->prevtimes[ind] > timestamp ) + ptr->prevtimes[ind] = 0; + if ( ptr->hSocket == INVALID_SOCKET ) + continue; + if ( (ptr->nServices & mask) == mask && timestamp > ptr->prevtimes[ind] ) + { + flag = 1; + pnodes[n++] = ptr; + if ( n == sizeof(pnodes)/sizeof(*pnodes) ) + break; + } // else fprintf(stderr,"nServices %llx vs mask %llx, t%u vs %u, ind.%d\n",(long long)ptr->nServices,(long long)mask,timestamp,ptr->prevtimes[ind],ind); + } + if ( n > 0 ) + pnode = pnodes[rand() % n]; + } else flag = 1; + if ( pnode != 0 ) + { + std::vector request; + request.resize(len); + memcpy(&request[0],msg,len); + //fprintf(stderr,"pushmessage [%d] len.%d\n",msg[0],len); + pnode->PushMessage("getnSPV",request); + pnode->prevtimes[ind] = timestamp; + return(pnode); + } else fprintf(stderr,"no pnodes\n"); + return(0); +} + +UniValue NSPV_logout() +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result","success")); + if ( NSPV_logintime != 0 ) + fprintf(stderr,"scrub wif and privkey from NSPV memory\n"); + else result.push_back(Pair("status","wasnt logged in")); + memset(NSPV_wifstr,0,sizeof(NSPV_wifstr)); + memset(&NSPV_key,0,sizeof(NSPV_key)); + NSPV_logintime = 0; + return(result); +} + +// komodo_nSPV from main polling loop (really this belongs in its own file, but it is so small, it ended up here) + +void komodo_nSPV(CNode *pto) // polling loop from SendMessages +{ + uint8_t msg[256]; int32_t i,len=0; uint32_t timestamp = (uint32_t)time(NULL); + if ( NSPV_logintime != 0 && timestamp > NSPV_logintime+NSPV_AUTOLOGOUT ) + NSPV_logout(); + if ( (pto->nServices & NODE_NSPV) == 0 ) + return; + if ( pto->prevtimes[NSPV_INFO>>1] > timestamp ) + pto->prevtimes[NSPV_INFO>>1] = 0; + if ( KOMODO_NSPV != 0 ) + { + if ( timestamp > NSPV_lastinfo + ASSETCHAINS_BLOCKTIME/2 && timestamp > pto->prevtimes[NSPV_INFO>>1] + 2*ASSETCHAINS_BLOCKTIME/3 ) + { + int32_t reqht; + reqht = 0; + len = 0; + msg[len++] = NSPV_INFO; + len += iguana_rwnum(1,&msg[len],sizeof(reqht),&reqht); + //fprintf(stderr,"issue getinfo\n"); + NSPV_req(pto,msg,len,NODE_NSPV,NSPV_INFO>>1); + } + } +} + +UniValue NSPV_txproof_json(struct NSPV_txproof *ptr) +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("txid",ptr->txid.GetHex())); + result.push_back(Pair("height",(int64_t)ptr->height)); + result.push_back(Pair("txlen",(int64_t)ptr->txlen)); + result.push_back(Pair("txprooflen",(int64_t)ptr->txprooflen)); + return(result); +} + +UniValue NSPV_spentinfo_json(struct NSPV_spentinfo *ptr) +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result","success")); + result.push_back(Pair("txid",ptr->txid.GetHex())); + result.push_back(Pair("vout",(int64_t)ptr->vout)); + result.push_back(Pair("spentheight",(int64_t)ptr->spent.height)); + result.push_back(Pair("spenttxid",ptr->spent.txid.GetHex())); + result.push_back(Pair("spentvini",(int64_t)ptr->spentvini)); + result.push_back(Pair("spenttxlen",(int64_t)ptr->spent.txlen)); + result.push_back(Pair("spenttxprooflen",(int64_t)ptr->spent.txprooflen)); + return(result); +} + +UniValue NSPV_ntz_json(struct NSPV_ntz *ptr) +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("notarized_height",(int64_t)ptr->height)); + result.push_back(Pair("notarized_blockhash",ptr->blockhash.GetHex())); + result.push_back(Pair("notarization_txid",ptr->txid.GetHex())); + result.push_back(Pair("notarization_txidheight",(int64_t)ptr->txidheight)); + result.push_back(Pair("notarization_desttxid",ptr->othertxid.GetHex())); + return(result); +} + +UniValue NSPV_header_json(struct NSPV_equihdr *hdr,int32_t height) +{ + UniValue item(UniValue::VOBJ); + item.push_back(Pair("height",(int64_t)height)); + item.push_back(Pair("blockhash",NSPV_hdrhash(hdr).GetHex())); + item.push_back(Pair("hashPrevBlock",hdr->hashPrevBlock.GetHex())); + item.push_back(Pair("hashMerkleRoot",hdr->hashMerkleRoot.GetHex())); + item.push_back(Pair("nTime",(int64_t)hdr->nTime)); + item.push_back(Pair("nBits",(int64_t)hdr->nBits)); + return(item); +} + +UniValue NSPV_headers_json(struct NSPV_equihdr *hdrs,int32_t numhdrs,int32_t height) +{ + UniValue array(UniValue::VARR); int32_t i; + for (i=0; iheight)); + result.push_back(Pair("chaintip",ptr->blockhash.GetHex())); + result.push_back(Pair("notarization",NSPV_ntz_json(&ptr->notarization))); + result.push_back(Pair("header",NSPV_header_json(&ptr->H,ptr->hdrheight))); + return(result); +} + +UniValue NSPV_utxoresp_json(struct NSPV_utxoresp *utxos,int32_t numutxos) +{ + UniValue array(UniValue::VARR); int32_t i; + for (i=0; iutxos,ptr->numutxos))); + result.push_back(Pair("address",ptr->coinaddr)); + result.push_back(Pair("height",(int64_t)ptr->nodeheight)); + result.push_back(Pair("numutxos",(int64_t)ptr->numutxos)); + result.push_back(Pair("balance",(double)ptr->total/COIN)); + if ( ASSETCHAINS_SYMBOL[0] == 0 ) + result.push_back(Pair("interest",(double)ptr->interest/COIN)); + return(result); +} + +UniValue NSPV_ntzs_json(struct NSPV_ntzsresp *ptr) +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result","success")); + result.push_back(Pair("prev",NSPV_ntz_json(&ptr->prevntz))); + result.push_back(Pair("next",NSPV_ntz_json(&ptr->nextntz))); + return(result); +} + +UniValue NSPV_ntzsproof_json(struct NSPV_ntzsproofresp *ptr) +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result","success")); + result.push_back(Pair("prevht",(int64_t)ptr->common.prevht)); + result.push_back(Pair("nextht",(int64_t)ptr->common.nextht)); + result.push_back(Pair("prevtxid",ptr->prevtxid.GetHex())); + result.push_back(Pair("prevtxidht",(int64_t)ptr->prevtxidht)); + result.push_back(Pair("prevtxlen",(int64_t)ptr->prevtxlen)); + result.push_back(Pair("nexttxid",ptr->nexttxid.GetHex())); + result.push_back(Pair("nexttxidht",(int64_t)ptr->nexttxidht)); + result.push_back(Pair("nexttxlen",(int64_t)ptr->prevtxlen)); + result.push_back(Pair("numhdrs",(int64_t)ptr->common.numhdrs)); + result.push_back(Pair("headers",NSPV_headers_json(ptr->common.hdrs,ptr->common.numhdrs,ptr->common.prevht))); + return(result); +} + +UniValue NSPV_broadcast_json(struct NSPV_broadcastresp *ptr,uint256 txid) +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result","success")); + result.push_back(Pair("expected",txid.GetHex())); + result.push_back(Pair("broadcast",ptr->txid.GetHex())); + result.push_back(Pair("retcode",(int64_t)ptr->retcode)); + switch ( ptr->retcode ) + { + case 1: result.push_back(Pair("type","broadcast and mempool")); break; + case 0: result.push_back(Pair("type","broadcast")); break; + case -1: result.push_back(Pair("type","decode error")); break; + case -2: result.push_back(Pair("type","timeout")); break; + default: result.push_back(Pair("type","unknown")); break; + } + return(result); +} + +UniValue NSPV_login(char *wifstr) +{ + UniValue result(UniValue::VOBJ); char coinaddr[64]; uint8_t data[128]; int32_t len,valid = 0; + len = bitcoin_base58decode(data,wifstr); + if ( strlen(wifstr) < 64 && (len == 38 && data[len-5] == 1) || (len == 37 && data[len-5] != 1) ) + valid = 1; + if ( valid == 0 || data[0] != 188 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid wif")); + result.push_back(Pair("len",(int64_t)len)); + result.push_back(Pair("prefix",(int64_t)data[0])); + return(result); + } + memset(NSPV_wifstr,0,sizeof(NSPV_wifstr)); + NSPV_logintime = (uint32_t)time(NULL); + if ( strcmp(NSPV_wifstr,wifstr) != 0 ) + { + strncpy(NSPV_wifstr,wifstr,sizeof(NSPV_wifstr)-1); + NSPV_key = DecodeSecret(wifstr); + } + result.push_back(Pair("result","success")); + result.push_back(Pair("status","wif will expire in 777 seconds")); + CPubKey pubkey = NSPV_key.GetPubKey(); + CKeyID vchAddress = pubkey.GetID(); + NSPV_address = EncodeDestination(vchAddress); + result.push_back(Pair("address",NSPV_address)); + result.push_back(Pair("pubkey",HexStr(pubkey))); + strcpy(NSPV_pubkeystr,HexStr(pubkey).c_str()); + if ( KOMODO_NSPV != 0 ) + decode_hex(NOTARY_PUBKEY33,33,NSPV_pubkeystr); + result.push_back(Pair("wifprefix",(int64_t)data[0])); + result.push_back(Pair("compressed",(int64_t)(data[len-5] == 1))); + memset(data,0,sizeof(data)); + return(result); +} + +UniValue NSPV_getinfo_req(int32_t reqht) +{ + uint8_t msg[64]; int32_t i,iter,len = 0; struct NSPV_inforesp I; + NSPV_inforesp_purge(&NSPV_inforesult); + msg[len++] = NSPV_INFO; + len += iguana_rwnum(1,&msg[len],sizeof(reqht),&reqht); + for (iter=0; iter<3; iter++); + if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) + { + for (i=0; i>1) != 0 ) + { + for (i=0; i= NSPV_inforesult.height && strcmp(coinaddr,NSPV_utxosresult.coinaddr) == 0 ) + return(NSPV_utxosresp_json(&NSPV_utxosresult)); + } + } else sleep(1); + result.push_back(Pair("result","error")); + result.push_back(Pair("error","no utxos result")); + return(result); +} + +UniValue NSPV_notarizations(int32_t height) +{ + uint8_t msg[64]; int32_t i,iter,len = 0; struct NSPV_ntzsresp N; + //if ( NSPV_ntzsresult.prevntz.height <= height && NSPV_ntzsresult.nextntz.height >= height ) + // return(NSPV_ntzs_json(&NSPV_ntzsresult)); + NSPV_ntzsresp_purge(&NSPV_ntzsresult); + msg[len++] = NSPV_NTZS; + len += iguana_rwnum(1,&msg[len],sizeof(height),&height); + for (iter=0; iter<3; iter++); + if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) + { + for (i=0; i= height ) + return(NSPV_ntzs_json(&NSPV_ntzsresult)); + } + } else sleep(1); + memset(&N,0,sizeof(N)); + return(NSPV_ntzs_json(&N)); +} + +UniValue NSPV_txidhdrsproof(uint256 prevtxid,uint256 nexttxid) +{ + uint8_t msg[64]; int32_t i,iter,len = 0; struct NSPV_ntzsproofresp H; + NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresult); + msg[len++] = NSPV_NTZSPROOF; + len += iguana_rwbignum(1,&msg[len],sizeof(prevtxid),(uint8_t *)&prevtxid); + len += iguana_rwbignum(1,&msg[len],sizeof(nexttxid),(uint8_t *)&nexttxid); + for (iter=0; iter<3; iter++); + if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) + { + for (i=0; i>1) != 0 ) + { + for (i=0; i>1) != 0 ) + { + for (i=0; i> 1; + data = (uint8_t *)malloc(n); + decode_hex(data,n,hex); + txid = NSPV_doublesha256(data,n); + msg = (uint8_t *)malloc(1 + sizeof(txid) + sizeof(n) + n); + msg[len++] = NSPV_BROADCAST; + len += iguana_rwbignum(1,&msg[len],sizeof(txid),(uint8_t *)&txid); + len += iguana_rwnum(1,&msg[len],sizeof(n),&n); + memcpy(&msg[len],data,n), len += n; + free(data); + //fprintf(stderr,"send txid.%s\n",txid.GetHex().c_str()); + for (iter=0; iter<3; iter++); + if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) + { + for (i=0; icommon.nextht-ptr->common.prevht+1) != ptr->common.numhdrs ) + return(-1); + else if ( NSPV_txextract(tx,ptr->nextntz,ptr->nexttxlen) < 0 ) + return(-2); + else if ( tx.GetHash() != ptr->nexttxid ) + return(-3); + else if ( NSPV_notarizationextract(1,&height,&blockhash,&desttxid,tx) < 0 ) + return(-4); + else if ( height != ptr->common.nextht ) + return(-5); + else if ( NSPV_hdrhash(&ptr->common.hdrs[ptr->common.numhdrs-1]) != blockhash ) + return(-6); + for (i=ptr->common.numhdrs-1; i>0; i--) + { + blockhash = NSPV_hdrhash(&ptr->common.hdrs[i-1]); + if ( blockhash != ptr->common.hdrs[i].hashPrevBlock ) + return(-i-12); + } + if ( NSPV_txextract(tx,ptr->prevntz,ptr->prevtxlen) < 0 ) + return(-7); + else if ( tx.GetHash() != ptr->prevtxid ) + return(-8); + else if ( NSPV_notarizationextract(1,&height,&blockhash,&desttxid,tx) < 0 ) + return(-9); + else if ( height != ptr->common.prevht ) + return(-10); + else if ( NSPV_hdrhash(&ptr->common.hdrs[0]) != blockhash ) + return(-11); + return(0); +} + +int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int32_t height,CTransaction &tx) +{ + int32_t i,offset,retval = 0; std::vector proof; + for (i=0; i<3; i++) + { + NSPV_txproof(vout,txid,height); + if ( NSPV_txproofresult.txlen != 0 ) + break; + sleep(1); + } + if ( NSPV_txproofresult.txid != txid ) + { + fprintf(stderr,"txproof error %s != %s\n",NSPV_txproofresult.txid.GetHex().c_str(),txid.GetHex().c_str()); + return(-1); + } + else if ( NSPV_txextract(tx,NSPV_txproofresult.tx,NSPV_txproofresult.txlen) < 0 || NSPV_txproofresult.txlen <= 0 ) + retval = -2000; + else if ( skipvalidation == 0 && NSPV_txproofresult.unspentvalue <= 0 ) + retval = -2001; + else if ( skipvalidation == 0 ) + { + if ( NSPV_txproofresult.txprooflen > 0 ) + { + proof.resize(NSPV_txproofresult.txprooflen); + memcpy(&proof[0],NSPV_txproofresult.txproof,NSPV_txproofresult.txprooflen); + } + NSPV_notarizations(height); // gets the prev and next notarizations + if ( NSPV_inforesult.notarization.height >= height && (NSPV_ntzsresult.prevntz.height == 0 || NSPV_ntzsresult.prevntz.height >= NSPV_ntzsresult.nextntz.height) ) + { + fprintf(stderr,"issue manual bracket\n"); + NSPV_notarizations(height-1); + NSPV_notarizations(height+1); + NSPV_notarizations(height); // gets the prev and next notarizations + } + if ( NSPV_ntzsresult.prevntz.height != 0 && NSPV_ntzsresult.prevntz.height <= NSPV_ntzsresult.nextntz.height ) + { + fprintf(stderr,"gettx ht.%d prev.%d next.%d\n",height,NSPV_ntzsresult.prevntz.height, NSPV_ntzsresult.nextntz.height); + offset = (height - NSPV_ntzsresult.prevntz.height); + if ( offset >= 0 && height <= NSPV_ntzsresult.nextntz.height ) + { + NSPV_txidhdrsproof(NSPV_ntzsresult.prevntz.txid,NSPV_ntzsresult.nextntz.txid); + if ( (retval= NSPV_validatehdrs(&NSPV_ntzsproofresult)) == 0 ) + { + std::vector txids; uint256 proofroot; + proofroot = BitcoinGetProofMerkleRoot(proof,txids); + if ( proofroot != NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot ) + { + fprintf(stderr,"prooflen.%d proofroot.%s vs %s\n",NSPV_txproofresult.txprooflen,proofroot.GetHex().c_str(),NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot.GetHex().c_str()); + retval = -2003; + } + } + } else retval = -2002; + } else retval = -2004; + } + return(retval); +} + +#endif // KOMODO_NSPVSUPERLITE_H diff --git a/src/komodo_nSPV_wallet.h b/src/komodo_nSPV_wallet.h new file mode 100644 index 000000000..f04cef11b --- /dev/null +++ b/src/komodo_nSPV_wallet.h @@ -0,0 +1,294 @@ + +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#ifndef KOMODO_NSPVWALLET_H +#define KOMODO_NSPVWALLET_H + +// nSPV wallet uses superlite functions (and some komodod built in functions) to implement nSPV_spend +extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); + +int32_t NSPV_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t *belowp,struct NSPV_utxoresp utxos[],int32_t numunspents,int64_t value) +{ + int32_t i,abovei,belowi; int64_t above,below,gap,atx_value; + abovei = belowi = -1; + for (above=below=i=0; i value ) + { + gap = (atx_value - value); + if ( above == 0 || gap < above ) + { + above = gap; + abovei = i; + } + } + else + { + gap = (value - atx_value); + if ( below == 0 || gap < below ) + { + below = gap; + belowi = i; + } + } + //printf("value %.8f gap %.8f abovei.%d %.8f belowi.%d %.8f\n",dstr(value),dstr(gap),abovei,dstr(above),belowi,dstr(below)); + } + *aboveip = abovei; + *abovep = above; + *belowip = belowi; + *belowp = below; + //printf("above.%d below.%d\n",abovei,belowi); + if ( abovei >= 0 && belowi >= 0 ) + { + if ( above < (below >> 1) ) + return(abovei); + else return(belowi); + } + else if ( abovei >= 0 ) + return(abovei); + else return(belowi); +} + +int64_t NSPV_addinputs(struct NSPV_utxoresp *used,CMutableTransaction &mtx,int64_t total,int32_t maxinputs,struct NSPV_utxoresp *ptr,int32_t num) +{ + int32_t abovei,belowi,ind,vout,i,n = 0; int64_t threshold,above,below; int64_t remains,totalinputs = 0; CTransaction tx; struct NSPV_utxoresp utxos[NSPV_MAXVINS],*up; + memset(utxos,0,sizeof(utxos)); + if ( maxinputs > NSPV_MAXVINS ) + maxinputs = NSPV_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; + for (i=0; i threshold ) + utxos[n++] = ptr[i]; + } + remains = total; + //fprintf(stderr,"threshold %.8f n.%d for total %.8f\n",(double)threshold/COIN,n,(double)total/COIN); + for (i=0; i0; i++) + { + below = above = 0; + abovei = belowi = -1; + if ( NSPV_vinselect(&abovei,&above,&belowi,&below,utxos,n,remains) < 0 ) + { + fprintf(stderr,"error finding unspent i.%d of %d, %.8f vs %.8f\n",i,n,(double)remains/COIN,(double)total/COIN); + return(0); + } + if ( belowi < 0 || abovei >= 0 ) + ind = abovei; + else ind = belowi; + if ( ind < 0 ) + { + fprintf(stderr,"error finding unspent i.%d of %d, %.8f vs %.8f, abovei.%d belowi.%d ind.%d\n",i,n,(double)remains/COIN,(double)total/COIN,abovei,belowi,ind); + return(0); + } + //fprintf(stderr,"i.%d ind.%d abovei.%d belowi.%d n.%d\n",i,ind,abovei,belowi,n); + up = &utxos[ind]; + mtx.vin.push_back(CTxIn(up->txid,up->vout,CScript())); + used[i] = *up; + totalinputs += up->satoshis; + remains -= up->satoshis; + utxos[ind] = utxos[--n]; + memset(&utxos[n],0,sizeof(utxos[n])); + //fprintf(stderr,"totalinputs %.8f vs total %.8f i.%d vs max.%d\n",(double)totalinputs/COIN,(double)total/COIN,i,maxinputs); + if ( totalinputs >= total || (i+1) >= maxinputs ) + break; + } + //fprintf(stderr,"totalinputs %.8f vs total %.8f\n",(double)totalinputs/COIN,(double)total/COIN); + if ( totalinputs >= total ) + return(totalinputs); + return(0); +} + +bool NSPV_SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey) +{ + CTransaction txNewConst(mtx); SignatureData sigdata; CBasicKeyStore keystore; + keystore.AddKey(NSPV_key); + if ( 0 ) + { + int32_t i; + for (i=0; i= totaloutputs+2*txfee ) + { + change = (totalinputs+interest) - (totaloutputs+txfee); + mtx.vout.push_back(CTxOut(change,CScript() << ParseHex(NSPV_pubkeystr) << OP_CHECKSIG)); + } + if ( opret.size() > 0 ) + mtx.vout.push_back(CTxOut(0,opret)); + for (i=0; i 0 ) + sleep(1); + validation = NSPV_gettransaction(NSPV_SKIPFULLVALIDATION,utxovout,mtx.vin[i].prevout.hash,used[i].height,vintx); + retcodes.push_back(validation); + if ( validation != -1 ) // most others are degraded security + { + if ( vintx.vout[utxovout].nValue != used[i].satoshis ) + { + fprintf(stderr,"vintx mismatch %.8f != %.8f\n",(double)vintx.vout[utxovout].nValue/COIN,(double)used[i].satoshis/COIN); + return(""); + } + else if ( utxovout != used[i].vout ) + { + fprintf(stderr,"vintx vout mismatch %d != %d\n",utxovout,used[i].vout); + return(""); + } + else if ( NSPV_SignTx(mtx,i,vintx.vout[utxovout].nValue,vintx.vout[utxovout].scriptPubKey) == 0 ) + { + fprintf(stderr,"signing error for vini.%d\n",i); + return(""); + } + } else fprintf(stderr,"couldnt find txid.%s/v%d or it was spent\n",mtx.vin[i].prevout.hash.GetHex().c_str(),utxovout); // of course much better handling is needed + } + fprintf(stderr,"sign %d inputs %.8f + interest %.8f -> %d outputs %.8f change %.8f\n",(int32_t)mtx.vin.size(),(double)totalinputs/COIN,(double)interest/COIN,(int32_t)mtx.vout.size(),(double)totaloutputs/COIN,(double)change/COIN); + return(EncodeHexTx(mtx)); +} + +UniValue NSPV_spend(char *srcaddr,char *destaddr,int64_t satoshis) // what its all about! +{ + UniValue result(UniValue::VOBJ),retcodes(UniValue::VARR); uint8_t rmd160[128]; int64_t txfee = 10000; + if ( NSPV_logintime == 0 || time(NULL) > NSPV_logintime+NSPV_AUTOLOGOUT ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","wif expired")); + return(result); + } + if ( strcmp(srcaddr,NSPV_address.c_str()) != 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid address")); + result.push_back(Pair("mismatched",srcaddr)); + return(result); + } + else if ( bitcoin_base58decode(rmd160,destaddr) != 25 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid destaddr")); + return(result); + } + if ( NSPV_inforesult.height == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt getinfo")); + return(result); + } + if ( strcmp(NSPV_utxosresult.coinaddr,srcaddr) != 0 || NSPV_utxosresult.nodeheight < NSPV_inforesult.height ) + NSPV_addressutxos(srcaddr); + if ( strcmp(NSPV_utxosresult.coinaddr,srcaddr) != 0 || NSPV_utxosresult.nodeheight < NSPV_inforesult.height ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("address",NSPV_utxosresult.coinaddr)); + result.push_back(Pair("srcaddr",srcaddr)); + result.push_back(Pair("nodeheight",(int64_t)NSPV_utxosresult.nodeheight)); + result.push_back(Pair("infoheight",(int64_t)NSPV_inforesult.height)); + result.push_back(Pair("error","couldnt get addressutxos")); + return(result); + } + if ( NSPV_utxosresult.total < satoshis+txfee ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough funds")); + result.push_back(Pair("balance",(double)NSPV_utxosresult.total/COIN)); + result.push_back(Pair("amount",(double)satoshis/COIN)); + return(result); + } + printf("%s numutxos.%d balance %.8f\n",NSPV_utxosresult.coinaddr,NSPV_utxosresult.numutxos,(double)NSPV_utxosresult.total/COIN); + std::vector data; CScript opret; std::string hex; struct NSPV_utxoresp used[NSPV_MAXVINS]; CMutableTransaction mtx; CTransaction tx; + mtx.fOverwintered = true; + mtx.nExpiryHeight = 0; + mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID; + mtx.nVersion = SAPLING_TX_VERSION; + if ( ASSETCHAINS_SYMBOL[0] == 0 ) + mtx.nLockTime = (uint32_t)time(NULL) - 777; + memset(used,0,sizeof(used)); + data.resize(20); + memcpy(&data[0],&rmd160[1],20); + + if ( NSPV_addinputs(used,mtx,satoshis+txfee,64,NSPV_utxosresult.utxos,NSPV_utxosresult.numutxos) > 0 ) + { + mtx.vout.push_back(CTxOut(satoshis,CScript() << OP_DUP << OP_HASH160 << ParseHex(HexStr(data)) << OP_EQUALVERIFY << OP_CHECKSIG)); + if ( NSPV_logintime == 0 || time(NULL) > NSPV_logintime+NSPV_AUTOLOGOUT ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","wif expired")); + return(result); + } + hex = NSPV_signtx(retcodes,mtx,txfee,opret,used); + if ( hex.size() > 0 ) + { + if ( DecodeHexTx(tx,hex) != 0 ) + { + TxToJSON(tx,uint256(),result); + result.push_back(Pair("result","success")); + result.push_back(Pair("hex",hex)); + result.push_back(Pair("retcodes",retcodes)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt decode")); + result.push_back(Pair("hex",hex)); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("retcodes",retcodes)); + result.push_back(Pair("error","signing error")); + } + return(result); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt create tx")); + return(result); + } +} + +#endif // KOMODO_NSPVWALLET_H diff --git a/src/komodo_utils.h b/src/komodo_utils.h index dbbc9ecaf..8217f0e03 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1695,6 +1695,7 @@ void komodo_args(char *argv0) std::string name,addn,hexstr,symbol; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[32756],disablebits[32],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz=0,baseid,len,n,extralen = 0; uint64_t ccenables[256], ccEnablesHeight[512] = {0}; IS_KOMODO_NOTARY = GetBoolArg("-notary", false); IS_STAKED_NOTARY = GetArg("-stakednotary", -1); + KOMODO_NSPV = GetArg("-nSPV",0); memset(ccenables,0,sizeof(ccenables)); memset(disablebits,0,sizeof(disablebits)); if ( GetBoolArg("-gen", false) != 0 ) diff --git a/src/main.cpp b/src/main.cpp index e01cba61f..4c4dbad75 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -352,7 +352,10 @@ namespace { int GetHeight() { - return chainActive.LastTip()->GetHeight(); + CBlockIndex *pindex; + if ( (pindex= chainActive.LastTip()) != 0 ) + return pindex->GetHeight(); + else return(0); } void UpdatePreferredDownload(CNode* node, CNodeState* state) @@ -1100,7 +1103,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in * Ensure that a coinbase transaction is structured according to the consensus rules of the * chain */ -bool ContextualCheckCoinbaseTransaction(const CBlock *block,CBlockIndex * const previndex,const CTransaction& tx, const int nHeight,int32_t validateprices) +bool ContextualCheckCoinbaseTransaction(int32_t slowflag,const CBlock *block,CBlockIndex * const previndex,const CTransaction& tx, const int nHeight,int32_t validateprices) { // if time locks are on, ensure that this coin base is time locked exactly as it should be if (((uint64_t)(tx.GetValueOut()) >= ASSETCHAINS_TIMELOCKGTE) || @@ -1141,7 +1144,7 @@ bool ContextualCheckCoinbaseTransaction(const CBlock *block,CBlockIndex * const { } - else if ( ASSETCHAINS_CBOPRET != 0 && validateprices != 0 && nHeight > 0 && tx.vout.size() > 0 ) + else if ( slowflag != 0 && ASSETCHAINS_CBOPRET != 0 && validateprices != 0 && nHeight > 0 && tx.vout.size() > 0 ) { if ( komodo_opretvalidate(block,previndex,nHeight,tx.vout[tx.vout.size()-1].scriptPubKey) < 0 ) return(false); @@ -1158,7 +1161,7 @@ bool ContextualCheckCoinbaseTransaction(const CBlock *block,CBlockIndex * const * and ContextualCheckBlock (which calls this function). * 3. The isInitBlockDownload argument is only to assist with testing. */ -bool ContextualCheckTransaction(const CBlock *block, CBlockIndex * const previndex, +bool ContextualCheckTransaction(int32_t slowflag,const CBlock *block, CBlockIndex * const previndex, const CTransaction& tx, CValidationState &state, const int nHeight, @@ -1294,7 +1297,7 @@ bool ContextualCheckTransaction(const CBlock *block, CBlockIndex * const prevind if (tx.IsCoinBase()) { - if (!ContextualCheckCoinbaseTransaction(block,previndex,tx, nHeight,validateprices)) + if (!ContextualCheckCoinbaseTransaction(slowflag,block,previndex,tx, nHeight,validateprices)) return state.DoS(100, error("CheckTransaction(): invalid script data for coinbase time lock"), REJECT_INVALID, "bad-txns-invalid-script-data-for-coinbase-time-lock"); } @@ -1801,7 +1804,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } // DoS level set to 10 to be more forgiving. // Check transaction contextually against the set of consensus rules which apply in the next block to be mined. - if (!fSkipExpiry && !ContextualCheckTransaction(0,0,tx, state, nextBlockHeight, (dosLevel == -1) ? 10 : dosLevel,0)) + if (!fSkipExpiry && !ContextualCheckTransaction(0,0,0,tx, state, nextBlockHeight, (dosLevel == -1) ? 10 : dosLevel,0)) { return error("AcceptToMemoryPool: ContextualCheckTransaction failed"); } @@ -3375,6 +3378,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin { CDiskBlockPos blockPos; const CChainParams& chainparams = Params(); + if ( KOMODO_NSPV != 0 ) + return(true); if ( KOMODO_STOPAT != 0 && pindex->GetHeight() > KOMODO_STOPAT ) return(false); //fprintf(stderr,"connectblock ht.%d\n",(int32_t)pindex->GetHeight()); @@ -3399,7 +3404,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } if ( fCheckPOW != 0 && (pindex->nStatus & BLOCK_VALID_CONTEXT) != BLOCK_VALID_CONTEXT ) // Activate Jan 15th, 2019 { - if ( !ContextualCheckBlock(block, state, pindex->pprev) ) + if ( !ContextualCheckBlock(1,block, state, pindex->pprev) ) { fprintf(stderr,"ContextualCheckBlock failed ht.%d\n",(int32_t)pindex->GetHeight()); if ( pindex->nTime > 1547510400 ) @@ -4106,7 +4111,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { if ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED != 0 && (komodo_isPoS((CBlock *)&block,pindexDelete->GetHeight(),true) != 0))) { #ifdef ENABLE_WALLET - if ( !GetBoolArg("-disablewallet", false) ) + if ( !GetBoolArg("-disablewallet", false) && KOMODO_NSPV == 0 ) pwalletMain->EraseFromWallet(tx.GetHash()); #endif } @@ -4211,8 +4216,11 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * // Get the current commitment tree SproutMerkleTree oldSproutTree; SaplingMerkleTree oldSaplingTree; - assert(pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), oldSproutTree)); - assert(pcoinsTip->GetSaplingAnchorAt(pcoinsTip->GetBestAnchor(SAPLING), oldSaplingTree)); + if ( KOMODO_NSPV == 0 ) + { + assert(pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), oldSproutTree)); + assert(pcoinsTip->GetSaplingAnchorAt(pcoinsTip->GetBestAnchor(SAPLING), oldSaplingTree)); + } // Apply the block atomically to the chain state. int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1; int64_t nTime3; @@ -4237,13 +4245,17 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * mapBlockSource.erase(pindexNew->GetBlockHash()); nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); - assert(view.Flush()); + if ( KOMODO_NSPV == 0 ) + assert(view.Flush()); } int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3; LogPrint("bench", " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001); // Write the chain state to disk, if necessary. - if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) - return false; + if ( KOMODO_NSPV == 0 ) + { + if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) + return false; + } int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); // Remove conflicting transactions from the mempool. @@ -4255,14 +4267,17 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * // Update chainActive & related variables. UpdateTip(pindexNew); - // Tell wallet about transactions that went from mempool - // to conflicted: - BOOST_FOREACH(const CTransaction &tx, txConflicted) { - SyncWithWallets(tx, NULL); - } - // ... and about transactions that got confirmed: - BOOST_FOREACH(const CTransaction &tx, pblock->vtx) { - SyncWithWallets(tx, pblock); + if ( KOMODO_NSPV == 0 ) + { + // Tell wallet about transactions that went from mempool + // to conflicted: + BOOST_FOREACH(const CTransaction &tx, txConflicted) { + SyncWithWallets(tx, NULL); + } + // ... and about transactions that got confirmed: + BOOST_FOREACH(const CTransaction &tx, pblock->vtx) { + SyncWithWallets(tx, pblock); + } } // Update cached incremental witnesses GetMainSignals().ChainTip(pindexNew, pblock, oldSproutTree, oldSaplingTree, true); @@ -4280,19 +4295,22 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * komodo_broadcast(pblock,8); else if ( ASSETCHAINS_SYMBOL[0] != 0 ) komodo_broadcast(pblock,4);*/ - if ( ASSETCHAINS_CBOPRET != 0 ) - komodo_pricesupdate(pindexNew->GetHeight(),pblock); - if ( ASSETCHAINS_SAPLING <= 0 && pindexNew->nTime > KOMODO_SAPLING_ACTIVATION - 24*3600 ) - komodo_activate_sapling(pindexNew); - if ( ASSETCHAINS_CC != 0 && KOMODO_SNAPSHOT_INTERVAL != 0 && (pindexNew->GetHeight() % KOMODO_SNAPSHOT_INTERVAL) == 0 && pindexNew->GetHeight() >= KOMODO_SNAPSHOT_INTERVAL ) + if ( KOMODO_NSPV == 0 ) { - uint64_t start = time(NULL); - if ( !komodo_dailysnapshot(pindexNew->GetHeight()) ) + if ( ASSETCHAINS_CBOPRET != 0 ) + komodo_pricesupdate(pindexNew->GetHeight(),pblock); + if ( ASSETCHAINS_SAPLING <= 0 && pindexNew->nTime > KOMODO_SAPLING_ACTIVATION - 24*3600 ) + komodo_activate_sapling(pindexNew); + if ( ASSETCHAINS_CC != 0 && KOMODO_SNAPSHOT_INTERVAL != 0 && (pindexNew->GetHeight() % KOMODO_SNAPSHOT_INTERVAL) == 0 && pindexNew->GetHeight() >= KOMODO_SNAPSHOT_INTERVAL ) { - fprintf(stderr, "daily snapshot failed, please reindex your chain\n"); - StartShutdown(); + uint64_t start = time(NULL); + if ( !komodo_dailysnapshot(pindexNew->GetHeight()) ) + { + fprintf(stderr, "daily snapshot failed, please reindex your chain\n"); + StartShutdown(); + } + fprintf(stderr, "snapshot completed in: %lu seconds\n", time(NULL)-start); } - fprintf(stderr, "snapshot completed in: %lu seconds\n", time(NULL)-start); } return true; } @@ -5278,7 +5296,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return true; } -bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev) +bool ContextualCheckBlock(int32_t slowflag,const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev) { const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->GetHeight() + 1; const Consensus::Params& consensusParams = Params().GetConsensus(); @@ -5289,7 +5307,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn const CTransaction& tx = block.vtx[i]; // Check transaction contextually against consensus rules at block height - if (!ContextualCheckTransaction(&block,pindexPrev,tx, state, nHeight, 100)) { + if (!ContextualCheckTransaction(slowflag,&block,pindexPrev,tx, state, nHeight, 100)) { return false; // Failure reason has been set in validation state object } @@ -5458,7 +5476,7 @@ bool AcceptBlock(int32_t *futureblockp,CBlock& block, CValidationState& state, C // See method docstring for why this is always disabled auto verifier = libzcash::ProofVerifier::Disabled(); - bool fContextualCheckBlock = ContextualCheckBlock(block, state, pindex->pprev); + bool fContextualCheckBlock = ContextualCheckBlock(0,block, state, pindex->pprev); if ( (!CheckBlock(futureblockp,pindex->GetHeight(),pindex,block, state, verifier,0)) || !fContextualCheckBlock ) { static int32_t saplinght = -1; @@ -5726,7 +5744,7 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex //fprintf(stderr,"TestBlockValidity failure B checkPOW.%d\n",fCheckPOW); return false; } - if (!ContextualCheckBlock(block, state, pindexPrev)) + if (!ContextualCheckBlock(0,block, state, pindexPrev)) { //fprintf(stderr,"TestBlockValidity failure C checkPOW.%d\n",fCheckPOW); return false; @@ -6440,22 +6458,24 @@ bool InitBlockIndex() { { return true; } - // Use the provided setting for -txindex in the new database - fTxIndex = GetBoolArg("-txindex", true); - pblocktree->WriteFlag("txindex", fTxIndex); - // Use the provided setting for -addressindex in the new database - fAddressIndex = GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX); - pblocktree->WriteFlag("addressindex", fAddressIndex); - - // Use the provided setting for -timestampindex in the new database - fTimestampIndex = GetBoolArg("-timestampindex", DEFAULT_TIMESTAMPINDEX); - pblocktree->WriteFlag("timestampindex", fTimestampIndex); - - fSpentIndex = GetBoolArg("-spentindex", DEFAULT_SPENTINDEX); - pblocktree->WriteFlag("spentindex", fSpentIndex); - fprintf(stderr,"fAddressIndex.%d/%d fSpentIndex.%d/%d\n",fAddressIndex,DEFAULT_ADDRESSINDEX,fSpentIndex,DEFAULT_SPENTINDEX); - LogPrintf("Initializing databases...\n"); - + if ( pblocktree != 0 ) + { + // Use the provided setting for -txindex in the new database + fTxIndex = GetBoolArg("-txindex", true); + pblocktree->WriteFlag("txindex", fTxIndex); + // Use the provided setting for -addressindex in the new database + fAddressIndex = GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX); + pblocktree->WriteFlag("addressindex", fAddressIndex); + + // Use the provided setting for -timestampindex in the new database + fTimestampIndex = GetBoolArg("-timestampindex", DEFAULT_TIMESTAMPINDEX); + pblocktree->WriteFlag("timestampindex", fTimestampIndex); + + fSpentIndex = GetBoolArg("-spentindex", DEFAULT_SPENTINDEX); + pblocktree->WriteFlag("spentindex", fSpentIndex); + fprintf(stderr,"fAddressIndex.%d/%d fSpentIndex.%d/%d\n",fAddressIndex,DEFAULT_ADDRESSINDEX,fSpentIndex,DEFAULT_SPENTINDEX); + LogPrintf("Initializing databases...\n"); + } // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) if (!fReindex) { try { @@ -6476,7 +6496,9 @@ bool InitBlockIndex() { if (!ActivateBestChain(true, state, &block)) return error("LoadBlockIndex(): genesis block cannot be activated"); // Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data - return FlushStateToDisk(state, FLUSH_STATE_ALWAYS); + if ( KOMODO_NSPV == 0 ) + return FlushStateToDisk(state, FLUSH_STATE_ALWAYS); + else return(true); } catch (const std::runtime_error& e) { return error("LoadBlockIndex(): failed to initialize block database: %s", e.what()); } @@ -7024,11 +7046,18 @@ void static ProcessGetData(CNode* pfrom) } } +#include "komodo_nSPV.h" // shared defines, structs, serdes, purge functions +#include "komodo_nSPV_fullnode.h" // nSPV fullnode handling of the getnSPV request messages +#include "komodo_nSPV_superlite.h" // nSPV superlite client, issuing requests and handling nSPV responses +#include "komodo_nSPV_wallet.h" // nSPV_send and support functions, really all the rest is to support this + bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { const CChainParams& chainparams = Params(); LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); - //fprintf(stderr, "recv: %s peer=%d\n", SanitizeString(strCommand).c_str(), (int32_t)pfrom->GetId()); + //if ( KOMODO_NSPV != 0 ) + //if ( strCommand != "version" && strCommand != "verack" ) + // fprintf(stderr, "recv: %s peer=%d\n", SanitizeString(strCommand).c_str(), (int32_t)pfrom->GetId()); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); @@ -7187,7 +7216,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } else if ( strCommand == "events" ) { - int32_t i; if ( ASSETCHAINS_CCLIB != "gamescc" ) { Misbehaving(pfrom->GetId(), 1); @@ -7202,6 +7230,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); + if ( KOMODO_NSPV != 0 ) + { + if ( (pfrom->nServices & NODE_NSPV) == 0 ) + { + //fprintf(stderr,"invalid nSPV peer.%d\n",pfrom->id); + pfrom->fDisconnect = true; + return false; + } + } // Mark this node as currently connected, so we update its timestamp later. if (pfrom->fNetworkNode) { LOCK(cs_main); @@ -7291,8 +7328,124 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->fOneShot) pfrom->fDisconnect = true; } + else if (strCommand == "ping") + { + if (pfrom->nVersion > BIP0031_VERSION) + { + uint64_t nonce = 0; + vRecv >> nonce; + // Echo the message back with the nonce. This allows for two useful features: + // + // 1) A remote node can quickly check if the connection is operational + // 2) Remote nodes can measure the latency of the network thread. If this node + // is overloaded it won't respond to pings quickly and the remote node can + // avoid sending us more work, like chain download requests. + // + // The nonce stops the remote getting confused between different pings: without + // it, if the remote node sends a ping once per second and this node takes 5 + // seconds to respond to each, the 5th ping the remote sends would appear to + // return very quickly. + pfrom->PushMessage("pong", nonce); + } + } + + + else if (strCommand == "pong") + { + int64_t pingUsecEnd = nTimeReceived; + uint64_t nonce = 0; + size_t nAvail = vRecv.in_avail(); + bool bPingFinished = false; + std::string sProblem; + + if (nAvail >= sizeof(nonce)) { + vRecv >> nonce; + + // Only process pong message if there is an outstanding ping (old ping without nonce should never pong) + if (pfrom->nPingNonceSent != 0) { + if (nonce == pfrom->nPingNonceSent) { + // Matching pong received, this ping is no longer outstanding + bPingFinished = true; + int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart; + if (pingUsecTime > 0) { + // Successful ping time measurement, replace previous + pfrom->nPingUsecTime = pingUsecTime; + pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime, pingUsecTime); + } else { + // This should never happen + sProblem = "Timing mishap"; + } + } else { + // Nonce mismatches are normal when pings are overlapping + sProblem = "Nonce mismatch"; + if (nonce == 0) { + // This is most likely a bug in another implementation somewhere; cancel this ping + bPingFinished = true; + sProblem = "Nonce zero"; + } + } + } else { + sProblem = "Unsolicited pong without ping"; + } + } else { + // This is most likely a bug in another implementation somewhere; cancel this ping + bPingFinished = true; + sProblem = "Short payload"; + } + + if (!(sProblem.empty())) { + LogPrint("net", "pong peer=%d %s: %s, %x expected, %x received, %u bytes\n", + pfrom->id, + pfrom->cleanSubVer, + sProblem, + pfrom->nPingNonceSent, + nonce, + nAvail); + } + if (bPingFinished) { + pfrom->nPingNonceSent = 0; + } + } - + // This asymmetric behavior for inbound and outbound connections was introduced + // to prevent a fingerprinting attack: an attacker can send specific fake addresses + // to users' AddrMan and later request them by sending getaddr messages. + // Making nodes which are behind NAT and can only make outgoing connections ignore + // the getaddr message mitigates the attack. + else if ((strCommand == "getaddr") && (pfrom->fInbound)) + { + // Only send one GetAddr response per connection to reduce resource waste + // and discourage addr stamping of INV announcements. + if (pfrom->fSentAddr) { + LogPrint("net", "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->id); + return true; + } + pfrom->fSentAddr = true; + + pfrom->vAddrToSend.clear(); + vector vAddr = addrman.GetAddr(); + BOOST_FOREACH(const CAddress &addr, vAddr) + pfrom->PushAddress(addr); + } + else if (strCommand == "getnSPV") + { + std::vector payload; + vRecv >> payload; + komodo_nSPVreq(pfrom,payload); + return(true); + } + else if (strCommand == "nSPV") + { + if ( KOMODO_NSPV != 0 ) + { + std::vector payload; + vRecv >> payload; + komodo_nSPVresp(pfrom,payload); + } + return(true); + } + else if ( KOMODO_NSPV != 0 ) + return(true); else if (strCommand == "inv") { vector vInv; @@ -7704,28 +7857,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } - // This asymmetric behavior for inbound and outbound connections was introduced - // to prevent a fingerprinting attack: an attacker can send specific fake addresses - // to users' AddrMan and later request them by sending getaddr messages. - // Making nodes which are behind NAT and can only make outgoing connections ignore - // the getaddr message mitigates the attack. - else if ((strCommand == "getaddr") && (pfrom->fInbound)) - { - // Only send one GetAddr response per connection to reduce resource waste - // and discourage addr stamping of INV announcements. - if (pfrom->fSentAddr) { - LogPrint("net", "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->id); - return true; - } - pfrom->fSentAddr = true; - - pfrom->vAddrToSend.clear(); - vector vAddr = addrman.GetAddr(); - BOOST_FOREACH(const CAddress &addr, vAddr) - pfrom->PushAddress(addr); - } - - else if (strCommand == "mempool") { LOCK2(cs_main, pfrom->cs_filter); @@ -7750,88 +7881,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (vInv.size() > 0) pfrom->PushMessage("inv", vInv); } - - - else if (strCommand == "ping") - { - if (pfrom->nVersion > BIP0031_VERSION) - { - uint64_t nonce = 0; - vRecv >> nonce; - // Echo the message back with the nonce. This allows for two useful features: - // - // 1) A remote node can quickly check if the connection is operational - // 2) Remote nodes can measure the latency of the network thread. If this node - // is overloaded it won't respond to pings quickly and the remote node can - // avoid sending us more work, like chain download requests. - // - // The nonce stops the remote getting confused between different pings: without - // it, if the remote node sends a ping once per second and this node takes 5 - // seconds to respond to each, the 5th ping the remote sends would appear to - // return very quickly. - pfrom->PushMessage("pong", nonce); - } - } - - - else if (strCommand == "pong") - { - int64_t pingUsecEnd = nTimeReceived; - uint64_t nonce = 0; - size_t nAvail = vRecv.in_avail(); - bool bPingFinished = false; - std::string sProblem; - - if (nAvail >= sizeof(nonce)) { - vRecv >> nonce; - - // Only process pong message if there is an outstanding ping (old ping without nonce should never pong) - if (pfrom->nPingNonceSent != 0) { - if (nonce == pfrom->nPingNonceSent) { - // Matching pong received, this ping is no longer outstanding - bPingFinished = true; - int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart; - if (pingUsecTime > 0) { - // Successful ping time measurement, replace previous - pfrom->nPingUsecTime = pingUsecTime; - pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime, pingUsecTime); - } else { - // This should never happen - sProblem = "Timing mishap"; - } - } else { - // Nonce mismatches are normal when pings are overlapping - sProblem = "Nonce mismatch"; - if (nonce == 0) { - // This is most likely a bug in another implementation somewhere; cancel this ping - bPingFinished = true; - sProblem = "Nonce zero"; - } - } - } else { - sProblem = "Unsolicited pong without ping"; - } - } else { - // This is most likely a bug in another implementation somewhere; cancel this ping - bPingFinished = true; - sProblem = "Short payload"; - } - - if (!(sProblem.empty())) { - LogPrint("net", "pong peer=%d %s: %s, %x expected, %x received, %u bytes\n", - pfrom->id, - pfrom->cleanSubVer, - sProblem, - pfrom->nPingNonceSent, - nonce, - nAvail); - } - if (bPingFinished) { - pfrom->nPingNonceSent = 0; - } - } - - else if (fAlerts && strCommand == "alert") { CAlert alert; @@ -8185,7 +8234,11 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } state.fShouldBan = false; } - + if ( KOMODO_NSPV != 0 ) + { + komodo_nSPV(pto); + return(true); + } BOOST_FOREACH(const CBlockReject& reject, state.rejects) pto->PushMessage("reject", (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); state.rejects.clear(); @@ -8194,7 +8247,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (pindexBestHeader == NULL) pindexBestHeader = chainActive.Tip(); bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do. - if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) { + if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex && pindexBestHeader!=0) { // Only actively request headers from a single peer, unless we're close to today. if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) { state.fSyncStarted = true; diff --git a/src/main.h b/src/main.h index d507f9dd6..931954da0 100644 --- a/src/main.h +++ b/src/main.h @@ -706,7 +706,7 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons std::vector *pvChecks = NULL); /** Check a transaction contextually against a set of consensus rules */ -bool ContextualCheckTransaction(const CBlock *block, CBlockIndex * const pindexPrev,const CTransaction& tx, CValidationState &state, int nHeight, int dosLevel, +bool ContextualCheckTransaction(int32_t slowflag,const CBlock *block, CBlockIndex * const pindexPrev,const CTransaction& tx, CValidationState &state, int nHeight, int dosLevel, bool (*isInitBlockDownload)() = IsInitialBlockDownload,int32_t validateprices=1); /** Apply the effects of this transaction on the UTXO set represented by view */ @@ -829,7 +829,7 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C /** Context-dependent validity checks */ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex *pindexPrev); -bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex *pindexPrev); +bool ContextualCheckBlock(int32_t slowflag,const CBlock& block, CValidationState& state, CBlockIndex *pindexPrev); /** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex *pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true); diff --git a/src/net.cpp b/src/net.cpp index 83dc0782e..0586d7124 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -83,7 +83,7 @@ extern char ASSETCHAINS_SYMBOL[65]; bool fDiscover = true; bool fListen = true; -uint64_t nLocalServices = NODE_NETWORK; +uint64_t nLocalServices = NODE_NETWORK | NODE_NSPV; CCriticalSection cs_mapLocalHost; map mapLocalHost; static bool vfLimited[NET_MAX] = {}; @@ -458,6 +458,7 @@ void CNode::PushVersion() LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id); PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce, strSubVersion, nBestHeight, true); +//fprintf(stderr,"PUSH services.%llx\n",(long long)nLocalServices); } @@ -681,12 +682,6 @@ int CNetMessage::readData(const char *pch, unsigned int nBytes) - - - - - - // requires LOCK(cs_vSend) void SocketSendData(CNode *pnode) { @@ -1926,8 +1921,7 @@ void RelayTransaction(const CTransaction& tx, const CDataStream& ss) { if (pnode->pfilter->IsRelevantAndUpdate(tx)) pnode->PushInventory(inv); - } else - pnode->PushInventory(inv); + } else pnode->PushInventory(inv); } } diff --git a/src/net.h b/src/net.h index b7975bfec..0b8dcd901 100644 --- a/src/net.h +++ b/src/net.h @@ -276,6 +276,7 @@ public: int64_t nLastRecv; int64_t nTimeConnected; int64_t nTimeOffset; + uint32_t prevtimes[8]; CAddress addr; std::string addrName; CService addrLocal; @@ -462,6 +463,7 @@ public: void PushMessage(const char* pszCommand) { + //fprintf(stderr,"push.(%s)\n",pszCommand); try { BeginMessage(pszCommand); @@ -477,6 +479,7 @@ public: template void PushMessage(const char* pszCommand, const T1& a1) { + //fprintf(stderr,"push.(%s)\n",pszCommand); try { BeginMessage(pszCommand); diff --git a/src/notarisationdb.cpp b/src/notarisationdb.cpp index 201743dbd..b148deccf 100644 --- a/src/notarisationdb.cpp +++ b/src/notarisationdb.cpp @@ -135,3 +135,30 @@ int ScanNotarisationsDB(int height, std::string symbol, int scanLimitBlocks, Not } return 0; } + +int ScanNotarisationsDB2(int height, std::string symbol, int scanLimitBlocks, Notarisation& out) +{ + int32_t i,maxheight,ht; + maxheight = chainActive.Height(); + if ( height < 0 || height > maxheight ) + return false; + for (i=0; i maxheight ) + break; + NotarisationsInBlock notarisations; + uint256 blockHash = *chainActive[ht]->phashBlock; + if ( !GetBlockNotarisations(blockHash,notarisations) ) + continue; + BOOST_FOREACH(Notarisation& nota,notarisations) + { + if ( strcmp(nota.second.symbol,symbol.data()) == 0 ) + { + out = nota; + return(ht); + } + } + } + return 0; +} diff --git a/src/notarisationdb.h b/src/notarisationdb.h index 38b7b3671..af5d4df28 100644 --- a/src/notarisationdb.h +++ b/src/notarisationdb.h @@ -24,6 +24,7 @@ bool GetBackNotarisation(uint256 notarisationHash, Notarisation &n); void WriteBackNotarisations(const NotarisationsInBlock notarisations, CDBBatch &batch); void EraseBackNotarisations(const NotarisationsInBlock notarisations, CDBBatch &batch); int ScanNotarisationsDB(int height, std::string symbol, int scanLimitBlocks, Notarisation& out); +int ScanNotarisationsDB2(int height, std::string symbol, int scanLimitBlocks, Notarisation& out); bool IsTXSCL(const char* symbol); #endif /* NOTARISATIONDB_H */ diff --git a/src/protocol.cpp b/src/protocol.cpp index c7d95f809..36ed63934 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -102,7 +102,7 @@ CAddress::CAddress(CService ipIn, uint64_t nServicesIn) : CService(ipIn) void CAddress::Init() { - nServices = NODE_NETWORK; + nServices = NODE_NETWORK | NODE_NSPV; nTime = 100000000; } diff --git a/src/protocol.h b/src/protocol.h index d2a3cf556..b7ad483ac 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -91,6 +91,10 @@ enum { // but no longer do as of protocol version 170004 (= NO_BLOOM_VERSION) NODE_BLOOM = (1 << 2), + NODE_NSPV = (1 << 30), + NODE_ADDRINDEX = (1 << 29), + NODE_SPENTINDEX = (1 << 28), + // Bits 24-31 are reserved for temporary experiments. Just pick a bit that // isn't getting used, or one not being used much, and notify the // bitcoin-development mailing list. Remember that service bits are just diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 7d490dff2..c9d16a0de 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -368,13 +368,13 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) entry.push_back(Pair("vin", vin)); UniValue vout(UniValue::VARR); BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); - CBlockIndex *tipindex,*pindex = it->second; + CBlockIndex *tipindex;//,*pindex = it->second; uint64_t interest; for (unsigned int i = 0; i < tx.vout.size(); i++) { const CTxOut& txout = tx.vout[i]; UniValue out(UniValue::VOBJ); out.push_back(Pair("value", ValueFromAmount(txout.nValue))); - if ( ASSETCHAINS_SYMBOL[0] == 0 && pindex != 0 && tx.nLockTime >= 500000000 && (tipindex= chainActive.LastTip()) != 0 ) + if ( KOMODO_NSPV == 0 && ASSETCHAINS_SYMBOL[0] == 0 && tx.nLockTime >= 500000000 && (tipindex= chainActive.LastTip()) != 0 ) { int64_t interest; int32_t txheight; uint32_t locktime; interest = komodo_accrued_interest(&txheight,&locktime,tx.GetHash(),i,0,txout.nValue,(int32_t)tipindex->GetHeight()); @@ -1282,6 +1282,8 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) return result; } +extern UniValue NSPV_broadcast(char *hex); + UniValue sendrawtransaction(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) @@ -1317,30 +1319,35 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) bool fOverrideFees = false; if (params.size() > 1) fOverrideFees = params[1].get_bool(); - - CCoinsViewCache &view = *pcoinsTip; - const CCoins* existingCoins = view.AccessCoins(hashTx); - bool fHaveMempool = mempool.exists(hashTx); - bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000; - if (!fHaveMempool && !fHaveChain) { - // push to local node and sync with wallets - CValidationState state; - bool fMissingInputs; - if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, !fOverrideFees)) { - if (state.IsInvalid()) { - throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); - } else { - if (fMissingInputs) { - throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs"); + if ( KOMODO_NSPV == 0 ) + { + CCoinsViewCache &view = *pcoinsTip; + const CCoins* existingCoins = view.AccessCoins(hashTx); + bool fHaveMempool = mempool.exists(hashTx); + bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000; + if (!fHaveMempool && !fHaveChain) { + // push to local node and sync with wallets + CValidationState state; + bool fMissingInputs; + if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, !fOverrideFees)) { + if (state.IsInvalid()) { + throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); + } else { + if (fMissingInputs) { + throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs"); + } + throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); } - throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); } + } else if (fHaveChain) { + throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); } - } else if (fHaveChain) { - throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); + RelayTransaction(tx); + } + else + { + NSPV_broadcast((char *)params[0].get_str().c_str()); } - RelayTransaction(tx); - return hashTx.GetHex(); } diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 75360fd35..d3e0646ea 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -416,6 +416,18 @@ static const CRPCCommand vRPCCommands[] = { "FSM", "FSMlist", &FSMlist, true }, { "FSM", "FSMinfo", &FSMinfo, true }, + // fsm + { "nSPV", "nspv_getinfo", &nspv_getinfo, true }, + { "nSPV", "nspv_login", &nspv_login, true }, + { "nSPV", "nspv_listunspent", &nspv_listunspent, true }, + { "nSPV", "nspv_spentinfo", &nspv_spentinfo, true }, + { "nSPV", "nspv_notarizations", &nspv_notarizations, true }, + { "nSPV", "nspv_hdrsproof", &nspv_hdrsproof, true }, + { "nSPV", "nspv_txproof", &nspv_txproof, true }, + { "nSPV", "nspv_spend", &nspv_spend, true }, + { "nSPV", "nspv_broadcast", &nspv_broadcast, true }, + { "nSPV", "nspv_logout", &nspv_logout, true }, + // rewards { "rewards", "rewardslist", &rewardslist, true }, { "rewards", "rewardsinfo", &rewardsinfo, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index d447d2472..35af45a18 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -455,6 +455,17 @@ extern UniValue importgatewaypendingdeposits(const UniValue& params, bool fHelp) extern UniValue importgatewaypendingwithdraws(const UniValue& params, bool fHelp); extern UniValue importgatewayprocessed(const UniValue& params, bool fHelp); +extern UniValue nspv_getinfo(const UniValue& params, bool fHelp); +extern UniValue nspv_login(const UniValue& params, bool fHelp); +extern UniValue nspv_listunspent(const UniValue& params, bool fHelp); +extern UniValue nspv_spentinfo(const UniValue& params, bool fHelp); +extern UniValue nspv_notarizations(const UniValue& params, bool fHelp); +extern UniValue nspv_hdrsproof(const UniValue& params, bool fHelp); +extern UniValue nspv_txproof(const UniValue& params, bool fHelp); +extern UniValue nspv_spend(const UniValue& params, bool fHelp); +extern UniValue nspv_broadcast(const UniValue& params, bool fHelp); +extern UniValue nspv_logout(const UniValue& params, bool fHelp); + extern UniValue getblocksubsidy(const UniValue& params, bool fHelp); extern UniValue z_exportkey(const UniValue& params, bool fHelp); // in rpcdump.cpp diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 65524a07e..b6a6ebc26 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -491,7 +491,7 @@ void test_simple_joinsplit_invalidity(uint32_t consensusBranchId, CMutableTransa jsdesc->nullifiers[1] = GetRandHash(); BOOST_CHECK(CheckTransactionWithoutProofVerification(newTx, state)); - BOOST_CHECK(!ContextualCheckTransaction(newTx, state, 0, 100)); + BOOST_CHECK(!ContextualCheckTransaction(0,newTx, state, 0, 100)); BOOST_CHECK(state.GetRejectReason() == "bad-txns-invalid-joinsplit-signature"); // Empty output script. @@ -505,7 +505,7 @@ void test_simple_joinsplit_invalidity(uint32_t consensusBranchId, CMutableTransa ) == 0); BOOST_CHECK(CheckTransactionWithoutProofVerification(newTx, state)); - BOOST_CHECK(ContextualCheckTransaction(newTx, state, 0, 100)); + BOOST_CHECK(ContextualCheckTransaction(0,newTx, state, 0, 100)); } { // Ensure that values within the joinsplit are well-formed. diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index d35b09a96..b197b73b0 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -971,3 +971,115 @@ UniValue z_exportviewingkey(const UniValue& params, bool fHelp) return EncodeViewingKey(vk); } + +UniValue NSPV_getinfo_req(int32_t reqht); +UniValue NSPV_login(char *wifstr); +UniValue NSPV_logout(); +UniValue NSPV_addressutxos(char *coinaddr); +UniValue NSPV_broadcast(char *hex); +UniValue NSPV_spend(char *srcaddr,char *destaddr,int64_t satoshis); +UniValue NSPV_spentinfo(uint256 txid,int32_t vout); +UniValue NSPV_notarizations(int32_t height); +UniValue NSPV_hdrsproof(int32_t prevheight,int32_t nextheight); +UniValue NSPV_txproof(int32_t vout,uint256 txid,int32_t height); +uint256 Parseuint256(const char *hexstr); +extern std::string NSPV_address; + +UniValue nspv_getinfo(const UniValue& params, bool fHelp) +{ + int32_t reqht = 0; + if ( fHelp || params.size() > 1 ) + throw runtime_error("nspv_getinfo [hdrheight]\n"); + if ( params.size() == 1 ) + reqht = atoi((char *)params[0].get_str().c_str()); + return(NSPV_getinfo_req(reqht)); +} + +UniValue nspv_logout(const UniValue& params, bool fHelp) +{ + if ( fHelp || params.size() != 0 ) + throw runtime_error("nspv_logout\n"); + return(NSPV_logout()); +} + +UniValue nspv_login(const UniValue& params, bool fHelp) +{ + if ( fHelp || params.size() != 1 ) + throw runtime_error("nspv_login wif\n"); + return(NSPV_login((char *)params[0].get_str().c_str())); +} + +UniValue nspv_listunspent(const UniValue& params, bool fHelp) +{ + if ( fHelp || params.size() > 1 ) + throw runtime_error("nspv_listunspent address\n"); + if ( params.size() == 0 ) + { + if ( NSPV_address.size() != 0 ) + return(NSPV_addressutxos((char *)NSPV_address.c_str())); + else throw runtime_error("nspv_listunspent address\n"); + } + if ( params.size() == 1 ) + return(NSPV_addressutxos((char *)params[0].get_str().c_str())); + else throw runtime_error("nspv_listunspent address\n"); +} + +UniValue nspv_spentinfo(const UniValue& params, bool fHelp) +{ + uint256 txid; int32_t vout; + if ( fHelp || params.size() != 2 ) + throw runtime_error("nspv_spentinfo txid vout\n"); + txid = Parseuint256((char *)params[0].get_str().c_str()); + vout = atoi((char *)params[1].get_str().c_str()); + return(NSPV_spentinfo(txid,vout)); +} + +UniValue nspv_notarizations(const UniValue& params, bool fHelp) +{ + int32_t height; + if ( fHelp || params.size() != 1 ) + throw runtime_error("nspv_notarizations height\n"); + height = atoi((char *)params[0].get_str().c_str()); + return(NSPV_notarizations(height)); +} + +UniValue nspv_hdrsproof(const UniValue& params, bool fHelp) +{ + int32_t prevheight,nextheight; + if ( fHelp || params.size() != 2 ) + throw runtime_error("nspv_hdrsproof prevheight nextheight\n"); + prevheight = atoi((char *)params[0].get_str().c_str()); + nextheight = atoi((char *)params[1].get_str().c_str()); + return(NSPV_hdrsproof(prevheight,nextheight)); +} + +UniValue nspv_txproof(const UniValue& params, bool fHelp) +{ + uint256 txid; int32_t height; + if ( fHelp || params.size() != 2 ) + throw runtime_error("nspv_txproof txid height\n"); + txid = Parseuint256((char *)params[0].get_str().c_str()); + height = atoi((char *)params[1].get_str().c_str()); + return(NSPV_txproof(0,txid,height)); +} + +UniValue nspv_spend(const UniValue& params, bool fHelp) +{ + uint64_t satoshis; + if ( fHelp || params.size() != 2 ) + throw runtime_error("nspv_spend destaddr amount\n"); + if ( NSPV_address.size() == 0 ) + throw runtime_error("to nspv_send you need an active nspv_login\n"); + satoshis = atof(params[1].get_str().c_str())*COIN + 0.0000000049; + //fprintf(stderr,"satoshis.%lld from %.8f\n",(long long)satoshis,atof(params[1].get_str().c_str())); + if ( satoshis < 1000 ) + throw runtime_error("amount too small\n"); + return(NSPV_spend((char *)NSPV_address.c_str(),(char *)params[0].get_str().c_str(),satoshis)); +} + +UniValue nspv_broadcast(const UniValue& params, bool fHelp) +{ + if ( fHelp || params.size() != 1 ) + throw runtime_error("nspv_broadcast hex\n"); + return(NSPV_broadcast((char *)params[0].get_str().c_str())); +} diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ce377d094..1d7768255 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -158,9 +158,11 @@ char *komodo_chainname() return(ASSETCHAINS_SYMBOL[0] == 0 ? (char *)"KMD" : ASSETCHAINS_SYMBOL); } +void OS_randombytes(unsigned char *x,long xlen); + UniValue getnewaddress(const UniValue& params, bool fHelp) { - if (!EnsureWalletIsAvailable(fHelp)) + if ( KOMODO_NSPV == 0 && !EnsureWalletIsAvailable(fHelp) ) return NullUniValue; if (fHelp || params.size() > 1) @@ -176,6 +178,23 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) + HelpExampleRpc("getnewaddress", "") ); + if ( KOMODO_NSPV != 0 ) + { + UniValue result(UniValue::VOBJ); uint8_t priv32[32]; +#ifndef __WIN32 + OS_randombytes(priv32,sizeof(priv32)); +#else + randombytes_buf(priv32,sizeof(priv32)); +#endif + CKey key; + key.Set(&priv32[0],&priv32[32], true); + CPubKey pubkey = key.GetPubKey(); + CKeyID vchAddress = pubkey.GetID(); + result.push_back(Pair("wif",EncodeSecret(key))); + result.push_back(Pair("address",EncodeDestination(vchAddress))); + result.push_back(Pair("pubkey",HexStr(pubkey))); + return(result); + } LOCK2(cs_main, pwalletMain->cs_wallet); // Parse the account first so we don't generate a key if there's an error @@ -2955,7 +2974,7 @@ UniValue listunspent(const UniValue& params, bool fHelp) uint64_t komodo_interestsum() { #ifdef ENABLE_WALLET - if ( ASSETCHAINS_SYMBOL[0] == 0 && GetBoolArg("-disablewallet", false) == 0 ) + if ( ASSETCHAINS_SYMBOL[0] == 0 && GetBoolArg("-disablewallet", false) == 0 && KOMODO_NSPV == 0 ) { uint64_t interest,sum = 0; int32_t txheight; uint32_t locktime; vector vecOutputs;