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/assetchains.old b/src/assetchains.old index 4cf427c4d..1784bf2e1 100755 --- a/src/assetchains.old +++ b/src/assetchains.old @@ -4,37 +4,37 @@ delay=60 source pubkey.txt echo $pubkey -./komodod -pubkey=$pubkey -ac_name=REVS -ac_supply=1300000 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=SUPERNET -ac_supply=816061 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=DEX -ac_supply=999999 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=PANGEA -ac_supply=999999 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=JUMBLR -ac_supply=999999 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=BET -ac_supply=999999 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=CRYPTO -ac_supply=999999 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=HODL -ac_supply=9999999 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=MSHARK -ac_supply=1400000 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=BOTS -ac_supply=999999 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=MGW -ac_supply=999999 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=COQUI -ac_supply=72000000 -ac_ccactivate=200000 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=WLC -ac_supply=210000000 -addnode=148.251.190.89 $1 & -./komodod -pubkey=$pubkey -ac_name=KV -ac_supply=1000000 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=CEAL -ac_supply=366666666 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=MESH -ac_supply=1000007 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=AXO -ac_supply=200000000 -ac_ccactivate=130000 -addnode=78.47.196.146 & -./komodod -pubkey=$pubkey -ac_name=ETOMIC -ac_supply=100000000 -addnode=78.47.196.146 & -./komodod -pubkey=$pubkey -ac_name=BTCH -ac_supply=20998641 -addnode=78.47.196.146 & -./komodod -pubkey=$pubkey -ac_name=BEER -ac_supply=100000000 -addnode=78.47.196.146 & -./komodod -pubkey=$pubkey -ac_name=PIZZA -ac_supply=100000000 -addnode=78.47.196.146 & -./komodod -pubkey=$pubkey -ac_name=NINJA -ac_supply=100000000 -addnode=78.47.196.146 & -./komodod -pubkey=$pubkey -ac_name=OOT -ac_supply=216000000 -ac_sapling=5000000 -addnode=174.138.107.226 & -./komodod -pubkey=$pubkey -ac_name=BNTN -ac_supply=500000000 -addnode=94.130.169.205 & -./komodod -pubkey=$pubkey -ac_name=CHAIN -ac_supply=999999 -addnode=78.47.146.222 & +./komodod -pubkey=$pubkey -ac_name=REVS -ac_supply=1300000 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=SUPERNET -ac_supply=816061 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=DEX -ac_supply=999999 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=PANGEA -ac_supply=999999 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=JUMBLR -ac_supply=999999 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=BET -ac_supply=999999 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=CRYPTO -ac_supply=999999 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=HODL -ac_supply=9999999 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=MSHARK -ac_supply=1400000 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=BOTS -ac_supply=999999 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=MGW -ac_supply=999999 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=COQUI -ac_supply=72000000 -ac_ccactivate=200000 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=WLC -ac_supply=210000000 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=KV -ac_supply=1000000 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=CEAL -ac_supply=366666666 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=MESH -ac_supply=1000007 -addnode=95.213.238.98 $1 & +./komodod -pubkey=$pubkey -ac_name=AXO -ac_supply=200000000 -ac_ccactivate=130000 -addnode=95.213.238.98 & +./komodod -pubkey=$pubkey -ac_name=ETOMIC -ac_supply=100000000 -addnode=95.213.238.98 & +./komodod -pubkey=$pubkey -ac_name=BTCH -ac_supply=20998641 -addnode=95.213.238.98 & +./komodod -pubkey=$pubkey -ac_name=BEER -ac_supply=100000000 -addnode=95.213.238.98 & +./komodod -pubkey=$pubkey -ac_name=PIZZA -ac_supply=100000000 -addnode=95.213.238.98 & +./komodod -pubkey=$pubkey -ac_name=NINJA -ac_supply=100000000 -addnode=95.213.238.98 & +./komodod -pubkey=$pubkey -ac_name=OOT -ac_supply=216000000 -ac_sapling=5000000 -addnode=95.213.238.98 & +./komodod -pubkey=$pubkey -ac_name=BNTN -ac_supply=500000000 -addnode=95.213.238.98 & +./komodod -pubkey=$pubkey -ac_name=CHAIN -ac_supply=999999 -addnode=95.213.238.98 & ./komodod -pubkey=$pubkey -ac_name=PRLPAY -ac_supply=500000000 -addnode=13.250.226.125 & ./komodod -pubkey=$pubkey -ac_name=DSEC -ac_supply=7000000 -addnode=185.148.147.30 & ./komodod -pubkey=$pubkey -ac_name=GLXT -ac_supply=10000000000 -addnode=13.230.224.15 & ./komodod -pubkey=$pubkey -ac_name=EQL -ac_supply=500000000 -ac_ccactivate=205000 -addnode=46.101.124.153 & ./komodod -pubkey=$pubkey -ac_name=ZILLA -ac_supply=11000000 -ac_sapling=5000000 -addnode=51.68.215.104 & -./komodod -pubkey=$pubkey -ac_name=RFOX -ac_supply=1000000000 -ac_reward=100000000 -addnode=78.47.196.146 & +./komodod -pubkey=$pubkey -ac_name=RFOX -ac_supply=1000000000 -ac_reward=100000000 -addnode=95.213.238.98 & ~/VerusCoin/src/komodod -pubkey=$pubkey -ac_name=VRSC -ac_algo=verushash -ac_cc=1 -ac_veruspos=50 -ac_supply=0 -ac_eras=3 -ac_reward=0,38400000000,2400000000 -ac_halving=1,43200,1051920 -ac_decay=100000000,0,0 -ac_end=10080,226080,0 -ac_timelockgte=19200000000 -ac_timeunlockfrom=129600 -ac_timeunlockto=1180800 -addnode=185.25.48.236 -addnode=185.64.105.111 & ./komodod -pubkey=$pubkey -ac_name=SEC -ac_cc=333 -ac_supply=1000000000 -addnode=185.148.145.43 & ./komodod -pubkey=$pubkey -ac_name=CCL -ac_supply=200000000 -ac_end=1 -ac_cc=2 -addressindex=1 -spentindex=1 -addnode=142.93.136.89 -addnode=195.201.22.89 & diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 68e0da8b3..a356e20a7 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -68,8 +68,13 @@ void komodo_cbopretupdate(int32_t forceflag); void WaitForShutdown(boost::thread_group* threadGroup) { - int32_t i; bool fShutdown = ShutdownRequested(); + int32_t i,height; bool fShutdown = ShutdownRequested(); const uint256 zeroid; // Tell the main threads to shutdown. + if (komodo_currentheight()>KOMODO_EARLYTXID_HEIGHT && KOMODO_EARLYTXID!=zeroid && ((height=tx_height(KOMODO_EARLYTXID))==0 || height>KOMODO_EARLYTXID_HEIGHT)) + { + fprintf(stderr,"error: earlytx must be before block height 100 or tx does not exist\n"); + StartShutdown(); + } if ( ASSETCHAINS_CBOPRET != 0 ) komodo_pricesinit(); while (!fShutdown) @@ -77,7 +82,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/CCPegs.h b/src/cc/CCPegs.h index 8d2a268f1..e4d390d0e 100644 --- a/src/cc/CCPegs.h +++ b/src/cc/CCPegs.h @@ -22,6 +22,15 @@ bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom -UniValue PegsInfo(); +std::string PegsCreate(uint64_t txfee,int64_t amount,std::vector bindtxids); +std::string PegsFund(uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount); +std::string PegsGet(uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount); +std::string PegsRedeem(uint64_t txfee,uint256 pegstxid, uint256 tokenid); +std::string PegsLiquidate(uint64_t txfee,uint256 pegstxid, uint256 tokenid, uint256 liquidatetxid); +std::string PegsExchange(uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount); +UniValue PegsAccountHistory(uint256 pegstxid); +UniValue PegsAccountInfo(uint256 pegstxid); +UniValue PegsWorstAccounts(uint256 pegstxid); +UniValue PegsInfo(uint256 pegstxid); #endif diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 1d8cf669e..4c6791676 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -79,6 +79,7 @@ one other technical note is that komodod has the insight-explorer extensions bui OPRETID_CHANNELSDATA = 0x14, OPRETID_HEIRDATA = 0x15, OPRETID_ROGUEGAMEDATA = 0x16, + OPRETID_PEGSDATA = 0x17, // non cc contract data: OPRETID_FIRSTNONCCDATA = 0x80, @@ -126,7 +127,7 @@ struct CCcontract_info // the same for tokens 1of2 keys cc char tokens1of2addr[64]; - CPubKey tokens1of2pk[2]; + CPubKey tokens1of2pk[2]; uint8_t tokens1of2priv[32]; // this is for spending from two additional 'unspendable' CC addresses of other eval codes // (that is, for spending from several cc contract 'unspendable' addresses): @@ -174,7 +175,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); @@ -249,7 +250,7 @@ CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk); CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk); bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk); bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2); -void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr); +void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, uint8_t *priv, char *coinaddr); int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode); bool IsCCInput(CScript const& scriptSig); @@ -289,6 +290,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,uint32_t nTime); // curve25519 and sha256 bits256 curve25519_shared(bits256 privkey,bits256 otherpub); diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index 5f5e56ebb..0319af622 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -397,6 +397,21 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true } } + //special check for tx when spending from 1of2 CC address and one of pubkeys is global CC pubkey + struct CCcontract_info *cpEvalCode1,CEvalCode1; + cpEvalCode1 = CCinit(&CEvalCode1,evalCode1); + CPubKey pk=GetUnspendable(cpEvalCode1,0); + testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeys[0], pk), std::string("dual-eval1 pegscc cc1of2 pk[0] globalccpk")) ); + if (voutPubkeys.size() == 2) testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeys[1], pk), std::string("dual-eval1 pegscc cc1of2 pk[1] globalccpk")) ); + if (evalCode2!=0) + { + struct CCcontract_info *cpEvalCode2,CEvalCode2; + cpEvalCode2 = CCinit(&CEvalCode2,evalCode2); + CPubKey pk=GetUnspendable(cpEvalCode2,0); + testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode2, tx.vout[v].nValue, voutPubkeys[0], pk), std::string("dual-eval2 pegscc cc1of2 pk[0] globalccpk")) ); + if (voutPubkeys.size() == 2) testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode2, tx.vout[v].nValue, voutPubkeys[1], pk), std::string("dual-eval2 pegscc cc1of2 pk[1] globalccpk")) ); + } + // maybe it is single-eval or dual/three-eval token change? std::vector vinPubkeys, vinPubkeysUnfiltered; ExtractTokensCCVinPubkeys(tx, vinPubkeysUnfiltered); diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index f5ebf194f..0eb1ac3c0 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -96,6 +96,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran //This is a must to avoid hardfork change of validation in every CC, because there could be maximum one normal vin at the begining with current validation. for (i=0; itokens1of2addr, destaddr) == 0) { //fprintf(stderr,"FinalizeCCTx() matched %s cp->tokens1of2addr!\n", cp->tokens1of2addr); - privkey = myprivkey; + privkey = cp->tokens1of2priv;//myprivkey; if (othercond1of2tokens == 0) // NOTE: if additionalEvalcode2 is not set then it is dual-eval cc else three-eval cc // TODO: verify evalcodes order if additionalEvalcode2 is not 0 @@ -358,7 +369,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); @@ -511,6 +522,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; @@ -606,6 +621,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..2430a9b37 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -187,7 +187,7 @@ void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t * } // set pubkeys, myprivkey and 1of2 cc addr for spending from 1of2 cryptocondition vout: -void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2,uint8_t *priv,char *coinaddr) +void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, uint8_t *priv, char *coinaddr) { cp->coins1of2pk[0] = pk1; cp->coins1of2pk[1] = pk2; @@ -197,10 +197,11 @@ void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2,uint8_t // set pubkeys, myprivkey and 1of2 cc addr for spending from 1of2 token cryptocondition vout // to get tokenaddr use GetTokensCCaddress() -void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *tokenaddr) +void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, uint8_t *priv, char *tokenaddr) { cp->tokens1of2pk[0] = pk1; cp->tokens1of2pk[1] = pk2; + memcpy(cp->tokens1of2priv,priv,32); strcpy(cp->tokens1of2addr, tokenaddr); } @@ -437,9 +438,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/cc/channels.cpp b/src/cc/channels.cpp index 64bb95572..2d161675c 100644 --- a/src/cc/channels.cpp +++ b/src/cc/channels.cpp @@ -451,7 +451,7 @@ int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, C mtx.vin.push_back(CTxIn(txid,0,CScript())); mtx.vin.push_back(CTxIn(txid,marker,CScript())); Myprivkey(myprivkey); - if (tokenid!=zeroid) CCaddrTokens1of2set(cp,srcpub,destpub,coinaddr); + if (tokenid!=zeroid) CCaddrTokens1of2set(cp,srcpub,destpub,myprivkey,coinaddr); else CCaddr1of2set(cp,srcpub,destpub,myprivkey,coinaddr); return totalinputs; } diff --git a/src/cc/heir_validate.h b/src/cc/heir_validate.h index a27dc3340..c7424bc16 100644 --- a/src/cc/heir_validate.h +++ b/src/cc/heir_validate.h @@ -98,8 +98,9 @@ public: } static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { - - CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); + uint8_t mypriv[32]; + Myprivkey(mypriv); + CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, mypriv, coinaddr); } }; diff --git a/src/cc/import.cpp b/src/cc/import.cpp index 35b4f5405..aa2ea5d11 100644 --- a/src/cc/import.cpp +++ b/src/cc/import.cpp @@ -681,7 +681,7 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp return Invalid("invalid-params"); // Control all aspects of this transaction // It should not be at all malleable - if (MakeImportCoinTransaction(proof, burnTx, payouts, importTx.nExpiryHeight).GetHash() != importTx.GetHash()) // ExistsImportTombstone prevents from duplication + if (ASSETCHAINS_SELFIMPORT!="PEGSCC" && MakeImportCoinTransaction(proof, burnTx, payouts, importTx.nExpiryHeight).GetHash() != importTx.GetHash()) // ExistsImportTombstone prevents from duplication return Invalid("non-canonical"); // burn params if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCcid, payoutsHash, rawproof)) @@ -736,10 +736,17 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp else if ( UnmarshalBurnTx(burnTx,srcaddr,receipt)==0 || CheckCODAimport(importTx,burnTx,payouts,srcaddr,receipt) < 0 ) return Invalid("CODA-import-failure"); } + else if ( targetSymbol == "PEGSCC" ) + { + if ( ASSETCHAINS_SELFIMPORT != "PEGSCC" ) + return Invalid("PEGSCC-import-when-not PEGSCC"); + // else if ( CheckPUBKEYimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 ) + // return Invalid("PEGSCC-import-failure"); + } else if ( targetSymbol == "PUBKEY" ) { if ( ASSETCHAINS_SELFIMPORT != "PUBKEY" ) - return Invalid("PUBKEY-import-when-notPUBKEY"); + return Invalid("PUBKEY-import-when-not PUBKEY"); else if ( CheckPUBKEYimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 ) return Invalid("PUBKEY-import-failure"); } @@ -747,7 +754,7 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp { if ( targetSymbol != ASSETCHAINS_SELFIMPORT ) return Invalid("invalid-gateway-import-coin"); - else if ( UnmarshalBurnTx(burnTx,bindtxid,publishers,txids,burntxid,height,burnvout,rawburntx,destpub,amount)==0 || CheckGATEWAYimport(importTx,burnTx,targetSymbol,rawproof,bindtxid,publishers,txids,burntxid,height,burnvout,rawburntx,destpub,amount) < 0 ) + else if ( UnmarshalBurnTx(burnTx,bindtxid,publishers,txids,burntxid,height,burnvout,rawburntx,destpub,amount)==0 || CheckGATEWAYimport(importTx,burnTx,targetSymbol,rawproof,bindtxid,publishers,txids,burntxid,height,burnvout,rawburntx,destpub,amount) < 0 ) return Invalid("GATEWAY-import-failure"); } } diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index 21cee8ca1..96c41b9ff 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -14,6 +14,8 @@ ******************************************************************************/ #include "CCPegs.h" +#include "../importcoin.h" +#include "key_io.h" /* pegs CC is able to create a coin backed (by any supported coin with gateways CC deposits) and pegged to any synthetic price that is able to be calculated based on prices CC @@ -84,6 +86,197 @@ pegs CC is able to create a coin backed (by any supported coin with gateways CC */ // start of consensus code +#ifndef PEGS_THRESHOLDS +#define PEGS_THRESHOLDS +#define PEGS_ACCOUNT_YELLOW_ZONE 60 +#define PEGS_ACCOUNT_THRESHOLD 90 +#define PEGS_GLOBAL_THRESHOLD 60 +#endif // PEGS_THRESHOLDS +#define CC_MARKER_VALUE 10000 + +extern uint64_t ASSETCHAINS_PEGSCCPARAMS[3]; + +extern uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,uint256 &tokenid,std::string &coin,int64_t &totalsupply,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &gatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype); +extern int64_t GetTokenBalance(CPubKey pk, uint256 tokenid); +extern int32_t komodo_currentheight(); + +CScript EncodePegsCreateOpRet(std::vector bindtxids) +{ + CScript opret; uint8_t evalcode = EVAL_PEGS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'C' << bindtxids); + return(opret); +} + +uint8_t DecodePegsCreateOpRet(const CScript &scriptPubKey,std::vector &bindtxids) +{ + std::vector vopret; uint8_t *script,e,f; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> bindtxids) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodePegsFundOpRet(uint256 tokenid,uint256 pegstxid,CPubKey srcpub,int64_t amount,std::pair account) +{ + CScript opret; uint8_t evalcode=EVAL_PEGS,funcid='F'; struct CCcontract_info *cp,C; CPubKey pegspk; + std::vector pubkeys; vscript_t vopret; + + cp = CCinit(&C,EVAL_PEGS); + pegspk = GetUnspendable(cp,0); + pubkeys.push_back(srcpub); + pubkeys.push_back(pegspk); + LOGSTREAM("pegscc", CCLOG_DEBUG1, stream << "EncodePegsFundOpRet [" << account.first << "," << account.second << "]" << std::endl); + vopret = E_MARSHAL(ss << evalcode << funcid << pegstxid << srcpub << amount << account); + return(EncodeTokenOpRet(tokenid,pubkeys,make_pair(OPRETID_PEGSDATA, vopret))); +} + +uint8_t DecodePegsFundOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &pegstxid,CPubKey &srcpub,int64_t &amount,std::pair &account) +{ + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid; ss >> srcpub; ss >> amount; ss >> account) != 0 ) + { + return(f); + } + return(0); +} + +uint8_t DecodePegsGetOpRet(const CTransaction tx,uint256& pegstxid,uint256 &tokenid,CPubKey &srcpub,int64_t &amount,std::pair &account) +{ + std::vector vopret; uint8_t *script; + ImportProof proof; CTransaction burntx; std::vector payouts; + + GetOpReturnData(tx.vout[tx.vout.size()-1].scriptPubKey, vopret); + + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_IMPORTCOIN && UnmarshalImportTx(tx,proof,burntx,payouts) && UnmarshalBurnTx(burntx,pegstxid,tokenid,srcpub,amount,account)) + { + return('G'); + } + return(0); +} + +CScript EncodePegsReedemOpRet(uint256 tokenid,uint256 pegstxid,CPubKey srcpub,int64_t amount,std::pair account) +{ + CScript opret; uint8_t evalcode=EVAL_PEGS,funcid='R'; struct CCcontract_info *cp,C; + std::vector pubkeys; vscript_t vopret; + + cp = CCinit(&C,EVAL_PEGS); + pubkeys.push_back(srcpub); + vopret = E_MARSHAL(ss << evalcode << funcid << pegstxid << srcpub << amount << account); + return(EncodeTokenOpRet(tokenid,pubkeys,make_pair(OPRETID_PEGSDATA, vopret))); +} + +uint8_t DecodePegsRedeemOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &pegstxid,CPubKey &srcpub,int64_t &amount,std::pair &account) +{ + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid; ss >> srcpub; ss >> amount; ss >> account) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodePegsExchangeOpRet(uint256 tokenid,uint256 pegstxid,CPubKey pk1,CPubKey pk2,int64_t amount,std::pair account) +{ + CScript opret; uint8_t evalcode=EVAL_PEGS,funcid='E'; struct CCcontract_info *cp,C; + std::vector pubkeys; vscript_t vopret; CPubKey pegspk; + + cp = CCinit(&C,EVAL_PEGS); + pegspk = GetUnspendable(cp,0); + pubkeys.push_back(pk1); + pubkeys.push_back(pk2); + vopret = E_MARSHAL(ss << evalcode << funcid << pegstxid << pk1 << amount << account); + return(EncodeTokenOpRet(tokenid,pubkeys,make_pair(OPRETID_PEGSDATA, vopret))); +} + +uint8_t DecodePegsExchangeOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &pegstxid,CPubKey &srcpub,int64_t &amount,std::pair &account) +{ + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid; ss >> srcpub; ss >> amount; ss >> account) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodePegsLiquidateOpRet(uint256 tokenid,uint256 pegstxid,CPubKey srcpub,int64_t amount,std::pair account) +{ + CScript opret; uint8_t evalcode=EVAL_PEGS,funcid='L'; struct CCcontract_info *cp,C; + std::vector pubkeys; vscript_t vopret; + + cp = CCinit(&C,EVAL_PEGS); + pubkeys.push_back(srcpub); + vopret = E_MARSHAL(ss << evalcode << funcid << pegstxid << srcpub << amount << account); + return(EncodeTokenOpRet(tokenid,pubkeys,make_pair(OPRETID_PEGSDATA, vopret))); +} + +uint8_t DecodePegsLiquidateOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &pegstxid,CPubKey &srcpub,int64_t &amount,std::pair &account) +{ + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid; ss >> srcpub; ss >> amount; ss >> account) != 0 ) + { + return(f); + } + return(0); +} + +uint8_t DecodePegsOpRet(CTransaction tx,uint256& pegstxid,uint256& tokenid) +{ + std::vector> oprets; int32_t numvouts=tx.vout.size(); + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + ImportProof proof; CTransaction burntx; std::vector payouts; uint256 tmppegstxid; CPubKey srcpub; int64_t amount; std::pair account; + + if (DecodeTokenOpRet(tx.vout[numvouts-1].scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(tx.vout[numvouts-1].scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if (tx.IsPegsImport()) + return(DecodePegsGetOpRet(tx,pegstxid,tokenid,srcpub,amount,account)); + else if ( vopret.size() > 2 && script[0] == EVAL_PEGS) + { + E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid); + return(f); + } + return(0); +} int64_t IsPegsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) { @@ -137,7 +330,7 @@ bool PegsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction & bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; - return eval->Invalid("no validation yet"); + return (true); std::vector > txids; numvins = tx.vin.size(); numvouts = tx.vout.size(); @@ -175,12 +368,14 @@ bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, // helper functions for rpc calls in rpcwallet.cpp -int64_t AddPegsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) +int64_t AddPegsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk1,CPubKey pk2,int64_t total,int32_t maxinputs) { // add threshold check char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; - GetCCaddress(cp,coinaddr,pk); + + if (pk2.IsValid()) GetCCaddress1of2(cp,coinaddr,pk1,pk2); + else GetCCaddress(cp,coinaddr,pk1); SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { @@ -189,7 +384,7 @@ int64_t AddPegsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKe // no need to prevent dup if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( (nValue= IsPegsvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + if (myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); @@ -204,76 +399,1005 @@ int64_t AddPegsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKe return(totalinputs); } -std::string PegsGet(uint64_t txfee,int64_t nValue) +int64_t AddPegsTokenInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,uint256 pegstxid, uint256 tokenid, CPubKey pk1,CPubKey pk2, int64_t total,int32_t maxinputs) { - CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,Pegspk; int64_t inputs,CCchange=0; struct CCcontract_info *cp,C; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash; - cp = CCinit(&C,EVAL_PEGS); - if ( txfee == 0 ) - txfee = 10000; - Pegspk = GetUnspendable(cp,0); - mypk = pubkey2pk(Mypubkey()); - if ( (inputs= AddPegsInputs(cp,mtx,Pegspk,nValue+txfee,60)) > 0 ) + // add threshold check + char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + std::vector > unspentOutputs; uint256 tmppegstxid,tmptokenid; CPubKey mypk; + + if (pk2.IsValid()) GetTokensCCaddress1of2(cp,coinaddr,pk1,pk2); + else GetTokensCCaddress(cp,coinaddr,pk1); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - if ( inputs > nValue ) - CCchange = (inputs - nValue - txfee); - if ( CCchange != 0 ) - mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,CCchange,Pegspk)); - mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - fprintf(stderr,"start at %u\n",(uint32_t)time(NULL)); - j = rand() & 0xfffffff; - for (i=0; i<1000000; i++,j++) + txid = it->first.txhash; + vout = (int32_t)it->first.index; + // no need to prevent dup + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - tmpmtx = mtx; - rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_PEGS << (uint8_t)'G' << j)); - if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 ) + if (myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 && DecodePegsOpRet(vintx,tmppegstxid,tmptokenid)!=0 && tmppegstxid==pegstxid && tmptokenid==tokenid) { - len >>= 1; - decode_hex(buf,len,(char *)rawhex.c_str()); - hash = bits256_doublesha256(0,buf,len); - if ( (hash.bytes[0] & 0xff) == 0 && (hash.bytes[31] & 0xff) == 0 ) - { - fprintf(stderr,"found valid txid after %d iterations %u\n",i,(uint32_t)time(NULL)); - return(rawhex); - } - //fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]); + if ( total != 0 && maxinputs != 0 ) + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + nValue = it->second.satoshis; + totalinputs += nValue; + n++; + if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + break; } } - fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL)); - return(""); - } else fprintf(stderr,"cant find Pegs inputs\n"); + } + if (pk2.IsValid()) + { + mypk = pubkey2pk(Mypubkey()); + if (mypk!=pk1 && mypk!=pk2) + { + CCaddrTokens1of2set(cp,pk1,pk2,cp->CCpriv,coinaddr); + } + else + { + uint8_t mypriv[32]; + Myprivkey(mypriv); + CCaddrTokens1of2set(cp,pk1,pk2,mypriv,coinaddr); + } + } + return(totalinputs); +} + +std::string PegsDecodeAccountTx(CTransaction tx,CPubKey& pk,int64_t &amount,std::pair &account) +{ + uint256 hashBlock,tokenid,pegstxid; int32_t numvouts=tx.vout.size(); char funcid; + + if ((funcid=DecodePegsOpRet(tx,pegstxid,tokenid))!=0) + { + switch(funcid) + { + case 'F': if (DecodePegsFundOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account)=='F') return("fund"); + break; + case 'G': if (DecodePegsGetOpRet(tx,pegstxid,tokenid,pk,amount,account)=='G') return("get"); + break; + case 'R': if (DecodePegsRedeemOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account)=='R') return("redeem"); + break; + case 'E': if (DecodePegsExchangeOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account)=='R') return("exchange"); + break; + case 'L': if (DecodePegsLiquidateOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account)=='L') return("liquidate"); + break; + } + } + return (""); +} + +char PegsFindAccount(struct CCcontract_info *cp,CPubKey pk,uint256 pegstxid, uint256 tokenid, uint256 &accounttxid, std::pair &account) +{ + char coinaddr[64]; int64_t nValue,tmpamount; uint256 txid,hashBlock,tmptokenid,tmppegstxid; + CTransaction tx,acctx; int32_t numvouts,vout,ratio; char funcid,f; CPubKey pegspk,tmppk; + std::vector > unspentOutputs; + ImportProof proof; CTransaction burntx; std::vector payouts; + + accounttxid=zeroid; + pegspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,pk,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "txid=" << txid.GetHex() << ", vout=" << vout << ", nValue=" << nValue << std::endl); + if (vout == 1 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts=tx.vout.size())>0 && + (f=DecodePegsOpRet(tx,tmppegstxid,tmptokenid))!=0 && pegstxid==tmppegstxid && tokenid==tmptokenid) + { + accounttxid=txid; + funcid=f; + acctx=tx; + } + } + if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) + { + accounttxid=zeroid; + BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) + { + const CTransaction &txmempool = e.GetTx(); + const uint256 &hash = txmempool.GetHash(); + + if ((numvouts=txmempool.vout.size()) > 0 && (f=DecodePegsOpRet(txmempool,tmppegstxid,tmptokenid))!=0 && pegstxid==tmppegstxid && tokenid==tmptokenid) + { + funcid=f; + accounttxid=hash; + acctx=txmempool; + } + } + } + if (accounttxid!=zeroid) + { + PegsDecodeAccountTx(acctx,tmppk,tmpamount,account); + return(funcid); + } + else return(0); +} + +double PegsGetTokenPrice(uint256 tokenid) +{ + int64_t *tokensyn,*btcusd; double price; CTransaction tokentx; uint256 hashBlock; + std::string name,desc; std::vector vorigpubkey; int32_t numvouts; + + if (GetTransaction(tokenid,tokentx,hashBlock,false)!=0 && (numvouts=tokentx.vout.size())>0 && DecodeTokenCreateOpRet(tokentx.vout[numvouts-1].scriptPubKey,vorigpubkey,name,desc)=='c') + { + tokensyn = (int64_t *)calloc(sizeof(*tokensyn) * 3, 1 + PRICES_DAYWINDOW * 2 + PRICES_SMOOTHWIDTH); + btcusd = (int64_t *)calloc(sizeof(*btcusd) * 3, 1 + PRICES_DAYWINDOW * 2 + PRICES_SMOOTHWIDTH); + if (komodo_priceget(tokensyn, komodo_priceind((name+"_BTC").c_str()), komodo_currentheight(), 1) >= 0 && komodo_priceget(btcusd, komodo_priceind("BTC_USD"), komodo_currentheight(), 1) >= 0) + { + price=tokensyn[2]*btcusd[2]; + price=price/COIN/COIN; + return (price); + } + } + return (0); +} + +std::string PegsGetTokenName(uint256 tokenid) +{ + CTransaction tokentx; uint256 hashBlock; std::string name,desc; std::vector vorigpubkey; int32_t numvouts; + + if (GetTransaction(tokenid,tokentx,hashBlock,false)!=0 && (numvouts=tokentx.vout.size())>0 && DecodeTokenCreateOpRet(tokentx.vout[numvouts-1].scriptPubKey,vorigpubkey,name,desc)=='c') + { + return (name); + } + CCerror = strprintf("cant find token create or invalid tokenid %s",tokenid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } -std::string PegsFund(uint64_t txfee,int64_t funds) +double PegsGetAccountRatio(uint256 pegstxid,uint256 tokenid,uint256 accounttxid) +{ + int64_t amount; uint256 hashBlock,tmptokenid,tmppegstxid; + CTransaction tx; int32_t numvouts; char funcid; CPubKey pk; + std::pair account; struct CCcontract_info *cp,C; + + cp = CCinit(&C,EVAL_PEGS); + if (GetTransaction(accounttxid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + (funcid=DecodePegsOpRet(tx,tmppegstxid,tmptokenid))!=0 && pegstxid==tmppegstxid && tokenid==tmptokenid) + { + PegsDecodeAccountTx(tx,pk,amount,account); + return ((double)account.second*100/(account.first*PegsGetTokenPrice(tokenid))); + } + return (0); +} + +double PegsGetGlobalRatio(uint256 pegstxid) +{ + char coinaddr[64]; int64_t nValue,amount,globaldebt=0; uint256 txid,accounttxid,hashBlock,tmppegstxid,tokenid; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,pk; + std::vector > unspentOutputs; std::pair account; + std::map> globalaccounts; double globaldeposit=0; + struct CCcontract_info *cp,C; + + cp = CCinit(&C,EVAL_PEGS); + pegspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,pegspk,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if (vout == 0 && nValue == CC_MARKER_VALUE && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + (funcid=DecodePegsOpRet(tx,tmppegstxid,tokenid))!=0 && pegstxid==tmppegstxid && (funcid=='F' || funcid=='G' || funcid=='E')) + { + PegsDecodeAccountTx(tx,pk,amount,account); + globalaccounts[tokenid].first+=account.first; + globalaccounts[tokenid].second+=account.second; + } + } + unspentOutputs.clear(); + GetTokensCCaddress(cp,coinaddr,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if (GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && DecodePegsOpRet(tx,tmppegstxid,tokenid)!=0 && pegstxid==tmppegstxid) + { + globalaccounts[tokenid].first+=nValue; + } + } + for (std::map>::iterator it = globalaccounts.begin(); it != globalaccounts.end(); ++it) + { + globaldeposit+=globalaccounts[it->first].first*PegsGetTokenPrice(it->first); + globaldebt+=globalaccounts[it->first].second; + } + if (globaldebt>0) return ((double)globaldebt*100/globaldeposit); + return (0); +} + +std::string PegsFindBestAccount(struct CCcontract_info *cp,uint256 pegstxid, uint256 tokenid, int64_t tokenamount,uint256 &accounttxid, std::pair &account) +{ + char coinaddr[64]; int64_t nValue,tmpamount; uint256 txid,hashBlock,tmptokenid,tmppegstxid; + CTransaction tx,acctx; int32_t numvouts,vout; char funcid,f; CPubKey pegspk,tmppk; + std::vector > unspentOutputs; + ImportProof proof; CTransaction burntx; std::vector payouts; double ratio,maxratio=0; + std::pair tmpaccount; + + accounttxid=zeroid; + pegspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,pegspk,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "txid=" << txid.GetHex() << ", vout=" << vout << ", nValue=" << nValue << std::endl); + if (vout == 0 && nValue == CC_MARKER_VALUE && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,0) == 0 && + (ratio=PegsGetAccountRatio(pegstxid,tokenid,txid))>(ASSETCHAINS_PEGSCCPARAMS[2]?ASSETCHAINS_PEGSCCPARAMS[2]:PEGS_ACCOUNT_YELLOW_ZONE) && ratio>maxratio) + { + if (myGetTransaction(txid,tx,hashBlock)!=0 && !PegsDecodeAccountTx(tx,tmppk,tmpamount,tmpaccount).empty() && tmpaccount.first>=tokenamount) + { + accounttxid=txid; + acctx=tx; + maxratio=ratio; + } + } + } + if (accounttxid!=zeroid) + { + return(PegsDecodeAccountTx(acctx,tmppk,tmpamount,account)); + } + else return(""); +} + +std::string PegsCreate(uint64_t txfee,int64_t amount, std::vector bindtxids) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,Pegspk; CScript opret; struct CCcontract_info *cp,C; + CPubKey mypk,pegspk; struct CCcontract_info *cp,C; CTransaction tx; int32_t numvouts; int64_t totalsupply; std::string coin; + char depositaddr[64]; uint256 txid,hashBlock,tmptokenid,oracletxid; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + cp = CCinit(&C,EVAL_PEGS); if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); - Pegspk = GetUnspendable(cp,0); - if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 ) + pegspk = GetUnspendable(cp,0); + for(auto txid : bindtxids) { - mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,funds,Pegspk)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); + if (GetTransaction(txid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') + { + CCerror = strprintf("invalid bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } } + if ( AddNormalinputs(mtx,mypk,amount,64) >= amount ) + { + for (int i=0; i<100; i++) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,(amount-txfee)/100,pegspk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePegsCreateOpRet(bindtxids))); + } + CCerror = strprintf("error adding normal inputs"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } -UniValue PegsInfo() +std::string PegsFund(uint64_t txfee,uint256 pegstxid, uint256 tokenid,int64_t amount) { - UniValue result(UniValue::VOBJ); char numstr[64]; - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey Pegspk; struct CCcontract_info *cp,C; int64_t funding; - result.push_back(Pair("result","success")); - result.push_back(Pair("name","Pegs")); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string coin; + CTransaction pegstx,tx; int32_t numvouts; int64_t totalsupply,balance=0,funds=0,tokenfunds=0; uint256 accounttxid=zeroid,hashBlock,txid,tmptokenid,oracletxid; + CPubKey mypk,pegspk,tmppk; struct CCcontract_info *cp,*cpTokens,CTokens,C; char depositaddr[64],coinaddr[64]; std::pair account(0,0); + uint8_t M,N,taddr,prefix,prefix2,wiftype,mypriv[32]; std::vector pubkeys; bool found=false; std::vector bindtxids; + cp = CCinit(&C,EVAL_PEGS); - Pegspk = GetUnspendable(cp,0); - funding = AddPegsInputs(cp,mtx,Pegspk,0,0); - sprintf(numstr,"%.8f",(double)funding/COIN); - result.push_back(Pair("funding",numstr)); + cpTokens = CCinit(&CTokens,EVAL_TOKENS); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + + pegspk = GetUnspendable(cp,0); + if (GetTransaction(pegstxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find pegstxid %s",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') + { + CCerror = strprintf("invalid pegstxid ",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + for(auto txid : bindtxids) + { + if (GetTransaction(txid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') + { + CCerror = strprintf("invalid bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (tmptokenid==tokenid) + { + found=true; + break; + } + } + if (!found) + { + CCerror = strprintf("invalid tokenid ",tokenid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ((balance=GetTokenBalance(mypk,tokenid))>=amount) + { + PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,account); + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "current accounttxid=" << accounttxid.GetHex() << " [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) + { + CCerror = strprintf("previous account tx not yet confirmed"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (accounttxid!=zeroid && (funds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))>=txfee) + { + funds+=2*CC_MARKER_VALUE; + mtx.vin.push_back(CTxIn(accounttxid,0,CScript())); + Myprivkey(mypriv); + mtx.vin.push_back(CTxIn(accounttxid,1,CScript())); + GetCCaddress1of2(cp,coinaddr,mypk,pegspk); + CCaddr1of2set(cp,mypk,pegspk,mypriv,coinaddr); + } + else funds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee+2*CC_MARKER_VALUE,3); + if (funds>=txfee+2*CC_MARKER_VALUE) + { + if ((tokenfunds=AddTokenCCInputs(cpTokens,mtx,mypk,tokenid,amount,64))>=amount) + { + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,mypk,pegspk)); + mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_PEGS,amount,mypk,pegspk)); + if (tokenfunds-amount>0) mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS,tokenfunds-amount,mypk)); + if (funds>txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,funds-(txfee+2*CC_MARKER_VALUE),pegspk)); + account.first+=amount; + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePegsFundOpRet(tokenid,pegstxid,mypk,amount,account))); + } + } + else + { + CCerror = strprintf("not enough balance in pegs global CC address"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + + } + CCerror = strprintf("not enough balance (%lld) for this amount of tokens %lld",balance,amount); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +std::string PegsGet(uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount) +{ + CMutableTransaction burntx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()),mtx; + CTransaction pegstx,tx; int32_t numvouts; int64_t funds=0; uint256 accounttxid=zeroid,hashBlock,pricestxid; char coinaddr[64]; + CPubKey mypk,pegspk,tmppk; struct CCcontract_info *cp,C; std::pair account(0,0); uint8_t mypriv[32]; + std::vector dummyproof; std::vector vouts; std::vector bindtxids; CScript opret; + + cp = CCinit(&C,EVAL_PEGS); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + if (GetTransaction(pegstxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find pegstxid %s",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') + { + CCerror = strprintf("invalid pegstxid ",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,account)==0) + { + CCerror = strprintf("cannot find account from which to issue coins, fund account first with pegsfund!"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) + { + CCerror = strprintf("previous account tx not yet confirmed"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "current accounttxid=" << accounttxid.GetHex() << " [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + // spending markers + vouts.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); + vouts.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,mypk,pegspk)); + // coin issue + vouts.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + account.second+=amount; + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + // burn tx does not exist in pegs method but it must be created in order for import validation to pass + // fictive burntx input of previous account state tx + burntx.vin.push_back(CTxIn(accounttxid,0,CScript())); + // fictive output of coins in burn tx + burntx.vout.push_back(MakeBurnOutput(amount,0xffffffff,"PEGSCC",vouts,dummyproof,pegstxid,tokenid,mypk,amount,account)); + std::vector leaftxids; + BitcoinGetProofMerkleRoot(dummyproof, leaftxids); + MerkleBranch newBranch(0, leaftxids); + TxProof txProof = std::make_pair(burntx.GetHash(), newBranch); + mtx=MakePegsImportCoinTransaction(txProof,burntx,vouts); + Myprivkey(mypriv); + GetCCaddress1of2(cp,coinaddr,mypk,pegspk); + CCaddr1of2set(cp,mypk,pegspk,mypriv,coinaddr); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); +} + +std::string PegsRedeem(uint64_t txfee,uint256 pegstxid, uint256 tokenid) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string coin; + CTransaction pegstx,tx; int32_t numvouts; int64_t totalsupply,pegsfunds=0,funds=0,tokenfunds=0,amount; uint256 accounttxid=zeroid,hashBlock,txid,tmptokenid,oracletxid; + CPubKey mypk,pegspk,tmppk; struct CCcontract_info *cp,*cpTokens,CTokens,C; char depositaddr[64],coinaddr[64]; std::pair account(0,0); + uint8_t M,N,taddr,prefix,prefix2,wiftype,mypriv[32]; std::vector pubkeys; bool found=false; std::vector bindtxids; + + cp = CCinit(&C,EVAL_PEGS); + cpTokens = CCinit(&CTokens,EVAL_TOKENS); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + if (GetTransaction(pegstxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find pegstxid %s",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') + { + CCerror = strprintf("invalid pegstxid ",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + for(auto txid : bindtxids) + { + if (GetTransaction(txid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') + { + CCerror = strprintf("invalid bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (tmptokenid==tokenid) + { + found=true; + break; + } + } + if (!found) + { + CCerror = strprintf("invalid tokenid ",tokenid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,account)==0) + { + CCerror = strprintf("cannot find account from which to redeem tokens!"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) + { + CCerror = strprintf("previous account tx not yet confirmed"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "current accounttxid=" << accounttxid.GetHex() << " [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + if ((funds=AddNormalinputs(mtx,mypk,account.second,64))>=account.second ) + { + if (accounttxid!=zeroid && (pegsfunds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))>=txfee) + { + pegsfunds+=2*CC_MARKER_VALUE; + mtx.vin.push_back(CTxIn(accounttxid,0,CScript())); + mtx.vin.push_back(CTxIn(accounttxid,1,CScript())); + Myprivkey(mypriv); + GetCCaddress1of2(cp,coinaddr,mypk,pegspk); + CCaddr1of2set(cp,mypk,pegspk,mypriv,coinaddr); + amount=account.first; + if ((tokenfunds=AddPegsTokenInputs(cp,mtx,pegstxid,tokenid,mypk,pegspk,amount,64))>=amount) + { + if (pegsfunds>=txfee+2*CC_MARKER_VALUE) + { + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,mypk,pegspk)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS,amount,mypk)); + mtx.vout.push_back(CTxOut(account.second,CScript() << ParseHex(HexStr(CCtxidaddr(coinaddr,pegstxid))) << OP_CHECKSIG)); + if (pegsfunds>txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-(txfee+2*CC_MARKER_VALUE),pegspk)); + account.first=0; + account.second=0; + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePegsReedemOpRet(tokenid,pegstxid,mypk,amount,account))); + } + else + { + CCerror = strprintf("not enough balance in pegs global CC address"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + CCerror = strprintf("not enough tokens in pegs account (%lld) to redeem this amount of tokens %lld",tokenfunds,account.first); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else + { + CCerror = strprintf("not enough balance in pegs global CC address"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + CCerror = strprintf("to redeem from account and close it you must redeem full debt ammount %lld instead of %lld",account.second,funds); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + + +std::string PegsExchange(uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string coin; + CTransaction pegstx,tx; int32_t numvouts; int64_t totalsupply,pegsfunds=0,funds=0,tokenfunds=0,tokenamount,tmpamount; uint256 accounttxid=zeroid,hashBlock,txid,tmptokenid,oracletxid; + CPubKey mypk,pegspk,tmppk; struct CCcontract_info *cp,*cpTokens,CTokens,C; char depositaddr[64],coinaddr[64]; std::pair account(0,0); + uint8_t M,N,taddr,prefix,prefix2,wiftype,mypriv[32]; std::vector pubkeys; bool found=false; std::vector bindtxids; + + cp = CCinit(&C,EVAL_PEGS); + cpTokens = CCinit(&CTokens,EVAL_TOKENS); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + if (GetTransaction(pegstxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find pegstxid %s",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') + { + CCerror = strprintf("invalid pegstxid ",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + for(auto txid : bindtxids) + { + if (GetTransaction(txid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') + { + CCerror = strprintf("invalid bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (tmptokenid==tokenid) + { + found=true; + break; + } + } + if (!found) + { + CCerror = strprintf("invalid tokenid ",tokenid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,account)!=0) + { + CCerror = strprintf("you have active account, please close account first before exchanging other coins!"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ((funds=AddNormalinputs(mtx,mypk,amount,64))>=amount ) + { + if ((pegsfunds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))>=txfee) + { + tokenamount=amount/PegsGetTokenPrice(tokenid); + tokenfunds=AddPegsTokenInputs(cp,mtx,pegstxid,tokenid,pegspk,CPubKey(),tokenamount,64); + if (tokenfundsCCpriv,coinaddr); + pegsfunds+=2*CC_MARKER_VALUE; + } + if (tokenfunds>=tokenamount) + { + if (accounttxid!=zeroid) + { + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,tmppk,pegspk)); + } + if ((accounttxid!=zeroid && pegsfunds>=txfee+2*CC_MARKER_VALUE) || pegsfunds>=txfee) + { + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS,tokenamount,mypk)); + mtx.vout.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(CCtxidaddr(coinaddr,pegstxid))) << OP_CHECKSIG)); + if (tokenfunds>tokenamount) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_PEGS,tokenfunds-tokenamount,tmppk,pegspk)); + if (accounttxid!=zeroid) + { + if (pegsfunds>txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-(txfee+2*CC_MARKER_VALUE),pegspk)); + account.first=account.first-tokenamount; + account.second=account.second-amount; + } + else if (pegsfunds>txfee) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-txfee,pegspk)); + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "modified account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePegsExchangeOpRet(tokenid,pegstxid,mypk,tmppk,amount,account))); + } + else + { + CCerror = strprintf("not enough balance in pegs global CC address"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + CCerror = strprintf("not enough tokens in pegs account (%lld) to exchange to this amount of tokens %lld",tokenfunds,tokenamount); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else + { + CCerror = strprintf("not enough balance in pegs global CC address"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + CCerror = strprintf("not enough funds to exchange %lld coins to tokens - balance %lld",amount,funds); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +std::string PegsLiquidate(uint64_t txfee,uint256 pegstxid, uint256 tokenid, uint256 liquidatetxid) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string coin; + CTransaction pegstx,tx; int32_t numvouts; int64_t totalsupply,pegsfunds=0,funds=0,tokenfunds=0,amount,burnamount; + CPubKey mypk,pegspk,tmppk; struct CCcontract_info *cp,*cpTokens,CTokens,C; char depositaddr[64],coinaddr[64]; std::pair account(0,0),myaccount(0,0); + uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; bool found=false; std::vector bindtxids; + uint256 hashBlock,txid,tmptokenid,oracletxid,accounttxid; + + cp = CCinit(&C,EVAL_PEGS); + cpTokens = CCinit(&CTokens,EVAL_TOKENS); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + if (GetTransaction(pegstxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find pegstxid %s",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') + { + CCerror = strprintf("invalid pegstxid ",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + for(auto txid : bindtxids) + { + if (GetTransaction(txid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') + { + CCerror = strprintf("invalid bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (tmptokenid==tokenid) + { + found=true; + break; + } + } + if (!found) + { + CCerror = strprintf("invalid tokenid ",tokenid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,myaccount)==0) + { + CCerror = strprintf("cannot find account, you must have an account to liquidate another account!"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) + { + CCerror = strprintf("previous account tx not yet confirmed"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (PegsGetAccountRatio(pegstxid,tokenid,liquidatetxid)<(ASSETCHAINS_PEGSCCPARAMS[0]?ASSETCHAINS_PEGSCCPARAMS[0]:PEGS_ACCOUNT_THRESHOLD) || PegsGetGlobalRatio(pegstxid)<(ASSETCHAINS_PEGSCCPARAMS[1]?ASSETCHAINS_PEGSCCPARAMS[1]:PEGS_GLOBAL_THRESHOLD)) + { + CCerror = strprintf("not able to liquidate account until account ratio > %lu%% and global ratio > %lu%%",(ASSETCHAINS_PEGSCCPARAMS[0]?ASSETCHAINS_PEGSCCPARAMS[0]:PEGS_ACCOUNT_THRESHOLD),(ASSETCHAINS_PEGSCCPARAMS[1]?ASSETCHAINS_PEGSCCPARAMS[1]:PEGS_GLOBAL_THRESHOLD)); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (liquidatetxid!=zeroid && GetTransaction(liquidatetxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0 || PegsDecodeAccountTx(tx,tmppk,amount,account).empty()) + { + CCerror = strprintf("cannot find account to liquidate or invalid tx %s!",liquidatetxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (liquidatetxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,liquidatetxid,1) != 0) + { + CCerror = strprintf("previous liquidate account tx not yet confirmed"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "current accounttxid=" << accounttxid.GetHex() << " [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + amount=account.first; + burnamount=account.second*0.9; + if ((funds=AddNormalinputs(mtx,mypk,txfee+account.second,64))>=txfee+burnamount) + { + if (liquidatetxid!=zeroid && (pegsfunds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))>=txfee) + { + pegsfunds+=2*CC_MARKER_VALUE; + mtx.vin.push_back(CTxIn(liquidatetxid,0,CScript())); + mtx.vin.push_back(CTxIn(liquidatetxid,1,CScript())); + GetCCaddress1of2(cp,coinaddr,tmppk,pegspk); + CCaddr1of2set(cp,tmppk,pegspk,cp->CCpriv,coinaddr); + if ((tokenfunds=AddPegsTokenInputs(cp,mtx,pegstxid,tokenid,tmppk,pegspk,amount,64))==amount) + { + if (pegsfunds>=txfee+2*CC_MARKER_VALUE) + { + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,tmppk,pegspk)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS,(int64_t)(amount*0.95),mypk)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_PEGS,amount-(int64_t)(amount*0.95),pegspk)); + mtx.vout.push_back(CTxOut(burnamount,CScript() << ParseHex(HexStr(CCtxidaddr(coinaddr,pegstxid))) << OP_CHECKSIG)); + if (pegsfunds>txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-(txfee+2*CC_MARKER_VALUE),pegspk)); + account.first=0; + account.second=0; + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePegsLiquidateOpRet(tokenid,pegstxid,mypk,amount,account))); + } + CCerror = strprintf("not enough balance in pegs global CC address"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + CCerror = strprintf("tokens amount in pegs account (%lld) not matching amount in account %lld",tokenfunds,account.first); // this shouldn't happen + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + CCerror = strprintf("not enough balance in pegs global CC address"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + CCerror = strprintf("not enough funds to liquidate account, you must liquidate full debt ammount %lld instead of %lld",txfee+account.second,funds); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +UniValue PegsAccountHistory(uint256 pegstxid) +{ + char coinaddr[64]; int64_t nValue,amount; uint256 txid,accounttxid,hashBlock,tmptokenid,tmppegstxid; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,pk; std::map> accounts; + std::vector > txids; std::pair account; + UniValue result(UniValue::VOBJ),acc(UniValue::VARR); struct CCcontract_info *cp,C; + + result.push_back(Pair("result","success")); + result.push_back(Pair("name","pegsaccounthistory")); + cp = CCinit(&C,EVAL_PEGS); + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,mypk,pegspk); + SetCCtxids(txids,coinaddr,true); + for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second; + if (vout == 1 && nValue == CC_MARKER_VALUE && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + (funcid=DecodePegsOpRet(tx,tmppegstxid,tmptokenid))!=0 && pegstxid==tmppegstxid) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("action",PegsDecodeAccountTx(tx,pk,amount,account))); + obj.push_back(Pair("amount",amount)); + obj.push_back(Pair("accounttxid",txid.GetHex())); + obj.push_back(Pair("token",PegsGetTokenName(tmptokenid))); + obj.push_back(Pair("deposit",account.first)); + obj.push_back(Pair("debt",account.second)); + acc.push_back(obj); + } + } + result.push_back(Pair("account history",acc)); return(result); } +UniValue PegsAccountInfo(uint256 pegstxid) +{ + char coinaddr[64]; int64_t nValue,amount; uint256 txid,accounttxid,hashBlock,tmptokenid,tmppegstxid; std::map> accounts; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,pk; + std::vector > unspentOutputs; std::pair account; + UniValue result(UniValue::VOBJ),acc(UniValue::VARR); struct CCcontract_info *cp,C; + + result.push_back(Pair("result","success")); + result.push_back(Pair("name","pegsaccountinfo")); + cp = CCinit(&C,EVAL_PEGS); + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,mypk,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + //LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "txid=" << txid.GetHex() << ", vout=" << vout << ", nValue=" << nValue << std::endl); + if (vout == 1 && nValue == CC_MARKER_VALUE && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + (funcid=DecodePegsOpRet(tx,tmppegstxid,tmptokenid))!=0 && pegstxid==tmppegstxid) + { + //LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "txid=" << txid.GetHex() << ", vout=" << vout << ", nValue=" << nValue << ", tokenid=" << tmptokenid.GetHex() << std::endl); + PegsDecodeAccountTx(tx,pk,amount,account); + accounts[tmptokenid].first=account.first; + accounts[tmptokenid].second=account.second; + } + } + for (std::map>::iterator it = accounts.begin(); it != accounts.end(); ++it) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("token",PegsGetTokenName(it->first))); + obj.push_back(Pair("deposit",(double)accounts[it->first].first/COIN)); + obj.push_back(Pair("debt",(double)accounts[it->first].second/COIN)); + if (accounts[it->first].first==0 || accounts[it->first].second==0 || PegsGetTokenPrice(it->first)==0) obj.push_back(Pair("ratio",0)); + else obj.push_back(Pair("ratio",strprintf("%.2f%%",(double)accounts[it->first].second*100/(accounts[it->first].first*PegsGetTokenPrice(it->first))))); + acc.push_back(obj); + } + result.push_back(Pair("account info",acc)); + return(result); +} + +UniValue PegsWorstAccounts(uint256 pegstxid) +{ + char coinaddr[64]; int64_t nValue,amount; uint256 txid,accounttxid,hashBlock,tmppegstxid,tokenid,prev; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,pk; double ratio; + std::vector > unspentOutputs; std::pair account; + UniValue result(UniValue::VOBJ),acc(UniValue::VARR); struct CCcontract_info *cp,C; std::multimap map; + + result.push_back(Pair("result","success")); + result.push_back(Pair("name","pegsworstaccounts")); + cp = CCinit(&C,EVAL_PEGS); + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,pegspk,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if (vout == 0 && nValue == CC_MARKER_VALUE && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + (funcid=DecodePegsOpRet(tx,tmppegstxid,tokenid))!=0 && pegstxid==tmppegstxid) + { + PegsDecodeAccountTx(tx,pk,amount,account); + if (account.first==0 || account.second==0 || PegsGetTokenPrice(tokenid)==0) ratio=0; + else ratio=(double)account.second*100/(account.first*PegsGetTokenPrice(tokenid)); + if (ratio>80) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("accounttxid",txid.GetHex())); + obj.push_back(Pair("deposit",account.first)); + obj.push_back(Pair("debt",account.second)); + obj.push_back(Pair("ratio",strprintf("%.2f%%",ratio))); + map.insert(std::pair(tokenid,obj)); + } + } + } + std::multimap::iterator it = map.begin(); + for (prev=it->first; it != map.end(); ++it) + { + if (it->first!=prev) + { + result.push_back(Pair(PegsGetTokenName(prev),acc)); + acc.clear(); + prev=it->first; + } + acc.push_back(it->second); + } + result.push_back(Pair(PegsGetTokenName(prev),acc)); + return(result); +} + +UniValue PegsInfo(uint256 pegstxid) +{ + char coinaddr[64]; int64_t nValue,amount; uint256 txid,accounttxid,hashBlock,tmppegstxid,tokenid; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,pk; + std::vector > unspentOutputs; std::pair account; + std::map> globalaccounts; double globaldeposit=0; + UniValue result(UniValue::VOBJ),acc(UniValue::VARR); struct CCcontract_info *cp,C; + + result.push_back(Pair("result","success")); + result.push_back(Pair("name","pegsinfo")); + cp = CCinit(&C,EVAL_PEGS); + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,pegspk,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if (vout == 0 && nValue == CC_MARKER_VALUE && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + (funcid=DecodePegsOpRet(tx,tmppegstxid,tokenid))!=0 && pegstxid==tmppegstxid) + { + PegsDecodeAccountTx(tx,pk,amount,account); + globalaccounts[tokenid].first+=account.first; + globalaccounts[tokenid].second+=account.second; + } + } + unspentOutputs.clear(); + GetTokensCCaddress(cp,coinaddr,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if (GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && DecodePegsOpRet(tx,tmppegstxid,tokenid)!=0 && pegstxid==tmppegstxid) + { + globalaccounts[tokenid].first+=nValue; + } + } + for (std::map>::iterator it = globalaccounts.begin(); it != globalaccounts.end(); ++it) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("token",PegsGetTokenName(it->first))); + obj.push_back(Pair("total deposit",globalaccounts[it->first].first)); + obj.push_back(Pair("total debt",globalaccounts[it->first].second)); + if (globalaccounts[it->first].first==0 || globalaccounts[it->first].second==0 || PegsGetTokenPrice(it->first)==0) obj.push_back(Pair("total ratio",0)); + else obj.push_back(Pair("total ratio",strprintf("%.2f%%",(double)globalaccounts[it->first].second*100/(globalaccounts[it->first].first*PegsGetTokenPrice(it->first))))); + acc.push_back(obj); + } + result.push_back(Pair("info",acc)); + result.push_back(Pair("global ratio",strprintf("%.2f%%",PegsGetGlobalRatio(pegstxid)))); + return(result); +} diff --git a/src/coins.cpp b/src/coins.cpp index f97ba5eee..92206b653 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -604,6 +604,11 @@ CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTr return 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.IsPegsImport() && i==0) + { + nResult = GetCoinImportValue(tx); + continue; + } value = GetOutputFor(tx.vin[i]).nValue; nResult += value; #ifdef KOMODO_ENABLE_INTEREST @@ -675,6 +680,7 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const { if (!tx.IsMint()) { for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.IsPegsImport() && i==0) continue; const COutPoint &prevout = tx.vin[i].prevout; const CCoins* coins = AccessCoins(prevout.hash); if (!coins || !coins->IsAvailable(prevout.n)) { @@ -696,7 +702,7 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const // use the maximum priority for all (partially or fully) shielded transactions. // (Note that coinbase transactions cannot contain JoinSplits, or Sapling shielded Spends or Outputs.) - if (tx.vjoinsplit.size() > 0 || tx.vShieldedSpend.size() > 0 || tx.vShieldedOutput.size() > 0 || tx.IsCoinImport()) { + if (tx.vjoinsplit.size() > 0 || tx.vShieldedSpend.size() > 0 || tx.vShieldedOutput.size() > 0 || tx.IsCoinImport() || tx.IsPegsImport()) { return MAX_PRIORITY; } 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/importcoin.cpp b/src/importcoin.cpp index 921d9a745..c3da613c8 100644 --- a/src/importcoin.cpp +++ b/src/importcoin.cpp @@ -75,6 +75,18 @@ CTransaction MakeImportCoinTransaction(const ImportProof proof, const CTransacti return CTransaction(mtx); } +CTransaction MakePegsImportCoinTransaction(const ImportProof proof, const CTransaction burnTx, const std::vector payouts, uint32_t nExpiryHeightOverride) +{ + CMutableTransaction mtx; uint256 accounttxid,pegstxid,tokenid; CScript opret; CScript scriptSig; + + mtx=MakeImportCoinTransaction(proof,burnTx,payouts); + // for spending markers in import tx - to track account state + accounttxid=burnTx.vin[0].prevout.hash; + mtx.vin.push_back(CTxIn(accounttxid,0,CScript())); + mtx.vin.push_back(CTxIn(accounttxid,1,CScript())); + return (mtx); +} + CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, const std::string targetSymbol, const std::vector payouts, const std::vector rawproof) { @@ -123,13 +135,30 @@ CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymb return CTxOut(value, CScript() << OP_RETURN << opret); } +CTxOut MakeBurnOutput(CAmount value,uint32_t targetCCid,std::string targetSymbol,const std::vector payouts,std::vector rawproof,uint256 pegstxid, + uint256 tokenid,CPubKey srcpub,int64_t amount,std::pair account) +{ + std::vector opret; + opret = E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN; + ss << VARINT(targetCCid); + ss << targetSymbol; + ss << SerializeHash(payouts); + ss << rawproof; + ss << pegstxid; + ss << tokenid; + ss << srcpub; + ss << amount; + ss << account); + return CTxOut(value, CScript() << OP_RETURN << opret); +} + bool UnmarshalImportTx(const CTransaction importTx, ImportProof &proof, CTransaction &burnTx, std::vector &payouts) { if (importTx.vout.size() < 1) return false; - if (importTx.vin.size() != 1 || importTx.vin[0].scriptSig != (CScript() << E_MARSHAL(ss << EVAL_IMPORTCOIN))) { + if ((!importTx.IsPegsImport() && importTx.vin.size() != 1) || importTx.vin[0].scriptSig != (CScript() << E_MARSHAL(ss << EVAL_IMPORTCOIN))) { LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalImportTx() incorrect import tx vin" << std::endl); return false; } @@ -263,17 +292,35 @@ bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector> amount)); } +bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &pegstxid,uint256 &tokenid,CPubKey &srcpub, int64_t &amount,std::pair &account) +{ + std::vector burnOpret,rawproof; bool isEof=true; + uint32_t targetCCid; uint256 payoutsHash; std::string targetSymbol; + uint8_t evalCode; + + + if (burnTx.vout.size() == 0) return false; + GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret); + return (E_UNMARSHAL(burnOpret, ss >> evalCode; + ss >> VARINT(targetCCid); + ss >> targetSymbol; + ss >> payoutsHash; + ss >> rawproof; + ss >> pegstxid; + ss >> tokenid; + ss >> srcpub; + ss >> amount; + ss >> account)); +} /* * Required by main */ CAmount GetCoinImportValue(const CTransaction &tx) { - ImportProof proof; - CTransaction burnTx; - std::vector payouts; - + ImportProof proof; CTransaction burnTx; std::vector payouts; bool isNewImportTx = false; + if ((isNewImportTx = UnmarshalImportTx(tx, proof, burnTx, payouts))) { if (burnTx.vout.size() > 0) { vscript_t vburnOpret; diff --git a/src/importcoin.h b/src/importcoin.h index e016a7c52..955ead825 100644 --- a/src/importcoin.h +++ b/src/importcoin.h @@ -95,16 +95,20 @@ public: CAmount GetCoinImportValue(const CTransaction &tx); CTransaction MakeImportCoinTransaction(const ImportProof proof, const CTransaction burnTx, const std::vector payouts, uint32_t nExpiryHeightOverride = 0); +CTransaction MakePegsImportCoinTransaction(const ImportProof proof, const CTransaction burnTx, const std::vector payouts, uint32_t nExpiryHeightOverride = 0); CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, const std::string targetSymbol, const std::vector payouts, const std::vector rawproof); CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts,std::vector rawproof, uint256 bindtxid,std::vector publishers,std::vectortxids,uint256 burntxid,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub, int64_t amount); CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts,std::vector rawproof,std::string srcaddr, std::string receipt); +CTxOut MakeBurnOutput(CAmount value,uint32_t targetCCid,std::string targetSymbol,const std::vector payouts,std::vector rawproof,uint256 pegstxid, + uint256 tokenid,CPubKey srcpub,int64_t amount,std::pair account); bool UnmarshalBurnTx(const CTransaction burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector &rawproof); bool UnmarshalBurnTx(const CTransaction burnTx, std::string &srcaddr, std::string &receipt); bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector &publishers,std::vector &txids,uint256& burntxid,int32_t &height,int32_t &burnvout,std::string &rawburntx,CPubKey &destpub, int64_t &amount); +bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &pegstxid,uint256 &tokenid,CPubKey &srcpub,int64_t &amount,std::pair &account); bool UnmarshalImportTx(const CTransaction importTx, ImportProof &proof, CTransaction &burnTx,std::vector &payouts); bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state); diff --git a/src/init.cpp b/src/init.cpp index 107e3e476..4adde6681 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 @@ -1142,9 +1147,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Option to startup with mocktime set (used for regression testing): SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op - if (GetBoolArg("-peerbloomfilters", true)) - nLocalServices |= NODE_BLOOM; - + if ( KOMODO_NSPV == 0 ) + { + if (GetBoolArg("-peerbloomfilters", true)) + nLocalServices |= NODE_BLOOM; + } nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE); #ifdef ENABLE_MINING @@ -1299,9 +1306,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 +1487,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 +1910,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_bitcoind.h b/src/komodo_bitcoind.h index 48962b015..b3f870170 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -2051,7 +2051,7 @@ uint64_t komodo_checknotarypay(CBlock *pblock,int32_t height) total += txout.nValue; //fprintf(stderr, "MATCHED AmountPaid.%lu notaryid.%i\n",AmountToPay,NotarisationNotaries[n-1]); } - else fprintf(stderr, "NOT MATCHED AmountPaid.%lu AmountToPay.%lu notaryid.%i\n", pblock->vtx[0].vout[n].nValue, AmountToPay, NotarisationNotaries[n-1]); + else fprintf(stderr, "NOT MATCHED AmountPaid.%llu AmountToPay.%llu notaryid.%i\n", (long long)pblock->vtx[0].vout[n].nValue, (long long)AmountToPay, NotarisationNotaries[n-1]); } n++; } @@ -2347,7 +2347,7 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) numSN = komodo_notaries(notarypubkeys, height, pblock->nTime); if ( pblock->vtx[1].vin.size() < numSN/5 ) { - fprintf(stderr, "ht.%i does not meet minsigs.%i sigs.%li\n",height,numSN/5,pblock->vtx[1].vin.size()); + fprintf(stderr, "ht.%i does not meet minsigs.%i sigs.%lld\n",height,numSN/5,(long long)pblock->vtx[1].vin.size()); return(-1); } } diff --git a/src/komodo_defs.h b/src/komodo_defs.h index 5074b4303..001ee0dce 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -17,6 +17,7 @@ #define KOMODO_DEFS_H #include "komodo_nk.h" +#define KOMODO_EARLYTXID_HEIGHT 200 #define ASSETCHAINS_MINHEIGHT 128 #define ASSETCHAINS_MAX_ERAS 7 #define KOMODO_ELECTION_GAP 2000 @@ -271,7 +272,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 +336,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_gateway.h b/src/komodo_gateway.h index 9a80e5e24..5c60503bf 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -652,6 +652,7 @@ const char *banned_txids[] = //"01d8c839463bda2f2f6400ede4611357913684927a767422a8560ead1b22557c", //"6e4980a9e1bd669f4df04732dc6f11b7773b6de88d1abcf89a6b9007d72ef9ac", //"6cc1d0495170bc0e11fd3925297623562e529ea1336b66ea61f8a1159041aed2", + //"250875424cece9bcd98cb226b09da7671625633d6958589e3a462bad89ad87cc", // missed }; int32_t komodo_checkvout(int32_t vout,int32_t k,int32_t indallvouts) @@ -668,7 +669,7 @@ int32_t komodo_bannedset(int32_t *indallvoutsp,uint256 *array,int32_t max) int32_t i; if ( sizeof(banned_txids)/sizeof(*banned_txids) > max ) { - fprintf(stderr,"komodo_bannedset: buffer too small %ld vs %d\n",sizeof(banned_txids)/sizeof(*banned_txids),max); + fprintf(stderr,"komodo_bannedset: buffer too small %d vs %d\n",(int32_t)(sizeof(banned_txids)/sizeof(*banned_txids)),max); StartShutdown(); } for (i=0; i indcounter %u, prevpos100 %u offset.%ld\n",datalen,validated,indcounter,prevpos100,indcounter * sizeof(uint32_t)); + fprintf(stderr,"datalen.%ld validated %ld -> indcounter %u, prevpos100 %u offset.%d\n",datalen,validated,indcounter,prevpos100,(int32_t)(indcounter * sizeof(uint32_t))); if ( fpos < datalen ) { fseek(indfp,indcounter * sizeof(uint32_t),SEEK_SET); diff --git a/src/komodo_globals.h b/src/komodo_globals.h index 9ffa147ea..533faf981 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; @@ -74,7 +74,7 @@ uint64_t ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF; uint64_t ASSETCHAINS_TIMEUNLOCKFROM = 0, ASSETCHAINS_TIMEUNLOCKTO = 0,ASSETCHAINS_CBOPRET=0; uint64_t ASSETCHAINS_LASTERA = 1; -uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_DECAY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS]; +uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_DECAY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_PEGSCCPARAMS[3]; uint8_t ASSETCHAINS_CCDISABLES[256]; std::vector ASSETCHAINS_PRICES,ASSETCHAINS_STOCKS; diff --git a/src/komodo_nSPV.h b/src/komodo_nSPV.h new file mode 100644 index 000000000..c8338d365 --- /dev/null +++ b/src/komodo_nSPV.h @@ -0,0 +1,582 @@ + +/****************************************************************************** + * 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 + +// 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) +// bug: under load, fullnode was returning all 0 nServices + +#ifndef KOMODO_NSPV_H +#define KOMODO_NSPV_H + +#define NSPV_POLLITERS 15 +#define NSPV_POLLMICROS 100000 +#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,int64_t extradata,uint32_t tiptime,int64_t &rewardsum); +extern uint256 SIG_TXHASH; +uint32_t NSPV_blocktime(int32_t hdrheight); + +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; uint8_t CCflag,pad8; +}; + +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->CCflag),&ptr->CCflag); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad8),&ptr->pad8); + 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 reqheight; +}; + +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); + len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->reqheight),&ptr->reqheight); + return(len); +} + +void NSPV_ntzsresp_copy(struct NSPV_ntzsresp *dest,struct NSPV_ntzsresp *ptr) +{ + *dest = *ptr; +} + +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_copy(struct NSPV_txproof *dest,struct NSPV_txproof *ptr) +{ + *dest = *ptr; + if ( ptr->tx != 0 ) + { + dest->tx = (uint8_t *)malloc(ptr->txlen); + memcpy(dest->tx,ptr->tx,ptr->txlen); + } + if ( ptr->txproof != 0 ) + { + dest->txproof = (uint8_t *)malloc(ptr->txprooflen); + memcpy(dest->txproof,ptr->txproof,ptr->txprooflen); + } +} + +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_copy(struct NSPV_ntzsproofresp *dest,struct NSPV_ntzsproofresp *ptr) +{ + *dest = *ptr; + if ( ptr->common.hdrs != 0 ) + { + dest->common.hdrs = (struct NSPV_equihdr *)malloc(ptr->common.numhdrs * sizeof(*ptr->common.hdrs)); + memcpy(dest->common.hdrs,ptr->common.hdrs,ptr->common.numhdrs * sizeof(*ptr->common.hdrs)); + } + if ( ptr->prevntz != 0 ) + { + dest->prevntz = (uint8_t *)malloc(ptr->prevtxlen); + memcpy(dest->prevntz,ptr->prevntz,ptr->prevtxlen); + } + if ( ptr->nextntz != 0 ) + { + dest->nextntz = (uint8_t *)malloc(ptr->nexttxlen); + memcpy(dest->nextntz,ptr->nextntz,ptr->nexttxlen); + } +} + +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); +} + +bool NSPV_SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey,uint32_t nTime); + +int32_t NSPV_fastnotariescount(CTransaction tx,uint8_t elected[64][33],uint32_t nTime) +{ + CPubKey pubkeys[64]; uint8_t sig[512]; CScript scriptPubKeys[64]; CMutableTransaction mtx(tx); int32_t vini,j,siglen,retval; uint64_t mask = 0; char *str; std::vector> vData; + for (j=0; j<64; j++) + { + pubkeys[j] = buf2pk(elected[j]); + scriptPubKeys[j] = (CScript() << ParseHex(HexStr(pubkeys[j])) << OP_CHECKSIG); + //fprintf(stderr,"%d %s\n",j,HexStr(pubkeys[j]).c_str()); + } + fprintf(stderr,"txid %s\n",tx.GetHash().GetHex().c_str()); + //for (vini=0; vini 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; uint32_t nTime; + 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 ) + { + //sleep(1); // needed to avoid no pnodes error + *desttxidp = NSPV_opretextract(ntzheightp,blockhashp,symbol,opret,tx.GetHash()); + nTime = NSPV_blocktime(*ntzheightp); + komodo_notaries(elected,*ntzheightp,nTime); + if ( verifyntz != 0 && (numsigs= NSPV_fastnotariescount(tx,elected,nTime)) < 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..bbc1712b8 --- /dev/null +++ b/src/komodo_nSPV_fullnode.h @@ -0,0 +1,522 @@ + +/****************************************************************************** + * 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 origreqheight) +{ + struct NSPV_ntzargs prev,next; int32_t reqheight = origreqheight; + if ( reqheight < chainActive.LastTip()->GetHeight() ) + reqheight++; + if ( NSPV_notarized_bracket(&prev,&next,reqheight) == 0 ) + { + if ( prev.ntzheight != 0 ) + { + ptr->reqheight = origreqheight; + 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,bool isCC) // 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,isCC); + maxlen = MAX_BLOCK_SIZE(tipheight) - 512; + maxlen /= sizeof(*ptr->utxos); + strncpy(ptr->coinaddr,coinaddr,sizeof(ptr->coinaddr)-1); + ptr->CCflag = isCC; + 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,"try to addmempool transaction %s\n",ptr->txid.GetHex().c_str()); + if ( myAddtomempool(tx) != 0 ) + { + ptr->retcode = 1; + //int32_t i; + //for (i=0; itxid.GetHex().c_str(),ptr->retcode); + RelayTransaction(tx); + } else ptr->retcode = -3; + + } 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 || request[1] == len-3) ) + { + uint8_t isCC = 0; + memcpy(coinaddr,&request[2],request[1]); + coinaddr[request[1]] = 0; + if ( request[1] == len-3 ) + isCC = (request[len-1] != 0); + if ( isCC != 0 ) + fprintf(stderr,"%s isCC.%d\n",coinaddr,isCC); + memset(&U,0,sizeof(U)); + if ( (slen= NSPV_getaddressutxos(&U,coinaddr,isCC)) > 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..b31115079 --- /dev/null +++ b/src/komodo_nSPV_superlite.h @@ -0,0 +1,690 @@ + +/****************************************************************************** + * 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,NSPV_tiptime; +CKey NSPV_key; +char NSPV_wifstr[64],NSPV_pubkeystr[67],NSPV_lastpeer[128]; +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; + +struct NSPV_ntzsresp NSPV_ntzsresp_cache[NSPV_MAXVINS]; +struct NSPV_ntzsproofresp NSPV_ntzsproofresp_cache[NSPV_MAXVINS * 2]; +struct NSPV_txproof NSPV_txproof_cache[NSPV_MAXVINS * 4]; + +struct NSPV_ntzsresp *NSPV_ntzsresp_find(int32_t reqheight) +{ + int32_t i; + for (i=0; ireqheight); + return(&NSPV_ntzsresp_cache[i]); +} + +struct NSPV_txproof *NSPV_txproof_find(uint256 txid) +{ + int32_t i; + for (i=0; itxid.GetHex().c_str()); + return(&NSPV_txproof_cache[i]); +} + +struct NSPV_ntzsproofresp *NSPV_ntzsproof_find(uint256 prevtxid,uint256 nexttxid) +{ + int32_t i; + for (i=0; iprevtxid.GetHex().c_str(),ptr->nexttxid.GetHex().c_str()); + return(&NSPV_ntzsproofresp_cache[i]); +} + +// 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); + strncpy(NSPV_lastpeer,pfrom->addr.ToString().c_str(),sizeof(NSPV_lastpeer)-1); + 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; + // need to validate new header to make sure it is valid mainchain + if ( NSPV_inforesult.height == NSPV_inforesult.hdrheight ) + NSPV_tiptime = NSPV_inforesult.H.nTime; + } + 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); + if ( NSPV_ntzsresp_find(NSPV_ntzsresult.reqheight) == 0 ) + NSPV_ntzsresp_add(&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); + if ( NSPV_ntzsproof_find(NSPV_ntzsproofresult.prevtxid,NSPV_ntzsproofresult.nexttxid) == 0 ) + NSPV_ntzsproof_add(&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); + if ( NSPV_txproof_find(NSPV_txproofresult.txid) == 0 ) + NSPV_txproof_add(&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 ( KOMODO_NSPV == 0 ) + return(0); + 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); + if ( (0) && KOMODO_NSPV != 0 ) + 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_ntzsproofresp_cache,0,sizeof(NSPV_ntzsproofresp_cache)); + memset(NSPV_txproof_cache,0,sizeof(NSPV_txproof_cache)); + memset(NSPV_ntzsresp_cache,0,sizeof(NSPV_ntzsresp_cache)); + 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)); + result.push_back(Pair("lastpeer",NSPV_lastpeer)); + 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)); + result.push_back(Pair("lastpeer",NSPV_lastpeer)); + 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))); + result.push_back(Pair("lastpeer",NSPV_lastpeer)); + 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("isCC",ptr->CCflag)); + 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)); + result.push_back(Pair("lastpeer",NSPV_lastpeer)); + return(result); +} + +UniValue NSPV_ntzsresp_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))); + result.push_back(Pair("lastpeer",NSPV_lastpeer)); + 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))); + result.push_back(Pair("lastpeer",NSPV_lastpeer)); + //fprintf(stderr,"ntzs_proof %s %d, %s %d\n",ptr->prevtxid.GetHex().c_str(),ptr->common.prevht,ptr->nexttxid.GetHex().c_str(),ptr->common.nextht); + 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; + case -3: result.push_back(Pair("type","error adding to mempool")); break; + default: result.push_back(Pair("type","unknown")); break; + } + result.push_back(Pair("lastpeer",NSPV_lastpeer)); + 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 0 ) + { + NSPV_getinfo_req(hdrheight); + if ( NSPV_inforesult.hdrheight == hdrheight ) + { + timestamp = NSPV_inforesult.H.nTime; + NSPV_inforesult = old; + fprintf(stderr,"NSPV_blocktime ht.%d -> t%u\n",hdrheight,timestamp); + return(timestamp); + } + } + NSPV_inforesult = old; + return(0); +} + +UniValue NSPV_addressutxos(char *coinaddr,int32_t CCflag) +{ + UniValue result(UniValue::VOBJ); uint8_t msg[64]; int32_t i,iter,slen,len = 0; + //fprintf(stderr,"utxos %s NSPV addr %s\n",coinaddr,NSPV_address.c_str()); + if ( NSPV_utxosresult.nodeheight >= NSPV_inforesult.height && strcmp(coinaddr,NSPV_utxosresult.coinaddr) == 0 && CCflag == NSPV_utxosresult.CCflag ) + return(NSPV_utxosresp_json(&NSPV_utxosresult)); + NSPV_utxosresp_purge(&NSPV_utxosresult); + if ( bitcoin_base58decode(msg,coinaddr) != 25 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid address")); + return(result); + } + slen = (int32_t)strlen(coinaddr); + msg[len++] = NSPV_UTXOS; + msg[len++] = slen; + memcpy(&msg[len],coinaddr,slen), len += slen; + msg[len++] = (CCflag != 0); + for (iter=0; iter<3; iter++); + if ( NSPV_req(0,msg,len,NODE_ADDRINDEX,msg[0]>>1) != 0 ) + { + for (i=0; i= NSPV_inforesult.height && strcmp(coinaddr,NSPV_utxosresult.coinaddr) == 0 && CCflag == NSPV_utxosresult.CCflag ) + 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 reqheight) +{ + uint8_t msg[64]; int32_t i,iter,len = 0; struct NSPV_ntzsresp N,*ptr; + if ( (ptr= NSPV_ntzsresp_find(reqheight)) != 0 ) + { + fprintf(stderr,"FROM CACHE NSPV_notarizations.%d\n",reqheight); + NSPV_ntzsresp_purge(&NSPV_ntzsresult); + NSPV_ntzsresp_copy(&NSPV_ntzsresult,ptr); + return(NSPV_ntzsresp_json(ptr)); + } + msg[len++] = NSPV_NTZS; + len += iguana_rwnum(1,&msg[len],sizeof(reqheight),&reqheight); + for (iter=0; iter<3; iter++); + if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) + { + for (i=0; iprevtxid.GetHex().c_str(),ptr->nexttxid.GetHex().c_str()); + NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresult); + NSPV_ntzsproofresp_copy(&NSPV_ntzsproofresult,ptr); + return(NSPV_ntzsproof_json(ptr)); + } + 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 ) + { + fprintf(stderr,"next.%d prev.%d -> %d vs %d\n",ptr->common.nextht,ptr->common.prevht,ptr->common.nextht-ptr->common.prevht+1,ptr->common.numhdrs); + return(-2); + } + else if ( NSPV_txextract(tx,ptr->nextntz,ptr->nexttxlen) < 0 ) + return(-3); + else if ( tx.GetHash() != ptr->nexttxid ) + return(-4); + else if ( NSPV_notarizationextract(1,&height,&blockhash,&desttxid,tx) < 0 ) + return(-5); + else if ( height != ptr->common.nextht ) + return(-6); + else if ( NSPV_hdrhash(&ptr->common.hdrs[ptr->common.numhdrs-1]) != blockhash ) + return(-7); + 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-13); + } + sleep(1); + if ( NSPV_txextract(tx,ptr->prevntz,ptr->prevtxlen) < 0 ) + return(-8); + else if ( tx.GetHash() != ptr->prevtxid ) + return(-9); + else if ( NSPV_notarizationextract(1,&height,&blockhash,&desttxid,tx) < 0 ) + return(-10); + else if ( height != ptr->common.prevht ) + return(-11); + else if ( NSPV_hdrhash(&ptr->common.hdrs[0]) != blockhash ) + return(-12); + return(0); +} + +int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int32_t height,CTransaction &tx,int64_t extradata,uint32_t tiptime,int64_t &rewardsum) +{ + struct NSPV_txproof *ptr; int32_t i,offset,retval = 0; int64_t rewards = 0; uint32_t nLockTime; std::vector proof; + if ( (ptr= NSPV_txproof_find(txid)) == 0 ) + { + NSPV_txproof(vout,txid,height); + ptr = &NSPV_txproofresult; + } + if ( ptr->txid != txid ) + { + fprintf(stderr,"txproof error %s != %s\n",ptr->txid.GetHex().c_str(),txid.GetHex().c_str()); + return(-1); + } + else if ( NSPV_txextract(tx,ptr->tx,ptr->txlen) < 0 || ptr->txlen <= 0 ) + retval = -2000; + else if ( skipvalidation == 0 && ptr->unspentvalue <= 0 ) + retval = -2001; + else if ( ASSETCHAINS_SYMBOL[0] == 0 && extradata >= 0 && tiptime != 0 ) + { + rewards = komodo_interestnew(height,tx.vout[vout].nValue,tx.nLockTime,tiptime); + if ( rewards != extradata ) + fprintf(stderr,"extradata %.8f vs rewards %.8f\n",dstr(extradata),dstr(rewards)); + rewardsum += rewards; + } + + if ( skipvalidation == 0 ) + { + if ( ptr->txprooflen > 0 ) + { + proof.resize(ptr->txprooflen); + memcpy(&proof[0],ptr->txproof,ptr->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 ) + { + //fprintf(stderr,"call NSPV_txidhdrsproof %s %s\n",NSPV_ntzsresult.prevntz.txid.GetHex().c_str(),NSPV_ntzsresult.nextntz.txid.GetHex().c_str()); + NSPV_txidhdrsproof(NSPV_ntzsresult.prevntz.txid,NSPV_ntzsresult.nextntz.txid); + usleep(10000); + 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",(int32_t)proof.size(),proofroot.GetHex().c_str(),NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot.GetHex().c_str()); + retval = -2003; + } + } + } else retval = -2002; + } else retval = -2004; + } + return(retval); +} + +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,uint32_t nTime) +{ + CTransaction txNewConst(mtx); SignatureData sigdata; CBasicKeyStore keystore; int64_t branchid = NSPV_BRANCHID; + 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(0,utxovout,mtx.vin[i].prevout.hash,used[i].height,vintx,used[i].extradata,NSPV_tiptime,rewardsum); + 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) == 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 ( NSPV_utxosresult.CCflag != 0 || strcmp(NSPV_utxosresult.coinaddr,srcaddr) != 0 || NSPV_utxosresult.nodeheight < NSPV_inforesult.height ) + NSPV_addressutxos(srcaddr,0); + if ( NSPV_utxosresult.CCflag != 0 || 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; int64_t rewardsum=0,interestsum=0; + 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(rewardsum,interestsum,retcodes,mtx,txfee,opret,used); + if ( ASSETCHAINS_SYMBOL[0] == 0 ) + { + char numstr[64]; + sprintf(numstr,"%.8f",(double)interestsum/COIN); + result.push_back(Pair("rewards",numstr)); + sprintf(numstr,"%.8f",(double)rewardsum/COIN); + result.push_back(Pair("validated",numstr)); + } + 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 57d652309..0821017ff 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1692,9 +1692,12 @@ int8_t equihash_params_possible(uint64_t n, uint64_t k) 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}; + 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}; CTransaction earlytx; uint256 hashBlock; + 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 ) @@ -1784,7 +1787,7 @@ void komodo_args(char *argv0) { printf("KOMODO_REWIND %d\n",KOMODO_REWIND); } - KOMODO_EARLYTXID = Parseuint256(GetArg("-earlytxid","0").c_str()); + KOMODO_EARLYTXID = Parseuint256(GetArg("-earlytxid","0").c_str()); ASSETCHAINS_EARLYTXIDCONTRACT = GetArg("-ac_earlytxidcontract",0); if ( name.c_str()[0] != 0 ) { @@ -1938,7 +1941,7 @@ void komodo_args(char *argv0) } }*/ } - if ( ASSETCHAINS_BEAMPORT != 0 && ASSETCHAINS_CODAPORT != 0 ) + if ( ASSETCHAINS_BEAMPORT != 0 ) { fprintf(stderr,"can only have one of -ac_beam or -ac_coda\n"); StartShutdown(); @@ -1952,18 +1955,33 @@ void komodo_args(char *argv0) StartShutdown(); } } - else if ( ASSETCHAINS_SELFIMPORT == "BEAM" && ASSETCHAINS_BEAMPORT == 0 ) + else if ( ASSETCHAINS_SELFIMPORT == "BEAM" ) { - fprintf(stderr,"missing -ac_beam for BEAM rpcport\n"); - StartShutdown(); + if (ASSETCHAINS_BEAMPORT == 0) + { + fprintf(stderr,"missing -ac_beam for BEAM rpcport\n"); + StartShutdown(); + } } - else if ( ASSETCHAINS_SELFIMPORT == "CODA" && ASSETCHAINS_CODAPORT == 0 ) + else if ( ASSETCHAINS_SELFIMPORT == "CODA" ) { - fprintf(stderr,"missing -ac_coda for CODA rpcport\n"); - StartShutdown(); + if (ASSETCHAINS_CODAPORT == 0) + { + fprintf(stderr,"missing -ac_coda for CODA rpcport\n"); + StartShutdown(); + } + } + else if ( ASSETCHAINS_SELFIMPORT == "PEGSCC") + { + Split(GetArg("-ac_pegsccparams",""), ASSETCHAINS_PEGSCCPARAMS, 0); + if (ASSETCHAINS_ENDSUBSIDY[0]!=1 || ASSETCHAINS_COMMISSION!=0) + { + fprintf(stderr,"when using import for pegsCC these must be set: -ac_end=1 -ac_perc=0\n"); + StartShutdown(); + } } // else it can be gateway coin - else if (!ASSETCHAINS_SELFIMPORT.empty() && (ASSETCHAINS_ENDSUBSIDY[0]!=1 || ASSETCHAINS_SUPPLY>10 || ASSETCHAINS_COMMISSION!=0)) + else if (!ASSETCHAINS_SELFIMPORT.empty() && (ASSETCHAINS_ENDSUBSIDY[0]!=1 || ASSETCHAINS_SUPPLY>0 || ASSETCHAINS_COMMISSION!=0)) { fprintf(stderr,"when using gateway import these must be set: -ac_end=1 -ac_supply=0 -ac_perc=0\n"); StartShutdown(); @@ -2101,7 +2119,7 @@ void komodo_args(char *argv0) extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_FOUNDERS),(void *)&ASSETCHAINS_FOUNDERS); if ( ASSETCHAINS_FOUNDERS_REWARD != 0 ) { - fprintf(stderr, "set founders reward.%li\n",ASSETCHAINS_FOUNDERS_REWARD); + fprintf(stderr, "set founders reward.%lld\n",(long long)ASSETCHAINS_FOUNDERS_REWARD); extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_FOUNDERS_REWARD),(void *)&ASSETCHAINS_FOUNDERS_REWARD); } } @@ -2324,7 +2342,7 @@ void komodo_args(char *argv0) if ( strcmp("PIRATE",ASSETCHAINS_SYMBOL) == 0 && ASSETCHAINS_HALVING[0] == 77777 ) { ASSETCHAINS_HALVING[0] *= 5; - fprintf(stderr,"PIRATE halving changed to %d %.1f days ASSETCHAINS_LASTERA.%lu\n",(int32_t)ASSETCHAINS_HALVING[0],(double)ASSETCHAINS_HALVING[0]/1440,ASSETCHAINS_LASTERA); + fprintf(stderr,"PIRATE halving changed to %d %.1f days ASSETCHAINS_LASTERA.%llu\n",(int32_t)ASSETCHAINS_HALVING[0],(double)ASSETCHAINS_HALVING[0]/1440,(long long)ASSETCHAINS_LASTERA); } else if ( strcmp("VRSC",ASSETCHAINS_SYMBOL) == 0 ) dpowconfs = 0; diff --git a/src/main.cpp b/src/main.cpp index e01cba61f..a8ff33c2b 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) @@ -735,6 +738,7 @@ bool komodo_dailysnapshot(int32_t height) for (unsigned int j = tx.vin.size(); j-- > 0;) { uint256 blockhash; CTransaction txin; + if (tx.IsPegsImport() && j==0) continue; if ( !tx.IsCoinImport() && !tx.IsCoinBase() && myGetTransaction(tx.vin[j].prevout.hash,txin,blockhash) ) { int vout = tx.vin[j].prevout.n; @@ -758,7 +762,7 @@ bool komodo_dailysnapshot(int32_t height) // include only top 3999 address. if ( vAddressSnapshot.size() > 3999 ) vAddressSnapshot.resize(3999); lastSnapShotHeight = undo_height; - fprintf(stderr, "vAddressSnapshot.size.%li\n", vAddressSnapshot.size()); + fprintf(stderr, "vAddressSnapshot.size.%d\n", (int32_t)vAddressSnapshot.size()); return true; } @@ -1012,6 +1016,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.IsPegsImport() && i==0) continue; const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]); vector > vSolutions; @@ -1089,6 +1094,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in unsigned int nSigOps = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.IsPegsImport() && i==0) continue; const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]); if (prevout.scriptPubKey.IsPayToScriptHash()) nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); @@ -1100,7 +1106,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 +1147,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 +1164,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 +1300,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 +1807,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"); } @@ -1873,7 +1879,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return state.Invalid(false, REJECT_DUPLICATE, "already have coins"); } - if (tx.IsCoinImport()) + if (tx.IsCoinImport() || tx.IsPegsImport()) { // Inverse of normal case; if input exists, it's been spent if (ExistsImportTombstone(tx, view)) @@ -1945,7 +1951,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. bool fSpendsCoinbase = false; - if (!fSkipExpiry && !tx.IsCoinImport()) { + if (!fSkipExpiry && !tx.IsCoinImport() && !tx.IsPegsImport()) { BOOST_FOREACH(const CTxIn &txin, tx.vin) { const CCoins *coins = view.AccessCoins(txin.prevout.hash); if (coins->IsCoinBase()) { @@ -1985,7 +1991,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Continuously rate-limit free (really, very-low-fee) transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make others' transactions take longer to confirm. - if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize) && !tx.IsCoinImport()) + if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize) && !tx.IsCoinImport() && !tx.IsPegsImport()) { static CCriticalSection csFreeLimiter; static double dFreeCount; @@ -2008,7 +2014,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa dFreeCount += nSize; } - if (!tx.IsCoinImport() && fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000 && nFees > nValueOut/19) + if (!tx.IsCoinImport() && !tx.IsPegsImport() && fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000 && nFees > nValueOut/19) { string errmsg = strprintf("absurdly high fees %s, %d > %d", hash.ToString(), @@ -2689,6 +2695,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund { txundo.vprevout.reserve(tx.vin.size()); BOOST_FOREACH(const CTxIn &txin, tx.vin) { + if (tx.IsPegsImport() && txin.prevout.n==10e8) continue; CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash); unsigned nPos = txin.prevout.n; @@ -2712,7 +2719,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); // add outputs // Unorthodox state - if (tx.IsCoinImport()) { + if (tx.IsCoinImport() || tx.IsPegsImport()) { // add a tombstone for the burnTx AddImportTombstone(tx, inputs, nHeight); } @@ -2756,6 +2763,11 @@ namespace Consensus { CAmount nFees = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.IsPegsImport() && i==0) + { + nValueIn=GetCoinImportValue(tx); + continue; + } const COutPoint &prevout = tx.vin[i].prevout; const CCoins *coins = inputs.AccessCoins(prevout.hash); assert(coins); @@ -2867,6 +2879,7 @@ bool ContextualCheckInputs( // still computed and checked, and any change will be caught at the next checkpoint. if (fScriptChecks) { for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.IsPegsImport() && i==0) continue; const COutPoint &prevout = tx.vin[i].prevout; const CCoins* coins = inputs.AccessCoins(prevout.hash); assert(coins); @@ -2902,7 +2915,7 @@ bool ContextualCheckInputs( } } - if (tx.IsCoinImport()) + if (tx.IsCoinImport() || tx.IsPegsImport()) { LOCK(cs_main); ServerTransactionSignatureChecker checker(&tx, 0, 0, false, txdata); @@ -3188,10 +3201,12 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex // restore inputs if (!tx.IsMint()) { - const CTxUndo &txundo = blockUndo.vtxundo[i-1]; + CTxUndo &txundo = blockUndo.vtxundo[i-1]; + if (tx.IsPegsImport()) txundo.vprevout.insert(txundo.vprevout.begin(),CTxInUndo()); if (txundo.vprevout.size() != tx.vin.size()) return error("DisconnectBlock(): transaction and undo data inconsistent"); for (unsigned int j = tx.vin.size(); j-- > 0;) { + if (tx.IsPegsImport() && j==0) continue; const COutPoint &out = tx.vin[j].prevout; const CTxInUndo &undo = txundo.vprevout[j]; if (!ApplyTxInUndo(undo, view, out)) @@ -3225,7 +3240,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } } } - else if (tx.IsCoinImport()) + else if (tx.IsCoinImport() || tx.IsPegsImport()) { RemoveImportTombstone(tx, view); } @@ -3375,6 +3390,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 +3416,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 ) @@ -3573,6 +3590,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin { for (size_t j = 0; j < tx.vin.size(); j++) { + if (tx.IsPegsImport() && j==0) continue; const CTxIn input = tx.vin[j]; const CTxOut &prevout = view.GetOutputFor(tx.vin[j]); @@ -3956,7 +3974,8 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { void FlushStateToDisk() { CValidationState state; - FlushStateToDisk(state, FLUSH_STATE_ALWAYS); + if ( KOMODO_NSPV == 0 ) + FlushStateToDisk(state, FLUSH_STATE_ALWAYS); } void PruneAndFlush() { @@ -4106,7 +4125,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 +4230,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 +4259,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 +4281,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 +4309,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: %d seconds\n", (int32_t)(time(NULL)-start)); } - fprintf(stderr, "snapshot completed in: %lu seconds\n", time(NULL)-start); } return true; } @@ -5278,7 +5310,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 +5321,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 +5490,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 +5758,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 +6472,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 +6510,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 +7060,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 +7230,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 +7244,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 nServices.%llx nSPV peer.%d\n",(long long)pfrom->nServices,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 +7342,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 +7871,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 +7895,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 +8248,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 +8261,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/miner.cpp b/src/miner.cpp index e4e907447..55e2c708f 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -323,6 +323,13 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 BOOST_FOREACH(const CTxIn& txin, tx.vin) { + if (tx.IsPegsImport() && txin.prevout.n==10e8) + { + CAmount nValueIn = GetCoinImportValue(tx); // burn amount + nTotalIn += nValueIn; + dPriority += (double)nValueIn * 1000; // flat multiplier... max = 1e16. + continue; + } // Read prev transaction if (!view.HaveCoins(txin.prevout.hash)) { diff --git a/src/net.cpp b/src/net.cpp index 83dc0782e..02cddd3e2 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] = {}; @@ -444,6 +444,8 @@ void CNode::CloseSocketDisconnect() vRecvMsg.clear(); } +extern int32_t KOMODO_NSPV; + void CNode::PushVersion() { int nBestHeight = g_signals.GetHeight().get_value_or(0); @@ -458,6 +460,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,"KOMODO_NSPV.%d PUSH services.%llx\n",KOMODO_NSPV,(long long)nLocalServices); } @@ -681,12 +684,6 @@ int CNetMessage::readData(const char *pch, unsigned int nBytes) - - - - - - // requires LOCK(cs_vSend) void SocketSendData(CNode *pnode) { @@ -1846,7 +1843,7 @@ bool StopNode() for (int i=0; ipost(); - if (fAddressesInitialized) + if (KOMODO_NSPV == 0 && fAddressesInitialized) { DumpAddresses(); fAddressesInitialized = false; @@ -1926,8 +1923,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/primitives/transaction.h b/src/primitives/transaction.h index d97ff03fb..0c20908b0 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -46,6 +46,7 @@ #include "zcash/Proof.hpp" extern uint32_t ASSETCHAINS_MAGIC; +extern std::string ASSETCHAINS_SELFIMPORT; // Overwinter transaction version static const int32_t OVERWINTER_TX_VERSION = 3; @@ -711,6 +712,11 @@ public: return (vin.size() == 1 && vin[0].prevout.n == 10e8); } + bool IsPegsImport() const + { + return (ASSETCHAINS_SELFIMPORT=="PEGSCC" && vin[0].prevout.n == 10e8); + } + friend bool operator==(const CTransaction& a, const CTransaction& b) { return a.hash == b.hash; 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..b00f8c6c5 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -206,7 +206,7 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue& UniValue in(UniValue::VOBJ); if (tx.IsCoinBase()) in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); - else if (tx.IsCoinImport()) { + else if (tx.IsCoinImport() && txin.prevout.n==10e8) { in.push_back(Pair("is_import", "1")); ImportProof proof; CTransaction burnTx; std::vector payouts; CTxDestination importaddress; if (UnmarshalImportTx(tx, proof, burnTx, payouts)) @@ -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..1c8a65fe5 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 }, @@ -554,6 +566,18 @@ static const CRPCCommand vRPCCommands[] = //{ "tokens", "tokenfillswap", &tokenfillswap, true }, { "tokens", "tokenconvert", &tokenconvert, true }, + // pegs + { "pegs", "pegscreate", &pegscreate, true }, + { "pegs", "pegsfund", &pegsfund, true }, + { "pegs", "pegsget", &pegsget, true }, + { "pegs", "pegsredeem", &pegsredeem, true }, + { "pegs", "pegsliquidate", &pegsliquidate, true }, + { "pegs", "pegsexchange", &pegsexchange, true }, + { "pegs", "pegsaccounthistory", &pegsaccounthistory, true }, + { "pegs", "pegsaccountinfo", &pegsaccountinfo, true }, + { "pegs", "pegsworstaccounts", &pegsworstaccounts, true }, + { "pegs", "pegsinfo", &pegsinfo, true }, + /* Address index */ { "addressindex", "getaddressmempool", &getaddressmempool, true }, { "addressindex", "getaddressutxos", &getaddressutxos, false }, diff --git a/src/rpc/server.h b/src/rpc/server.h index d447d2472..0e8c9db11 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -349,6 +349,16 @@ extern UniValue FSMcreate(const UniValue& params, bool fHelp); extern UniValue FSMlist(const UniValue& params, bool fHelp); extern UniValue FSMinfo(const UniValue& params, bool fHelp); extern UniValue auctionaddress(const UniValue& params, bool fHelp); +extern UniValue pegscreate(const UniValue& params, bool fHelp); +extern UniValue pegsfund(const UniValue& params, bool fHelp); +extern UniValue pegsget(const UniValue& params, bool fHelp); +extern UniValue pegsredeem(const UniValue& params, bool fHelp); +extern UniValue pegsliquidate(const UniValue& params, bool fHelp); +extern UniValue pegsexchange(const UniValue& params, bool fHelp); +extern UniValue pegsaccounthistory(const UniValue& params, bool fHelp); +extern UniValue pegsaccountinfo(const UniValue& params, bool fHelp); +extern UniValue pegsworstaccounts(const UniValue& params, bool fHelp); +extern UniValue pegsinfo(const UniValue& params, bool fHelp); extern UniValue getnewaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp //extern UniValue getnewaddress64(const UniValue& params, bool fHelp); // in rpcwallet.cpp @@ -455,6 +465,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/script/sign.cpp b/src/script/sign.cpp index 685018828..d0ae52b3a 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -35,24 +35,24 @@ using namespace std; typedef vector valtype; extern uint8_t ASSETCHAINS_TXPOW; +uint256 SIG_TXHASH; TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode, uint32_t consensusBranchId, CKey *pprivKey, void *extraData) const { - CKey key; - if (pprivKey) - key = *pprivKey; - else if (!keystore || !keystore->GetKey(address, key)) - return false; - - uint256 hash; + CKey key; uint256 hash; try { hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, consensusBranchId); } catch (logic_error ex) { return false; } - + SIG_TXHASH = hash; + if (pprivKey) + key = *pprivKey; + else if (!keystore || !keystore->GetKey(address, key)) + return false; + if (scriptCode.IsPayToCryptoCondition()) { CC *cc = (CC *)extraData; 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/txmempool.cpp b/src/txmempool.cpp index 2b38d7153..5f0fb2a47 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -121,7 +121,10 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, const CTransaction& tx = mapTx.find(hash)->GetTx(); if (!tx.IsCoinImport()) { for (unsigned int i = 0; i < tx.vin.size(); i++) + { + if (tx.IsPegsImport() && i==0) continue; mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i); + } } BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { @@ -147,6 +150,7 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC uint256 txhash = tx.GetHash(); for (unsigned int j = 0; j < tx.vin.size(); j++) { + if (tx.IsPegsImport() && j==0) continue; const CTxIn input = tx.vin[j]; const CTxOut &prevout = view.GetOutputFor(input); @@ -252,6 +256,7 @@ void CTxMemPool::addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCac uint256 txhash = tx.GetHash(); for (unsigned int j = 0; j < tx.vin.size(); j++) { + if (tx.IsPegsImport() && j==0) continue; const CTxIn input = tx.vin[j]; const CTxOut &prevout = view.GetOutputFor(input); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 563fc875c..82e44190c 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -89,6 +89,65 @@ std::string DecodeDumpString(const std::string &str) { return ret.str(); } +UniValue convertpassphrase(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + throw runtime_error( + "convertpassphrase \"agamapassphrase\"\n" + "\nConverts Agama passphrase to a private key and WIF (for import with importprivkey).\n" + "\nArguments:\n" + "1. \"agamapassphrase\" (string, required) Agama passphrase\n" + "\nResult:\n" + "\"agamapassphrase\": \"agamapassphrase\", (string) Agama passphrase you entered\n" + "\"address\": \"komodoaddress\", (string) Address corresponding to your passphrase\n" + "\"pubkey\": \"publickeyhex\", (string) The hex value of the raw public key\n" + "\"privkey\": \"privatekeyhex\", (string) The hex value of the raw private key\n" + "\"wif\": \"wif\" (string) The private key in WIF format to use with 'importprivkey'\n" + "\nExamples:\n" + + HelpExampleCli("convertpassphrase", "\"agamapassphrase\"") + + HelpExampleRpc("convertpassphrase", "\"agamapassphrase\"") + ); + + bool fCompressed = true; + string strAgamaPassphrase = params[0].get_str(); + + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("agamapassphrase", strAgamaPassphrase)); + + CKey tempkey = DecodeSecret(strAgamaPassphrase); + /* first we should check if user pass wif to method, instead of passphrase */ + if (!tempkey.IsValid()) { + /* it's a passphrase, not wif */ + uint256 sha256; + CSHA256().Write((const unsigned char *)strAgamaPassphrase.c_str(), strAgamaPassphrase.length()).Finalize(sha256.begin()); + std::vector privkey(sha256.begin(), sha256.begin() + sha256.size()); + privkey.front() &= 0xf8; + privkey.back() &= 0x7f; + privkey.back() |= 0x40; + CKey key; + key.Set(privkey.begin(),privkey.end(), fCompressed); + CPubKey pubkey = key.GetPubKey(); + assert(key.VerifyPubKey(pubkey)); + CKeyID vchAddress = pubkey.GetID(); + + ret.push_back(Pair("address", EncodeDestination(vchAddress))); + ret.push_back(Pair("pubkey", HexStr(pubkey))); + ret.push_back(Pair("privkey", HexStr(privkey))); + ret.push_back(Pair("wif", EncodeSecret(key))); + } else { + /* seems it's a wif */ + CPubKey pubkey = tempkey.GetPubKey(); + assert(tempkey.VerifyPubKey(pubkey)); + CKeyID vchAddress = pubkey.GetID(); + ret.push_back(Pair("address", EncodeDestination(vchAddress))); + ret.push_back(Pair("pubkey", HexStr(pubkey))); + ret.push_back(Pair("privkey", HexStr(tempkey))); + ret.push_back(Pair("wif", strAgamaPassphrase)); + } + + return ret; +} + UniValue importprivkey(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -912,3 +971,120 @@ 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,int32_t CCflag); +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) +{ + int32_t CCflag = 0; + if ( fHelp || params.size() > 2 ) + throw runtime_error("nspv_listunspent address [isCC]\n"); + if ( params.size() == 0 ) + { + if ( NSPV_address.size() != 0 ) + return(NSPV_addressutxos((char *)NSPV_address.c_str(),0)); + else throw runtime_error("nspv_listunspent address [isCC]\n"); + } + if ( params.size() >= 1 ) + { + if ( params.size() == 2 ) + CCflag = atoi((char *)params[1].get_str().c_str()); + return(NSPV_addressutxos((char *)params[0].get_str().c_str(),CCflag)); + } + else throw runtime_error("nspv_listunspent address [isCC]\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 5071f7935..5ee1c512e 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; @@ -5336,6 +5355,7 @@ int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits #include "../cc/CCHeir.h" #include "../cc/CCMarmara.h" #include "../cc/CCPayments.h" +#include "../cc/CCPegs.h" int32_t ensure_CCrequirements(uint8_t evalcode) { @@ -7973,10 +7993,209 @@ UniValue heirlist(const UniValue& params, bool fHelp) return (HeirList()); } +UniValue pegscreate(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); int32_t i; std::vector txids; + uint8_t N; std::string hex; uint256 txid; int64_t amount; + + if ( fHelp || params.size()<3) + throw runtime_error("pegscreate amount N bindtxid1 [bindtxid2 ...]\n"); + if ( ensure_CCrequirements(EVAL_PEGS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + amount = atof((char *)params[0].get_str().c_str()) * COIN + 0.00000000499999; + N = atoi((char *)params[1].get_str().c_str()); + if ( params.size() < N+1 ) + throw runtime_error("not enough parameters for N gatewaysbind\n"); + for (i=0; i 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt pegscreate"); + return(result); +} + +UniValue pegsfund(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string hex; uint256 pegstxid,tokenid; int64_t amount; + if ( fHelp || params.size()!=3) + throw runtime_error("pegsfund pegstxid tokenid amount\n"); + if ( ensure_CCrequirements(EVAL_PEGS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256(params[0].get_str().c_str()); + tokenid = Parseuint256(params[1].get_str().c_str()); + amount = atof((char *)params[2].get_str().c_str()) * COIN + 0.00000000499999; + hex = PegsFund(0,pegstxid,tokenid,amount); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt pegsfund"); + return(result); +} + +UniValue pegsget(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string hex; uint256 pegstxid,tokenid; int64_t amount; + + if ( fHelp || params.size()!=3) + throw runtime_error("pegsget pegstxid tokenid amount\n"); + if ( ensure_CCrequirements(EVAL_PEGS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256(params[0].get_str().c_str()); + tokenid = Parseuint256(params[1].get_str().c_str()); + amount = atof((char *)params[2].get_str().c_str()) * COIN + 0.00000000499999; + hex = PegsGet(0,pegstxid,tokenid,amount); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt pegsget"); + return(result); +} + +UniValue pegsredeem(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string hex; uint256 pegstxid,tokenid; int64_t amount; + + if ( fHelp || params.size()!=2) + throw runtime_error("pegsredem pegstxid tokenid\n"); + if ( ensure_CCrequirements(EVAL_PEGS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256(params[0].get_str().c_str()); + tokenid = Parseuint256(params[1].get_str().c_str()); + hex = PegsRedeem(0,pegstxid,tokenid); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt pegsredeem"); + return(result); +} + +UniValue pegsliquidate(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string hex; uint256 pegstxid,tokenid,accounttxid; + + if ( fHelp || params.size()!=3) + throw runtime_error("pegsliquidate pegstxid tokenid accounttxid\n"); + if ( ensure_CCrequirements(EVAL_PEGS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256(params[0].get_str().c_str()); + tokenid = Parseuint256(params[1].get_str().c_str()); + accounttxid = Parseuint256(params[2].get_str().c_str()); + hex = PegsLiquidate(0,pegstxid,tokenid,accounttxid); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt pegsliquidate"); + return(result); +} + +UniValue pegsexchange(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string hex; uint256 pegstxid,tokenid,accounttxid; int64_t amount; + + if ( fHelp || params.size()!=3) + throw runtime_error("pegsliquidate pegstxid tokenid accounttxid\n"); + if ( ensure_CCrequirements(EVAL_PEGS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256(params[0].get_str().c_str()); + tokenid = Parseuint256(params[1].get_str().c_str()); + amount = atof((char *)params[2].get_str().c_str()) * COIN + 0.00000000499999; + hex = PegsExchange(0,pegstxid,tokenid,amount); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt pegsliquidate"); + return(result); +} + +UniValue pegsaccounthistory(const UniValue& params, bool fHelp) +{ + uint256 pegstxid; + + if ( fHelp || params.size() != 1 ) + throw runtime_error("pegsaccounthistory pegstxid\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256((char *)params[0].get_str().c_str()); + return(PegsAccountHistory(pegstxid)); +} + +UniValue pegsaccountinfo(const UniValue& params, bool fHelp) +{ + uint256 pegstxid; + + if ( fHelp || params.size() != 1 ) + throw runtime_error("pegsaccountinfo pegstxid\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256((char *)params[0].get_str().c_str()); + return(PegsAccountInfo(pegstxid)); +} + +UniValue pegsworstaccounts(const UniValue& params, bool fHelp) +{ + uint256 pegstxid; + + if ( fHelp || params.size() != 1 ) + throw runtime_error("pegsworstaccounts pegstxid\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256((char *)params[0].get_str().c_str()); + return(PegsWorstAccounts(pegstxid)); +} + +UniValue pegsinfo(const UniValue& params, bool fHelp) +{ + uint256 pegstxid; + + if ( fHelp || params.size() != 1 ) + throw runtime_error("pegsinfo pegstxid\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256((char *)params[0].get_str().c_str()); + return(PegsInfo(pegstxid)); +} extern UniValue dumpprivkey(const UniValue& params, bool fHelp); // in rpcdump.cpp +extern UniValue convertpassphrase(const UniValue& params, bool fHelp); extern UniValue importprivkey(const UniValue& params, bool fHelp); extern UniValue importaddress(const UniValue& params, bool fHelp); extern UniValue dumpwallet(const UniValue& params, bool fHelp); @@ -8012,6 +8231,7 @@ static const CRPCCommand commands[] = { "wallet", "gettransaction", &gettransaction, false }, { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false }, { "wallet", "getwalletinfo", &getwalletinfo, false }, + { "wallet", "convertpassphrase", &convertpassphrase, true }, { "wallet", "importprivkey", &importprivkey, true }, { "wallet", "importwallet", &importwallet, true }, { "wallet", "importaddress", &importaddress, true },