diff --git a/qa/pull-tester/cc-tests.sh b/qa/pull-tester/cc-tests.sh index f2abde883..d68671e12 100755 --- a/qa/pull-tester/cc-tests.sh +++ b/qa/pull-tester/cc-tests.sh @@ -13,12 +13,14 @@ export BITCOIND=${REAL_BITCOIND} testScripts=( 'cryptoconditions_faucet.py' - 'cryptoconditions_channels.py' 'cryptoconditions_dice.py' 'cryptoconditions_oracles.py' 'cryptoconditions_rewards.py' 'cryptoconditions_token.py' #'cryptoconditions_gateways.py' + 'cryptoconditions_heir.py' + # TODO: cant reconnect nodes back in channels test because of crash (seems regtest only specific) + 'cryptoconditions_channels.py' ); extArg="-extended" diff --git a/qa/rpc-tests/cryptoconditions_channels.py b/qa/rpc-tests/cryptoconditions_channels.py index cec45687f..b2a49b477 100755 --- a/qa/rpc-tests/cryptoconditions_channels.py +++ b/qa/rpc-tests/cryptoconditions_channels.py @@ -4,6 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. +import time from test_framework.test_framework import CryptoconditionsTestFramework from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, assert_greater_than, \ @@ -71,6 +72,58 @@ class CryptoconditionsChannelsTest(CryptoconditionsTestFramework): result = rpc.channelsinfo(channel_txid) assert_equal(result["Transactions"][1]["Payment"], payment_tx_id) + # number of payments should be equal 1 (one denomination used) + result = rpc.channelsinfo(channel_txid)["Transactions"][1]["Number of payments"] + assert_equal(result, 1) + # payments left param should reduce 1 and be equal 9 now ( 10 - 1 = 9 ) + result = rpc.channelsinfo(channel_txid)["Transactions"][1]["Payments left"] + assert_equal(result, 9) + + # lets try payment with x2 amount to ensure that counters works correct + result = rpc.channelspayment(channel_txid, "200000") + assert_success(result) + payment_tx_id = self.send_and_mine(result["hex"], rpc) + assert payment_tx_id, "got txid" + + result = rpc.channelsinfo(channel_txid) + assert_equal(result["Transactions"][2]["Payment"], payment_tx_id) + + result = rpc.channelsinfo(channel_txid)["Transactions"][2]["Number of payments"] + assert_equal(result, 2) + + result = rpc.channelsinfo(channel_txid)["Transactions"][2]["Payments left"] + assert_equal(result, 7) + + # check if payment value really transferred + raw_transaction = rpc.getrawtransaction(payment_tx_id, 1) + + result = raw_transaction["vout"][3]["valueSat"] + assert_equal(result, 200000) + + result = rpc1.validateaddress(raw_transaction["vout"][3]["scriptPubKey"]["addresses"][0])["ismine"] + assert_equal(result, True) + + # have to check that second node have coins to cover txfee at least + rpc.sendtoaddress(rpc1.getnewaddress(), 1) + rpc.sendtoaddress(rpc1.getnewaddress(), 1) + rpc.generate(2) + self.sync_all() + result = rpc1.getbalance() + assert_greater_than(result, 0.1) + + # trying to initiate channels payment from node B without any secret + # TODO: have to add RPC validation + payment_hex = rpc1.channelspayment(channel_txid, "100000") + try: + result = rpc1.sendrawtransaction(payment_hex["hex"]) + except Exception as e: + pass + + # trying to initiate channels payment from node B with secret from previous payment + result = rpc1.channelspayment(channel_txid, "100000", rpc1.channelsinfo(channel_txid)["Transactions"][1]["Secret"]) + #result = rpc1.sendrawtransaction(payment_hex["hex"]) + assert_error(result) + # executing channel close result = rpc.channelsclose(channel_txid) assert_success(result) @@ -82,7 +135,7 @@ class CryptoconditionsChannelsTest(CryptoconditionsTestFramework): # now in channelinfo closed flag should appear result = rpc.channelsinfo(channel_txid) - assert_equal(result["Transactions"][2]["Close"], channel_close_txid) + assert_equal(result["Transactions"][3]["Close"], channel_close_txid) # executing channel refund result = rpc.channelsrefund(channel_txid, channel_close_txid) @@ -90,6 +143,92 @@ class CryptoconditionsChannelsTest(CryptoconditionsTestFramework): refund_txid = self.send_and_mine(result["hex"], rpc) assert refund_txid, "got txid" + # TODO: check if it refunded to opener address + raw_transaction = rpc.getrawtransaction(refund_txid, 1) + + result = raw_transaction["vout"][2]["valueSat"] + assert_equal(result, 700000) + + result = rpc.validateaddress(raw_transaction["vout"][2]["scriptPubKey"]["addresses"][0])["ismine"] + assert_equal(result, True) + + + # creating and draining channel (10 payment by 100000 satoshies in total to fit full capacity) + new_channel_hex1 = rpc.channelsopen(self.pubkey1, "10", "100000") + assert_success(new_channel_hex1) + channel1_txid = self.send_and_mine(new_channel_hex1["hex"], rpc) + assert channel1_txid, "got channel txid" + + # need to have 2+ confirmations in the test mode + rpc.generate(2) + self.sync_all() + + for i in range(10): + result = rpc.channelspayment(channel1_txid, "100000") + assert_success(result) + payment_tx_id = self.send_and_mine(result["hex"], rpc) + assert payment_tx_id, "got txid" + + # last payment should indicate that 0 payments left + result = rpc.channelsinfo(channel1_txid)["Transactions"][10]["Payments left"] + assert_equal(result, 0) + + # no more payments possible + result = rpc.channelspayment(channel1_txid, "100000") + assert_error(result) + + # creating new channel to test the case when node B initiate payment when node A revealed secret in offline + # 10 payments, 100000 sat denomination channel opening with second node pubkey + new_channel_hex2 = rpc.channelsopen(self.pubkey1, "10", "100000") + assert_success(new_channel_hex) + channel2_txid = self.send_and_mine(new_channel_hex2["hex"], rpc) + assert channel2_txid, "got channel txid" + + rpc.generate(2) + self.sync_all() + + # disconnecting first node from network + rpc.setban("127.0.0.0/24","add") + assert_equal(rpc.getinfo()["connections"], 0) + assert_equal(rpc1.getinfo()["connections"], 0) + + rpc1.generate(1) + + # sending one payment to mempool to reveal the secret but not mine it + payment_hex = rpc.channelspayment(channel2_txid, "100000") + result = rpc.sendrawtransaction(payment_hex["hex"]) + assert result, "got payment txid" + + secret = rpc.channelsinfo(channel2_txid)["Transactions"][1]["Secret"] + assert secret, "Secret revealed" + + # secret shouldn't be available for node B + secret_not_revealed = None + try: + rpc1.channelsinfo(channel2_txid)["Transactions"][1]["Secret"] + except Exception: + secret_not_revealed = True + assert_equal(secret_not_revealed, True) + + # trying to initiate payment from second node with revealed secret + assert_equal(rpc1.getinfo()["connections"], 0) + dc_payment_hex = rpc1.channelspayment(channel2_txid, "100000", secret) + assert_success(dc_payment_hex) + result = rpc1.sendrawtransaction(dc_payment_hex["hex"]) + assert result, "got channelspayment transaction id" + + # TODO: it crash first node after block generating on mempools merging + # # restoring connection between nodes + # rpc.setban("127.0.0.0/24","remove") + # #rpc.generate(1) + # #rpc1.generate(1) + # sync_blocks(self.nodes) + # rpc.generate(1) + # sync_blocks(self.nodes) + # sync_mempools(self.nodes) + # assert_equal(rpc.getinfo()["connections"], 1) + # assert_equal(rpc1.getinfo()["connections"], 1) + def run_test(self): print("Mining blocks...") rpc = self.nodes[0] diff --git a/qa/rpc-tests/cryptoconditions_heir.py b/qa/rpc-tests/cryptoconditions_heir.py new file mode 100755 index 000000000..95b90b397 --- /dev/null +++ b/qa/rpc-tests/cryptoconditions_heir.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 SuperNET developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +import time +from test_framework.test_framework import CryptoconditionsTestFramework +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, assert_greater_than, \ + initialize_chain_clean, initialize_chain, start_nodes, start_node, connect_nodes_bi, \ + stop_nodes, sync_blocks, sync_mempools, wait_bitcoinds, rpc_port, assert_raises +from cryptoconditions import assert_success, assert_error, generate_random_string + + +class CryptoconditionsHeirTest(CryptoconditionsTestFramework): + + def run_heir_tests(self): + + rpc = self.nodes[0] + rpc1 = self.nodes[1] + + result = rpc.heiraddress() + assert_success(result) + # verify all keys look like valid AC addrs, could be better + for x in ['myCCaddress', 'HeirCCaddress', 'Heirmarker', 'myaddress']: + assert_equal(result[x][0], 'R') + + result = rpc.heiraddress(self.pubkey) + assert_success(result) + # test that additional CCaddress key is returned + for x in ['myCCaddress', 'HeirCCaddress', 'Heirmarker', 'myaddress', 'CCaddress']: + assert_equal(result[x][0], 'R') + + # getting empty heir list + result = rpc.heirlist() + assert_equal(len(result), 1) + assert_success(result) + + # valid heirfund case with coins + result = rpc.heirfund("0", "1000", "UNITHEIR", self.pubkey1, "10") + assert_success(result) + + heir_fund_txid = self.send_and_mine(result["hextx"], rpc) + assert heir_fund_txid, "got heir funding txid" + + # heir fund txid should be in heirlist now + result = rpc.heirlist() + assert_equal(len(result), 2) + assert_success(result) + assert_equal(result["fundingtxid"], heir_fund_txid) + + # checking heirinfo + result = rpc.heirinfo(heir_fund_txid) + assert_success(result) + assert_equal(result["fundingtxid"], heir_fund_txid) + assert_equal(result["name"], "UNITHEIR") + assert_equal(result["owner"], self.pubkey) + assert_equal(result["heir"], self.pubkey1) + assert_equal(result["funding total in coins"], "1000.00000000") + assert_equal(result["funding available in coins"], "1000.00000000") + assert_equal(result["inactivity time setting, sec"], "10") + assert_equal(result["spending allowed for the heir"], "false") + + # TODO: heirlist keys are duplicating now + + # waiting for 11 seconds to be sure that needed time passed for heir claiming + time.sleep(11) + rpc.generate(1) + self.sync_all() + result = rpc.heirinfo(heir_fund_txid) + assert_equal(result["funding available in coins"], "1000.00000000") + assert_equal(result["spending allowed for the heir"], "true") + + # have to check that second node have coins to cover txfee at least + rpc.sendtoaddress(rpc1.getnewaddress(), 1) + rpc.sendtoaddress(rpc1.getnewaddress(), 1) + rpc.generate(2) + self.sync_all() + second_node_balance = rpc1.getbalance() + assert_greater_than(second_node_balance, 0.1) + + # let's claim whole heir sum from second node + result = rpc1.heirclaim("0", "1000", heir_fund_txid) + assert_success(result) + + heir_claim_txid = self.send_and_mine(result["hextx"], rpc1) + assert heir_claim_txid, "got claim txid" + + # balance of second node after heirclaim should increase for 1000 coins - txfees + # + get one block reward when broadcasted heir_claim_txid + result = round(rpc1.getbalance()) - round(second_node_balance) + assert_greater_than(result, 100999) + + self.sync_all() + + # no more funds should be available for claiming + result = rpc.heirinfo(heir_fund_txid) + assert_equal(result["funding available in coins"], "0.00000000") + + # TODO: valid heirfund case with tokens + + def run_test(self): + print("Mining blocks...") + rpc = self.nodes[0] + rpc1 = self.nodes[1] + # utxos from block 1 become mature in block 101 + if not self.options.noshutdown: + rpc.generate(101) + self.sync_all() + rpc.getinfo() + rpc1.getinfo() + # this corresponds to -pubkey above + print("Importing privkeys") + rpc.importprivkey(self.privkey) + rpc1.importprivkey(self.privkey1) + self.run_heir_tests() + + +if __name__ == '__main__': + CryptoconditionsHeirTest().main() diff --git a/qa/rpc-tests/cryptoconditions_token.py b/qa/rpc-tests/cryptoconditions_token.py index 630d8f6e3..97ed86f8d 100755 --- a/qa/rpc-tests/cryptoconditions_token.py +++ b/qa/rpc-tests/cryptoconditions_token.py @@ -15,21 +15,35 @@ from cryptoconditions import assert_success, assert_error, generate_random_strin class CryptoconditionsTokenTest(CryptoconditionsTestFramework): def run_token_tests(self): - rpc = self.nodes[0] + + rpc = self.nodes[0] + rpc1 = self.nodes[1] + result = rpc.tokenaddress() assert_success(result) - for x in ['AssetsCCaddress', 'myCCaddress', 'Assetsmarker', 'myaddress']: + for x in ['TokensCCaddress', 'myCCaddress', 'Tokensmarker', 'myaddress']: assert_equal(result[x][0], 'R') result = rpc.tokenaddress(self.pubkey) assert_success(result) + for x in ['TokensCCaddress', 'myCCaddress', 'Tokensmarker', 'myaddress', 'CCaddress']: + assert_equal(result[x][0], 'R') + + result = rpc.assetsaddress() + assert_success(result) + for x in ['AssetsCCaddress', 'myCCaddress', 'Assetsmarker', 'myaddress']: + assert_equal(result[x][0], 'R') + + result = rpc.assetsaddress(self.pubkey) + assert_success(result) for x in ['AssetsCCaddress', 'myCCaddress', 'Assetsmarker', 'myaddress', 'CCaddress']: assert_equal(result[x][0], 'R') + # there are no tokens created yet result = rpc.tokenlist() assert_equal(result, []) - # trying to create token with negaive supply + # trying to create token with negative supply result = rpc.tokencreate("NUKE", "-1987420", "no bueno supply") assert_error(result) @@ -50,12 +64,9 @@ class CryptoconditionsTokenTest(CryptoconditionsTestFramework): result = rpc.tokenorders() assert_equal(result, []) - # getting token balance for pubkey + # getting token balance for non existing tokenid result = rpc.tokenbalance(self.pubkey) - assert_success(result) - assert_equal(result['balance'], 0) - assert_equal(result['CCaddress'], 'RCRsm3VBXz8kKTsYaXKpy7pSEzrtNNQGJC') - assert_equal(result['tokenid'], self.pubkey) + assert_error(result) # get token balance for token with pubkey result = rpc.tokenbalance(tokenid, self.pubkey) @@ -131,11 +142,25 @@ class CryptoconditionsTokenTest(CryptoconditionsTestFramework): # checking ask cancellation testorder = rpc.tokenask("100", tokenid, "7.77") testorderid = self.send_and_mine(testorder['hex'], rpc) + # from other node (ensuring that second node have enough balance to cover txfee + # to get the actual error - not "not enough balance" one + rpc.sendtoaddress(rpc1.getnewaddress(), 1) + rpc.sendtoaddress(rpc1.getnewaddress(), 1) + rpc.generate(2) + self.sync_all() + result = rpc1.getbalance() + assert_greater_than(result, 0.1) + + result = rpc1.tokencancelask(tokenid, testorderid) + assert_error(result) + + # from valid node cancel = rpc.tokencancelask(tokenid, testorderid) self.send_and_mine(cancel["hex"], rpc) result = rpc.tokenorders() assert_equal(result, []) + # invalid numtokens bid result = rpc.tokenbid("-1", tokenid, "1") assert_error(result) @@ -184,6 +209,15 @@ class CryptoconditionsTokenTest(CryptoconditionsTestFramework): # checking bid cancellation testorder = rpc.tokenbid("100", tokenid, "7.77") testorderid = self.send_and_mine(testorder['hex'], rpc) + + # from other node + result = rpc1.getbalance() + assert_greater_than(result, 0.1) + + result = rpc1.tokencancelbid(tokenid, testorderid) + assert_error(result) + + # from valid node cancel = rpc.tokencancelbid(tokenid, testorderid) self.send_and_mine(cancel["hex"], rpc) result = rpc.tokenorders() @@ -220,5 +254,6 @@ class CryptoconditionsTokenTest(CryptoconditionsTestFramework): rpc1.importprivkey(self.privkey1) self.run_token_tests() + if __name__ == '__main__': CryptoconditionsTokenTest().main() diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 86d3165cf..b2fa534a1 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -94,7 +94,8 @@ void WaitForShutdown(boost::thread_group* threadGroup) // // Start // -extern int32_t IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,ASSETCHAIN_INIT; +extern int32_t IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY; +extern uint32_t ASSETCHAIN_INIT; extern std::string NOTARY_PUBKEY; int32_t komodo_is_issuer(); void komodo_passport_iteration(); diff --git a/src/cc/CC made easy.md b/src/cc/CC made easy.md index 453aa5467..2d1081003 100644 --- a/src/cc/CC made easy.md +++ b/src/cc/CC made easy.md @@ -212,7 +212,7 @@ bool FaucetExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction bool FaucetValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) -int64_t AddFaucetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) +int64_t AddFaucetInputs(struct CCcontract_infoCC_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) std::string FaucetGet(uint64_t txfee) diff --git a/src/cc/CCHeir.h b/src/cc/CCHeir.h index ecaff9cdb..30334f6e1 100644 --- a/src/cc/CCHeir.h +++ b/src/cc/CCHeir.h @@ -27,10 +27,10 @@ bool HeirValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, class CoinHelper; class TokenHelper; -UniValue HeirFundCoinCaller(uint64_t txfee, int64_t funds, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid); -UniValue HeirFundTokenCaller(uint64_t txfee, int64_t funds, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid); -UniValue HeirClaimCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount); -UniValue HeirAddCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount); +UniValue HeirFundCoinCaller(int64_t txfee, int64_t satoshis, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid); +UniValue HeirFundTokenCaller(int64_t txfee, int64_t satoshis, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid); +UniValue HeirClaimCaller(uint256 fundingtxid, int64_t txfee, std::string amount); +UniValue HeirAddCaller(uint256 fundingtxid, int64_t txfee, std::string amount); UniValue HeirInfo(uint256 fundingtxid); UniValue HeirList(); diff --git a/src/cc/CCassetstx.cpp b/src/cc/CCassetstx.cpp index 968775d38..9d83beb2c 100644 --- a/src/cc/CCassetstx.cpp +++ b/src/cc/CCassetstx.cpp @@ -477,14 +477,10 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction vintx; - uint64_t mask; - uint256 hashBlock; - int64_t bidamount; - CPubKey mypk; - struct CCcontract_info *cpAssets, C; - - uint8_t dummyEvalCode; uint256 dummyAssetid, dummyAssetid2; int64_t dummyPrice; std::vector dummyOrigpubkey; + CTransaction vintx; uint64_t mask; + uint256 hashBlock; int64_t bidamount; + CPubKey mypk; struct CCcontract_info *cpAssets, C; + uint8_t funcid,dummyEvalCode; uint256 dummyAssetid, dummyAssetid2; int64_t dummyPrice; std::vector dummyOrigpubkey; cpAssets = CCinit(&C, EVAL_ASSETS); @@ -501,9 +497,12 @@ std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid) bidamount = vintx.vout[0].nValue; mtx.vin.push_back(CTxIn(bidtxid, 0, CScript())); // coins in Assets - if( DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey) == 'b') - mtx.vin.push_back(CTxIn(bidtxid, 1, CScript())); // spend marker if funcid='b' (not 'B') - // TODO: spend it also in FillBuyOffer? + if((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0) + { + + if (funcid == 's') mtx.vin.push_back(CTxIn(bidtxid, 1, CScript())); // spend marker if funcid='b' + else if (funcid=='S') mtx.vin.push_back(CTxIn(bidtxid, 3, CScript())); // spend marker if funcid='B' + } mtx.vout.push_back(CTxOut(bidamount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); @@ -523,12 +522,9 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CTransaction vintx; uint64_t mask; - uint256 hashBlock; - int64_t askamount; - CPubKey mypk; - struct CCcontract_info *cpTokens, *cpAssets, tokensC, assetsC; - - uint8_t dummyEvalCode; uint256 dummyAssetid, dummyAssetid2; int64_t dummyPrice; std::vector dummyOrigpubkey; + uint256 hashBlock; int64_t askamount; + CPubKey mypk; struct CCcontract_info *cpTokens, *cpAssets, tokensC, assetsC; + uint8_t funcid,dummyEvalCode; uint256 dummyAssetid, dummyAssetid2; int64_t dummyPrice; std::vector dummyOrigpubkey; cpAssets = CCinit(&assetsC, EVAL_ASSETS); @@ -545,9 +541,11 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid) askamount = vintx.vout[0].nValue; mtx.vin.push_back(CTxIn(asktxid, 0, CScript())); - if (DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey) == 's') - mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // marker if funcid='s' (not 'S') - // TODO: spend it also in FillSell? + if ((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0) + { + if (funcid == 's') mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // marker if funcid='s' + else if (funcid=='S') mtx.vin.push_back(CTxIn(asktxid, 3, CScript())); // marker if funcid='S' + } mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, askamount, mypk)); // one-eval token vout mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); @@ -607,7 +605,7 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f mypk = pubkey2pk(Mypubkey()); - if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) + if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) { mask = ~((1LL << mtx.vin.size()) - 1); if (GetTransaction(bidtxid, vintx, hashBlock, false) != 0) @@ -637,9 +635,10 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount - paid_amount, unspendableAssetsPk)); // vout0 coins remainder mtx.vout.push_back(CTxOut(paid_amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); // vout1 coins to normal mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, fillamount, pubkey2pk(origpubkey))); // vout2 single-eval tokens sent to the buyer + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,txfee,origpubkey)); // vout3 marker to origpubkey if (CCchange != 0) - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // vout3 change in single-eval tokens + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // vout4 change in single-eval tokens fprintf(stderr,"FillBuyOffer remaining %llu -> origpubkey\n", (long long)remaining_required); @@ -698,7 +697,7 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a txfee = 10000; mypk = pubkey2pk(Mypubkey()); - if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) + if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) { mask = ~((1LL << mtx.vin.size()) - 1); if (GetTransaction(asktxid, vintx, hashBlock, false) != 0) @@ -747,6 +746,7 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a //std::cerr << "FillSell() paid_value=" << paid_nValue << " origpubkey=" << HexStr(pubkey2pk(origpubkey)) << std::endl; mtx.vout.push_back(CTxOut(paid_nValue, CScript() << origpubkey << OP_CHECKSIG)); //vout.2 coins to tokens seller's normal addr } + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,txfee,origpubkey)); //vout.3 marker to origpubkey // not implemented if (CCchange != 0) { diff --git a/src/cc/CCcustom.cpp b/src/cc/CCcustom.cpp index 918ceb24a..577332b63 100644 --- a/src/cc/CCcustom.cpp +++ b/src/cc/CCcustom.cpp @@ -234,6 +234,34 @@ uint8_t TokensCCpriv[32] = { 0x1d, 0x0d, 0x0d, 0xce, 0x2d, 0xd2, 0xe1, 0x9d, 0xf #undef FUNCNAME #undef EVALCODE +#define FUNCNAME IsCClibInput +#define EVALCODE EVAL_FIRSTUSER +const char *CClibNormaladdr = "RVVeUg43rNcq3mZFnvZ8yqagyzqFgUnq4u"; +char CClibCChexstr[67] = { "032447d97655da079729dc024c61088ea415b22f4c15d4810ddaf2069ac6468d2f" }; +uint8_t CClibCCpriv[32] = { 0x57, 0xcf, 0x49, 0x71, 0x7d, 0xb4, 0x15, 0x1b, 0x4f, 0x98, 0xc5, 0x45, 0x8d, 0x26, 0x52, 0x4b, 0x7b, 0xe9, 0xbd, 0x55, 0xd8, 0x20, 0xd6, 0xc4, 0x82, 0x0f, 0xf5, 0xec, 0x6c, 0x1c, 0xa0, 0xc0 }; +#include "CCcustom.inc" +#undef FUNCNAME +#undef EVALCODE + +int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode) +{ + CPubKey pk; uint8_t pub33[33]; char CCaddr[64]; + if ( evalcode == EVAL_FIRSTUSER ) // eventually make a hashchain for each evalcode + { + cp->evalcode = evalcode; + cp->ismyvin = IsCClibInput; + strcpy(cp->CChexstr,CClibCChexstr); + memcpy(cp->CCpriv,CClibCCpriv,32); + decode_hex(pub33,33,cp->CChexstr); + pk = buf2pk(pub33); + Getscriptaddress(cp->normaladdr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG); + if ( strcmp(cp->normaladdr,CClibNormaladdr) != 0 ) + fprintf(stderr,"CClib_initcp addr mismatch %s vs %s\n",cp->normaladdr,CClibNormaladdr); + GetCCaddress(cp,cp->unspendableCCaddr,pk); + return(0); + } + return(-1); +} struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) { @@ -369,6 +397,10 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) cp->validate = TokensValidate; cp->ismyvin = IsTokensInput; break; + default: + if ( CClib_initcp(cp,evalcode) < 0 ) + return(0); + break; } return(cp); } diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 680e4ca5c..66c001969 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -51,11 +51,6 @@ one other technical note is that komodod has the insight-explorer extensions bui #include "../utlist.h" #include "../uthash.h" -extern int32_t KOMODO_CONNECTING,KOMODO_CCACTIVATE,KOMODO_DEALERNODE; -extern uint32_t ASSETCHAINS_CC; -extern char ASSETCHAINS_SYMBOL[]; -extern std::string CCerror,ASSETCHAINS_CCLIB; -extern uint8_t ASSETCHAINS_CCDISABLES[256]; #define CC_MAXVINS 1024 @@ -69,6 +64,7 @@ extern uint8_t ASSETCHAINS_CCDISABLES[256]; typedef union _bits256 bits256; #endif +#include "../komodo_cJSON.h" struct CC_utxo { @@ -134,6 +130,9 @@ CBlockIndex *komodo_getblockindex(uint256 hash); int32_t komodo_nextheight(); int32_t CCgetspenttxid(uint256 &spenttxid,int32_t &vini,int32_t &height,uint256 txid,int32_t vout); +void CCclearvars(struct CCcontract_info *cp); +UniValue CClib(struct CCcontract_info *cp,char *method,cJSON *params); +UniValue CClib_info(struct CCcontract_info *cp); static const uint256 zeroid; bool myGetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock); @@ -200,6 +199,7 @@ CC *MakeTokensCCcond1(uint8_t evalcode, 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); +int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode); bool IsCCInput(CScript const& scriptSig); int32_t unstringbits(char *buf,uint64_t bits); diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index 2961f6597..d7db32b99 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -404,7 +404,6 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c uint8_t evalCodeInOpret; if (vopretExtra.size() >= 2 /*|| vopretExtra.size() != vopretExtra.begin()[0] <-- shold we check this?*/) { - std::cerr << "IsTokensvout() empty or incorrect contract opret" << std::endl; evalCodeInOpret = vopretExtra.begin()[1]; } else { @@ -766,4 +765,4 @@ UniValue TokenList() } } return(result); -} \ No newline at end of file +} diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index e9c30acc1..766ce1188 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -493,39 +493,10 @@ CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv) return(pubkey2pk(ParseHex(cp->CChexstr))); } -bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector paramsNull,const CTransaction &ctx, unsigned int nIn) +void CCclearvars(struct CCcontract_info *cp) { - CTransaction createTx; uint256 assetid,assetid2,hashBlock; uint8_t funcid; int32_t height,i,n,from_mempool = 0; int64_t amount; std::vector origpubkey; - height = KOMODO_CONNECTING; - if ( KOMODO_CONNECTING < 0 ) // always comes back with > 0 for final confirmation - return(true); - if ( ASSETCHAINS_CC == 0 || (height & ~(1<<30)) < KOMODO_CCACTIVATE ) - return eval->Invalid("CC are disabled or not active yet"); - if ( (KOMODO_CONNECTING & (1<<30)) != 0 ) - { - from_mempool = 1; - height &= ((1<<30) - 1); - } - //fprintf(stderr,"KOMODO_CONNECTING.%d mempool.%d vs CCactive.%d\n",height,from_mempool,KOMODO_CCACTIVATE); - // there is a chance CC tx is valid in mempool, but invalid when in block, so we cant filter duplicate requests. if any of the vins are spent, for example - //txid = ctx.GetHash(); - //if ( txid == cp->prevtxid ) - // return(true); - //fprintf(stderr,"process CC %02x\n",cp->evalcode); cp->evalcode2 = cp->evalcode3 = 0; cp->unspendableaddr2[0] = cp->unspendableaddr3[0] = 0; - if ( paramsNull.size() != 0 ) // Don't expect params - return eval->Invalid("Cannot have params"); - //else if ( ctx.vout.size() == 0 ) // spend can go to z-addresses - // return eval->Invalid("no-vouts"); - else if ( (*cp->validate)(cp,eval,ctx,nIn) != 0 ) - { - //fprintf(stderr,"done CC %02x\n",cp->evalcode); - //cp->prevtxid = txid; - return(true); - } - //fprintf(stderr,"invalid CC %02x\n",cp->evalcode); - return(false); } int64_t CCduration(int32_t &numblocks,uint256 txid) @@ -602,7 +573,6 @@ bool komodo_txnotarizedconfirmed(uint256 txid) CPubKey check_signing_pubkey(CScript scriptSig) { - bool found = false; CPubKey pubkey; @@ -630,3 +600,79 @@ CPubKey check_signing_pubkey(CScript scriptSig) } return CPubKey(); } + +bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector paramsNull,const CTransaction &ctx, unsigned int nIn) +{ + CTransaction createTx; uint256 assetid,assetid2,hashBlock; uint8_t funcid; int32_t height,i,n,from_mempool = 0; int64_t amount; std::vector origpubkey; + height = KOMODO_CONNECTING; + if ( KOMODO_CONNECTING < 0 ) // always comes back with > 0 for final confirmation + return(true); + if ( ASSETCHAINS_CC == 0 || (height & ~(1<<30)) < KOMODO_CCACTIVATE ) + return eval->Invalid("CC are disabled or not active yet"); + if ( (KOMODO_CONNECTING & (1<<30)) != 0 ) + { + from_mempool = 1; + height &= ((1<<30) - 1); + } + //fprintf(stderr,"KOMODO_CONNECTING.%d mempool.%d vs CCactive.%d\n",height,from_mempool,KOMODO_CCACTIVATE); + // there is a chance CC tx is valid in mempool, but invalid when in block, so we cant filter duplicate requests. if any of the vins are spent, for example + //txid = ctx.GetHash(); + //if ( txid == cp->prevtxid ) + // return(true); + //fprintf(stderr,"process CC %02x\n",cp->evalcode); + CCclearvars(cp); + if ( paramsNull.size() != 0 ) // Don't expect params + return eval->Invalid("Cannot have params"); + //else if ( ctx.vout.size() == 0 ) // spend can go to z-addresses + // return eval->Invalid("no-vouts"); + else if ( (*cp->validate)(cp,eval,ctx,nIn) != 0 ) + { + //fprintf(stderr,"done CC %02x\n",cp->evalcode); + //cp->prevtxid = txid; + return(true); + } + //fprintf(stderr,"invalid CC %02x\n",cp->evalcode); + return(false); +} + +extern struct CCcontract_info CCinfos[0x100]; +extern std::string MYCCLIBNAME; +bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx,unsigned int nIn); + +bool CClib_Dispatch(const CC *cond,Eval *eval,std::vector paramsNull,const CTransaction &txTo,unsigned int nIn) +{ + uint8_t evalcode; int32_t height,from_mempool; struct CCcontract_info *cp; + if ( ASSETCHAINS_CCLIB != MYCCLIBNAME ) + { + fprintf(stderr,"-ac_cclib=%s vs myname %s\n",ASSETCHAINS_CCLIB.c_str(),MYCCLIBNAME.c_str()); + return eval->Invalid("-ac_cclib name mismatches myname"); + } + height = KOMODO_CONNECTING; + if ( KOMODO_CONNECTING < 0 ) // always comes back with > 0 for final confirmation + return(true); + if ( ASSETCHAINS_CC == 0 || (height & ~(1<<30)) < KOMODO_CCACTIVATE ) + return eval->Invalid("CC are disabled or not active yet"); + if ( (KOMODO_CONNECTING & (1<<30)) != 0 ) + { + from_mempool = 1; + height &= ((1<<30) - 1); + } + evalcode = cond->code[0]; + if ( evalcode >= EVAL_FIRSTUSER && evalcode <= EVAL_LASTUSER ) + { + cp = &CCinfos[(int32_t)evalcode]; + if ( cp->didinit == 0 ) + { + if ( CClib_initcp(cp,evalcode) == 0 ) + cp->didinit = 1; + else return eval->Invalid("unsupported CClib evalcode"); + } + CCclearvars(cp); + if ( paramsNull.size() != 0 ) // Don't expect params + return eval->Invalid("Cannot have params"); + else if ( CClib_validate(cp,height,eval,txTo,nIn) != 0 ) + return(true); + return(false); //eval->Invalid("error in CClib_validate"); + } + return eval->Invalid("cclib CC must have evalcode between 16 and 127"); +} diff --git a/src/cc/assets.cpp b/src/cc/assets.cpp index 3ccafa34f..2433473b9 100644 --- a/src/cc/assets.cpp +++ b/src/cc/assets.cpp @@ -153,8 +153,9 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti return eval->Invalid("AssetValidate: invalid opreturn payload"); // find dual-eval tokens unspendable addr: - char tokensUnspendableAddr[64]; + char tokensUnspendableAddr[64],origpubkeyCCaddr[64]; GetTokensCCaddress(cpAssets, tokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); + GetCCaddress(cpAssets, origpubkeyCCaddr, origpubkey); // we need this for validating single-eval tokens' vins/vous: struct CCcontract_info *cpTokens, tokensC; @@ -258,7 +259,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, assetsCCaddr, origaddr, tx, assetid)) == 0 ) return(false); - else if( numvouts < 3 ) + else if( numvouts < 4 ) return eval->Invalid("not enough vouts for fillbuy"); else if( tmporigpubkey != origpubkey ) return eval->Invalid("mismatched origpubkeys for fillbuy"); @@ -266,17 +267,19 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti { if( nValue != tx.vout[0].nValue + tx.vout[1].nValue ) return eval->Invalid("locked value doesnt match vout0+1 fillbuy"); - else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 ) + else if( tx.vout[4].scriptPubKey.IsPayToCryptoCondition() != 0 ) { if( ConstrainVout(tx.vout[2], 1, assetsCCaddr, 0) == 0 ) // tokens on user cc addr return eval->Invalid("vout2 doesnt go to origpubkey fillbuy"); - else if ( inputs != tx.vout[2].nValue + tx.vout[3].nValue ) + else if ( inputs != tx.vout[2].nValue + tx.vout[4].nValue ) return eval->Invalid("asset inputs doesnt match vout2+3 fillbuy"); } else if( ConstrainVout(tx.vout[2], 1, assetsCCaddr, inputs) == 0 ) // tokens on user cc addr return eval->Invalid("vout2 doesnt match inputs fillbuy"); else if( ConstrainVout(tx.vout[1],0,0,0) == 0 ) return eval->Invalid("vout1 is CC for fillbuy"); + else if( ConstrainVout(tx.vout[3], 1, origpubkeyCCaddr, 10000) == 0 ) + return eval->Invalid("invalid marker for original pubkey"); else if( ValidateBidRemainder(remaining_price, tx.vout[0].nValue, nValue, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false ) return eval->Invalid("mismatched remainder for fillbuy"); else if( remaining_price != 0 ) @@ -345,7 +348,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti if( (assetoshis = AssetValidateSellvin(cpAssets, eval, totalunits, tmporigpubkey, userTokensCCaddr, origaddr, tx, assetid)) == 0 ) return(false); - else if( numvouts < 3 ) + else if( numvouts < 4 ) return eval->Invalid("not enough vouts for fillask"); else if( tmporigpubkey != origpubkey ) return eval->Invalid("mismatched origpubkeys for fillask"); @@ -359,6 +362,8 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti return eval->Invalid("normal vout1 for fillask"); else if( ConstrainVout(tx.vout[2], 0, origaddr, 0) == 0 ) return eval->Invalid("normal vout1 for fillask"); + else if( ConstrainVout(tx.vout[3], 1, origpubkeyCCaddr, 10000) == 0 ) + return eval->Invalid("invalid marker for original pubkey"); else if( remaining_price != 0 ) { //char tokensUnspendableAddr[64]; diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp index 889c40d8c..971258d90 100644 --- a/src/cc/cclib.cpp +++ b/src/cc/cclib.cpp @@ -27,32 +27,291 @@ #include "core_io.h" #include "crosschain.h" -#define MYCCLIBNAME ((char *)"stub") +#define FAUCET2SIZE COIN -char *CClib_name() { return(MYCCLIBNAME); } - -bool CClib_Dispatch(const CC *cond,Eval *eval,std::vector paramsNull,const CTransaction &txTo,unsigned int nIn) +struct CClib_rpcinfo { - uint8_t evalcode; int32_t height,from_mempool; - if ( ASSETCHAINS_CCLIB != MYCCLIBNAME ) - { - fprintf(stderr,"-ac_cclib=%s vs myname %s\n",ASSETCHAINS_CCLIB.c_str(),MYCCLIBNAME); - return eval->Invalid("-ac_cclib name mismatches myname"); - } - height = KOMODO_CONNECTING; - if ( KOMODO_CONNECTING < 0 ) // always comes back with > 0 for final confirmation - return(true); - if ( ASSETCHAINS_CC == 0 || (height & ~(1<<30)) < KOMODO_CCACTIVATE ) - return eval->Invalid("CC are disabled or not active yet"); - if ( (KOMODO_CONNECTING & (1<<30)) != 0 ) - { - from_mempool = 1; - height &= ((1<<30) - 1); - } - evalcode = cond->code[0]; - if ( evalcode >= EVAL_FIRSTUSER && evalcode <= EVAL_LASTUSER ) - { - return(true); - } - return eval->Invalid("cclib CC must have evalcode between 16 and 127"); + char *method,*help; + int32_t numrequiredargs,maxargs; // frontloaded with required + uint8_t funcid; +} +CClib_methods[] = +{ + { (char *)"faucet2_fund", (char *)"amount", 1, 1, 'F' }, + { (char *)"faucet2_get", (char *)"", 0, 0, 'G' }, +}; + +std::string MYCCLIBNAME = (char *)"faucet2"; + +char *CClib_name() { return((char *)MYCCLIBNAME.c_str()); } + +std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *params); + +UniValue CClib_info(struct CCcontract_info *cp) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i; char str[2]; + result.push_back(Pair("result","success")); + result.push_back(Pair("CClib",CClib_name())); + for (i=0; i= 128 ) + obj.push_back(Pair("funcid",CClib_methods[i].funcid)); + else + { + str[0] = CClib_methods[i].funcid; + str[1] = 0; + obj.push_back(Pair("funcid",str)); + } + obj.push_back(Pair("name",CClib_methods[i].method)); + obj.push_back(Pair("help",CClib_methods[i].help)); + obj.push_back(Pair("params_required",CClib_methods[i].numrequiredargs)); + obj.push_back(Pair("params_max",CClib_methods[i].maxargs)); + a.push_back(obj); + } + result.push_back(Pair("methods",a)); + return(result); +} + +UniValue CClib(struct CCcontract_info *cp,char *method,cJSON *params) +{ + UniValue result(UniValue::VOBJ); int32_t i; std::string rawtx; + for (i=0; i 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) + return(tx.vout[v].nValue); + } + return(0); +} + +bool CClibExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) +{ + static uint256 zerohash; + CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; + numvins = tx.vin.size(); + numvouts = tx.vout.size(); + for (i=0; iismyvin)(tx.vin[i].scriptSig) != 0 ) + { + //fprintf(stderr,"vini.%d check mempool\n",i); + if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) + return eval->Invalid("cant find vinTx"); + else + { + //fprintf(stderr,"vini.%d check hash and vout\n",i); + if ( hashBlock == zerohash ) + return eval->Invalid("cant faucet2 from mempool"); + if ( (assetoshis= IsCClibvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) + inputs += assetoshis; + } + } + } + for (i=0; iInvalid("mismatched inputs != outputs + FAUCET2SIZE + txfee"); + } + else return(true); +} + +bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx,unsigned int nIn) +{ + int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; + std::vector > txids; + numvins = tx.vin.size(); + numvouts = tx.vout.size(); + preventCCvins = preventCCvouts = -1; + if ( numvouts < 1 ) + return eval->Invalid("no vouts"); + else + { + for (i=0; iInvalid("illegal normal vini"); + } + } + //fprintf(stderr,"check amounts\n"); + if ( CClibExactAmounts(cp,eval,tx,1,10000) == false ) + { + fprintf(stderr,"faucetget invalid amount\n"); + return false; + } + else + { + preventCCvouts = 1; + if ( IsCClibvout(cp,tx,0) != 0 ) + { + preventCCvouts++; + i = 1; + } else i = 0; + txid = tx.GetHash(); + memcpy(hash,&txid,sizeof(hash)); + fprintf(stderr,"check faucetget txid %s %02x/%02x\n",uint256_str(str,txid),hash[0],hash[31]); + if ( tx.vout[i].nValue != FAUCET2SIZE ) + return eval->Invalid("invalid faucet output"); + else if ( (hash[0] & 0xff) != 0 || (hash[31] & 0xff) != 0 ) + return eval->Invalid("invalid faucetget txid"); + Getscriptaddress(destaddr,tx.vout[i].scriptPubKey); + SetCCtxids(txids,destaddr); + for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) + { + //int height = it->first.blockHeight; + if ( CCduration(numblocks,it->first.txhash) > 0 && numblocks > 3 ) + { + //fprintf(stderr,"would return error %s numblocks.%d ago\n",uint256_str(str,it->first.txhash),numblocks); + return eval->Invalid("faucet2 is only for brand new addresses"); + } + } + retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); + if ( retval != 0 ) + fprintf(stderr,"faucet2get validated\n"); + else fprintf(stderr,"faucet2get invalid\n"); + return(retval); + } + } +} + +int64_t AddCClibInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) +{ + char coinaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + std::vector > unspentOutputs; + GetCCaddress(cp,coinaddr,pk); + SetCCunspents(unspentOutputs,coinaddr); + threshold = total/(maxinputs+1); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + if ( it->second.satoshis < threshold ) + continue; + //char str[65]; fprintf(stderr,"check %s/v%d %.8f`\n",uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); + // no need to prevent dup + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + { + if ( (nValue= IsCClibvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) + { + 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; + } else fprintf(stderr,"nValue too small or already spent in mempool\n"); + } else fprintf(stderr,"couldnt get tx\n"); + } + return(totalinputs); +} + + +std::string Faucet2Fund(struct CCcontract_info *cp,uint64_t txfee,int64_t funds) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk,cclibpk; CScript opret; + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + cclibpk = GetUnspendable(cp,0); + if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,cclibpk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); + } + return(""); +} + +/*UniValue FaucetInfo() +{ + UniValue result(UniValue::VOBJ); char numstr[64]; + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey faucetpk; struct CCcontract_info *cp,C; int64_t funding; + result.push_back(Pair("result","success")); + result.push_back(Pair("name","Faucet")); + cp = CCinit(&C,EVAL_FAUCET); + faucetpk = GetUnspendable(cp,0); + funding = AddFaucetInputs(cp,mtx,faucetpk,0,0); + sprintf(numstr,"%.8f",(double)funding/COIN); + result.push_back(Pair("funding",numstr)); + return(result); +}*/ + +std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *params) +{ + CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk,cclibpk; int64_t funds,txfee=0,inputs,CCchange=0,nValue=FAUCET2SIZE; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash; + if ( txfee == 0 ) + txfee = 10000; + if ( funcid == 'F' ) + { + if ( cJSON_GetArraySize(params) > 0 ) + { + funds = (int64_t)jdouble(jitem(params,0),0)*COIN + 0.0000000049; + return(Faucet2Fund(cp,0,funds)); + } else return(""); + } + else if ( funcid != 'G' ) + return(""); + cclibpk = GetUnspendable(cp,0); + mypk = pubkey2pk(Mypubkey()); + if ( (inputs= AddCClibInputs(cp,mtx,cclibpk,nValue+txfee,60)) > 0 ) + { + if ( inputs > nValue ) + CCchange = (inputs - nValue - txfee); + if ( CCchange != 0 ) + mtx.vout.push_back(MakeCC1vout(EVAL_FIRSTUSER,CCchange,cclibpk)); + 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++) + { + tmpmtx = mtx; + rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_FIRSTUSER << (uint8_t)'G' << j)); + if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 ) + { + 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]); + } + } + fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL)); + return(""); + } else fprintf(stderr,"cant find faucet inputs\n"); + return(""); } diff --git a/src/cc/channels.cpp b/src/cc/channels.cpp index a375edbda..6cd379eed 100644 --- a/src/cc/channels.cpp +++ b/src/cc/channels.cpp @@ -502,6 +502,8 @@ std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64 if (tokenid!=zeroid && tokens>funds) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,tokens-funds,mypk)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('O',tokenid,zeroid,mypk,destpub,numpayments,payment,hashchain))); } + CCerror = strprintf("error adding funds"); + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } @@ -520,25 +522,29 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 mypk = pubkey2pk(Mypubkey()); if (GetTransaction(opentxid,channelOpenTx,hashblock,false) == 0) { - fprintf(stderr, "invalid channel open txid\n"); - return (""); + CCerror = strprintf("invalid channel open txid"); + fprintf(stderr,"%s\n",CCerror.c_str()); + return(""); } if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, totalnumpayments, payment, hashchain)=='O') { if (mypk != srcpub && mypk != destpub) { - fprintf(stderr,"this is not our channel\n"); + CCerror = strprintf("this is not our channel"); + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } else if (amount % payment != 0 || amount 0) @@ -552,11 +558,13 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 { if (numpayments > prevdepth) { - fprintf(stderr,"not enough funds in channel for that amount\n"); + CCerror = strprintf("not enough funds in channel for that amount"); + fprintf(stderr,"%s\n",CCerror.c_str()); return (""); } else if (numpayments == 0) { - fprintf(stderr,"invalid amount\n"); + CCerror = strprintf("invalid amount"); + fprintf(stderr,"%s\n",CCerror.c_str()); return (""); } if (secret!=zeroid) @@ -570,7 +578,8 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 endiancpy((uint8_t * ) & gensecret, hashdest, 32); if (gensecret!=hashchain) { - fprintf(stderr,"invalid secret supplied\n"); + CCerror = strprintf("invalid secret supplied"); + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } } @@ -592,7 +601,8 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 } else { - fprintf(stderr,"invalid previous tx\n"); + CCerror = strprintf("invalid previous tx"); + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS, change, srcpub, destpub)); @@ -605,11 +615,13 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 } else { - fprintf(stderr,"error adding CC inputs\n"); + CCerror = strprintf("error adding CC inputs"); + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } } - fprintf(stderr,"error adding normal inputs\n"); + CCerror = strprintf("error adding normal inputs"); + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } @@ -629,36 +641,41 @@ std::string ChannelClose(uint64_t txfee,uint256 opentxid) mypk = pubkey2pk(Mypubkey()); if (GetTransaction(opentxid,channelOpenTx,hashblock,false) == 0) { - fprintf(stderr, "invalid channel open txid\n"); + CCerror = strprintf("invalid channel open txid"); + fprintf(stderr,"%s\n",CCerror.c_str()); return (""); } if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain)!='O') { - fprintf(stderr, "invalid channel open tx\n"); + CCerror = strprintf("invalid channel open tx"); + fprintf(stderr,"%s\n",CCerror.c_str()); return (""); } if (mypk != srcpub) { - fprintf(stderr,"cannot close, you are not channel owner\n"); + CCerror = strprintf("cannot close, you are not channel owner"); + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) { - if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds-txfee>0) + if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0) { - if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS, funds-txfee, mypk, destpub)); - else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, funds-txfee, mypk, destpub)); + if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS, funds, mypk, destpub)); + else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, funds, mypk, destpub)); mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('C',tokenid,opentxid,mypk,destpub,(funds-txfee)/payment,payment,zeroid))); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('C',tokenid,opentxid,mypk,destpub,funds/payment,payment,zeroid))); } else { - fprintf(stderr,"error adding CC inputs\n"); + CCerror = strprintf("error adding CC inputs"); + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } } - fprintf(stderr,"error adding normal inputs\n"); + CCerror = strprintf("error adding normal inputs"); + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } @@ -678,61 +695,72 @@ std::string ChannelRefund(uint64_t txfee,uint256 opentxid,uint256 closetxid) mypk = pubkey2pk(Mypubkey()); if (GetTransaction(closetxid,channelCloseTx,hashblock,false) == 0) { - fprintf(stderr, "invalid channel close txid\n"); + CCerror = strprintf("invalid channel close txid"); + fprintf(stderr,"%s\n",CCerror.c_str()); return (""); } if ((numvouts=channelCloseTx.vout.size()) < 1 || DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,param1,param2,param3)!='C') { - fprintf(stderr, "invalid channel close tx\n"); + CCerror = strprintf("invalid channel close tx"); + fprintf(stderr,"%s\n",CCerror.c_str()); return (""); } if (txid!=opentxid) { - fprintf(stderr, "open and close txid are not from same channel\n"); + CCerror = strprintf("open and close txid are not from same channel"); + fprintf(stderr,"%s\n",CCerror.c_str()); return (""); } if (GetTransaction(opentxid,channelOpenTx,hashblock,false) == 0) { - fprintf(stderr, "invalid channel open txid\n"); + CCerror = strprintf("invalid channel open txid"); + fprintf(stderr,"%s\n",CCerror.c_str()); return (""); } if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,numpayments,payment,hashchain)!='O') { - fprintf(stderr, "invalid channel open tx\n"); + CCerror = strprintf("invalid channel open tx"); + fprintf(stderr,"%s\n",CCerror.c_str()); return (""); } if (mypk != srcpub) { - fprintf(stderr,"cannot refund, you are not the channel owenr\n"); + CCerror = strprintf("cannot refund, you are not the channel owner"); + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) { - if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds-txfee>0) + if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0) { if ((GetTransaction(prevtxid,prevTx,hashblock,false) != 0) && (numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3) != 0) { mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); - if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,funds-txfee,mypk)); - else mtx.vout.push_back(CTxOut(funds-txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('R',tokenid,opentxid,mypk,destpub,param1,payment,closetxid))); + if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,funds,mypk)); + else mtx.vout.push_back(CTxOut(funds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('R',tokenid,opentxid,mypk,destpub,funds/payment,payment,closetxid))); } else { - fprintf(stderr,"previous tx is invalid\n"); + CCerror = strprintf("previous tx is invalid"); + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } } else { - fprintf(stderr,"error adding CC inputs\n"); + CCerror = strprintf("error adding CC inputs"); + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } } + CCerror = strprintf("error adding normal inputs"); + fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } + UniValue ChannelsList() { UniValue result(UniValue::VOBJ); std::vector > txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock,tmp_txid,param3,tokenid; diff --git a/src/cc/dapps/zmigrate.c b/src/cc/dapps/zmigrate.c index cfbd29d70..37a2bc7ca 100644 --- a/src/cc/dapps/zmigrate.c +++ b/src/cc/dapps/zmigrate.c @@ -20,18 +20,18 @@ #include "cJSON.c" /* -z_migrate: the purpose of z_migrate is to make converting of all sprout outputs into sapling. the usage would be for the user to specify a sapling address and call z_migrate zsaddr, until it returns that there is nothing left to be done. - -its main functionality is quite similar to a z_mergetoaddress ANY_ZADDR -> onetime_taddr followed by a z_sendmany onetime_taddr -> zsaddr - -since the z_mergetoaddress will take time, it would just queue up an async operation. When it starts, it should see if there are any onetime_taddr with 10000.0001 funds in it, that is a signal for it to do the sapling tx and it can just do that without async as it is fast enough, especially with a taddr input. Maybe it limits itself to one, or it does all possible taddr -> sapling as fast as it can. either is fine as it will be called over and over anyway. - -It might be that there is nothing to do, but some operations are pending. in that case it would return such a status. as soon as the operation finishes, there would be more work to do. - -the amount sent to the taddr, should be 10000.0001 - -The GUI or user would be expected to generate a sapling address and then call z_migrate saplingaddr in a loop, until it returns that it is all done. this loop should pause for 10 seconds or so, if z_migrate is just waiting for opid to complete. -*/ + z_migrate: the purpose of z_migrate is to make converting of all sprout outputs into sapling. the usage would be for the user to specify a sapling address and call z_migrate zsaddr, until it returns that there is nothing left to be done. + + its main functionality is quite similar to a z_mergetoaddress ANY_ZADDR -> onetime_taddr followed by a z_sendmany onetime_taddr -> zsaddr + + since the z_mergetoaddress will take time, it would just queue up an async operation. When it starts, it should see if there are any onetime_taddr with 10000.0001 funds in it, that is a signal for it to do the sapling tx and it can just do that without async as it is fast enough, especially with a taddr input. Maybe it limits itself to one, or it does all possible taddr -> sapling as fast as it can. either is fine as it will be called over and over anyway. + + It might be that there is nothing to do, but some operations are pending. in that case it would return such a status. as soon as the operation finishes, there would be more work to do. + + the amount sent to the taddr, should be 10000.0001 + + The GUI or user would be expected to generate a sapling address and then call z_migrate saplingaddr in a loop, until it returns that it is all done. this loop should pause for 10 seconds or so, if z_migrate is just waiting for opid to complete. + */ bits256 zeroid; @@ -331,7 +331,7 @@ cJSON *get_komodocli(char *refcoin,char **retstrp,char *acname,char *method,char system(cmdstr); *retstrp = 0; if ( (jsonstr= filestr(&fsize,fname)) != 0 ) - { + { jsonstr[strlen(jsonstr)-1]='\0'; //fprintf(stderr,"%s -> jsonstr.(%s)\n",cmdstr,jsonstr); if ( (jsonstr[0] != '{' && jsonstr[0] != '[') || (retjson= cJSON_Parse(jsonstr)) == 0 ) @@ -599,13 +599,13 @@ int32_t validateaddress(char *refcoin,char *acname,char *depositaddr, char* comp cJSON *retjson; char *retstr; int32_t res=0; if ( (retjson= get_komodocli(refcoin,&retstr,acname,"validateaddress",depositaddr,"","","")) != 0 ) { - if (is_cJSON_True(jobj(retjson,compare)) != 0 ) res=1; + if (is_cJSON_True(jobj(retjson,compare)) != 0 ) res=1; free_json(retjson); } else if ( retstr != 0 ) { fprintf(stderr,"validateaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr); - free(retstr); + free(retstr); } return (res); } @@ -649,21 +649,21 @@ int64_t z_getbalance(char *refcoin,char *acname,char *coinaddr) int32_t z_exportkey(char *privkey,char *refcoin,char *acname,char *zaddr) { - cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0; int32_t retval = -1; + cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0; privkey[0] = 0; if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_exportkey",zaddr,"","","")) != 0 ) { fprintf(stderr,"z_exportkey.(%s) %s returned json!\n",refcoin,acname); free_json(retjson); + return(-1); } else if ( retstr != 0 ) { //printf("retstr %s -> %.8f\n",retstr,dstr(amount)); strcpy(privkey,retstr); free(retstr); - retval = 0; + return(0); } - return(retval); } int32_t getnewaddress(char *coinaddr,char *refcoin,char *acname) @@ -700,6 +700,23 @@ int32_t z_getnewaddress(char *coinaddr,char *refcoin,char *acname,char *typestr) return(retval); } +int32_t z_getnewaddress(char *coinaddr,char *refcoin,char *acname,char *typestr) +{ + cJSON *retjson; char *retstr; int64_t amount=0; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_getnewaddress",typestr,"","","")) != 0 ) + { + fprintf(stderr,"z_getnewaddress.(%s) %s returned json!\n",refcoin,acname); + free_json(retjson); + return(-1); + } + else if ( retstr != 0 ) + { + strcpy(coinaddr,retstr); + free(retstr); + return(0); + } +} + int64_t find_onetime_amount(char *coinstr,char *coinaddr) { cJSON *array,*item; int32_t i,n; char *addr; int64_t amount = 0; @@ -824,6 +841,39 @@ int32_t z_mergetoaddress(char *opidstr,char *coinstr,char *acname,char *destaddr return(retval); } +int32_t z_mergetoaddress(char *opidstr,char *coinstr,char *acname,char *destaddr) +{ + cJSON *retjson; char *retstr,addr[128],*opstr; int32_t retval = -1; + sprintf(addr,"[\\\"ANY_SPROUT\\\"]"); + //printf("z_sendmany from.(%s) -> %s\n",addr,destaddr); + if ( (retjson= get_komodocli(coinstr,&retstr,acname,"z_mergetoaddress",addr,destaddr,"","")) != 0 ) + { + /*{ + "remainingUTXOs": 0, + "remainingTransparentValue": 0.00000000, + "remainingNotes": 222, + "remainingShieldedValue": 5413.39093055, + "mergingUTXOs": 0, + "mergingTransparentValue": 0.00000000, + "mergingNotes": 10, + "mergingShieldedValue": 822.47447172, + "opid": "opid-f28f6261-4120-436c-aca5-859870a40a70" + }*/ + if ( (opstr= jstr(retjson,"opid")) != 0 ) + strcpy(opidstr,opstr); + retval = jint(retjson,"remainingNotes"); + fprintf(stderr,"%s\n",jprint(retjson,0)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"z_mergetoaddress.(%s) -> opid.(%s)\n",coinstr,retstr); + strcpy(opidstr,retstr); + free(retstr); + } + return(retval); +} + int32_t empty_mempool(char *coinstr,char *acname) { cJSON *array; int32_t n; @@ -872,7 +922,7 @@ int32_t tx_has_voutaddress(char *refcoin,char *acname,bits256 txid,char *coinadd if ( (vouts= jarray(&numarray,txobj,"vout")) != 0 ) { for (i=0; i class CoinHelper; class TokenHelper; /* The idea of Heir CC is to allow crypto inheritance. - A special 1of2 CC address is created that is freely spendable by the creator (funds owner). + A special 1of2 CC address is created that is freely spendable by the creator (funds owner). The owner may add additional funds to this 1of2 address. - The heir is only allowed to spend after "the specified amount of idle blocks" (changed to "the owner inactivityTime"). - The idea is that if the address doesnt spend any funds for a year (or whatever amount set), then it is time to allow the heir to spend. + The heir is only allowed to spend after "the specified amount of idle blocks" (changed to "the owner inactivityTime"). + The idea is that if the address doesnt spend any funds for a year (or whatever amount set), then it is time to allow the heir to spend. "The design requires the heir to spend all the funds at once" (this requirement was changed to "after the inactivity time both the heir and owner may freely spend available funds") After the first heir spending a flag is set that spending is allowed for the heir whether the owner adds more funds or spends them. This Heir contract supports both coins and tokens. -*/ + */ // tx validation code @@ -36,69 +37,70 @@ class TokenHelper; // (sadly we cannot have yet 'templatized' lambdas, if we could we could capture all these params inside HeirValidation()...) template bool RunValidationPlans(uint8_t funcId, struct CCcontract_info* cp, Eval* eval, const CTransaction& tx, uint256 latestTxid, CScript fundingOpretScript, uint8_t hasHeirSpendingBegun) { - int32_t numvins = tx.vin.size(); - int32_t numvouts = tx.vout.size(); - - // setup validation framework (please see its description in heir_validate.h): - // validation 'plans': - CInputValidationPlan vinPlan; - COutputValidationPlan voutPlan; - - // vin 'identifiers' - CNormalInputIdentifier normalInputIdentifier(cp); - CCCInputIdentifier ccInputIdentifier(cp); - - // vin and vout 'validators' - // not used, too strict for 2 pubkeys: CMyPubkeyVoutValidator normalInputValidator(cp, fundingOpretScript, true); // check normal input for this opret cause this is first tx - CCC1of2AddressValidator cc1of2ValidatorThis(cp, fundingOpretScript, "checking this tx opreturn:"); // 1of2add validator with pubkeys from this tx opreturn - CHeirSpendValidator heirSpendValidator(cp, fundingOpretScript, latestTxid, hasHeirSpendingBegun); // check if heir allowed to spend - - // only for tokens: - CMyPubkeyVoutValidator ownerCCaddrValidator(cp, fundingOpretScript, false); // check if this correct owner's cc user addr corresponding to opret - COpRetValidator opRetValidator(cp, fundingOpretScript); // compare opRets in this and last tx - CNullValidator nullValidator(cp); - - switch (funcId) { - case 'F': // fund tokens - // vin validation plan: - vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin - vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &ownerCCaddrValidator); // check cc owner addr - - // vout validation plan: - voutPlan.pushValidators(0, &cc1of2ValidatorThis); // check 1of2 addr funding - // do not check change at this time - // no checking for opret yet - break; - - case 'A': // add tokens - // vin validation plan: - vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin - vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &ownerCCaddrValidator); // check cc owner addr - - // vout validation plan: - voutPlan.pushValidators(0, &cc1of2ValidatorThis); // check 1of2 addr funding - // do not check change at this time - voutPlan.pushValidators(numvouts - 1, &opRetValidator); // opreturn check, NOTE: only for C or A: - break; - - case 'C': - // vin validation plan: - vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin - vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &cc1of2ValidatorThis); // cc1of2 funding addr - - // vout validation plan: - voutPlan.pushValidators(0, &heirSpendValidator); // check if heir is allowed to spend - voutPlan.pushValidators(numvouts - 1, &opRetValidator); // opreturn check, NOTE: only for C or A - break; - } - - // call vin/vout validation - if (!vinPlan.validate(tx, eval)) - return false; - if (!voutPlan.validate(tx, eval)) - return false; - - return true; + int32_t numvins = tx.vin.size(); + int32_t numvouts = tx.vout.size(); + + // setup validation framework (please see its description in heir_validate.h): + // validation 'plans': + CInputValidationPlan vinPlan; + COutputValidationPlan voutPlan; + + // vin 'identifiers' + CNormalInputIdentifier normalInputIdentifier(cp); + CCCInputIdentifier ccInputIdentifier(cp); + + // vin and vout 'validators' + // not used, too strict for 2 pubkeys: CMyPubkeyVoutValidator normalInputValidator(cp, fundingOpretScript, true); // check normal input for this opret cause this is first tx + CCC1of2AddressValidator cc1of2ValidatorThis(cp, fundingOpretScript, "checking this tx opreturn:"); // 1of2add validator with pubkeys from this tx opreturn + CHeirSpendValidator heirSpendValidator(cp, fundingOpretScript, latestTxid, hasHeirSpendingBegun); // check if heir allowed to spend + + // only for tokens: + CMyPubkeyVoutValidator ownerCCaddrValidator(cp, fundingOpretScript, false); // check if this correct owner's cc user addr corresponding to opret + COpRetValidator opRetValidator(cp, fundingOpretScript); // compare opRets in this and last tx + CMarkerValidator markerValidator(cp); // initial tx marker spending protection + CNullValidator nullValidator(cp); + + switch (funcId) { + case 'F': // fund tokens (only for tokens) + // vin validation plan: + vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin + vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &markerValidator, &ownerCCaddrValidator); // check cc owner addr + + // vout validation plan: + voutPlan.pushValidators(0, &cc1of2ValidatorThis); // check 1of2 addr funding + // do not check change at this time + // no checking for opret yet + break; + + case 'A': // add tokens (only for tokens) + // vin validation plan: + vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin + vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &markerValidator, &ownerCCaddrValidator); // check cc owner addr + + // vout validation plan: + voutPlan.pushValidators(0, &cc1of2ValidatorThis); // check 1of2 addr funding + // do not check change at this time + voutPlan.pushValidators(numvouts - 1, &opRetValidator); // opreturn check, NOTE: only for C or A: + break; + + case 'C': // spend coins or tokens + // vin validation plan: + vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin + vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &markerValidator, &cc1of2ValidatorThis); // cc1of2 funding addr + + // vout validation plan: + voutPlan.pushValidators(0, &heirSpendValidator); // check if heir is allowed to spend + voutPlan.pushValidators(numvouts - 1, &opRetValidator); // opreturn check, NOTE: only for C or A + break; + } + + // call vin/vout validation + if (!vinPlan.validate(tx, eval)) + return false; + if (!voutPlan.validate(tx, eval)) + return false; + + return true; } /** @@ -110,121 +112,121 @@ bool HeirValidate(struct CCcontract_info* cpHeir, Eval* eval, const CTransaction int32_t numvouts = tx.vout.size(); //int32_t preventCCvins = -1; //int32_t preventCCvouts = -1; - - struct CCcontract_info *cpTokens, tokensC; - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - + + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, EVAL_TOKENS); + if (numvouts < 1) return eval->Invalid("no vouts"); - - //if (chainActive.Height() < 741) - // return true; - + + //if (chainActive.Height() < 741) + // return true; + uint256 fundingTxidInOpret = zeroid, latestTxid = zeroid, dummyTokenid, tokenidThis, tokenid = zeroid; - CScript fundingTxOpRetScript; - uint8_t hasHeirSpendingBegun = 0, hasHeirSpendingBegunDummy; - - CScript opret = (tx.vout.size() > 0) ? tx.vout[tx.vout.size() - 1].scriptPubKey : CScript(); // check boundary - uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenidThis, fundingTxidInOpret, hasHeirSpendingBegunDummy, true); - if (funcId == 0) + CScript fundingTxOpRetScript; + uint8_t hasHeirSpendingBegun = 0, hasHeirSpendingBegunDummy; + + CScript opret = (tx.vout.size() > 0) ? tx.vout[tx.vout.size() - 1].scriptPubKey : CScript(); // check boundary + uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenidThis, fundingTxidInOpret, hasHeirSpendingBegunDummy, true); + if (funcId == 0) return eval->Invalid("invalid opreturn format"); - + if (funcId != 'F') { if (fundingTxidInOpret == zeroid) { return eval->Invalid("incorrect tx opreturn: no fundingtxid present"); } - latestTxid = FindLatestFundingTx(fundingTxidInOpret, tokenid, fundingTxOpRetScript, hasHeirSpendingBegun); - - if( tokenid != zeroid && tokenid != tokenidThis ) - return eval->Invalid("incorrect tx tokenid"); - - if (latestTxid == zeroid) { + latestTxid = FindLatestFundingTx(fundingTxidInOpret, tokenid, fundingTxOpRetScript, hasHeirSpendingBegun); + + if( tokenid != zeroid && tokenid != tokenidThis ) + return eval->Invalid("incorrect tx tokenid"); + + if (latestTxid == zeroid) { return eval->Invalid("no fundingtx found"); } - } - else { - fundingTxOpRetScript = opret; - } - - std::cerr << "HeirValidate funcid=" << (char)funcId << " evalcode=" << (int)cpHeir->evalcode << std::endl; - - //////////////// temp //////////////////////// - ///return true; - + } + else { + fundingTxOpRetScript = opret; + } + + std::cerr << "HeirValidate funcid=" << (char)funcId << " evalcode=" << (int)cpHeir->evalcode << std::endl; + + //////////////// temp //////////////////////// + ///return true; + switch (funcId) { - case 'F': - // fund coins: - // vins.*: normal inputs - // ----------------------------- - // vout.0: funding CC 1of2 addr for the owner and heir - // vout.1: txfee for CC addr used as a marker - // vout.2: normal change - // vout.n-1: opreturn 'F' ownerpk heirpk inactivitytime heirname - - // fund tokens: - // vin.0: normal inputs txfee - // vins.1+: user's CC addr inputs - // ----------------------- - // vout.0: funding heir CC 1of2 addr for the owner and heir - // vout.1: txfee for CC addr used as a marker - // vout.2: normal change - // vout.n-1: opreturn 't' tokenid 'F' ownerpk heirpk inactivitytime heirname tokenid - if (tokenid != zeroid) - return RunValidationPlans(funcId, cpTokens, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); - else - return eval->Invalid("unexpected HeirValidate for heirfund"); - // break; - - case 'A': - // add funding coins: - // vins.*: normal inputs - // ------------------------ - // vout.0: funding CC 1of2 addr for the owner and heir - // vout.1: normal change - // vout.n-1: opreturn 'A' ownerpk heirpk inactivitytime fundingtx - - // add funding tokens: - // vins.0: normal inputs txfee - // vins.1+: user's CC addr inputs - // ------------------------ - // vout.0: funding CC 1of2 addr for the owner and heir - // vout.1: normal change - // vout.n-1: opreturn 't' tokenid 'A' ownerpk heirpk inactivitytime fundingtx - if (tokenid != zeroid) - return RunValidationPlans(funcId, cpTokens, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); - else - return eval->Invalid("unexpected HeirValidate for heiradd"); - //break; - - case 'C': - // claim coins: - // vin.0: normal input txfee - // vin.1+: input from CC 1of2 addr - // ------------------------------------- - // vout.0: normal output to owner or heir address - // vout.1: change to CC 1of2 addr - // vout.2: change to user's addr from txfee input if any - // vout.n-1: opreturn 'C' ownerpk heirpk inactivitytime fundingtx - - // claim tokens: - // vin.0: normal input txfee - // vin.1+: input from CC 1of2 addr - // -------------------------------------------- - // vout.0: output to user's cc address - // vout.1: change to CC 1of2 addr - // vout.2: change to normal from txfee input if any - // vout.n-1: opreturn 't' tokenid 'C' ownerpk heirpk inactivitytime fundingtx - if (tokenid != zeroid) - return RunValidationPlans(funcId, cpTokens, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); - else - return RunValidationPlans(funcId, cpHeir, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); - // break; - - default: - std::cerr << "HeirValidate() illegal heir funcid=" << (char)funcId << std::endl; - return eval->Invalid("unexpected HeirValidate funcid"); - // break; + case 'F': + // fund coins: + // vins.*: normal inputs + // ----------------------------- + // vout.0: funding CC 1of2 addr for the owner and heir + // vout.1: txfee for CC addr used as a marker + // vout.2: normal change + // vout.n-1: opreturn 'F' ownerpk heirpk inactivitytime heirname + + // fund tokens: + // vin.0: normal inputs txfee + // vins.1+: user's CC addr inputs + // ----------------------- + // vout.0: funding heir CC 1of2 addr for the owner and heir + // vout.1: txfee for CC addr used as a marker + // vout.2: normal change + // vout.n-1: opreturn 't' tokenid 'F' ownerpk heirpk inactivitytime heirname tokenid + if (tokenid != zeroid) + return RunValidationPlans(funcId, cpTokens, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); + else + return eval->Invalid("unexpected HeirValidate for heirfund"); + // break; + + case 'A': + // add funding coins: + // vins.*: normal inputs + // ------------------------ + // vout.0: funding CC 1of2 addr for the owner and heir + // vout.1: normal change + // vout.n-1: opreturn 'A' ownerpk heirpk inactivitytime fundingtx + + // add funding tokens: + // vins.0: normal inputs txfee + // vins.1+: user's CC addr inputs + // ------------------------ + // vout.0: funding CC 1of2 addr for the owner and heir + // vout.1: normal change + // vout.n-1: opreturn 't' tokenid 'A' ownerpk heirpk inactivitytime fundingtx + if (tokenid != zeroid) + return RunValidationPlans(funcId, cpTokens, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); + else + return eval->Invalid("unexpected HeirValidate for heiradd"); + //break; + + case 'C': + // claim coins: + // vin.0: normal input txfee + // vin.1+: input from CC 1of2 addr + // ------------------------------------- + // vout.0: normal output to owner or heir address + // vout.1: change to CC 1of2 addr + // vout.2: change to user's addr from txfee input if any + // vout.n-1: opreturn 'C' ownerpk heirpk inactivitytime fundingtx + + // claim tokens: + // vin.0: normal input txfee + // vin.1+: input from CC 1of2 addr + // -------------------------------------------- + // vout.0: output to user's cc address + // vout.1: change to CC 1of2 addr + // vout.2: change to normal from txfee input if any + // vout.n-1: opreturn 't' tokenid 'C' ownerpk heirpk inactivitytime fundingtx + if (tokenid != zeroid) + return RunValidationPlans(funcId, cpTokens, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); + else + return RunValidationPlans(funcId, cpHeir, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); + // break; + + default: + std::cerr << "HeirValidate() illegal heir funcid=" << (char)funcId << std::endl; + return eval->Invalid("unexpected HeirValidate funcid"); + // break; } return eval->Invalid("unexpected"); // (PreventCC(eval, tx, preventCCvins, numvins, preventCCvouts, numvouts)); } @@ -234,194 +236,211 @@ bool HeirValidate(struct CCcontract_info* cpHeir, Eval* eval, const CTransaction // helper functions used in implementations of rpc calls (in rpcwallet.cpp) or validation code /** -* Checks if vout is to cryptocondition address -* @return vout value in satoshis -*/ + * Checks if vout is to cryptocondition address + * @return vout value in satoshis + */ template int64_t IsHeirFundingVout(struct CCcontract_info* cp, const CTransaction& tx, int32_t voutIndex, CPubKey ownerPubkey, CPubKey heirPubkey) { - char destaddr[65], heirFundingAddr[65]; - - Helper::GetCoinsOrTokensCCaddress1of2(cp, heirFundingAddr, ownerPubkey, heirPubkey); - if (tx.vout[voutIndex].scriptPubKey.IsPayToCryptoCondition() != 0) { - // NOTE: dimxy it was unsafe 'Getscriptaddress(destaddr,tx.vout[voutIndex].scriptPubKey) > 0' here: - if (Getscriptaddress(destaddr, tx.vout[voutIndex].scriptPubKey) && strcmp(destaddr, heirFundingAddr) == 0) - return (tx.vout[voutIndex].nValue); - else - std::cerr << "IsHeirFundingVout() heirFundingAddr=" << heirFundingAddr << " not equal to destaddr=" << destaddr << std::endl; - } - return (0); + char destaddr[65], heirFundingAddr[65]; + + Helper::GetCoinsOrTokensCCaddress1of2(cp, heirFundingAddr, ownerPubkey, heirPubkey); + if (tx.vout[voutIndex].scriptPubKey.IsPayToCryptoCondition() != 0) { + // NOTE: dimxy it was unsafe 'Getscriptaddress(destaddr,tx.vout[voutIndex].scriptPubKey) > 0' here: + if (Getscriptaddress(destaddr, tx.vout[voutIndex].scriptPubKey) && strcmp(destaddr, heirFundingAddr) == 0) + return (tx.vout[voutIndex].nValue); + else + std::cerr << "IsHeirFundingVout() heirFundingAddr=" << heirFundingAddr << " not equal to destaddr=" << destaddr << std::endl; + } + return (0); } // makes coin initial tx opret CScript EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) { - uint8_t evalcode = EVAL_HEIR; - - return CScript() << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << ownerPubkey << heirPubkey << inactivityTimeSec << heirName); + uint8_t evalcode = EVAL_HEIR; + + return CScript() << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << ownerPubkey << heirPubkey << inactivityTimeSec << heirName); } // makes coin additional tx opret CScript EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t hasHeirSpendingBegun) { - uint8_t evalcode = EVAL_HEIR; - - fundingtxid = revuint256(fundingtxid); - return CScript() << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << fundingtxid << hasHeirSpendingBegun); + uint8_t evalcode = EVAL_HEIR; + + fundingtxid = revuint256(fundingtxid); + return CScript() << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << fundingtxid << hasHeirSpendingBegun); } // decode opret vout for Heir contract uint8_t _DecodeHeirOpRet(std::vector vopret, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { - uint8_t evalCodeInOpret = 0; - uint8_t heirFuncId = 0; - - fundingTxidInOpret = zeroid; //to init - - evalCodeInOpret = vopret.begin()[0]; - - if (vopret.size() > 1 && evalCodeInOpret == EVAL_HEIR) { - // NOTE: it unmarshals for all F, A and C - uint8_t heirFuncId = 0; - hasHeirSpendingBegun = 0; - - bool result = E_UNMARSHAL(vopret, { ss >> evalCodeInOpret; ss >> heirFuncId; \ - if (heirFuncId == 'F') { \ - ss >> ownerPubkey; ss >> heirPubkey; ss >> inactivityTime; ss >> heirName; \ - } \ - else { \ - ss >> fundingTxidInOpret >> hasHeirSpendingBegun; \ - } \ - }); - - if (!result) { - if (!noLogging) std::cerr << "_DecodeHeirOpRet() could not unmarshal opret, evalCode=" << (int)evalCodeInOpret << std::endl; - return (uint8_t)0; - } - - /* std::cerr << "DecodeHeirOpRet()" - << " heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') - << " ownerPubkey=" << HexStr(ownerPubkey) - << " heirPubkey=" << HexStr(heirPubkey) - << " heirName=" << heirName << " inactivityTime=" << inactivityTime - << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << std::endl; */ - - if (isMyFuncId(heirFuncId)) { - fundingTxidInOpret = revuint256(fundingTxidInOpret); - return heirFuncId; - } - else { - if(!noLogging) std::cerr << "_DecodeHeirOpRet() unexpected opret type, heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') << std::endl; - } - } - else { - if (!noLogging) std::cerr << "_DecodeHeirOpRet() not a heir opret, vopretExtra.size() == 0 or not EVAL_HEIR evalcode=" << (int)evalCodeInOpret << std::endl; - } - return (uint8_t)0; + uint8_t evalCodeInOpret = 0; + uint8_t heirFuncId = 0; + + fundingTxidInOpret = zeroid; //to init + + evalCodeInOpret = vopret.begin()[0]; + + if (vopret.size() > 1 && evalCodeInOpret == EVAL_HEIR) { + // NOTE: it unmarshals for all F, A and C + uint8_t heirFuncId = 0; + hasHeirSpendingBegun = 0; + + bool result = E_UNMARSHAL(vopret, { ss >> evalCodeInOpret; ss >> heirFuncId; \ + if (heirFuncId == 'F') { \ + ss >> ownerPubkey; ss >> heirPubkey; ss >> inactivityTime; ss >> heirName; \ + } \ + else { \ + ss >> fundingTxidInOpret >> hasHeirSpendingBegun; \ + } \ + }); + + if (!result) { + if (!noLogging) std::cerr << "_DecodeHeirOpRet() could not unmarshal opret, evalCode=" << (int)evalCodeInOpret << std::endl; + return (uint8_t)0; + } + + /* std::cerr << "DecodeHeirOpRet()" + << " heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') + << " ownerPubkey=" << HexStr(ownerPubkey) + << " heirPubkey=" << HexStr(heirPubkey) + << " heirName=" << heirName << " inactivityTime=" << inactivityTime + << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << std::endl; */ + + if (isMyFuncId(heirFuncId)) { + fundingTxidInOpret = revuint256(fundingTxidInOpret); + return heirFuncId; + } + else { + if(!noLogging) std::cerr << "_DecodeHeirOpRet() unexpected opret type, heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') << std::endl; + } + } + else { + if (!noLogging) std::cerr << "_DecodeHeirOpRet() not a heir opret, vopretExtra.size() == 0 or not EVAL_HEIR evalcode=" << (int)evalCodeInOpret << std::endl; + } + return (uint8_t)0; } /* not used, see DecodeHeirOpRet(vopret,...) -// overload for 'F' opret -uint8_t DecodeHeirOpRet(CScript scriptPubKey, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging) -{ + // overload for 'F' opret + uint8_t DecodeHeirOpRet(CScript scriptPubKey, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging) + { uint256 dummytxid; uint8_t dummyHasHeirSpendingBegun; std::vector vopret; - + GetOpReturnData(scriptPubKey, vopret); if (vopret.size() == 0) { - if (!noLogging) std::cerr << "DecodeHeirOpRet() warning: empty opret" << std::endl; - return (uint8_t)0; + if (!noLogging) std::cerr << "DecodeHeirOpRet() warning: empty opret" << std::endl; + return (uint8_t)0; } return _DecodeHeirOpRet(vopret, ownerPubkey, heirPubkey, inactivityTime, heirName, dummytxid, dummyHasHeirSpendingBegun, noLogging); -}*/ + }*/ /* not used, see DecodeHeirOpRet(vopret,...) -// overload for A, C oprets and AddHeirContractInputs -uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256& fundingtxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) -{ + // overload for A, C oprets and AddHeirContractInputs + uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256& fundingtxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) + { CPubKey dummyOwnerPubkey, dummyHeirPubkey; int64_t dummyInactivityTime; std::string dummyHeirName; std::vector vopret; - + GetOpReturnData(scriptPubKey, vopret); if (vopret.size() == 0) { - if (!noLogging) std::cerr << "DecodeHeirOpRet() warning: empty opret" << std::endl; - return (uint8_t)0; + if (!noLogging) std::cerr << "DecodeHeirOpRet() warning: empty opret" << std::endl; + return (uint8_t)0; } - + return _DecodeHeirOpRet(vopret, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, fundingtxidInOpret, hasHeirSpendingBegun, noLogging); -} */ + } */ // decode combined opret: uint8_t _DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { - uint8_t evalCodeTokens = 0; - std::vector voutPubkeysDummy; - std::vector vopretExtra, vopretStripped; - - if (DecodeTokenOpRet(scriptPubKey, evalCodeTokens, tokenid, voutPubkeysDummy, vopretExtra) != 0) { - if (vopretExtra.size() > 1) { - // restore the second opret: - - if (!E_UNMARSHAL(vopretExtra, { ss >> vopretStripped; })) { //strip string size - if (!noLogging) std::cerr << "_DecodeHeirEitherOpret() could not unmarshal vopretStripped" << std::endl; - return (uint8_t)0; - } - } - else { - if (!noLogging) std::cerr << "_DecodeHeirEitherOpret() empty vopretExtra" << std::endl; - return (uint8_t)0; - } - } - else - GetOpReturnData(scriptPubKey, vopretStripped); - - return _DecodeHeirOpRet(vopretStripped, ownerPubkey, heirPubkey, inactivityTime, heirName, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); - + uint8_t evalCodeTokens = 0; + std::vector voutPubkeysDummy; + std::vector vopretExtra, vopretStripped; + + if (DecodeTokenOpRet(scriptPubKey, evalCodeTokens, tokenid, voutPubkeysDummy, vopretExtra) != 0) { + if (vopretExtra.size() > 1) { + // restore the second opret: + + if (!E_UNMARSHAL(vopretExtra, { ss >> vopretStripped; })) { //strip string size + if (!noLogging) std::cerr << "_DecodeHeirEitherOpret() could not unmarshal vopretStripped" << std::endl; + return (uint8_t)0; + } + } + else { + if (!noLogging) std::cerr << "_DecodeHeirEitherOpret() empty vopretExtra" << std::endl; + return (uint8_t)0; + } + } + else + GetOpReturnData(scriptPubKey, vopretStripped); + + return _DecodeHeirOpRet(vopretStripped, ownerPubkey, heirPubkey, inactivityTime, heirName, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); + } // overload to decode opret in fundingtxid: uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging) { - uint256 dummyFundingTxidInOpret; - uint8_t dummyHasHeirSpendingBegun; - - return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, dummyFundingTxidInOpret, dummyHasHeirSpendingBegun, noLogging); + uint256 dummyFundingTxidInOpret; + uint8_t dummyHasHeirSpendingBegun; + + return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, dummyFundingTxidInOpret, dummyHasHeirSpendingBegun, noLogging); } // overload to decode opret in A and C heir tx: uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { - CPubKey dummyOwnerPubkey, dummyHeirPubkey; - int64_t dummyInactivityTime; - std::string dummyHeirName; + CPubKey dummyOwnerPubkey, dummyHeirPubkey; + int64_t dummyInactivityTime; + std::string dummyHeirName; + + return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); +} - return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); +// check if pubkey is in vins +void CheckVinPubkey(std::vector vins, CPubKey pubkey, bool &hasPubkey, bool &hasOtherPubkey) { + + hasPubkey = false; + hasOtherPubkey = false; + + for (auto vin : vins) { + CPubKey vinPubkey = check_signing_pubkey(vin.scriptSig); + if (vinPubkey.IsValid()) { + if (vinPubkey == pubkey) + hasPubkey = true; + if (vinPubkey != pubkey) + hasOtherPubkey = true; + } + } } /** - * find the latest funding tx: it may be the first F tx or one of A or C tx's - * Note: this function is also called from validation code (use non-locking calls) + * find the latest funding tx: it may be the first F tx or one of A or C tx's + * Note: this function is also called from validation code (use non-locking calls) */ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, CScript& fundingOpretScript, uint8_t &hasHeirSpendingBegun) { - CTransaction fundingtx; - uint256 hashBlock; + CTransaction fundingtx; + uint256 hashBlock; const bool allowSlow = false; - + //char markeraddr[64]; //CCtxidaddr(markeraddr, fundingtxid); //SetCCunspents(unspentOutputs, markeraddr); - - hasHeirSpendingBegun = 0; - funcId = 0; - + + hasHeirSpendingBegun = 0; + funcId = 0; + // get initial funding tx and set it as initial lasttx: if (myGetTransaction(fundingtxid, fundingtx, hashBlock) && fundingtx.vout.size()) { - - CScript heirScript = (fundingtx.vout.size() > 0) ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); - uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); + + CScript heirScript = (fundingtx.vout.size() > 0) ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); + uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); if (funcId != 0) { // found at least funding tx! //std::cerr << "FindLatestFundingTx() lasttx currently is fundingtx, txid=" << fundingtxid.GetHex() << " opreturn type=" << (char)funcId << '\n'; @@ -434,55 +453,65 @@ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &toke std::cerr << "FindLatestFundingTx() could not find funding tx for fundingtxid=" << fundingtxid.GetHex() << '\n'; return zeroid; } - - // TODO: correct cc addr: + + // TODO: correct cc addr: std::vector> unspentOutputs; struct CCcontract_info *cp, C; cp = CCinit(&C, EVAL_HEIR); char coinaddr[64]; GetCCaddress1of2(cp, coinaddr, ownerPubkey, heirPubkey); // get the address of cryptocondition '1 of 2 pubkeys' - + SetCCunspents(unspentOutputs, coinaddr); // get vector with tx's with unspent vouts of 1of2pubkey address: //std::cerr << "FindLatestFundingTx() using 1of2address=" << coinaddr << " unspentOutputs.size()=" << unspentOutputs.size() << '\n'; - + int32_t maxBlockHeight = 0; // max block height uint256 latesttxid = fundingtxid; - + // try to find the last funding or spending tx by checking fundingtxid in 'opreturn': for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { CTransaction regtx; uint256 hash; - + uint256 txid = it->first.txhash; //std::cerr << "FindLatestFundingTx() checking unspents for txid=" << txid.GetHex() << '\n'; - + int32_t blockHeight = (int32_t)it->second.blockHeight; - - //NOTE: maybe called from validation code: - if (myGetTransaction(txid, regtx, hash)) { - //std::cerr << "FindLatestFundingTx() found tx for txid=" << txid.GetHex() << " blockHeight=" << blockHeight << " maxBlockHeight=" << maxBlockHeight << '\n'; - uint256 fundingTxidInOpret; - uint256 tokenidInOpret; // not to contaminate the tokenid from the params! - uint8_t tmpFuncId; - uint8_t hasHeirSpendingBegunInOpret; - - CScript heirScript = (regtx.vout.size() > 0) ? regtx.vout[regtx.vout.size() - 1].scriptPubKey : CScript(); - tmpFuncId = DecodeHeirEitherOpRet(heirScript, tokenidInOpret, fundingTxidInOpret, hasHeirSpendingBegunInOpret, true); + + //NOTE: maybe called from validation code: + if (myGetTransaction(txid, regtx, hash)) { + //std::cerr << "FindLatestFundingTx() found tx for txid=" << txid.GetHex() << " blockHeight=" << blockHeight << " maxBlockHeight=" << maxBlockHeight << '\n'; + uint256 fundingTxidInOpret; + uint256 tokenidInOpret; // not to contaminate the tokenid from the params! + uint8_t tmpFuncId; + uint8_t hasHeirSpendingBegunInOpret; + + CScript heirScript = (regtx.vout.size() > 0) ? regtx.vout[regtx.vout.size() - 1].scriptPubKey : CScript(); + tmpFuncId = DecodeHeirEitherOpRet(heirScript, tokenidInOpret, fundingTxidInOpret, hasHeirSpendingBegunInOpret, true); if (tmpFuncId != 0 && fundingtxid == fundingTxidInOpret && (tokenid == zeroid || tokenid == tokenidInOpret)) { // check tokenid also - + if (blockHeight > maxBlockHeight) { - maxBlockHeight = blockHeight; - latesttxid = txid; - funcId = tmpFuncId; - hasHeirSpendingBegun = hasHeirSpendingBegunInOpret; - //std::cerr << "FindLatestFundingTx() txid=" << latesttxid.GetHex() << " at blockHeight=" << maxBlockHeight - // << " opreturn type=" << (char)(funcId ? funcId : ' ') << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << " - set as current lasttxid" << '\n'; + // check owner pubkey in vins + bool isOwner = false; + bool isNonOwner = false; + + CheckVinPubkey(regtx.vin, ownerPubkey, isOwner, isNonOwner); + + // we ignore 'donations' tx (with non-owner inputs) for calculating if heir is allowed to spend: + if (isOwner && !isNonOwner) { + hasHeirSpendingBegun = hasHeirSpendingBegunInOpret; + maxBlockHeight = blockHeight; + latesttxid = txid; + funcId = tmpFuncId; + } + + //std::cerr << "FindLatestFundingTx() txid=" << latesttxid.GetHex() << " at blockHeight=" << maxBlockHeight + // << " opreturn type=" << (char)(funcId ? funcId : ' ') << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << " - set as current lasttxid" << '\n'; } } } } - + return latesttxid; } @@ -494,7 +523,7 @@ uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRe CPubKey heirPubkey; int64_t inactivityTime; std::string heirName; - + return _FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, opRetScript, hasHeirSpendingBegun); } @@ -502,7 +531,7 @@ uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRe uint256 FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint8_t &hasHeirSpendingBegun) { CScript opRetScript; - + return _FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, opRetScript, hasHeirSpendingBegun); } @@ -514,41 +543,41 @@ template int64_t Add1of2AddressInputs(struct CCcontract_info* cp, CTransaction heirtx; int32_t n = 0; std::vector> unspentOutputs; - + char coinaddr[64]; - Helper::GetCoinsOrTokensCCaddress1of2(coinaddr, ownerPubkey, heirPubkey); // get address of cryptocondition '1 of 2 pubkeys' + Helper::GetCoinsOrTokensCCaddress1of2(coinaddr, ownerPubkey, heirPubkey); // get address of cryptocondition '1 of 2 pubkeys' SetCCunspents(unspentOutputs, coinaddr); - + // char markeraddr[64]; // CCtxidaddr(markeraddr, fundingtxid); // SetCCunspents(unspentOutputs, markeraddr); - + std::cerr << "Add1of2AddressInputs() using 1of2addr=" << coinaddr << " unspentOutputs.size()=" << unspentOutputs.size() << std::endl; - + for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { uint256 txid = it->first.txhash; uint256 hashBlock; int32_t voutIndex = (int32_t)it->first.index; // no need to prevent dup // dimxy: maybe it is good to put tx's in cache? - - std::cerr << "Add1of2AddressInputs() txid=" << txid.GetHex() << std::endl; - + + std::cerr << "Add1of2AddressInputs() txid=" << txid.GetHex() << std::endl; + if (GetTransaction(txid, heirtx, hashBlock, false) != 0) { - uint256 tokenid; + uint256 tokenid; uint256 fundingTxidInOpret; - uint8_t hasHeirSpendingBegunDummy; - - CScript heirScript = (heirtx.vout.size() > 0) ? heirtx.vout[heirtx.vout.size() - 1].scriptPubKey : CScript(); // check boundary - uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, fundingTxidInOpret, hasHeirSpendingBegunDummy, false); - - if ((txid == fundingtxid || fundingTxidInOpret == fundingtxid) && - funcId != 0 && - isMyFuncId(funcId) && - (typeid(Helper) != typeid(TokenHelper) || IsTokensvout(true, true, cp, nullptr, heirtx, voutIndex, tokenid) > 0) && // token validation logic + uint8_t hasHeirSpendingBegunDummy; + + CScript heirScript = (heirtx.vout.size() > 0) ? heirtx.vout[heirtx.vout.size() - 1].scriptPubKey : CScript(); // check boundary + uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, fundingTxidInOpret, hasHeirSpendingBegunDummy, false); + + if ((txid == fundingtxid || fundingTxidInOpret == fundingtxid) && + funcId != 0 && + isMyFuncId(funcId) && + (typeid(Helper) != typeid(TokenHelper) || IsTokensvout(true, true, cp, nullptr, heirtx, voutIndex, tokenid) > 0) && // token validation logic //(voutValue = IsHeirFundingVout(cp, heirtx, voutIndex, ownerPubkey, heirPubkey)) > 0 && // heir contract vout validation logic - not used since we moved to 2-eval vouts - !myIsutxo_spentinmempool(txid, voutIndex)) - { + !myIsutxo_spentinmempool(txid, voutIndex)) + { std::cerr << "Add1of2AddressInputs() satoshis=" << it->second.satoshis << std::endl; if (total != 0 && maxinputs != 0) mtx.vin.push_back(CTxIn(txid, voutIndex, CScript())); @@ -564,42 +593,42 @@ template int64_t Add1of2AddressInputs(struct CCcontract_info* cp, } /** - * enumerate all tx's sending to CCHeir 1of2address and calc total lifetime funds + * enumerate all tx's sending to CCHeir 1of2address and calc total lifetime funds */ template int64_t LifetimeHeirContractFunds(struct CCcontract_info* cp, uint256 fundingtxid, CPubKey ownerPubkey, CPubKey heirPubkey) { char coinaddr[64]; Helper::GetCoinsOrTokensCCaddress1of2(coinaddr, ownerPubkey, heirPubkey); // get the address of cryptocondition '1 of 2 pubkeys' - + std::vector> addressIndexes; SetCCtxids(addressIndexes, coinaddr); - + //fprintf(stderr,"LifetimeHeirContractFunds() scan lifetime of %s\n",coinaddr); int64_t total = 0; for (std::vector>::const_iterator it = addressIndexes.begin(); it != addressIndexes.end(); it++) { uint256 hashBlock; uint256 txid = it->first.txhash; CTransaction heirtx; - - // TODO: check all funding tx should contain unspendable markers + + // TODO: check all funding tx should contain unspendable markers if (GetTransaction(txid, heirtx, hashBlock, false) && heirtx.vout.size() > 0) { - uint256 tokenid; + uint256 tokenid; uint256 fundingTxidInOpret; - uint8_t hasHeirSpendingBegunDummy; + uint8_t hasHeirSpendingBegunDummy; const int32_t ivout = 0; - - CScript heirScript = (heirtx.vout.size() > 0) ? heirtx.vout[heirtx.vout.size() - 1].scriptPubKey : CScript(); // check boundary - uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, fundingTxidInOpret, hasHeirSpendingBegunDummy, false); - + + CScript heirScript = (heirtx.vout.size() > 0) ? heirtx.vout[heirtx.vout.size() - 1].scriptPubKey : CScript(); // check boundary + uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, fundingTxidInOpret, hasHeirSpendingBegunDummy, false); + //std::cerr << "LifetimeHeirContractFunds() found tx=" << txid.GetHex() << " vout[0].nValue=" << subtx.vout[ccVoutIdx].nValue << " opreturn=" << (char)funcId << '\n'; - - if (funcId != 0 && - (txid == fundingtxid || fundingTxidInOpret == fundingtxid) && - isMyFuncId(funcId) && !isSpendingTx(funcId) && - (typeid(Helper) != typeid(TokenHelper) || IsTokensvout(true, true, cp, nullptr, heirtx, ivout, tokenid) > 0) && - !myIsutxo_spentinmempool(txid, ivout)) // exclude tx in mempool + + if (funcId != 0 && + (txid == fundingtxid || fundingTxidInOpret == fundingtxid) && + isMyFuncId(funcId) && !isSpendingTx(funcId) && + (typeid(Helper) != typeid(TokenHelper) || IsTokensvout(true, true, cp, nullptr, heirtx, ivout, tokenid) > 0) && + !myIsutxo_spentinmempool(txid, ivout)) // exclude tx in mempool { - total += it->second; // dont do this: tx.vout[ivout].nValue; // in vin[0] always is the pay to 1of2 addr (funding or change) + total += it->second; // dont do this: tx.vout[ivout].nValue; // in vin[0] always is the pay to 1of2 addr (funding or change) //std::cerr << "LifetimeHeirContractFunds() added tx=" << txid.GetHex() << " it->second=" << it->second << " vout[0].nValue=" << tx.vout[ivout].nValue << " opreturn=" << (char)funcId << '\n'; } } @@ -611,90 +640,109 @@ template int64_t LifetimeHeirContractFunds(struct CCcontract_info /** * heirfund rpc call implementation - * creates tx for initial funds deposit on cryptocondition address which locks funds for spending by either of address. + * creates tx for initial funds deposit on cryptocondition address which locks funds for spending by either of address. * and also for setting spending plan for the funds' owner and heir * @return fundingtxid handle for subsequent references to this heir funding plan */ -template UniValue HeirFund(uint64_t txfee, int64_t amount, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid) +template UniValue _HeirFund(int64_t txfee, int64_t amount, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid) { - UniValue result(UniValue::VOBJ); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - struct CCcontract_info *cp, C; - + UniValue result(UniValue::VOBJ); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + struct CCcontract_info *cp, C; + cp = CCinit(&C, Helper::getMyEval()); if (txfee == 0) txfee = 10000; + int64_t markerfee = 10000; + //std::cerr << "HeirFund() amount=" << amount << " txfee=" << txfee << " heirPubkey IsValid()=" << heirPubkey.IsValid() << " inactivityTime(sec)=" << inactivityTimeSec << " tokenid=" << tokenid.GetHex() << std::endl; - - if (!heirPubkey.IsValid()) { - std::cerr << "HeirFund() heirPubkey is not valid!" << std::endl; - return std::string(""); - } - - CPubKey myPubkey = pubkey2pk(Mypubkey()); - - if (AddNormalinputs(mtx, myPubkey, txfee, 3) > 0) { // txfee for miners - int64_t inputs, change; - - if ((inputs=Helper::addOwnerInputs(tokenid, mtx, myPubkey, amount, (int32_t)64)) > 0) { // 2 x txfee: 1st for marker vout, 2nd to miners - //mtx.vout.push_back(MakeTokensCC1of2vout(/*Helper::getMyEval()*/EVAL_HEIR, amount, myPubkey, heirPubkey)); // add cryptocondition to spend amount for either pk - mtx.vout.push_back(Helper::make1of2Vout(amount, myPubkey, heirPubkey)); - - // add a marker for finding all plans in HeirList() - // TODO: change marker either to cc or normal txidaddr unspendable - CPubKey heirUnspendablePubKey = GetUnspendable(cp, 0); - mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(heirUnspendablePubKey)) << OP_CHECKSIG)); // TODO: do we need this marker? - - // calc and add change vout: - if (inputs > amount) - change = (inputs - amount); // -txfee <-- txfee pays user - - //std::cerr << "HeirFund() inputs=" << inputs << " amount=" << amount << " txfee=" << txfee << " change=" << change << '\n'; - - if (change != 0) { // vout[1] - mtx.vout.push_back(Helper::makeUserVout(change, myPubkey)); - } - - // add 1of2 vout validation pubkeys: - std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(myPubkey); - voutTokenPubkeys.push_back(heirPubkey); - - // add change for txfee and opreturn vouts and sign tx: - std::string rawhextx = FinalizeCCTx(0, cp, mtx, myPubkey, txfee, - Helper::makeCreateOpRet(tokenid, voutTokenPubkeys, myPubkey, heirPubkey, inactivityTimeSec, heirName)); - if (!rawhextx.empty()) { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hextx", rawhextx)); - } - else { - std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "sign error")); - } - } - else { // TODO: need result return unification with heiradd and claim - std::cerr << "HeirFund() could not find owner cc inputs" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "could not find owner cc inputs")); - } - } - else { - std::cerr << "HeirFund() could not find normal inputs" << std::endl; + + if (!heirPubkey.IsValid()) { + std::cerr << "HeirFund() heirPubkey is not valid!" << std::endl; result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "could not find normal inputs")); - } + result.push_back(Pair("error", "invalid heir pubkey")); + } + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + + if (AddNormalinputs(mtx, myPubkey, markerfee, 3) > 0) { + int64_t inputs, change; + + if ((inputs=Helper::addOwnerInputs(tokenid, mtx, myPubkey, amount, (int32_t)64)) > 0) { + + mtx.vout.push_back(Helper::make1of2Vout(amount, myPubkey, heirPubkey)); + + // add a marker for finding all plans in HeirList() + // TODO: change marker either to cc or normal txidaddr unspendable + struct CCcontract_info *cpHeir, heirC; + cpHeir = CCinit(&heirC, EVAL_HEIR); + CPubKey heirUnspendablePubKey = GetUnspendable(cpHeir, 0); + // mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(heirUnspendablePubKey)) << OP_CHECKSIG)); <-- bad marker cause it was spendable by anyone + mtx.vout.push_back(MakeCC1vout(EVAL_HEIR, markerfee, heirUnspendablePubKey)); // this marker spending is disabled in the validation code + + // calc and add change vout: + if (inputs > amount) + change = (inputs - amount); // -txfee <-- txfee pays user + + //std::cerr << "HeirFund() inputs=" << inputs << " amount=" << amount << " txfee=" << txfee << " change=" << change << '\n'; + + if (change != 0) { // vout[1] + mtx.vout.push_back(Helper::makeUserVout(change, myPubkey)); + } + + // check owner pubkey in vins + bool hasMypubkey = false; + bool hasNotMypubkey = false; + + CheckVinPubkey(mtx.vin, myPubkey, hasMypubkey, hasNotMypubkey); + + // for initial funding do not allow to sign by non-owner key: + if (hasNotMypubkey) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "using non-owner inputs not allowed")); + return result; + } + + // add 1of2 vout validation pubkeys: + std::vector voutTokenPubkeys; + voutTokenPubkeys.push_back(myPubkey); + voutTokenPubkeys.push_back(heirPubkey); + + // add change for txfee and opreturn vouts and sign tx: + std::string rawhextx = FinalizeCCTx(0, cp, mtx, myPubkey, txfee, + Helper::makeCreateOpRet(tokenid, voutTokenPubkeys, myPubkey, heirPubkey, inactivityTimeSec, heirName)); + if (!rawhextx.empty()) { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hextx", rawhextx)); + } + else { + std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "sign error")); + } + } + else { // TODO: need result return unification with heiradd and claim + std::cerr << "HeirFund() could not find owner cc inputs" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "could not find owner cc inputs")); + } + } + else { + std::cerr << "HeirFund() could not find normal inputs" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "could not find normal inputs")); + } return result; } -// if no these callers - it could not link -UniValue HeirFundCoinCaller(uint64_t txfee, int64_t funds, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid){ - return HeirFund(txfee, funds, heirName, heirPubkey, inactivityTimeSec, tokenid); +// if no these callers - it could not link +UniValue HeirFundCoinCaller(int64_t txfee, int64_t satoshis, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid){ + return _HeirFund(txfee, satoshis, heirName, heirPubkey, inactivityTimeSec, tokenid); } -UniValue HeirFundTokenCaller(uint64_t txfee, int64_t funds, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid) { - return HeirFund(txfee, funds, heirName, heirPubkey, inactivityTimeSec, tokenid); +UniValue HeirFundTokenCaller(int64_t txfee, int64_t satoshis, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid) { + return _HeirFund(txfee, satoshis, heirName, heirPubkey, inactivityTimeSec, tokenid); } /** @@ -702,115 +750,158 @@ UniValue HeirFundTokenCaller(uint64_t txfee, int64_t funds, std::string heirName * creates tx to add more funds to cryptocondition address for spending by either funds' owner or heir * @return result object with raw tx or error text */ -template UniValue _HeirAdd(uint256 fundingtxid, uint64_t txfee, int64_t amount, uint256 latesttxid, uint8_t funcId, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, uint8_t hasHeirSpendingBegun) +template UniValue _HeirAdd(uint256 fundingtxid, int64_t txfee, int64_t amount, uint256 latesttxid, uint8_t funcId, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, uint8_t hasHeirSpendingBegun) { - UniValue result(UniValue::VOBJ); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); int64_t inputs, CCchange = 0; struct CCcontract_info *cp, C; std::string rawhex; - - cp = CCinit(&C, Helper::getMyEval()); // for tokens shoud be EVAL_TOKENS to sign it correctly! - + + cp = CCinit(&C, Helper::getMyEval()); // for tokens shoud be EVAL_TOKENS to sign it correctly! + if (txfee == 0) txfee = 10000; + + int64_t markerfee = 10000; CPubKey myPubkey = pubkey2pk(Mypubkey()); - + // check if it is the owner if (myPubkey != ownerPubkey) { result.push_back(Pair("result", "error")); result.push_back(Pair("error", "adding funds is only allowed for the owner of this contract")); return result; } + + if (AddNormalinputs(mtx, myPubkey, markerfee, 3) > 0) { // some for marker + + int64_t inputs, change; + + if ((inputs = Helper::addOwnerInputs(tokenid, mtx, myPubkey, amount, 64)) > 0) { // TODO: why 64 max inputs? + + // we do not use markers anymore - storing data in opreturn is better + // add marker vout: + /* char markeraddr[64]; + CPubKey markerpubkey = CCtxidaddr(markeraddr, fundingtxid); + mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); // txfee 1, txfee 2 - for miners + std::cerr << "HeirAdd() adding markeraddr=" << markeraddr << '\n'; */ + + // add cryptocondition to spend this funded amount for either pk + mtx.vout.push_back(Helper::make1of2Vout(amount, ownerPubkey, heirPubkey)); - if (AddNormalinputs(mtx, myPubkey, txfee, 3) > 0) { // txfee for miners + char markeraddr[64]; + CPubKey markerPubkey = CCtxidaddr(markeraddr, fundingtxid); + mtx.vout.push_back(CTxOut(markerfee, CScript() << ParseHex(HexStr(markerPubkey)) << OP_CHECKSIG)); // marker to prevent archiving of the funds add vouts - int64_t inputs, change; + if (inputs > amount) + change = (inputs - amount); // -txfee <-- txfee pays user + + //std::cerr << "HeirAdd() inputs=" << inputs << " amount=" << amount << " txfee=" << txfee << " change=" << change << '\n'; + + if (change != 0) { // vout[1] + mtx.vout.push_back(Helper::makeUserVout(change, myPubkey)); + } - if ((inputs = Helper::addOwnerInputs(tokenid, mtx, myPubkey, amount, 64)) > 0) { // TODO: why 64 max inputs? + // check owner pubkey in vins + bool hasMypubkey = false; + bool hasNotMypubkey = false; - // we do not use markers anymore - storing data in opreturn is better - // add marker vout: - /* char markeraddr[64]; - CPubKey markerpubkey = CCtxidaddr(markeraddr, fundingtxid); - mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); // txfee 1, txfee 2 - for miners - std::cerr << "HeirAdd() adding markeraddr=" << markeraddr << '\n'; */ + CheckVinPubkey(mtx.vin, myPubkey, hasMypubkey, hasNotMypubkey); - // add cryptocondition to spend this funded amount for either pk - mtx.vout.push_back(Helper::make1of2Vout(amount, ownerPubkey, heirPubkey)); - - if (inputs > amount) - change = (inputs - amount); // -txfee <-- txfee pays user - - //std::cerr << "HeirAdd() inputs=" << inputs << " amount=" << amount << " txfee=" << txfee << " change=" << change << '\n'; - - if (change != 0) { // vout[1] - mtx.vout.push_back(Helper::makeUserVout(change, myPubkey)); + // for additional funding do not allow to sign by both owner and non-owner keys (is this a donation or not?): + if (hasMypubkey && hasNotMypubkey) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "using both owner and non-owner inputs is not allowed")); + return result; } - - // add 1of2 vout validation pubkeys - needed only for tokens: - std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(ownerPubkey); - voutTokenPubkeys.push_back(heirPubkey); - - // add opreturn 'A' and sign tx: // this txfee ignored - std::string rawhextx = (FinalizeCCTx(0, cp, mtx, myPubkey, txfee, - Helper::makeAddOpRet(tokenid, voutTokenPubkeys, fundingtxid, hasHeirSpendingBegun))); - - if (!rawhextx.empty()) { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hextx", rawhextx)); + + // warn the user he's making a donation if this is all non-owner keys: + if (hasNotMypubkey) { + result.push_back(Pair("result", "warning")); + result.push_back(Pair("warning", "you are about to make a donation to heir fund")); } else { - std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "sign error")); + result.push_back(Pair("result", "success")); } - - } - else { - std::cerr << "HeirAdd cannot find owner cc inputs" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "can't find owner cc inputs")); - } - } - else { - std::cerr << "HeirAdd cannot find normal inputs for tx fee" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "can't find normal inputs for tx fee")); - } - - + // add 1of2 vout validation pubkeys - needed only for tokens: + std::vector voutTokenPubkeys; + voutTokenPubkeys.push_back(ownerPubkey); + voutTokenPubkeys.push_back(heirPubkey); + + // add opreturn 'A' and sign tx: // this txfee ignored + std::string rawhextx = (FinalizeCCTx(0, cp, mtx, myPubkey, txfee, + Helper::makeAddOpRet(tokenid, voutTokenPubkeys, fundingtxid, hasHeirSpendingBegun))); + + if (!rawhextx.empty()) { + result.push_back(Pair("hextx", rawhextx)); + } + else { + std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; + result.clear(); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "sign error")); + } + + } + else { + std::cerr << "HeirAdd cannot find owner cc inputs" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "can't find owner cc inputs")); + } + } + else { + std::cerr << "HeirAdd cannot find normal inputs for tx fee" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "can't find normal inputs for tx fee")); + } + return result; } -UniValue HeirAddCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount) { +UniValue HeirAddCaller(uint256 fundingtxid, int64_t txfee, std::string strAmount) { + + CPubKey ownerPubkey, heirPubkey; + int64_t inactivityTimeSec; + + uint256 latesttxid, tokenid = zeroid; + uint8_t funcId; + std::string heirName; + uint8_t hasHeirSpendingBegun = 0; + + if ((latesttxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun)) != zeroid) { + if (tokenid == zeroid) { + int64_t amount = (int64_t)(atof(strAmount.c_str()) * COIN); + if (amount <= 0) { + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "invalid amount")); + return result; + } - CPubKey ownerPubkey, heirPubkey; - int64_t inactivityTimeSec; - - uint256 latesttxid, tokenid = zeroid; - uint8_t funcId; - std::string heirName; - uint8_t hasHeirSpendingBegun = 0; - - if ((latesttxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun)) != zeroid) { - if (tokenid == zeroid) return _HeirAdd(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); - else + } + else { + int64_t amount = atoll(strAmount.c_str()); + if (amount <= 0) { + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "invalid amount")); + return result; + } return _HeirAdd(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); - } - else { - UniValue result(UniValue::VOBJ); - - fprintf(stderr, "HeirAdd() can't find any heir CC funding tx's\n"); - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "can't find any heir CC funding transactions")); - return result; - } + } + } + else { + UniValue result(UniValue::VOBJ); + + fprintf(stderr, "HeirAdd() can't find any heir CC funding tx's\n"); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "can't find any heir CC funding transactions")); + return result; + } } @@ -819,112 +910,112 @@ UniValue HeirAddCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount) { * creates tx to spend funds from cryptocondition address by either funds' owner or heir * @return result object with raw tx or error text */ -template UniValue _HeirClaim(uint256 fundingtxid, uint64_t txfee, int64_t amount, uint256 latesttxid, uint8_t funcId, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, uint8_t hasHeirSpendingBegun) +template UniValue _HeirClaim(uint256 fundingtxid, int64_t txfee, int64_t amount, uint256 latesttxid, uint8_t funcId, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, uint8_t hasHeirSpendingBegun) { - UniValue result(UniValue::VOBJ); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey myPubkey; + UniValue result(UniValue::VOBJ); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey myPubkey; int64_t inputs, change = 0; struct CCcontract_info *cp, C; - - cp = CCinit(&C, EVAL_HEIR); - if (txfee == 0) + + cp = CCinit(&C, EVAL_HEIR); + if (txfee == 0) txfee = 10000; - + int32_t numblocks; uint64_t durationSec = 0; - - // we do not need to find duration if spending already has begun - if (!hasHeirSpendingBegun) { + + // we do not need to find duration if spending already has begun + if (!hasHeirSpendingBegun) { durationSec = CCduration(numblocks, latesttxid); std::cerr << "HeirClaim() duration=" << durationSec << " inactivityTime=" << inactivityTimeSec << " numblocks=" << numblocks << std::endl; } - + // spending is allowed if there is already spending tx or inactivity time //bool isAllowedToHeir = (funcId == 'C' || durationSec > inactivityTimeSec) ? true : false; - bool isAllowedToHeir = (hasHeirSpendingBegun || durationSec > inactivityTimeSec) ? true : false; + bool isAllowedToHeir = (hasHeirSpendingBegun || durationSec > inactivityTimeSec) ? true : false; myPubkey = pubkey2pk(Mypubkey()); - + // if it is the heir, check if spending not allowed to heir yet if (myPubkey == heirPubkey && !isAllowedToHeir) { result.push_back(Pair("result", "error")); result.push_back(Pair("error", "spending is not allowed yet for the heir")); return result; } - - // we do not use markers any more: - // we allow owner to spend funds at any time: - // if it is the owner, check if spending already allowed to heir - /* if (myPubkey == ownerPubkey && isAllowedToHeir) { - result.push_back(Pair("result", "spending is not already allowed for the owner")); - return result; - } */ - - // add spending txfee from the calling user + + // we do not use markers any more: + // we allow owner to spend funds at any time: + // if it is the owner, check if spending already allowed to heir + /* if (myPubkey == ownerPubkey && isAllowedToHeir) { + result.push_back(Pair("result", "spending is not already allowed for the owner")); + return result; + } */ + + // add spending txfee from the calling user if (AddNormalinputs(mtx, myPubkey, txfee, 3) > 0) { - - // add spending from cc 1of2 address - if ((inputs = Add1of2AddressInputs(cp, fundingtxid, mtx, ownerPubkey, heirPubkey, amount, 60)) >= amount) // TODO: why only 60 inputs? + + // add spending from cc 1of2 address + if ((inputs = Add1of2AddressInputs(cp, fundingtxid, mtx, ownerPubkey, heirPubkey, amount, 60)) >= amount) // TODO: why only 60 inputs? { - /*if (inputs < amount) { - std::cerr << "HeirClaim() cant find enough HeirCC 1of2 inputs, found=" << inputs << " required=" << amount << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "can't find heir CC funding")); - - return result; - }*/ - - // add vout with amount to claiming address - mtx.vout.push_back(Helper::makeUserVout(amount, myPubkey)); // vout[0] - + /*if (inputs < amount) { + std::cerr << "HeirClaim() cant find enough HeirCC 1of2 inputs, found=" << inputs << " required=" << amount << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "can't find heir CC funding")); + + return result; + }*/ + + // add vout with amount to claiming address + mtx.vout.push_back(Helper::makeUserVout(amount, myPubkey)); // vout[0] + // calc and add change vout: if (inputs > amount) - change = (inputs - amount); // -txfee <-- txfee pays user - + change = (inputs - amount); // -txfee <-- txfee pays user + //std::cerr << "HeirClaim() inputs=" << inputs << " amount=" << amount << " txfee=" << txfee << " change=" << change << '\n'; - - // change to 1of2 funding addr: + + // change to 1of2 funding addr: if (change != 0) { // vout[1] mtx.vout.push_back(Helper::make1of2Vout(change, ownerPubkey, heirPubkey)); // using always pubkeys from OP_RETURN in order to not mixing them up! } - + // add marker vout: /*char markeraddr[64]; - CPubKey markerpubkey = CCtxidaddr(markeraddr, fundingtxid); - // NOTE: amount = 0 is not working: causes error code: -26, error message : 64 : dust - mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); // txfee 1, txfee 2 - for miners - std::cerr << "HeirClaim() adding markeraddr=" << markeraddr << '\n'; */ - - // get address of 1of2 cond + CPubKey markerpubkey = CCtxidaddr(markeraddr, fundingtxid); + // NOTE: amount = 0 is not working: causes error code: -26, error message : 64 : dust + mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); // txfee 1, txfee 2 - for miners + std::cerr << "HeirClaim() adding markeraddr=" << markeraddr << '\n'; */ + + // get address of 1of2 cond char coinaddr[64]; Helper::GetCoinsOrTokensCCaddress1of2(coinaddr, ownerPubkey, heirPubkey); - - // retrieve priv key addresses for FinalizeCCtx: - uint8_t myprivkey[32]; + + // retrieve priv key addresses for FinalizeCCtx: + uint8_t myprivkey[32]; Myprivkey(myprivkey); - - // set pubkeys for finding 1of2 cc in FinalizeCCtx to sign it: - Helper::CCaddrCoinsOrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); - - // add 1of2 vout validation pubkeys (this is for tokens): - std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(ownerPubkey); - voutTokenPubkeys.push_back(heirPubkey); - + + // set pubkeys for finding 1of2 cc in FinalizeCCtx to sign it: + Helper::CCaddrCoinsOrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); + + // add 1of2 vout validation pubkeys (this is for tokens): + std::vector voutTokenPubkeys; + voutTokenPubkeys.push_back(ownerPubkey); + voutTokenPubkeys.push_back(heirPubkey); + // add opreturn 'C' and sign tx: // this txfee will be ignored - std::string rawhextx = FinalizeCCTx(0, cp, mtx, myPubkey, txfee, - Helper::makeClaimOpRet(tokenid, voutTokenPubkeys, fundingtxid, (myPubkey == heirPubkey) ? 1 : hasHeirSpendingBegun)); // forward isHeirSpending to the next latest tx - - if (!rawhextx.empty()) { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hextx", rawhextx)); - } - else { - std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "sign error")); - } - + std::string rawhextx = FinalizeCCTx(0, cp, mtx, myPubkey, txfee, + Helper::makeClaimOpRet(tokenid, voutTokenPubkeys, fundingtxid, (myPubkey == heirPubkey) ? 1 : hasHeirSpendingBegun)); // forward isHeirSpending to the next latest tx + + if (!rawhextx.empty()) { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hextx", rawhextx)); + } + else { + std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "sign error")); + } + } else { fprintf(stderr, "HeirClaim() cant find Heir CC inputs\n"); result.push_back(Pair("result", "error")); @@ -935,187 +1026,213 @@ template UniValue _HeirClaim(uint256 fundingtxid, uint64_t txfe result.push_back(Pair("result", "error")); result.push_back(Pair("error", "can't find sufficient user inputs to pay transaction fee")); } - - + + return result; } -UniValue HeirClaimCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount) { - - CPubKey ownerPubkey, heirPubkey; - int64_t inactivityTimeSec; - - uint256 latesttxid, tokenid = zeroid; - uint8_t funcId; - std::string heirName; - uint8_t hasHeirSpendingBegun = 0; - - if ((latesttxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun)) != zeroid) { - if( tokenid == zeroid ) +UniValue HeirClaimCaller(uint256 fundingtxid, int64_t txfee, std::string strAmount) { + + CPubKey ownerPubkey, heirPubkey; + int64_t inactivityTimeSec; + + uint256 latesttxid, tokenid = zeroid; + uint8_t funcId; + std::string heirName; + uint8_t hasHeirSpendingBegun = 0; + + if ((latesttxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun)) != zeroid) { + if (tokenid == zeroid) { + int64_t amount = (int64_t)(atof(strAmount.c_str()) * COIN); + if (amount < 0) { + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "invalid amount")); + return result; + } return _HeirClaim(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); - else + } + else { + int64_t amount = atoll(strAmount.c_str()); + if (amount <= 0) { + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "invalid amount")); + return result; + } return _HeirClaim(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); - - } - else { - UniValue result(UniValue::VOBJ); - - fprintf(stderr, "HeirClaim() can't find any heir CC funding tx's\n"); - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "can't find any heir CC funding transactions")); - return result; - } + } + + } + else { + UniValue result(UniValue::VOBJ); + + fprintf(stderr, "HeirClaim() can't find any heir CC funding tx's\n"); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "can't find any heir CC funding transactions")); + return result; + } } /** * heirinfo rpc call implementation - * returns some information about heir CC contract plan by a handle of initial fundingtxid: + * returns some information about heir CC contract plan by a handle of initial fundingtxid: * plan name, owner and heir pubkeys, funds deposited and available, flag if spending is enabled for the heir * @return heir info data */ UniValue HeirInfo(uint256 fundingtxid) { UniValue result(UniValue::VOBJ); + + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CTransaction fundingtx; + uint256 hashBlock; + const bool allowSlow = false; + + //char markeraddr[64]; + //CCtxidaddr(markeraddr, fundingtxid); + //SetCCunspents(unspentOutputs, markeraddr); + + // get initial funding tx and set it as initial lasttx: + if (myGetTransaction(fundingtxid, fundingtx, hashBlock) && fundingtx.vout.size()) { + + CPubKey ownerPubkey, heirPubkey; + uint256 dummyTokenid, tokenid = zeroid; // important to clear tokenid + std::string heirName; + int64_t inactivityTimeSec; + const bool noLogging = false; + uint8_t funcId; + + /*CScript opret = fundingtx.vout.size() > 0 ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); + uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, true); + if (funcId == 0) { + std::cerr << "HeirInfo() this fundingtx is incorrect" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "initial tx F not found")); + return result; + }*/ + + struct CCcontract_info *cp, C; + cp = CCinit(&C, EVAL_HEIR); + + uint8_t hasHeirSpendingBegun = 0; + + uint256 latestFundingTxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + + if (latestFundingTxid != zeroid) { + int32_t numblocks; + uint64_t durationSec = 0; + + //std::cerr << "HeirInfo() latesttxid=" << latestFundingTxid.GetHex() << '\n'; + + std::ostringstream stream; + std::string msg; + + //sleep(10); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction fundingtx; - uint256 hashBlock; - const bool allowSlow = false; + result.push_back(Pair("fundingtxid", fundingtxid.GetHex())); + result.push_back(Pair("name", heirName.c_str())); + + if (tokenid != zeroid) { // tokens + stream << tokenid.GetHex(); + msg = "tokenid"; + result.push_back(Pair(msg, stream.str().c_str())); + stream.str(""); + stream.clear(); + } + + char hexbuf[67]; + stream << pubkey33_str(hexbuf, (uint8_t*)ownerPubkey.begin()); + result.push_back(Pair("owner", stream.str().c_str())); + stream.str(""); + stream.clear(); + + stream << pubkey33_str(hexbuf, (uint8_t*)heirPubkey.begin()); + result.push_back(Pair("heir", stream.str().c_str())); + stream.str(""); + stream.clear(); + + int64_t total; + if (tokenid == zeroid) + total = LifetimeHeirContractFunds(cp, fundingtxid, ownerPubkey, heirPubkey); + else + total = LifetimeHeirContractFunds(cp, fundingtxid, ownerPubkey, heirPubkey); + + if (tokenid == zeroid) { + msg = "funding total in coins"; + stream << std::fixed << std::setprecision(8) << (double)total / COIN; + } + else { + msg = "funding total in tokens"; + stream << total; + } + result.push_back(Pair(msg, stream.str().c_str())); + stream.str(""); + stream.clear(); + + int64_t inputs; + if (tokenid == zeroid) + inputs = Add1of2AddressInputs(cp, fundingtxid, mtx, ownerPubkey, heirPubkey, 0, 60); //NOTE: amount = 0 means all unspent inputs + else + inputs = Add1of2AddressInputs(cp, fundingtxid, mtx, ownerPubkey, heirPubkey, 0, 60); + + if (tokenid == zeroid) { + msg = "funding available in coins"; + stream << std::fixed << std::setprecision(8) << (double)inputs / COIN; + } + else { + msg = "funding available in tokens"; + stream << inputs; + } + result.push_back(Pair(msg, stream.str().c_str())); + stream.str(""); + stream.clear(); + + if (tokenid != zeroid) { + int64_t ownerInputs = TokenHelper::addOwnerInputs(tokenid, mtx, ownerPubkey, 0, (int32_t)64); + stream << ownerInputs; + msg = "owner funding available in tokens"; + result.push_back(Pair(msg, stream.str().c_str())); + stream.str(""); + stream.clear(); + } + + stream << inactivityTimeSec; + result.push_back(Pair("inactivity time setting, sec", stream.str().c_str())); + stream.str(""); + stream.clear(); + + if (!hasHeirSpendingBegun) { // we do not need find duration if the spending already has begun + durationSec = CCduration(numblocks, latestFundingTxid); + std::cerr << "HeirInfo() duration (sec)=" << durationSec << " inactivityTime (sec)=" << inactivityTimeSec << " numblocks=" << numblocks << '\n'; + } + + stream << std::boolalpha << (hasHeirSpendingBegun || durationSec > inactivityTimeSec); + result.push_back(Pair("spending allowed for the heir", stream.str().c_str())); + stream.str(""); + stream.clear(); - //char markeraddr[64]; - //CCtxidaddr(markeraddr, fundingtxid); - //SetCCunspents(unspentOutputs, markeraddr); - - // get initial funding tx and set it as initial lasttx: - if (myGetTransaction(fundingtxid, fundingtx, hashBlock) && fundingtx.vout.size()) { - - CPubKey ownerPubkey, heirPubkey; - uint256 dummyTokenid, tokenid = zeroid; // important to clear tokenid - std::string heirName; - int64_t inactivityTimeSec; - const bool noLogging = false; - uint8_t funcId; - - /*CScript opret = fundingtx.vout.size() > 0 ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); - uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, true); - if (funcId == 0) { - std::cerr << "HeirInfo() this fundingtx is incorrect" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "initial tx F not found")); - return result; - }*/ - - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_HEIR); - - uint8_t hasHeirSpendingBegun = 0; - - uint256 latestFundingTxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); - - if (latestFundingTxid != zeroid) { - int32_t numblocks; - uint64_t durationSec = 0; - - //std::cerr << "HeirInfo() latesttxid=" << latestFundingTxid.GetHex() << '\n'; - - std::ostringstream stream; - std::string msg; - - result.push_back(Pair("fundingtxid", fundingtxid.GetHex())); - result.push_back(Pair("name", heirName.c_str())); - - if (tokenid != zeroid) { // tokens - stream << tokenid.GetHex(); - msg = "tokenid"; - result.push_back(Pair(msg, stream.str().c_str())); + // adding owner current inactivity time: + if (!hasHeirSpendingBegun && durationSec <= inactivityTimeSec) { + stream << durationSec; + result.push_back(Pair("owner inactivity time, sec", stream.str().c_str())); stream.str(""); stream.clear(); } - - char hexbuf[67]; - stream << pubkey33_str(hexbuf, (uint8_t*)ownerPubkey.begin()); - result.push_back(Pair("owner", stream.str().c_str())); - stream.str(""); - stream.clear(); - - stream << pubkey33_str(hexbuf, (uint8_t*)heirPubkey.begin()); - result.push_back(Pair("heir", stream.str().c_str())); - stream.str(""); - stream.clear(); - - int64_t total; - if (tokenid == zeroid) - total = LifetimeHeirContractFunds(cp, fundingtxid, ownerPubkey, heirPubkey); - else - total = LifetimeHeirContractFunds(cp, fundingtxid, ownerPubkey, heirPubkey); - - if (tokenid == zeroid) { - msg = "funding total in coins"; - stream << (double)total / COIN; - } - else { - msg = "funding total in tokens"; - stream << total; - } - result.push_back(Pair(msg, stream.str().c_str())); - stream.str(""); - stream.clear(); - - int64_t inputs; - if (tokenid == zeroid) - inputs = Add1of2AddressInputs(cp, fundingtxid, mtx, ownerPubkey, heirPubkey, 0, 60); //NOTE: amount = 0 means all unspent inputs - else - inputs = Add1of2AddressInputs(cp, fundingtxid, mtx, ownerPubkey, heirPubkey, 0, 60); - - if (tokenid == zeroid) { - msg = "funding available in coins"; - stream << (double)inputs / COIN; - } - else { - msg = "funding available in tokens"; - stream << inputs; - } - result.push_back(Pair(msg, stream.str().c_str())); - stream.str(""); - stream.clear(); - - if (tokenid != zeroid) { - int64_t ownerInputs = TokenHelper::addOwnerInputs(tokenid, mtx, ownerPubkey, 0, (int32_t)64); - stream << ownerInputs; - msg = "owner funding available in tokens"; - result.push_back(Pair(msg, stream.str().c_str())); - stream.str(""); - stream.clear(); - } - - stream << inactivityTimeSec; - result.push_back(Pair("inactivity time setting", stream.str().c_str())); - stream.str(""); - stream.clear(); - - if (!hasHeirSpendingBegun) { // we do not need find duration if the spending already has begun - durationSec = CCduration(numblocks, latestFundingTxid); - std::cerr << "HeirInfo() duration=" << durationSec << " inactivityTime=" << inactivityTimeSec << " numblocks=" << numblocks << '\n'; - } - - stream << std::boolalpha << (hasHeirSpendingBegun || durationSec > inactivityTimeSec); - result.push_back(Pair("spending allowed for the heir", stream.str().c_str())); - stream.str(""); - stream.clear(); - - result.push_back(Pair("result", "success")); - } - else { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "could not find heir cc plan for this txid")); - } - } - else { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "could not find heir cc plan for this txid (no initial tx)")); - } - return (result); + + result.push_back(Pair("result", "success")); + } + else { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "could not find heir cc plan for this txid")); + } + } + else { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "could not find heir cc plan for this txid (no initial tx)")); + } + return (result); } /** @@ -1123,49 +1240,49 @@ UniValue HeirInfo(uint256 fundingtxid) * @return list of heir plan handles (fundingtxid) */ -template void _HeirList(struct CCcontract_info *cp, UniValue &result) +void _HeirList(struct CCcontract_info *cp, UniValue &result) { - std::vector> unspentOutputs; - char coinaddr[64]; - CPubKey ccPubKeyEmpty; - GetCCaddress(cp, coinaddr, ccPubKeyEmpty); - SetCCunspents(unspentOutputs, cp->normaladdr); + std::vector> unspentOutputs; + char markeraddr[64]; - //std::cerr << "HeirList() finding heir marker from Heir contract addr=" << cp->normaladdr << " unspentOutputs.size()=" << unspentOutputs.size() << '\n'; - - // TODO: move marker to special cc addr to prevent checking all tokens - for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { - uint256 hashBlock; - uint256 txid = it->first.txhash; - uint256 tokenid; - int32_t vout = (int32_t)it->first.index; - - //std::cerr << "HeirList() checking txid=" << txid.GetHex() << " vout=" << vout << '\n'; - - CTransaction fundingtx; - if (GetTransaction(txid, fundingtx, hashBlock, false)) { - CPubKey ownerPubkey, heirPubkey; - std::string heirName; - int64_t inactivityTimeSec; - const bool noLogging = true; - uint256 tokenid; - - CScript opret = (fundingtx.vout.size() > 0) ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); - uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, true); - - // note: if it is not Heir token funcId would be equal to 0 - if (funcId == 'F') { - //result.push_back(Pair("fundingtxid kind name", txid.GetHex() + std::string(" ") + (typeid(Helper) == typeid(TokenHelper) ? std::string("token") : std::string("coin")) + std::string(" ") + heirName)); - result.push_back( Pair("fundingtxid", txid.GetHex()) ); - } - else { - std::cerr << "HeirList() this is not the initial F transaction=" << txid.GetHex() << std::endl; - } - } - else { - std::cerr << "HeirList() could not load transaction=" << txid.GetHex() << std::endl; - } - } + GetCCaddress(cp, markeraddr, GetUnspendable(cp, NULL)); + SetCCunspents(unspentOutputs, markeraddr); + + //std::cerr << "HeirList() finding heir marker from unspendable addr=" << markeraddr << " unspentOutputs.size()=" << unspentOutputs.size() << '\n'; + + // TODO: move marker to special cc addr to prevent checking all tokens + for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { + uint256 hashBlock; + uint256 txid = it->first.txhash; + uint256 tokenid; + int32_t vout = (int32_t)it->first.index; + + //std::cerr << "HeirList() checking txid=" << txid.GetHex() << " vout=" << vout << '\n'; + + CTransaction fundingtx; + if (GetTransaction(txid, fundingtx, hashBlock, false)) { + CPubKey ownerPubkey, heirPubkey; + std::string heirName; + int64_t inactivityTimeSec; + const bool noLogging = true; + uint256 tokenid; + + CScript opret = (fundingtx.vout.size() > 0) ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); + uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, true); + + // note: if it is not Heir token funcId would be equal to 0 + if (funcId == 'F') { + //result.push_back(Pair("fundingtxid kind name", txid.GetHex() + std::string(" ") + (typeid(Helper) == typeid(TokenHelper) ? std::string("token") : std::string("coin")) + std::string(" ") + heirName)); + result.push_back( Pair("fundingtxid", txid.GetHex()) ); + } + else { + std::cerr << "HeirList() this is not the initial F transaction=" << txid.GetHex() << std::endl; + } + } + else { + std::cerr << "HeirList() could not load transaction=" << txid.GetHex() << std::endl; + } + } } @@ -1173,15 +1290,15 @@ UniValue HeirList() { UniValue result(UniValue::VOBJ); result.push_back(Pair("result", "success")); - + struct CCcontract_info *cpHeir, *cpTokens, heirC, tokenC; // NOTE we must use a separate 'C' structure for each CCinit! - cpHeir = CCinit(&heirC, EVAL_HEIR); - cpTokens = CCinit(&tokenC, EVAL_TOKENS); - - _HeirList(cpHeir, result); - _HeirList(cpTokens, result); - - return result; + cpHeir = CCinit(&heirC, EVAL_HEIR); + //cpTokens = CCinit(&tokenC, EVAL_TOKENS); + + _HeirList(cpHeir, result); + //_HeirList(cpTokens, result); not used anymore + + return result; } diff --git a/src/cc/heir_validate.h b/src/cc/heir_validate.h index d66777933..df58e3bf1 100644 --- a/src/cc/heir_validate.h +++ b/src/cc/heir_validate.h @@ -22,100 +22,100 @@ inline static bool isSpendingTx(uint8_t funcid) { return (funcid == 'C'); } // helper class to allow polymorphic behaviour for HeirXXX() functions in case of coins class CoinHelper { public: - - static uint8_t getMyEval() { return EVAL_HEIR; } - static int64_t addOwnerInputs(uint256 dummyid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { - return AddNormalinputs(mtx, ownerPubkey, total, maxinputs); - } - - static CScript makeCreateOpRet(uint256 dummyid, std::vector dummyPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) { - return EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName); - } - static CScript makeAddOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan); - } - static CScript makeClaimOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan); - } - static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { - return MakeCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey); - } - static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) { - return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG); - } -/* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) { - return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG); - } */ - static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) { - struct CCcontract_info *cpHeir, heirC; - cpHeir = CCinit(&heirC, EVAL_HEIR); - return GetCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); - } - static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { - CCaddr1of2set(cp, ownerPubkey, heirPubkey, coinaddr); - } + + static uint8_t getMyEval() { return EVAL_HEIR; } + static int64_t addOwnerInputs(uint256 dummyid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { + return AddNormalinputs(mtx, ownerPubkey, total, maxinputs); + } + + static CScript makeCreateOpRet(uint256 dummyid, std::vector dummyPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) { + return EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName); + } + static CScript makeAddOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { + return EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan); + } + static CScript makeClaimOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { + return EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan); + } + static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { + return MakeCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey); + } + static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) { + return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG); + } + /* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) { + return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG); + } */ + static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) { + struct CCcontract_info *cpHeir, heirC; + cpHeir = CCinit(&heirC, EVAL_HEIR); + return GetCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); + } + static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { + CCaddr1of2set(cp, ownerPubkey, heirPubkey, coinaddr); + } }; // helper class to allow polymorphic behaviour for HeirXXX() functions in case of tokens class TokenHelper { public: - static uint8_t getMyEval() { return EVAL_TOKENS; } - static int64_t addOwnerInputs(uint256 tokenid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { - struct CCcontract_info *cpHeir, heirC; - cpHeir = CCinit(&heirC, EVAL_TOKENS); - return AddTokenCCInputs(cpHeir, mtx, ownerPubkey, tokenid, total, maxinputs); - } - - static CScript makeCreateOpRet(uint256 tokenid, std::vector voutTokenPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) { - return EncodeTokenOpRet(tokenid, voutTokenPubkeys, - EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName)); - } - static CScript makeAddOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeTokenOpRet(tokenid, voutTokenPubkeys, - EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan)); - } - static CScript makeClaimOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeTokenOpRet(tokenid, voutTokenPubkeys, - EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan)); - } - - static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { - return MakeTokensCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey); - } - static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) { - return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS - } -/* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) { - return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS - } */ - static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) { - struct CCcontract_info *cpHeir, heirC; - cpHeir = CCinit(&heirC, EVAL_HEIR); - return GetTokensCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); - } - - static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { - - CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); - } + static uint8_t getMyEval() { return EVAL_TOKENS; } + static int64_t addOwnerInputs(uint256 tokenid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { + struct CCcontract_info *cpHeir, heirC; + cpHeir = CCinit(&heirC, EVAL_TOKENS); + return AddTokenCCInputs(cpHeir, mtx, ownerPubkey, tokenid, total, maxinputs); + } + + static CScript makeCreateOpRet(uint256 tokenid, std::vector voutTokenPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) { + return EncodeTokenOpRet(tokenid, voutTokenPubkeys, + EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName)); + } + static CScript makeAddOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { + return EncodeTokenOpRet(tokenid, voutTokenPubkeys, + EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan)); + } + static CScript makeClaimOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { + return EncodeTokenOpRet(tokenid, voutTokenPubkeys, + EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan)); + } + + static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { + return MakeTokensCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey); + } + static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) { + return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS + } + /* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) { + return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS + } */ + static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) { + struct CCcontract_info *cpHeir, heirC; + cpHeir = CCinit(&heirC, EVAL_HEIR); + return GetTokensCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); + } + + static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { + + CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); + } }; /** -* Small framework for vins and vouts validation implementing a variation of 'chain of responsibility' pattern: -* It consists of two classes CInputValidationPlan and COutputValidationPlan which both are configured with an array of vectors of validators -* (These validators are derived from the class CValidatorBase). -* -* A example of a validator may verify for a vout if its public key corresponds to the public key which is stored in opreturn. -* Or, vin validator may check if this vin depicts correctly to the CC contract's address. -* -* For validating vins CInputValidator additionally is provided with an instance of a class derived from the CInputIdentifierBase class. -* this identifier class allows to select identical vins (for example, normal vins or cc input vins) and apply validators from the corresponding vector to it. -* Note: CInputValidator treats that at least one identified vin should be present, otherwise it returns eval->invalid() and false. -* -* For validating vouts COutputValidator is configured for each vector of validators with the vout index to which these validators are applied -* (see constructors of both CInputValidator and COutputValidator) -*/ + * Small framework for vins and vouts validation implementing a variation of 'chain of responsibility' pattern: + * It consists of two classes CInputValidationPlan and COutputValidationPlan which both are configured with an array of vectors of validators + * (These validators are derived from the class CValidatorBase). + * + * A example of a validator may verify for a vout if its public key corresponds to the public key which is stored in opreturn. + * Or, vin validator may check if this vin depicts correctly to the CC contract's address. + * + * For validating vins CInputValidator additionally is provided with an instance of a class derived from the CInputIdentifierBase class. + * this identifier class allows to select identical vins (for example, normal vins or cc input vins) and apply validators from the corresponding vector to it. + * Note: CInputValidator treats that at least one identified vin should be present, otherwise it returns eval->invalid() and false. + * + * For validating vouts COutputValidator is configured for each vector of validators with the vout index to which these validators are applied + * (see constructors of both CInputValidator and COutputValidator) + */ /** * base class for all validators @@ -123,13 +123,13 @@ public: class CValidatorBase { public: - CValidatorBase(CCcontract_info* cp) : m_cp(cp) {} - virtual bool isVinValidator() const = 0; - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const = 0; - virtual bool validateVout(CTxOut vout, std::string& message) const = 0; - + CValidatorBase(CCcontract_info* cp) : m_cp(cp) {} + virtual bool isVinValidator() const = 0; + virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const = 0; + virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const = 0; + protected: - CCcontract_info * m_cp; + CCcontract_info * m_cp; }; /** @@ -138,494 +138,530 @@ protected: class CInputIdentifierBase { public: - CInputIdentifierBase(CCcontract_info* cp) : m_cp(cp) {} - virtual std::string inputName() const = 0; - virtual bool identifyInput(CTxIn vin) const = 0; + CInputIdentifierBase(CCcontract_info* cp) : m_cp(cp) {} + virtual std::string inputName() const = 0; + virtual bool identifyInput(CTxIn vin) const = 0; protected: - CCcontract_info * m_cp; + CCcontract_info * m_cp; }; /** -* Encapsulates an array containing rows of validators -* Each row is a vector of validators (zero is possible) for validating vins or prev tx's vouts -* this validation plan is used for validating tx inputs -*/ + * Encapsulates an array containing rows of validators + * Each row is a vector of validators (zero is possible) for validating vins or prev tx's vouts + * this validation plan is used for validating tx inputs + */ template class CInputValidationPlan { - using ValidatorsRow = std::vector; - + using ValidatorsRow = std::vector; + public: - - // Pushes a row of validators for validating a vin or vout - // @param CInputIdentifierBase* pointer to class-identifier which determines several identical adjacent vins (like in schema "vin.0+: normal inputs") - // @param pargs parameter pack of zero or more pointer to validator objects - // Why pointers? because we store the base class in validators' row and then call its virtual functions - template - void pushValidators(CInputIdentifierBase *identifier, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ... - { - ValidatorsRow vValidators({ (TValidatorBase*)pargs... }); - m_arrayValidators.push_back(std::make_pair(identifier, vValidators)); - } - - // validate tx inputs and corresponding prev tx vouts - bool validate(const CTransaction& tx, Eval* eval) - { - std::string message = ""; - //std::cerr << "CInputValidationPlan::validate() starting vins validation..." << std::endl; - - int32_t ival = 0; - int32_t iv = 0; - int32_t numv = tx.vin.size(); - int32_t numValidators = m_arrayValidators.size(); - - // run over vins: - while (iv < numv && ival < numValidators) { - - int32_t identifiedCount = 0; - CInputIdentifierBase *identifier = m_arrayValidators[ival].first; - // check if this is 'our' input: - while (iv < numv && identifier->identifyInput(tx.vin[iv])) { - - // get prev tx: - CTransaction prevTx, *pPrevTxOrNull = NULL; - uint256 hashBlock; - - if (!eval->GetTxUnconfirmed(tx.vin[iv].prevout.hash, prevTx, hashBlock)) { - std::ostringstream stream; - stream << "can't find vinTx for vin=" << iv << "."; - return eval->Invalid(stream.str().c_str()); - } - pPrevTxOrNull = &prevTx; // TODO: get prev tx only if it required (i.e. if vout validators are present) - - // exec 'validators' from validator row of ival index, for tx.vin[iv] - if (!execValidatorsInRow(&tx, pPrevTxOrNull, iv, ival, message)) { - std::ostringstream stream; - stream << "invalid tx vin[" << iv << "]:" << message; - return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid' - } - - identifiedCount++; // how many vins we identified - iv++; // advance to the next vin - } - - // CInputValidationPlan treats that there must be at least one identified vin for configured validators' row - // like in 'vin.0: normal input' - if (identifiedCount == 0) { - std::ostringstream stream; - stream << "can't find required vins for " << identifier->inputName() << "."; - return eval->Invalid(stream.str().c_str()); - } - - ival++; // advance to the next validator row - // and it will try the same vin with the new CInputIdentifierBase and validators row - } - - // validation is successful if all validators have been used (i.e. ival = numValidators) - if (ival < numValidators) { - std::cerr << "CInputValidationPlan::validate() incorrect tx" << " ival=" << ival << " numValidators=" << numValidators << std::endl; - return eval->Invalid("incorrect tx structure: not all required vins are present."); - } - - //std::cerr << "CInputValidationPlan::validate() returns with true" << std::endl; - return true; - } - + + // Pushes a row of validators for validating a vin or vout + // @param CInputIdentifierBase* pointer to class-identifier which determines several identical adjacent vins (like in schema "vin.0+: normal inputs") + // @param pargs parameter pack of zero or more pointer to validator objects + // Why pointers? because we store the base class in validators' row and then call its virtual functions + template + void pushValidators(CInputIdentifierBase *identifier, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ... + { + ValidatorsRow vValidators({ (TValidatorBase*)pargs... }); + m_arrayValidators.push_back(std::make_pair(identifier, vValidators)); + } + + // validate tx inputs and corresponding prev tx vouts + bool validate(const CTransaction& tx, Eval* eval) + { + std::string message = ""; + //std::cerr << "CInputValidationPlan::validate() starting vins validation..." << std::endl; + + int32_t ival = 0; + int32_t iv = 0; + int32_t numv = tx.vin.size(); + int32_t numValidators = m_arrayValidators.size(); + + // run over vins: + while (iv < numv && ival < numValidators) { + + int32_t identifiedCount = 0; + CInputIdentifierBase *identifier = m_arrayValidators[ival].first; + // check if this is 'our' input: + while (iv < numv && identifier->identifyInput(tx.vin[iv])) { + + // get prev tx: + CTransaction prevTx, *pPrevTxOrNull = NULL; + uint256 hashBlock; + + if (!eval->GetTxUnconfirmed(tx.vin[iv].prevout.hash, prevTx, hashBlock)) { + std::ostringstream stream; + stream << "can't find vinTx for vin=" << iv << "."; + return eval->Invalid(stream.str().c_str()); + } + pPrevTxOrNull = &prevTx; // TODO: get prev tx only if it required (i.e. if vout validators are present) + + // exec 'validators' from validator row of ival index, for tx.vin[iv] + if (!execValidatorsInRow(&tx, pPrevTxOrNull, iv, ival, message)) { + std::ostringstream stream; + stream << "invalid tx vin[" << iv << "]:" << message; + return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid' + } + + identifiedCount++; // how many vins we identified + iv++; // advance to the next vin + } + + // CInputValidationPlan treats that there must be at least one identified vin for configured validators' row + // like in 'vin.0: normal input' + if (identifiedCount == 0) { + std::ostringstream stream; + stream << "can't find required vins for " << identifier->inputName() << "."; + return eval->Invalid(stream.str().c_str()); + } + + ival++; // advance to the next validator row + // and it will try the same vin with the new CInputIdentifierBase and validators row + } + + // validation is successful if all validators have been used (i.e. ival = numValidators) + if (ival < numValidators) { + std::cerr << "CInputValidationPlan::validate() incorrect tx" << " ival=" << ival << " numValidators=" << numValidators << std::endl; + return eval->Invalid("incorrect tx structure: not all required vins are present."); + } + + //std::cerr << "CInputValidationPlan::validate() returns with true" << std::endl; + return true; + } + private: - // Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv) - bool execValidatorsInRow(const CTransaction* pTx, const CTransaction* pPrevTx, int32_t iv, int32_t ival, std::string& refMessage) const - { - // check boundaries: - if (ival < 0 || ival >= m_arrayValidators.size()) { - std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size(); - refMessage = "internal error: incorrect param ival index"; - return false; - } - - if (iv < 0 || iv >= pTx->vin.size()) { - std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size(); - refMessage = "internal error: incorrect param iv index"; - return false; - } - - // get requested row of validators: - ValidatorsRow vValidators = m_arrayValidators[ival].second; - - //std::cerr << "CInputValidationPlan::execValidatorsInRow() calling validators" << " for vin iv=" << iv << " ival=" << ival << std::endl; - - for (auto v : vValidators) { - bool result; - - if (v->isVinValidator()) - // validate this vin and previous vout: - result = v->validateVin(pTx->vin[iv], pPrevTx->vout[pTx->vin[iv].prevout.n], refMessage); - else - // if it is vout validator pass the previous tx vout: - result = v->validateVout( pPrevTx->vout[pTx->vin[iv].prevout.n], refMessage); - if (!result) { - return result; - } - } - return true; // validation OK - } - - + // Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv) + bool execValidatorsInRow(const CTransaction* pTx, const CTransaction* pPrevTx, int32_t iv, int32_t ival, std::string& refMessage) const + { + // check boundaries: + if (ival < 0 || ival >= m_arrayValidators.size()) { + std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size(); + refMessage = "internal error: incorrect param ival index"; + return false; + } + + if (iv < 0 || iv >= pTx->vin.size()) { + std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size(); + refMessage = "internal error: incorrect param iv index"; + return false; + } + + // get requested row of validators: + ValidatorsRow vValidators = m_arrayValidators[ival].second; + + //std::cerr << "CInputValidationPlan::execValidatorsInRow() calling validators" << " for vin iv=" << iv << " ival=" << ival << std::endl; + + for (auto v : vValidators) { + bool result; + + if (v->isVinValidator()) + // validate this vin and previous vout: + result = v->validateVin(pTx->vin[iv], pPrevTx->vout, pTx->vin[iv].prevout.n, refMessage); + else + // if it is vout validator pass the previous tx vout: + result = v->validateVout( pPrevTx->vout[pTx->vin[iv].prevout.n], pTx->vin[iv].prevout.n, refMessage); + if (!result) { + return result; + } + } + return true; // validation OK + } + + private: - //std::map m_arrayValidators; - std::vector< std::pair > m_arrayValidators; - + //std::map m_arrayValidators; + std::vector< std::pair > m_arrayValidators; + }; /** -* Encapsulates an array containing rows of validators -* Each row is a vector of validators (zero is possible) for validating vouts -* this validation plan is used for validating tx outputs -*/ + * Encapsulates an array containing rows of validators + * Each row is a vector of validators (zero is possible) for validating vouts + * this validation plan is used for validating tx outputs + */ template class COutputValidationPlan { - using ValidatorsRow = std::vector; - + using ValidatorsRow = std::vector; + public: - // Pushes a row of validators for validating a vout - // @param ivout index to vout to validate - // @param pargs parameter pack of zero or more pointer to validator objects - // Why pointers? because we store base class and call its virtual functions - - template - void pushValidators(int32_t ivout, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ... - { - ValidatorsRow vValidators({ (TValidatorBase*)pargs... }); - m_arrayValidators.push_back(std::make_pair(ivout, vValidators)); - } - - // validate tx outputs - bool validate(const CTransaction& tx, Eval* eval) - { - std::string message = ""; - //std::cerr << "COutputValidationPlan::validateOutputs() starting vouts validation..." << std::endl; - - int32_t ival = 0; - int32_t numVouts = tx.vout.size(); - int32_t numValidators = m_arrayValidators.size(); - - // run over vouts: - while (ival < numValidators) { - - int32_t ivout = m_arrayValidators[ival].first; - if (ivout >= numVouts) { - std::cerr << "COutputValidationPlan::validate() incorrect tx" << "for ival=" << ival << " in tx.vout no such ivout=" << ivout << std::endl; - return eval->Invalid("incorrect tx structure: not all required vouts are present."); - } - else - { - // exec 'validators' from validator row of ival index, for tx.vout[ivout] - if (!execValidatorsInRow(&tx, ivout, ival, message)) { - std::ostringstream stream; - stream << "invalid tx vout[" << ivout << "]:" << message; - return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid' - } - } - ival++; // advance to the next vout - } - //std::cerr << "COutputValidationPlan::validate() returns with true" << std::endl; - return true; - } - + // Pushes a row of validators for validating a vout + // @param ivout index to vout to validate + // @param pargs parameter pack of zero or more pointer to validator objects + // Why pointers? because we store base class and call its virtual functions + + template + void pushValidators(int32_t ivout, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ... + { + ValidatorsRow vValidators({ (TValidatorBase*)pargs... }); + m_arrayValidators.push_back(std::make_pair(ivout, vValidators)); + } + + // validate tx outputs + bool validate(const CTransaction& tx, Eval* eval) + { + std::string message = ""; + //std::cerr << "COutputValidationPlan::validateOutputs() starting vouts validation..." << std::endl; + + int32_t ival = 0; + int32_t numVouts = tx.vout.size(); + int32_t numValidators = m_arrayValidators.size(); + + // run over vouts: + while (ival < numValidators) { + + int32_t ivout = m_arrayValidators[ival].first; + if (ivout >= numVouts) { + std::cerr << "COutputValidationPlan::validate() incorrect tx" << "for ival=" << ival << " in tx.vout no such ivout=" << ivout << std::endl; + return eval->Invalid("incorrect tx structure: not all required vouts are present."); + } + else + { + // exec 'validators' from validator row of ival index, for tx.vout[ivout] + if (!execValidatorsInRow(&tx, ivout, ival, message)) { + std::ostringstream stream; + stream << "invalid tx vout[" << ivout << "]:" << message; + return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid' + } + } + ival++; // advance to the next vout + } + //std::cerr << "COutputValidationPlan::validate() returns with true" << std::endl; + return true; + } + private: - // Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv) - bool execValidatorsInRow(const CTransaction* pTx, int32_t iv, int32_t ival, std::string& refMessage) const - { - // check boundaries: - if (ival < 0 || ival >= m_arrayValidators.size()) { - std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size(); - refMessage = "internal error: incorrect param ival index"; - return false; - } - - if (iv < 0 || iv >= pTx->vout.size()) { - std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size(); - refMessage = "internal error: incorrect param iv index"; - return false; - } - - // get requested row of validators: - ValidatorsRow vValidators = m_arrayValidators[ival].second; - - //std::cerr << "COutputValidationPlan::execRow() calling validators" << " for vout iv=" << iv << " ival=" << ival << std::endl; - - for (auto v : vValidators) { - - if (!v->isVinValidator()) { - // if this is a 'in' validation plan then pass the previous tx vout: - bool result = v->validateVout(pTx->vout[iv], refMessage); - if (!result) - return result; - } - } - return true; // validation OK - } - + // Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv) + bool execValidatorsInRow(const CTransaction* pTx, int32_t iv, int32_t ival, std::string& refMessage) const + { + // check boundaries: + if (ival < 0 || ival >= m_arrayValidators.size()) { + std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size(); + refMessage = "internal error: incorrect param ival index"; + return false; + } + + if (iv < 0 || iv >= pTx->vout.size()) { + std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size(); + refMessage = "internal error: incorrect param iv index"; + return false; + } + + // get requested row of validators: + ValidatorsRow vValidators = m_arrayValidators[ival].second; + + //std::cerr << "COutputValidationPlan::execRow() calling validators" << " for vout iv=" << iv << " ival=" << ival << std::endl; + + for (auto v : vValidators) { + + if (!v->isVinValidator()) { + // if this is a 'in' validation plan then pass the previous tx vout: + bool result = v->validateVout(pTx->vout[iv], iv, refMessage); + if (!result) + return result; + } + } + return true; // validation OK + } + private: - //std::map m_mapValidators; - std::vector< std::pair > m_arrayValidators; - + //std::map m_mapValidators; + std::vector< std::pair > m_arrayValidators; + }; class CNormalInputIdentifier : CInputIdentifierBase { public: - CNormalInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {} - virtual std::string inputName() const { return std::string("normal input"); } - virtual bool identifyInput(CTxIn vin) const { - return !IsCCInput(vin.scriptSig); - } + CNormalInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {} + virtual std::string inputName() const { return std::string("normal input"); } + virtual bool identifyInput(CTxIn vin) const { + return !IsCCInput(vin.scriptSig); + } }; class CCCInputIdentifier : CInputIdentifierBase { public: - CCCInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {} - virtual std::string inputName() const { return std::string("CC input"); } - virtual bool identifyInput(CTxIn vin) const { - return IsCCInput(vin.scriptSig); - } + CCCInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {} + virtual std::string inputName() const { return std::string("CC input"); } + virtual bool identifyInput(CTxIn vin) const { + return IsCCInput(vin.scriptSig); + } }; /** -* Validates 1of2address for vout (may be used for either this or prev tx) -*/ + * Validates 1of2address for vout (may be used for either this or prev tx) + */ template class CCC1of2AddressValidator : CValidatorBase { public: - CCC1of2AddressValidator(CCcontract_info* cp, CScript opRetScript, std::string customMessage = "") : - m_fundingOpretScript(opRetScript), m_customMessage(customMessage), CValidatorBase(cp) {} - - virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const - { - //std::cerr << "CCC1of2AddressValidator::validateVout() entered" << std::endl; - CPubKey ownerPubkey, heirPubkey; - int64_t inactivityTime; - std::string heirName; - uint256 tokenid; - - uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); - if (funcId == 0) { - message = m_customMessage + std::string(" invalid opreturn format"); - std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; - return false; - } - - char shouldBeAddr[65], ccAddr[65]; - - //GetCCaddress1of2(m_cp, shouldBeAddr, ownerPubkey, heirPubkey); - Helper::GetCoinsOrTokensCCaddress1of2(shouldBeAddr, ownerPubkey, heirPubkey); - - if (vout.scriptPubKey.IsPayToCryptoCondition()) { - if (Getscriptaddress(ccAddr, vout.scriptPubKey) && strcmp(shouldBeAddr, ccAddr) == 0) { - //std::cerr << "CCC1of2AddressValidator::validateVout() exits with true" << std::endl; - return true; - } - else { - message = m_customMessage + std::string(" incorrect heir funding address: incorrect pubkey(s)"); - } - } - else { - message = m_customMessage + std::string(" incorrect heir funding address: not a 1of2addr"); - } - - std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; - return false; - } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return false; } - + CCC1of2AddressValidator(CCcontract_info* cp, CScript opRetScript, std::string customMessage = "") : + m_fundingOpretScript(opRetScript), m_customMessage(customMessage), CValidatorBase(cp) {} + + virtual bool isVinValidator() const { return false; } + virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const + { + //std::cerr << "CCC1of2AddressValidator::validateVout() entered" << std::endl; + CPubKey ownerPubkey, heirPubkey; + int64_t inactivityTime; + std::string heirName; + uint256 tokenid; + + uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); + if (funcId == 0) { + message = m_customMessage + std::string(" invalid opreturn format"); + std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; + return false; + } + + char shouldBeAddr[65], ccAddr[65]; + + //GetCCaddress1of2(m_cp, shouldBeAddr, ownerPubkey, heirPubkey); + Helper::GetCoinsOrTokensCCaddress1of2(shouldBeAddr, ownerPubkey, heirPubkey); + + if (vout.scriptPubKey.IsPayToCryptoCondition()) { + if (Getscriptaddress(ccAddr, vout.scriptPubKey) && strcmp(shouldBeAddr, ccAddr) == 0) { + //std::cerr << "CCC1of2AddressValidator::validateVout() exits with true" << std::endl; + return true; + } + else { + message = m_customMessage + std::string(" incorrect heir funding address: incorrect pubkey(s)"); + } + } + else { + message = m_customMessage + std::string(" incorrect heir funding address: not a 1of2addr"); + } + + std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; + return false; + } + virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return false; } + private: - CScript m_fundingOpretScript; - std::string m_customMessage; + CScript m_fundingOpretScript; + std::string m_customMessage; }; /** -* Validates if this is vout to owner or heir from opret (funding or change) -*/ + * Validates if this is vout to owner or heir from opret (funding or change) + */ template class CMyPubkeyVoutValidator : CValidatorBase { public: - CMyPubkeyVoutValidator(CCcontract_info* cp, CScript opRetScript, bool checkNormals) - : m_fundingOpretScript(opRetScript), m_checkNormals(checkNormals), CValidatorBase(cp) { } - - virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const - { - //std::cerr << "CMyPubkeyVoutValidator::validateVout() entered" << std::endl; - - CPubKey ownerPubkey, heirPubkey; - int64_t inactivityTime; - std::string heirName; - uint256 tokenid; - - ///std::cerr << "CMyPubkeyVoutValidator::validateVout() m_opRetScript=" << m_opRetScript.ToString() << std::endl; - - // get both pubkeys: - uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); - if (funcId == 0) { - message = std::string("invalid opreturn format"); - return false; - } - - CScript ownerScript; - CScript heirScript; - if (m_checkNormals) { //not used, incorrect check, too strict - ownerScript = CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey; - heirScript = CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey; - std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(coin,owner)=" << CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(coin,heir)=" << CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl; - } - else { - ownerScript = Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey; - heirScript = Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey; - std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(owner)=" << Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(heir)=" << Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl; - } - - // recreate scriptPubKey for owner and heir and compare it with that of the vout to check: - if (vout.scriptPubKey == ownerScript || vout.scriptPubKey == heirScript) { - // this is vout to owner or heir addr: - //std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with true" << std::endl; - return true; - } - - std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with false (not the owner's or heir's addresses)" << std::endl; - message = std::string("invalid pubkey"); - return false; - } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } - + CMyPubkeyVoutValidator(CCcontract_info* cp, CScript opRetScript, bool checkNormals) + : m_fundingOpretScript(opRetScript), m_checkNormals(checkNormals), CValidatorBase(cp) { } + + virtual bool isVinValidator() const { return false; } + virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const + { + //std::cerr << "CMyPubkeyVoutValidator::validateVout() entered" << std::endl; + + CPubKey ownerPubkey, heirPubkey; + int64_t inactivityTime; + std::string heirName; + uint256 tokenid; + + ///std::cerr << "CMyPubkeyVoutValidator::validateVout() m_opRetScript=" << m_opRetScript.ToString() << std::endl; + + // get both pubkeys: + uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); + if (funcId == 0) { + message = std::string("invalid opreturn format"); + return false; + } + + CScript ownerScript; + CScript heirScript; + if (m_checkNormals) { //not used, incorrect check, too strict + ownerScript = CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey; + heirScript = CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey; + std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(coin,owner)=" << CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(coin,heir)=" << CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl; + } + else { + ownerScript = Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey; + heirScript = Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey; + std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(owner)=" << Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(heir)=" << Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl; + } + + // recreate scriptPubKey for owner and heir and compare it with that of the vout to check: + if (vout.scriptPubKey == ownerScript || vout.scriptPubKey == heirScript) { + // this is vout to owner or heir addr: + //std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with true" << std::endl; + return true; + } + + std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with false (not the owner's or heir's addresses)" << std::endl; + message = std::string("invalid pubkey"); + return false; + } + virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return true; } + private: - CScript m_fundingOpretScript; - //uint256 m_lasttxid; - bool m_checkNormals; + CScript m_fundingOpretScript; + //uint256 m_lasttxid; + bool m_checkNormals; }; /** -* Check if the user is the heir and the heir is allowed to spend (duration > inactivityTime) -*/ + * Check if the user is the heir and the heir is allowed to spend (duration > inactivityTime) + */ template class CHeirSpendValidator : CValidatorBase { public: - CHeirSpendValidator(CCcontract_info* cp, CScript opRetScript, uint256 latesttxid, uint8_t isHeirSpendingBegan) - : m_fundingOpretScript(opRetScript), m_latesttxid(latesttxid), m_isHeirSpendingBegan(isHeirSpendingBegan), CValidatorBase(cp) {} - - virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const - { - //std::cerr << "CHeirSpendValidator::validateVout() entered" << std::endl; - - CPubKey ownerPubkey, heirPubkey; - int64_t inactivityTime; - std::string heirName; - uint256 tokenid; - - // get heir pubkey: - uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); - if (funcId == 0) { - message = std::string("invalid opreturn format"); - return false; - } - - int32_t numblocks; - int64_t durationSec = CCduration(numblocks, m_latesttxid); - - // recreate scriptPubKey for heir and compare it with that of the vout: - if (vout.scriptPubKey == Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey) { - // this is the heir is trying to spend - if (!m_isHeirSpendingBegan && durationSec <= inactivityTime) { - message = "heir is not allowed yet to spend funds"; - std::cerr << "CHeirSpendValidator::validateVout() heir is not allowed yet to spend funds" << std::endl; - return false; - } - else { - // heir is allowed to spend - return true; - } - } - - //std::cerr << "CHeirSpendValidator::validateVout() exits with true" << std::endl; - - // this is not heir: - return true; - } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } - + CHeirSpendValidator(CCcontract_info* cp, CScript opRetScript, uint256 latesttxid, uint8_t isHeirSpendingBegan) + : m_fundingOpretScript(opRetScript), m_latesttxid(latesttxid), m_isHeirSpendingBegan(isHeirSpendingBegan), CValidatorBase(cp) {} + + virtual bool isVinValidator() const { return false; } + virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const + { + //std::cerr << "CHeirSpendValidator::validateVout() entered" << std::endl; + + CPubKey ownerPubkey, heirPubkey; + int64_t inactivityTime; + std::string heirName; + uint256 tokenid; + + // get heir pubkey: + uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); + if (funcId == 0) { + message = std::string("invalid opreturn format"); + return false; + } + + int32_t numblocks; + int64_t durationSec = CCduration(numblocks, m_latesttxid); + + // recreate scriptPubKey for heir and compare it with that of the vout: + if (vout.scriptPubKey == Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey) { + // this is the heir is trying to spend + if (!m_isHeirSpendingBegan && durationSec <= inactivityTime) { + message = "heir is not allowed yet to spend funds"; + std::cerr << "CHeirSpendValidator::validateVout() heir is not allowed yet to spend funds" << std::endl; + return false; + } + else { + // heir is allowed to spend + return true; + } + } + + //std::cerr << "CHeirSpendValidator::validateVout() exits with true" << std::endl; + + // this is not heir: + return true; + } + virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return true; } + private: - CScript m_fundingOpretScript; - uint256 m_latesttxid; - uint8_t m_isHeirSpendingBegan; + CScript m_fundingOpretScript; + uint256 m_latesttxid; + uint8_t m_isHeirSpendingBegan; }; /** -* Validates this opreturn and compares it with the opreturn from the previous tx -*/ + * Validates this opreturn and compares it with the opreturn from the previous tx + */ template class COpRetValidator : CValidatorBase { public: - COpRetValidator(CCcontract_info* cp, CScript opret) - : m_fundingOpretScript(opret), CValidatorBase(cp) {} + COpRetValidator(CCcontract_info* cp, CScript opret) + : m_fundingOpretScript(opret), CValidatorBase(cp) {} + + virtual bool isVinValidator() const { return false; } + virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const + { + //std::cerr << "COpRetValidator::validateVout() entered" << std::endl; + + uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid; + uint8_t dummyIsHeirSpendingBegan; + + uint8_t funcId = DecodeHeirEitherOpRet(vout.scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true); + if (funcId == 0) { + message = std::string("invalid opreturn format"); + return false; + } + + uint8_t initialFuncId = DecodeHeirEitherOpRet(m_fundingOpretScript, initialTokenid, dummyTxid, dummyIsHeirSpendingBegan, true); + if (initialFuncId == 0) { + message = std::string("invalid initial tx opreturn format"); + return false; + } + + // validation rules: + if (!isMyFuncId(funcId)) { + message = std::string("invalid funcid in opret"); + return false; + } + + if (typeid(Helper) == typeid(TokenHelper)) { + if (tokenid != initialTokenid) { + message = std::string("invalid tokenid in opret"); + return false; + } + } + + //std::cerr << "COpRetValidator::validateVout() exits with true" << std::endl; + return true; + } + virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return true; } + +private: + CScript m_fundingOpretScript; +}; - virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const - { - //std::cerr << "COpRetValidator::validateVout() entered" << std::endl; + +/** + * marker spending prevention validator, + * returns false if for tx with funcid=F vout.1 is being tried to spend + */ +template class CMarkerValidator : CValidatorBase +{ +public: + CMarkerValidator(CCcontract_info* cp) + : CValidatorBase(cp) { } + + virtual bool isVinValidator() const { return true; } // this is vin validator + virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { return true; } + virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid; uint8_t dummyIsHeirSpendingBegan; - uint8_t funcId = DecodeHeirEitherOpRet(vout.scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true); - if (funcId == 0) { - message = std::string("invalid opreturn format"); - return false; - } + //std::cerr << "CMarkerValidator::validateVin() prevVout.size()=" << prevVout.size() << " prevN=" << prevN << std::endl; + if (prevVout.size() > 0) { - uint8_t initialFuncId = DecodeHeirEitherOpRet(m_fundingOpretScript, initialTokenid, dummyTxid, dummyIsHeirSpendingBegan, true); - if (initialFuncId == 0) { - message = std::string("invalid initial tx opreturn format"); - return false; - } + // get funcId for prev tx: + uint8_t funcId = DecodeHeirEitherOpRet(prevVout[prevVout.size()-1].scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true); - // validation rules: - if (!isMyFuncId(funcId)) { - message = std::string("invalid funcid in opret"); - return false; - } + //std::cerr << "CMarkerValidator::validateVin() funcId=" << (funcId?funcId:' ') << std::endl; - if (typeid(Helper) == typeid(TokenHelper)) { - if (tokenid != initialTokenid) { - message = std::string("invalid tokenid in opret"); + if (funcId == 'F' && prevN == 1) { // do not allow to spend 'F' marker's vout + message = std::string("spending marker not allowed"); return false; } } - - //std::cerr << "COpRetValidator::validateVout() exits with true" << std::endl; - return true; + //std::cerr << "CMarkerValidator::validateVin() exits with true" << std::endl; + return true; } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } - -private: - CScript m_fundingOpretScript; }; /** -* empty validator always returns true -*/ + * empty validator always returns true + */ template class CNullValidator : CValidatorBase { public: - CNullValidator(CCcontract_info* cp) - : CValidatorBase(cp) { } - - virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const { return true; } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } + CNullValidator(CCcontract_info* cp) + : CValidatorBase(cp) { } + + virtual bool isVinValidator() const { return false; } + virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { return true; } + virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return true; } }; -#endif \ No newline at end of file +#endif diff --git a/src/cc/marmara.cpp b/src/cc/marmara.cpp index 795b95186..0e301ba97 100644 --- a/src/cc/marmara.cpp +++ b/src/cc/marmara.cpp @@ -94,7 +94,7 @@ uint8_t DecodeMaramaraCoinbaseOpRet(const CScript scriptPubKey,CPubKey &pk,int32 { return(script[1]); } else fprintf(stderr,"DecodeMaramaraCoinbaseOpRet unmarshal error for %c\n",script[1]); - } else fprintf(stderr,"script[1] is %d != 'C' %d or 'P' %d or 'L' %d\n",script[1],'C','P','L'); + } //else fprintf(stderr,"script[1] is %d != 'C' %d or 'P' %d or 'L' %d\n",script[1],'C','P','L'); } else fprintf(stderr,"vopret.size() is %d\n",(int32_t)vopret.size()); return(0); } @@ -255,6 +255,22 @@ int32_t MarmaraValidateCoinbase(int32_t height,CTransaction tx) return(-1); } +bool MarmaraPoScheck(char *destaddr,CScript opret,CTransaction staketx) +{ + CPubKey Marmarapk,pk; int32_t height,unlockht; uint8_t funcid; char coinaddr[64]; struct CCcontract_info *cp,C; + //fprintf(stderr,"%s numvins.%d numvouts.%d %.8f opret[%d]\n",staketx.GetHash().ToString().c_str(),(int32_t)staketx.vin.size(),(int32_t)staketx.vout.size(),(double)staketx.vout[0].nValue/COIN,(int32_t)opret.size()); + if ( staketx.vout.size() == 2 && opret == staketx.vout[1].scriptPubKey ) + { + cp = CCinit(&C,EVAL_MARMARA); + funcid = DecodeMaramaraCoinbaseOpRet(opret,pk,height,unlockht); + Marmarapk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,Marmarapk,pk); + //fprintf(stderr,"matched opret! funcid.%c ht.%d unlock.%d %s\n",funcid,height,unlockht,coinaddr); + return(strcmp(destaddr,coinaddr) == 0); + } + return(0); +} + bool MarmaraValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { std::vector vopret; CTransaction vinTx; uint256 hashBlock; int32_t numvins,numvouts,i,ht,unlockht,vht,vunlockht; uint8_t funcid,vfuncid,*script; CPubKey pk,vpk; @@ -318,6 +334,10 @@ bool MarmaraValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t { return(true); } + else if ( funcid == 'C' ) // coinbase + { + return(true); + } // staking only for locked utxo } return eval->Invalid("fall through error"); @@ -476,7 +496,7 @@ UniValue MarmaraLock(uint64_t txfee,int64_t amount,int32_t height) else { result.push_back(Pair("result",(char *)"success")); - result.push_back(Pair("rawtx",rawtx)); + result.push_back(Pair("hex",rawtx)); return(result); } } else errorstr = (char *)"insufficient funds"; @@ -485,7 +505,34 @@ UniValue MarmaraLock(uint64_t txfee,int64_t amount,int32_t height) return(result); } -// jl777: decide on what unlockht settlement change should have +int32_t MarmaraSignature(uint8_t *utxosig,CMutableTransaction &mtx) +{ + uint256 txid,hashBlock; uint8_t *ptr; int32_t i,siglen,vout,numvouts; CTransaction tx; std::string rawtx; CPubKey mypk; std::vector pubkeys; struct CCcontract_info *cp,C; uint64_t txfee; + txfee = 10000; + vout = mtx.vin[0].prevout.n; + if ( GetTransaction(mtx.vin[0].prevout.hash,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 1 && vout < numvouts ) + { + cp = CCinit(&C,EVAL_MARMARA); + mypk = pubkey2pk(Mypubkey()); + pubkeys.push_back(mypk); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,tx.vout[numvouts - 1].scriptPubKey,pubkeys); + if ( rawtx.size() > 0 ) + { + siglen = mtx.vin[0].scriptSig.size(); + ptr = &mtx.vin[0].scriptSig[0]; + for (i=0; i from utxo making change UniValue MarmaraSettlement(uint64_t txfee,uint256 refbatontxid) { @@ -553,7 +600,7 @@ UniValue MarmaraSettlement(uint64_t txfee,uint256 refbatontxid) mtx.vout.push_back(MakeCC1of2vout(EVAL_MARMARA,change,Marmarapk,pk)); rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,MarmaraLoopOpret('S',createtxid,mypk,0,refmatures,currency),pubkeys); result.push_back(Pair("result",(char *)"success")); - result.push_back(Pair("rawtx",rawtx)); + result.push_back(Pair("hex",rawtx)); return(result); } else remaining -= inputsum; if ( mtx.vin.size() >= CC_MAXVINS - MARMARA_VINS ) @@ -569,7 +616,7 @@ UniValue MarmaraSettlement(uint64_t txfee,uint256 refbatontxid) rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,MarmaraLoopOpret('D',createtxid,mypk,-remaining,refmatures,currency),pubkeys); result.push_back(Pair("result",(char *)"error")); result.push_back(Pair("error",(char *)"insufficient funds")); - result.push_back(Pair("rawtx",rawtx)); + result.push_back(Pair("hex",rawtx)); result.push_back(Pair("remaining",ValueFromAmount(remaining))); } else @@ -684,7 +731,7 @@ UniValue MarmaraReceive(uint64_t txfee,CPubKey senderpk,int64_t amount,std::stri else { result.push_back(Pair("result",(char *)"success")); - result.push_back(Pair("rawtx",rawtx)); + result.push_back(Pair("hex",rawtx)); result.push_back(Pair("funcid","R")); result.push_back(Pair("createtxid",createtxid.GetHex())); if ( batontxid != zeroid ) @@ -740,7 +787,7 @@ UniValue MarmaraIssue(uint64_t txfee,uint8_t funcid,CPubKey receiverpk,int64_t a else { result.push_back(Pair("result",(char *)"success")); - result.push_back(Pair("rawtx",rawtx)); + result.push_back(Pair("hex",rawtx)); char str[2]; str[0] = funcid, str[1] = 0; result.push_back(Pair("funcid",str)); result.push_back(Pair("createtxid",createtxid.GetHex())); @@ -978,7 +1025,7 @@ UniValue MarmaraPoolPayout(uint64_t txfee,int32_t firstheight,double perc,char * else { result.push_back(Pair("result",(char *)"success")); - result.push_back(Pair("rawtx",rawtx)); + result.push_back(Pair("hex",rawtx)); if ( totalpayout > 0 && total > totalpayout-txfee ) { result.push_back(Pair("firstheight",firstheight)); diff --git a/src/chain.h b/src/chain.h index 49b206480..ee56c7662 100644 --- a/src/chain.h +++ b/src/chain.h @@ -115,6 +115,7 @@ enum BlockStatus: uint32_t { BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD, BLOCK_ACTIVATES_UPGRADE = 128, //! block activates a network upgrade + BLOCK_IN_TMPFILE = 256 }; //! Short-hand for the highest consensus validity we implement. @@ -350,7 +351,7 @@ public: nSolution = block.nSolution; } - int32_t SetHeight(int32_t height) + void SetHeight(int32_t height) { this->chainPower.nHeight = height; } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 47f4d48a8..5c9efb276 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -92,11 +92,6 @@ static CBlock CreateGenesisBlock(uint32_t nTime, const uint256& nNonce, const st void *chainparams_commandline(void *ptr); #include "komodo_defs.h" -extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; -extern uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT; -extern uint32_t ASSETCHAIN_INIT, ASSETCHAINS_MAGIC; -extern int32_t VERUS_BLOCK_POSUNITS, ASSETCHAINS_LWMAPOS, ASSETCHAINS_SAPLING, ASSETCHAINS_OVERWINTER; -extern uint64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_ALGO, ASSETCHAINS_EQUIHASH, ASSETCHAINS_VERUSHASH; const arith_uint256 maxUint = UintToArith256(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); @@ -230,11 +225,32 @@ void CChainParams::SetCheckpointData(CChainParams::CCheckpointData checkpointDat CChainParams::checkpointData = checkpointData; } -int32_t MAX_BLOCK_SIZE(int32_t height) // make sure to change MAX_PROTOCOL_MESSAGE_LENGTH also!!!! +/* + To change the max block size, all that needs to be updated is the #define _MAX_BLOCK_SIZE in utils.h + + However, doing that without any other changes will allow forking non-updated nodes by creating a larger block. So, make sure to height activate the new blocksize properly. + + Assuming it is 8MB, then: + #define _OLD_MAX_BLOCK_SIZE (4096 * 1024) + #define _MAX_BLOCK_SIZE (2 * 4096 * 1024) + + change the body of if: + { + if ( height < saplinght+1000000 ) // activates 8MB blocks 1 million blocks after saplinght + return(_OLD_MAX_BLOCK_SIZE); + else return(_MAX_BLOCK_SIZE); + } + +*/ + +int32_t MAX_BLOCK_SIZE(int32_t height) { + int32_t saplinght = mainParams.consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight; //fprintf(stderr,"MAX_BLOCK_SIZE %d vs. %d\n",height,mainParams.consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight); - if ( height <= 0 || (mainParams.consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight > 0 && height >= mainParams.consensus.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight) ) - return(4096 * 1024); + if ( height <= 0 || (saplinght > 0 && height >= saplinght) ) + { + return(_MAX_BLOCK_SIZE); + } else return(2000000); } diff --git a/src/consensus/params.h b/src/consensus/params.h index 946b11c8d..67d84af0b 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -130,8 +130,8 @@ struct Params { int64_t AveragingWindowTimespan() const { return nPowAveragingWindow * nPowTargetSpacing; } int64_t MinActualTimespan() const { return (AveragingWindowTimespan() * (100 - nPowMaxAdjustUp )) / 100; } int64_t MaxActualTimespan() const { return (AveragingWindowTimespan() * (100 + nPowMaxAdjustDown)) / 100; } - int32_t SetSaplingHeight(int32_t height) { vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight = height; } - int32_t SetOverwinterHeight(int32_t height) { vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = height; } + void SetSaplingHeight(int32_t height) { vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight = height; } + void SetOverwinterHeight(int32_t height) { vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = height; } uint256 nMinimumChainWork; }; } // namespace Consensus diff --git a/src/crypto/verus_hash.h b/src/crypto/verus_hash.h index 5ac49c209..2f1f2cd26 100644 --- a/src/crypto/verus_hash.h +++ b/src/crypto/verus_hash.h @@ -86,6 +86,7 @@ class CVerusHashV2 result = buf2; curPos = 0; std::fill(buf1, buf1 + sizeof(buf1), 0); + return *this; } int64_t *ExtraI64Ptr() { return (int64_t *)(curBuf + 32); } diff --git a/src/komodo.h b/src/komodo.h index 7d2b17963..35fee05ad 100644 --- a/src/komodo.h +++ b/src/komodo.h @@ -707,6 +707,10 @@ int32_t komodo_voutupdate(int32_t *isratificationp,int32_t notaryid,uint8_t *scr komodo_stateupdate(height,0,0,0,zero,0,0,0,0,0,0,0,0,0,0,sp->MoM,sp->MoMdepth); if ( ASSETCHAINS_SYMBOL[0] != 0 ) printf("[%s] ht.%d NOTARIZED.%d %s.%s %sTXID.%s lens.(%d %d) MoM.%s %d\n",ASSETCHAINS_SYMBOL,height,*notarizedheightp,ASSETCHAINS_SYMBOL[0]==0?"KMD":ASSETCHAINS_SYMBOL,srchash.ToString().c_str(),ASSETCHAINS_SYMBOL[0]==0?"BTC":"KMD",desttxid.ToString().c_str(),opretlen,len,sp->MoM.ToString().c_str(),sp->MoMdepth); + if (RemoveOrphanedBlocks(*notarizedheightp)) + { + //fprintf(stderr, "Sucessfully removed all known orphaned blocks before height %d\n",*notarizedheightp); + } if ( ASSETCHAINS_SYMBOL[0] == 0 ) { if ( signedfp == 0 ) @@ -947,12 +951,15 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block) } } } - if ( ((signedmask & 1) != 0 && numvalid >= KOMODO_MINRATIFY) || bitweight(signedmask) > (numnotaries/3) ) + if ( ASSETCHAINS_SYMBOL[0] != 0 || height < 100000 ) { - memset(&txhash,0,sizeof(txhash)); - komodo_stateupdate(height,pubkeys,numvalid,0,txhash,0,0,0,0,0,0,0,0,0,0,zero,0); - printf("RATIFIED! >>>>>>>>>> new notaries.%d newheight.%d from height.%d\n",numvalid,(((height+KOMODO_ELECTION_GAP/2)/KOMODO_ELECTION_GAP)+1)*KOMODO_ELECTION_GAP,height); - } else printf("signedmask.%llx numvalid.%d wt.%d numnotaries.%d\n",(long long)signedmask,numvalid,bitweight(signedmask),numnotaries); + if ( ((signedmask & 1) != 0 && numvalid >= KOMODO_MINRATIFY) || bitweight(signedmask) > (numnotaries/3) ) + { + memset(&txhash,0,sizeof(txhash)); + komodo_stateupdate(height,pubkeys,numvalid,0,txhash,0,0,0,0,0,0,0,0,0,0,zero,0); + printf("RATIFIED! >>>>>>>>>> new notaries.%d newheight.%d from height.%d\n",numvalid,(((height+KOMODO_ELECTION_GAP/2)/KOMODO_ELECTION_GAP)+1)*KOMODO_ELECTION_GAP,height); + } else printf("signedmask.%llx numvalid.%d wt.%d numnotaries.%d\n",(long long)signedmask,numvalid,bitweight(signedmask),numnotaries); + } } } } diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index d24492317..c530db4fb 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -557,9 +557,9 @@ uint64_t komodo_seed(int32_t height) return(seed); } -uint32_t komodo_txtime(uint64_t *valuep,uint256 hash, int32_t n, char *destaddr) +uint32_t komodo_txtime(CScript &opret,uint64_t *valuep,uint256 hash, int32_t n, char *destaddr) { - CTxDestination address; CTransaction tx; uint256 hashBlock; + CTxDestination address; CTransaction tx; uint256 hashBlock; int32_t numvouts; *valuep = 0; if (!GetTransaction(hash, tx, #ifndef KOMODO_ZCASH @@ -570,10 +570,12 @@ uint32_t komodo_txtime(uint64_t *valuep,uint256 hash, int32_t n, char *destaddr) //fprintf(stderr,"ERROR: %s/v%d locktime.%u\n",hash.ToString().c_str(),n,(uint32_t)tx.nLockTime); return(0); } + numvouts = tx.vout.size(); //fprintf(stderr,"%s/v%d locktime.%u\n",hash.ToString().c_str(),n,(uint32_t)tx.nLockTime); - if ( n < tx.vout.size() ) + if ( n < numvouts ) { *valuep = tx.vout[n].nValue; + opret = tx.vout[numvouts-1].scriptPubKey; if (ExtractDestination(tx.vout[n].scriptPubKey, address)) strcpy(destaddr,CBitcoinAddress(address).ToString().c_str()); } @@ -614,12 +616,12 @@ uint32_t komodo_txtime2(uint64_t *valuep,uint256 hash,int32_t n,char *destaddr) int32_t komodo_WhoStaked(CBlock *pblock, CTxDestination &addressout) { - int32_t n,vout; uint32_t txtime; uint64_t value; char voutaddr[64],destaddr[64]; CTxDestination voutaddress; uint256 txid; + int32_t n,vout; uint32_t txtime; uint64_t value; char voutaddr[64],destaddr[64]; CTxDestination voutaddress; uint256 txid; CScript opret; if ( (n= pblock->vtx.size()) > 1 && pblock->vtx[n-1].vin.size() == 1 && pblock->vtx[n-1].vout.size() == 1 ) { txid = pblock->vtx[n-1].vin[0].prevout.hash; vout = pblock->vtx[n-1].vin[0].prevout.n; - txtime = komodo_txtime(&value,txid,vout,destaddr); + txtime = komodo_txtime(opret,&value,txid,vout,destaddr); if ( ExtractDestination(pblock->vtx[n-1].vout[0].scriptPubKey,voutaddress) ) { strcpy(voutaddr,CBitcoinAddress(voutaddress).ToString().c_str()); @@ -634,27 +636,41 @@ int32_t komodo_WhoStaked(CBlock *pblock, CTxDestination &addressout) return(0); } -int32_t komodo_isPoS(CBlock *pblock) +bool MarmaraPoScheck(char *destaddr,CScript opret,CTransaction staketx); + +int32_t komodo_isPoS(CBlock *pblock,int32_t height) { - int32_t n,vout; uint32_t txtime; uint64_t value; char voutaddr[64],destaddr[64]; CTxDestination voutaddress; uint256 txid; + int32_t n,vout,numvouts; uint32_t txtime; uint64_t value; char voutaddr[64],destaddr[64]; CTxDestination voutaddress; uint256 txid; CScript opret; if ( ASSETCHAINS_STAKED != 0 ) { - if ( (n= pblock->vtx.size()) > 1 && pblock->vtx[n-1].vin.size() == 1 && pblock->vtx[n-1].vout.size() == 1 ) + n = pblock->vtx.size(); + //fprintf(stderr,"ht.%d check for PoS numtx.%d numvins.%d numvouts.%d\n",height,n,(int32_t)pblock->vtx[n-1].vin.size(),(int32_t)pblock->vtx[n-1].vout.size()); + if ( n > 1 && pblock->vtx[n-1].vin.size() == 1 && pblock->vtx[n-1].vout.size() == 1+(ASSETCHAINS_MARMARA!=0) ) { txid = pblock->vtx[n-1].vin[0].prevout.hash; vout = pblock->vtx[n-1].vin[0].prevout.n; - if ( ASSETCHAINS_MARMARA != 0 ) - { - // need to verify it was signed by the non-Marmarapk of the 1of2 - } - txtime = komodo_txtime(&value,txid,vout,destaddr); + txtime = komodo_txtime(opret,&value,txid,vout,destaddr); if ( ExtractDestination(pblock->vtx[n-1].vout[0].scriptPubKey,voutaddress) ) { strcpy(voutaddr,CBitcoinAddress(voutaddress).ToString().c_str()); - if ( strcmp(destaddr,voutaddr) == 0 && pblock->vtx[n-1].vout[0].nValue == value ) + //fprintf(stderr,"voutaddr.%s vs destaddr.%s\n",voutaddr,destaddr); + if ( pblock->vtx[n-1].vout[0].nValue == value && strcmp(destaddr,voutaddr) == 0 ) { - //fprintf(stderr,"is PoS block!\n"); - return(1); + if ( ASSETCHAINS_MARMARA == 0 ) + return(1); + else + { + if ( pblock->vtx[n-1].vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 && (numvouts= pblock->vtx[n-1].vout.size()) == 2 ) + { +//fprintf(stderr,"validate proper %s %s signature and unlockht preservation\n",voutaddr,destaddr); + return(MarmaraPoScheck(destaddr,opret,pblock->vtx[n-1])); + } + else + { + fprintf(stderr,"reject ht.%d PoS block\n",height); + return(strcmp(ASSETCHAINS_SYMBOL,"MTST2") == 0); // allow until MTST3 + } + } } } } @@ -1194,7 +1210,7 @@ uint32_t komodo_segid32(char *coinaddr) int8_t komodo_segid(int32_t nocache,int32_t height) { - CTxDestination voutaddress; CBlock block; CBlockIndex *pindex; uint64_t value; uint32_t txtime; char voutaddr[64],destaddr[64]; int32_t txn_count,vout; uint256 txid; int8_t segid = -1; + CTxDestination voutaddress; CBlock block; CBlockIndex *pindex; uint64_t value; uint32_t txtime; char voutaddr[64],destaddr[64]; int32_t txn_count,vout; uint256 txid; CScript opret; int8_t segid = -1; if ( height > 0 && (pindex= komodo_chainactive(height)) != 0 ) { if ( nocache == 0 && pindex->segid >= -1 ) @@ -1206,7 +1222,7 @@ int8_t komodo_segid(int32_t nocache,int32_t height) { txid = block.vtx[txn_count-1].vin[0].prevout.hash; vout = block.vtx[txn_count-1].vin[0].prevout.n; - txtime = komodo_txtime(&value,txid,vout,destaddr); + txtime = komodo_txtime(opret,&value,txid,vout,destaddr); if ( ExtractDestination(block.vtx[txn_count-1].vout[0].scriptPubKey,voutaddress) ) { strcpy(voutaddr,CBitcoinAddress(voutaddress).ToString().c_str()); @@ -1448,12 +1464,14 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ pindex = it != mapBlockIndex.end() ? it->second : NULL; if ( pindex != 0 && pindex->segid >= -1 ) { + //fprintf(stderr,"isPoSblock segid.%d\n",pindex->segid); if ( pindex->segid == -1 ) return(0); else return(1); } txn_count = pblock->vtx.size(); - if ( txn_count > 1 && pblock->vtx[txn_count-1].vin.size() == 1 && pblock->vtx[txn_count-1].vout.size() == 1 ) + //fprintf(stderr,"checkblock n.%d vins.%d vouts.%d %.8f %.8f\n",txn_count,(int32_t)pblock->vtx[txn_count-1].vin.size(),(int32_t)pblock->vtx[txn_count-1].vout.size(),(double)pblock->vtx[txn_count-1].vout[0].nValue/COIN,(double)pblock->vtx[txn_count-1].vout[1].nValue/COIN); + if ( txn_count > 1 && pblock->vtx[txn_count-1].vin.size() == 1 && pblock->vtx[txn_count-1].vout.size() == 1 + (ASSETCHAINS_MARMARA!=0) ) { it = mapBlockIndex.find(pblock->hashPrevBlock); if ( it != mapBlockIndex.end() && (previndex = it->second) != NULL ) @@ -1463,7 +1481,7 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ vout = pblock->vtx[txn_count-1].vin[0].prevout.n; if ( prevtime != 0 ) { - if ( komodo_isPoS(pblock) != 0 ) + if ( komodo_isPoS(pblock,height) != 0 ) { eligible = komodo_stake(1,bnTarget,height,txid,vout,pblock->nTime,prevtime+27,(char *)""); } @@ -1499,7 +1517,7 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ } if ( slowflag == 0 && isPoS == 0 ) // maybe previous block is not seen yet, do the best approx { - if ( komodo_isPoS(pblock) != 0 ) + if ( komodo_isPoS(pblock,height) != 0 ) isPoS = 1; } if ( slowflag != 0 && isPoS != 0 ) @@ -1812,6 +1830,7 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) return(-1); } } + //fprintf(stderr,"ASSETCHAINS_STAKED.%d ht.%d\n",(int32_t)ASSETCHAINS_STAKED,height); if ( ASSETCHAINS_STAKED != 0 && height >= 2 ) // must PoS or have at least 16x better PoW { if ( (is_PoSblock= komodo_is_PoSblock(slowflag,height,pblock,bnTarget,bhash)) == 0 ) @@ -2068,6 +2087,9 @@ uint32_t komodo_eligible(arith_uint256 bnTarget,arith_uint256 ratio,struct komod return(0); } +int32_t MarmaraSignature(uint8_t *utxosig,CMutableTransaction &txNew); +uint8_t DecodeMaramaraCoinbaseOpRet(const CScript scriptPubKey,CPubKey &pk,int32_t &height,int32_t &unlockht); + int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blocktimep,uint32_t *txtimep,uint256 *utxotxidp,int32_t *utxovoutp,uint64_t *utxovaluep,uint8_t *utxosig) { static struct komodo_staking *array; static int32_t numkp,maxkp; static uint32_t lasttime; @@ -2098,7 +2120,9 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt { CBlockIndex* pblockindex = chainActive[tipindex->GetHeight()]; CBlock block; CTxDestination addressout; - if( ReadBlockFromDisk(block, pblockindex, 1) && komodo_WhoStaked(&block, addressout) != 0 && IsMine(*pwalletMain,addressout) != 0 ) + if ( ASSETCHAINS_MARMARA != 0 ) + resetstaker = true; + else if( ReadBlockFromDisk(block, pblockindex, 1) && komodo_WhoStaked(&block, addressout) != 0 && IsMine(*pwalletMain,addressout) != 0 ) { resetstaker = true; fprintf(stderr, "Reset ram staker after mining a block!\n"); @@ -2116,7 +2140,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt maxkp = numkp = 0; lasttime = 0; } - if ( ASSETCHAINS_MARMARA == 0 || 1 ) + if ( ASSETCHAINS_MARMARA == 0 ) { BOOST_FOREACH(const COutput& out, vecOutputs) { @@ -2149,8 +2173,29 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt } else { - // calc 1of2 address - // iterate all unspents not spent in mempool and komodo_addutxo + struct CCcontract_info *cp,C; uint256 txid; int32_t vout,ht,unlockht; CAmount nValue; char coinaddr[64]; CPubKey mypk,Marmarapk,pk; + std::vector > unspentOutputs; + cp = CCinit(&C,EVAL_MARMARA); + mypk = pubkey2pk(Mypubkey()); + Marmarapk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,Marmarapk,mypk); + SetCCunspents(unspentOutputs,coinaddr); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + if ( (nValue= it->second.satoshis) < COIN ) + continue; + if ( GetTransaction(txid,tx,hashBlock,true) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) + { + const CScript &scriptPubKey = tx.vout[vout].scriptPubKey; + if ( DecodeMaramaraCoinbaseOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,pk,ht,unlockht) != 0 && pk == mypk ) + { + array = komodo_addutxo(array,&numkp,&maxkp,(uint32_t)pindex->nTime,(uint64_t)nValue,txid,vout,coinaddr,hashbuf,(CScript)scriptPubKey); + } + // else fprintf(stderr,"SKIP addutxo %.8f numkp.%d vs max.%d\n",(double)nValue/COIN,numkp,maxkp); + } + } } lasttime = (uint32_t)time(NULL); //fprintf(stderr,"finished kp data of utxo for staking %u ht.%d numkp.%d maxkp.%d\n",(uint32_t)time(NULL),nHeight,numkp,maxkp); @@ -2230,24 +2275,26 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt txNew.vout[0].nValue = *utxovaluep - txfee; txNew.nLockTime = earliest; CTransaction txNewConst(txNew); - if ( ASSETCHAINS_MARMARA == 0 || 1 ) + if ( ASSETCHAINS_MARMARA == 0 ) { signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, *utxovaluep, SIGHASH_ALL), best_scriptPubKey, sigdata, consensusBranchId); - } - else - { - // add opreturn - // signSuccess = CCFinalizetx(...) - } - if (!signSuccess) - fprintf(stderr,"failed to create signature\n"); - else - { UpdateTransaction(txNew,0,sigdata); ptr = (uint8_t *)&sigdata.scriptSig[0]; siglen = sigdata.scriptSig.size(); for (i=0; i 0 ) + signSuccess = true; + else signSuccess = false; + } + if (!signSuccess) + fprintf(stderr,"failed to create signature\n"); + else + { //fprintf(stderr," siglen.%d\n",siglen); //fprintf(stderr,"best %u from %u, gap %d lag.%d\n",earliest,*blocktimep,(int32_t)(earliest - *blocktimep),(int32_t)(time(NULL) - *blocktimep)); *blocktimep = earliest; diff --git a/src/komodo_defs.h b/src/komodo_defs.h index 91fcadddb..0ea97d6db 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -36,5 +36,43 @@ extern uint8_t ASSETCHAINS_TXPOW,ASSETCHAINS_PUBLIC; int32_t MAX_BLOCK_SIZE(int32_t height); +extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; +extern uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT; +extern uint32_t ASSETCHAIN_INIT, ASSETCHAINS_MAGIC; +extern int32_t VERUS_BLOCK_POSUNITS, ASSETCHAINS_LWMAPOS, ASSETCHAINS_SAPLING, ASSETCHAINS_OVERWINTER; +extern uint64_t ASSETCHAINS_SUPPLY; + +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; +extern uint64_t ASSETCHAINS_COMMISSION, ASSETCHAINS_STAKED; +extern bool VERUS_MINTBLOCKS; +extern uint64_t ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_NONCEMASK[]; +extern const char *ASSETCHAINS_ALGORITHMS[]; +extern int32_t VERUS_MIN_STAKEAGE; +extern uint32_t ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA, ASSETCHAINS_NONCESHIFT[], ASSETCHAINS_HASHESPERROUND[]; +extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; +extern std::string NOTARY_PUBKEY,ASSETCHAINS_OVERRIDE_PUBKEY,ASSETCHAINS_SCRIPTPUB; +extern uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_MARMARA; + +extern char ASSETCHAINS_SYMBOL[65]; +extern int32_t VERUS_BLOCK_POSUNITS, VERUS_CONSECUTIVE_POS_THRESHOLD, VERUS_NOPOS_THRESHHOLD; + + +extern int32_t KOMODO_CONNECTING,KOMODO_CCACTIVATE,KOMODO_DEALERNODE; +extern uint32_t ASSETCHAINS_CC; +extern char ASSETCHAINS_SYMBOL[]; +extern std::string CCerror,ASSETCHAINS_CCLIB; +extern uint8_t ASSETCHAINS_CCDISABLES[256]; + +extern int32_t USE_EXTERNAL_PUBKEY; +extern std::string NOTARY_PUBKEY; +extern int32_t KOMODO_EXCHANGEWALLET; +extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; +extern int32_t VERUS_MIN_STAKEAGE; +extern std::string DONATION_PUBKEY; +extern uint8_t ASSETCHAINS_PRIVATE; +extern int32_t USE_EXTERNAL_PUBKEY; #endif diff --git a/src/komodo_utils.h b/src/komodo_utils.h index 289a84d7c..96c9cd922 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1654,7 +1654,7 @@ extern int64_t MAX_MONEY; void komodo_args(char *argv0) { extern const char *Notaries_elected1[][2]; - std::string name,addn; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[8192],disablebits[32],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz,baseid,len,n,extralen = 0; uint64_t ccenables[256]; + std::string name,addn; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[8192],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]; IS_KOMODO_NOTARY = GetBoolArg("-notary", false); memset(ccenables,0,sizeof(ccenables)); memset(disablebits,0,sizeof(disablebits)); @@ -1704,7 +1704,6 @@ void komodo_args(char *argv0) KOMODO_STOPAT = GetArg("-stopat",0); MAX_REORG_LENGTH = GetArg("-maxreorg",MAX_REORG_LENGTH); WITNESS_CACHE_SIZE = MAX_REORG_LENGTH+10; - ASSETCHAINS_CC = GetArg("-ac_cc",0); KOMODO_CCACTIVATE = GetArg("-ac_ccactivate",0); ASSETCHAINS_PUBLIC = GetArg("-ac_public",0); diff --git a/src/main.cpp b/src/main.cpp index 21b6b4eb0..45989a4f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -76,6 +76,7 @@ using namespace std; * Global state */ +#define TMPFILE_START 100000000 CCriticalSection cs_main; extern uint8_t NOTARY_PUBKEY33[33]; extern int32_t KOMODO_LOADINGBLOCKS,KOMODO_LONGESTCHAIN,KOMODO_INSYNC,KOMODO_CONNECTING,KOMODO_EXTRASATOSHI; @@ -182,8 +183,9 @@ namespace { multimap mapBlocksUnlinked; CCriticalSection cs_LastBlockFile; - std::vector vinfoBlockFile; + std::vector vinfoBlockFile,tmpBlockFiles; int nLastBlockFile = 0; + int nLastTmpFile = 0; /** Global flag to indicate we should check to see if there are * block/undo files that should be deleted. Set on startup * or if we allocate more file space when we're in prune mode @@ -1979,6 +1981,73 @@ bool GetAddressUnspent(uint160 addressHash, int type, return true; } +struct CompareBlocksByHeightMain +{ + bool operator()(const CBlockIndex* a, const CBlockIndex* b) const + { + /* Make sure that unequal blocks with the same height do not compare + equal. Use the pointers themselves to make a distinction. */ + + if (a->GetHeight() != b->GetHeight()) + return (a->GetHeight() > b->GetHeight()); + + return a < b; + } +}; + +bool RemoveOrphanedBlocks(int32_t notarized_height) +{ + LOCK(cs_main); + std::vector prunedblocks; + std::set setTips; + int32_t m=0,n = 0; + // get notarised timestamp and use this as a backup incase the forked block has no height. + // we -600 to make sure the time is within future block constraints. + uint32_t notarized_timestamp = komodo_heightstamp(notarized_height)-600; + // Most of this code is a direct copy from GetChainTips RPC. Which gives a return of all + // blocks that are not in the main chain. + BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) + { + n++; + setTips.insert(item.second); + } + n = 0; + BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) + { + const CBlockIndex* pprev=0; + n++; + if ( item.second != 0 ) + pprev = item.second->pprev; + if (pprev) + setTips.erase(pprev); + } + const CBlockIndex *forked; + BOOST_FOREACH(const CBlockIndex* block, setTips) + { + // We skip anything over notarised height to avoid breaking normal consensus rules. + if ( block->GetHeight() > notarized_height || block->nTime > notarized_timestamp ) + continue; + // We can also check if the block is in the active chain as a backup test. + forked = chainActive.FindFork(block); + // Here we save each forked block to a vector for removal later. + if ( forked != 0 ) + prunedblocks.push_back(block); + } + if (prunedblocks.size() > 0 && pblocktree->EraseBatchSync(prunedblocks)) + { + // Blocks cleared from disk succesfully, using internal DB batch erase function. Which exists, but has never been used before. + // We need to try and clear the block index from mapBlockIndex now, otherwise node will need a restart. + BOOST_FOREACH(const CBlockIndex* block, prunedblocks) + { + m++; + mapBlockIndex.erase(block->GetBlockHash()); + } + fprintf(stderr, "%s removed %d orphans from %d blocks before %d\n",ASSETCHAINS_SYMBOL,m,n, notarized_height); + return true; + } + return false; +} + /*uint64_t myGettxout(uint256 hash,int32_t n) { CCoins coins; @@ -2126,7 +2195,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock // CBlock and CBlockIndex // -bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart) +bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart) { // Open history file to append CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); @@ -3157,9 +3226,12 @@ static int64_t nTimeConnect = 0; static int64_t nTimeIndex = 0; static int64_t nTimeCallbacks = 0; static int64_t nTimeTotal = 0; +bool FindBlockPos(int32_t tmpflag,CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false); +bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos); bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck,bool fCheckPOW) { + CDiskBlockPos blockPos; const CChainParams& chainparams = Params(); if ( KOMODO_STOPAT != 0 && pindex->GetHeight() > KOMODO_STOPAT ) return(false); @@ -3189,6 +3261,21 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return false; fprintf(stderr,"grandfathered exception, until jan 15th 2019\n"); } + if ( (pindex->nStatus & BLOCK_IN_TMPFILE) != 0 ) + { + unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + if (!FindBlockPos(0,state, blockPos, nBlockSize+8, pindex->GetHeight(), block.GetBlockTime(),false)) + return error("ConnectBlock(): FindBlockPos failed"); + if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) + return error("ConnectBlock(): FindBlockPos failed"); + pindex->nStatus &= (~BLOCK_IN_TMPFILE); + pindex->nFile = blockPos.nFile; + pindex->nDataPos = blockPos.nPos; + if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) + return error("AcceptBlock(): ReceivedBlockTransactions failed"); + setDirtyFileInfo.insert(blockPos.nFile); + fprintf(stderr,"added ht.%d copy of tmpfile to %d.%d\n",pindex->GetHeight(),blockPos.nFile,blockPos.nPos); + } // verify that the view's current state corresponds to the previous block uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); if ( hashPrevBlock != view.GetBestBlock() ) @@ -3806,7 +3893,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { CValidationState stateDummy; // don't keep staking or invalid transactions - if (tx.IsCoinBase() || ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED && komodo_isPoS((CBlock *)&block) != 0)) || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) + if (tx.IsCoinBase() || ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED && komodo_isPoS((CBlock *)&block,pindexDelete->GetHeight()) != 0)) || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) { mempool.remove(tx, removed, true); } @@ -3837,7 +3924,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { { CTransaction &tx = block.vtx[i]; //if ((i == (block.vtx.size() - 1)) && ((ASSETCHAINS_LWMAPOS && block.IsVerusPOSBlock()) || (ASSETCHAINS_STAKED != 0 && (komodo_isPoS((CBlock *)&block) != 0)))) - if ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED != 0 && (komodo_isPoS((CBlock *)&block) != 0))) + if ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED != 0 && (komodo_isPoS((CBlock *)&block,pindexDelete->GetHeight()) != 0))) { EraseFromWallets(tx.GetHash()); } @@ -3853,7 +3940,8 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { int32_t komodo_activate_sapling(CBlockIndex *pindex) { - uint32_t blocktime,prevtime; CBlockIndex *prev; int32_t i,transition=0,height,prevht,activation = 0; + uint32_t blocktime,prevtime; CBlockIndex *prev; int32_t i,transition=0,height,prevht; + int32_t activation = 0; if ( pindex == 0 ) { fprintf(stderr,"komodo_activate_sapling null pindex\n"); @@ -3911,6 +3999,7 @@ int32_t komodo_activate_sapling(CBlockIndex *pindex) fprintf(stderr,"%s sapling activation at %d\n",ASSETCHAINS_SYMBOL,activation); ASSETCHAINS_SAPLING = activation; } + return activation; } static int64_t nTimeReadFromDisk = 0; @@ -4483,43 +4572,57 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl return true; } -bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false) +bool FindBlockPos(int32_t tmpflag,CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown) { + std::vector *ptr; int *lastfilep; LOCK(cs_LastBlockFile); - unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile; - if (vinfoBlockFile.size() <= nFile) { - vinfoBlockFile.resize(nFile + 1); + unsigned int nFile; + if ( tmpflag != 0 ) + { + ptr = &tmpBlockFiles; + nFile = nLastTmpFile; + lastfilep = &nLastTmpFile; + } + else + { + ptr = &vinfoBlockFile; + lastfilep = &nLastBlockFile; + nFile = fKnown ? pos.nFile : nLastBlockFile; + if (vinfoBlockFile.size() <= nFile) { + vinfoBlockFile.resize(nFile + 1); + } } - if (!fKnown) { - while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { + while ((*ptr)[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { nFile++; - if (vinfoBlockFile.size() <= nFile) { - vinfoBlockFile.resize(nFile + 1); + if ((*ptr).size() <= nFile) { + (*ptr).resize(nFile + 1); } } - pos.nFile = nFile; - pos.nPos = vinfoBlockFile[nFile].nSize; + pos.nFile = nFile + tmpflag*TMPFILE_START; + pos.nPos = (*ptr)[nFile].nSize; + if ( 0 && tmpflag != 0 ) + fprintf(stderr,"pos.nFile %d nPos %u\n",pos.nFile,pos.nPos); } - if (nFile != nLastBlockFile) { + if (nFile != *lastfilep) { if (!fKnown) { - LogPrintf("Leaving block file %i: %s\n", nFile, vinfoBlockFile[nFile].ToString()); + LogPrintf("Leaving block file %i: %s\n", nFile, (*ptr)[nFile].ToString()); } FlushBlockFile(!fKnown); - nLastBlockFile = nFile; + *lastfilep = nFile; } - vinfoBlockFile[nFile].AddBlock(nHeight, nTime); + (*ptr)[nFile].AddBlock(nHeight, nTime); if (fKnown) - vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize); + (*ptr)[nFile].nSize = std::max(pos.nPos + nAddSize, (*ptr)[nFile].nSize); else - vinfoBlockFile[nFile].nSize += nAddSize; + (*ptr)[nFile].nSize += nAddSize; if (!fKnown) { unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; - unsigned int nNewChunks = (vinfoBlockFile[nFile].nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; + unsigned int nNewChunks = ((*ptr)[nFile].nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; if (nNewChunks > nOldChunks) { if (fPruneMode) fCheckForPruning = true; @@ -4536,19 +4639,26 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd } } - setDirtyFileInfo.insert(nFile); + setDirtyFileInfo.insert(nFile + tmpflag*TMPFILE_START); return true; } bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize) { - pos.nFile = nFile; - + std::vector *ptr; int *lastfilep; LOCK(cs_LastBlockFile); + pos.nFile = nFile; + if ( nFile >= TMPFILE_START ) + { + fprintf(stderr,"skip tmp undo\n"); + return(false); + nFile %= TMPFILE_START; + ptr = &tmpBlockFiles; + } else ptr = &vinfoBlockFile; unsigned int nNewSize; - pos.nPos = vinfoBlockFile[nFile].nUndoSize; - nNewSize = vinfoBlockFile[nFile].nUndoSize += nAddSize; + pos.nPos = (*ptr)[nFile].nUndoSize; + nNewSize = (*ptr)[nFile].nUndoSize += nAddSize; setDirtyFileInfo.insert(nFile); unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; @@ -4592,7 +4702,7 @@ bool CheckBlockHeader(int32_t *futureblockp,int32_t height,CBlockIndex *pindex, *futureblockp = 0; if (blockhdr.GetBlockTime() > GetAdjustedTime() + 60) { - CBlockIndex *tipindex; + /*CBlockIndex *tipindex; //fprintf(stderr,"ht.%d future block %u vs time.%u + 60\n",height,(uint32_t)blockhdr.GetBlockTime(),(uint32_t)GetAdjustedTime()); if ( (tipindex= chainActive.Tip()) != 0 && tipindex->GetBlockHash() == blockhdr.hashPrevBlock && blockhdr.GetBlockTime() < GetAdjustedTime() + 60 + 5 ) { @@ -4601,9 +4711,9 @@ bool CheckBlockHeader(int32_t *futureblockp,int32_t height,CBlockIndex *pindex, sleep(1); //fprintf(stderr,"now its valid\n"); } - else + else*/ { - if (blockhdr.GetBlockTime() < GetAdjustedTime() + 600) + if (blockhdr.GetBlockTime() < GetAdjustedTime() + 300) *futureblockp = 1; //LogPrintf("CheckBlockHeader block from future %d error",blockhdr.GetBlockTime() - GetAdjustedTime()); return false; //state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"),REJECT_INVALID, "time-too-new"); @@ -4734,7 +4844,7 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C CValidationState state; CTransaction Tx; const CTransaction &tx = (CTransaction)block.vtx[i]; - if (tx.IsCoinBase() || !tx.vjoinsplit.empty() || !tx.vShieldedSpend.empty() || ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED && komodo_isPoS((CBlock *)&block) != 0))) + if (tx.IsCoinBase() || !tx.vjoinsplit.empty() || !tx.vShieldedSpend.empty() || ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED && komodo_isPoS((CBlock *)&block,height) != 0))) continue; Tx = tx; if ( myAddtomempool(Tx, &state, true) == false ) // happens with out of order tx in block on resync @@ -5098,19 +5208,22 @@ bool AcceptBlock(int32_t *futureblockp,CBlock& block, CValidationState& state, C } int nHeight = pindex->GetHeight(); + int32_t usetmp = 0; // Write block to history file try { unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); CDiskBlockPos blockPos; if (dbp != NULL) blockPos = *dbp; - if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != NULL)) + if (!FindBlockPos(usetmp,state, blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != NULL)) return error("AcceptBlock(): FindBlockPos failed"); if (dbp == NULL) if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) AbortNode(state, "Failed to write block"); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) return error("AcceptBlock(): ReceivedBlockTransactions failed"); + if ( usetmp != 0 ) // not during initialdownload or if futureflag==0 and contextchecks ok + pindex->nStatus |= BLOCK_IN_TMPFILE; } catch (const std::runtime_error& e) { return AbortNode(state, std::string("System error: ") + e.what()); } @@ -5428,7 +5541,7 @@ bool CheckDiskSpace(uint64_t nAdditionalBytes) FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) { - static int32_t didinit[64]; + static int32_t didinit[256]; if (pos.IsNull()) return NULL; boost::filesystem::path path = GetBlockPosFilename(pos, prefix); @@ -5572,6 +5685,7 @@ bool static LoadBlockIndexDB() // Load block file info pblocktree->ReadLastBlockFile(nLastBlockFile); vinfoBlockFile.resize(nLastBlockFile + 1); + tmpBlockFiles.resize(nLastTmpFile + 1); LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile); for (int nFile = 0; nFile <= nLastBlockFile; nFile++) { pblocktree->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]); @@ -5595,7 +5709,6 @@ bool static LoadBlockIndexDB() if (pindex->nStatus & BLOCK_HAVE_DATA) { setBlkDataFiles.insert(pindex->nFile); } - //komodo_pindex_init(pindex,(int32_t)pindex->GetHeight()); } //fprintf(stderr,"load blockindexDB %u\n",(uint32_t)time(NULL)); for (std::set::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) @@ -5936,6 +6049,7 @@ void UnloadBlockIndex() nSyncStarted = 0; mapBlocksUnlinked.clear(); vinfoBlockFile.clear(); + tmpBlockFiles.clear(); nLastBlockFile = 0; nBlockSequenceId = 1; mapBlockSource.clear(); @@ -5971,6 +6085,7 @@ bool LoadBlockIndex() bool InitBlockIndex() { const CChainParams& chainparams = Params(); LOCK(cs_main); + tmpBlockFiles.clear(); // Initialize global variables that cannot be constructed at startup. recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); @@ -6003,7 +6118,7 @@ bool InitBlockIndex() { unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); CDiskBlockPos blockPos; CValidationState state; - if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.GetBlockTime())) + if (!FindBlockPos(0,state, blockPos, nBlockSize+8, 0, block.GetBlockTime())) return error("LoadBlockIndex(): FindBlockPos failed"); if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) return error("LoadBlockIndex(): writing genesis block to disk failed"); diff --git a/src/main.h b/src/main.h index 463c91237..d19e3aa17 100644 --- a/src/main.h +++ b/src/main.h @@ -78,7 +78,7 @@ static const bool DEFAULT_ALERTS = true; /** Minimum alert priority for enabling safe mode. */ static const int ALERT_PRIORITY_SAFE_MODE = 4000; /** Maximum reorg length we will accept before we shut down and alert the user. */ -static unsigned int MAX_REORG_LENGTH = (_COINBASE_MATURITY - 1); +static unsigned int MAX_REORG_LENGTH = _COINBASE_MATURITY - 1; /** Maximum number of signature check operations in an IsStandard() P2SH script */ static const unsigned int MAX_P2SH_SIGOPS = 15; /** The maximum number of sigops we're willing to relay/mine in a single tx */ @@ -803,10 +803,10 @@ bool GetAddressUnspent(uint160 addressHash, int type, std::vector > &unspentOutputs); /** Functions for disk access for blocks */ -bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); +bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos,bool checkPOW); bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex,bool checkPOW); - +bool RemoveOrphanedBlocks(int32_t notarized_height); /** Functions for validating blocks and updating the block tree */ diff --git a/src/metrics.cpp b/src/metrics.cpp index a01d381a1..6b387f7dc 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -39,8 +39,7 @@ #endif #include -extern uint64_t ASSETCHAINS_TIMELOCKGTE; -extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH; +#include "komodo_defs.h" int64_t komodo_block_unlocktime(uint32_t nHeight); void AtomicTimer::start() diff --git a/src/miner.cpp b/src/miner.cpp index c71bc2461..7ffcaebc2 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -132,17 +132,8 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, #include "komodo_defs.h" extern CCriticalSection cs_metrics; -extern int32_t KOMODO_MININGTHREADS,KOMODO_LONGESTCHAIN,ASSETCHAINS_SEED,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAIN_INIT,KOMODO_INITDONE,KOMODO_ON_DEMAND,KOMODO_INITDONE,KOMODO_PASSPORT_INITDONE; -extern uint64_t ASSETCHAINS_COMMISSION, ASSETCHAINS_STAKED; -extern bool VERUS_MINTBLOCKS; -extern uint64_t ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_NONCEMASK[]; -extern const char *ASSETCHAINS_ALGORITHMS[]; -extern int32_t VERUS_MIN_STAKEAGE, ASSETCHAINS_ALGO, ASSETCHAINS_EQUIHASH, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA, ASSETCHAINS_LWMAPOS, ASSETCHAINS_NONCESHIFT[], ASSETCHAINS_HASHESPERROUND[]; -extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; -extern std::string NOTARY_PUBKEY,ASSETCHAINS_OVERRIDE_PUBKEY,ASSETCHAINS_SCRIPTPUB; void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uint8_t *src,int32_t len); -extern uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_MARMARA; uint32_t Mining_start,Mining_height; int32_t My_notaryid = -1; int32_t komodo_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33,uint32_t timestamp); @@ -487,7 +478,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 { LEAVE_CRITICAL_SECTION(cs_main); LEAVE_CRITICAL_SECTION(mempool.cs); - uint64_t txfees,utxovalue; uint32_t txtime; uint256 utxotxid; int32_t i,siglen,numsigs,utxovout; uint8_t utxosig[128],*ptr; + uint64_t txfees,utxovalue; uint32_t txtime; uint256 utxotxid; int32_t i,siglen,numsigs,utxovout; uint8_t utxosig[512],*ptr; CMutableTransaction txStaked = CreateNewContextualCMutableTransaction(Params().GetConsensus(), stakeHeight); if (ASSETCHAINS_LWMAPOS != 0) @@ -521,7 +512,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 pblocktemplate->vTxSigOps.push_back(GetLegacySigOpCount(txStaked)); nFees += txfees; pblock->nTime = blocktime; - //printf("staking PoS ht.%d t%u lag.%u\n",(int32_t)chainActive.LastTip()->GetHeight()+1,blocktime,(uint32_t)(GetAdjustedTime() - (blocktime-13))); +printf("staking PoS ht.%d t%u lag.%u\n",(int32_t)chainActive.LastTip()->GetHeight()+1,blocktime,(uint32_t)(GetAdjustedTime() - (blocktime-13))); } else return(0); //fprintf(stderr,"no utxos eligible for staking\n"); } // Create coinbase tx @@ -1405,6 +1396,8 @@ void static BitcoinMiner_noeq() miningTimer.stop(); } +int32_t gotinvalid; + #ifdef ENABLE_WALLET void static BitcoinMiner(CWallet *pwallet) #else @@ -1613,16 +1606,12 @@ void static BitcoinMiner() fprintf(stderr," PoW for staked coin PoS %d%% vs target %d%% ht.%d\n",percPoS,(int32_t)ASSETCHAINS_STAKED,Mining_height); } } + gotinvalid = 0; while (true) { - /*if ( KOMODO_INSYNC == 0 ) - { - KOMODO_LONGESTCHAIN = komodo_longestchain(); - fprintf(stderr,"Mining when blockchain might not be in sync longest.%d vs %d\n",KOMODO_LONGESTCHAIN,Mining_height); - if ( KOMODO_LONGESTCHAIN != 0 && Mining_height >= KOMODO_LONGESTCHAIN ) - KOMODO_INSYNC = Mining_height; - sleep(3); - }*/ + //fprintf(stderr,"gotinvalid.%d\n",gotinvalid); + if ( gotinvalid != 0 ) + break; komodo_longestchain(); // Hash state KOMODO_CHOSEN_ONE = 0; @@ -1716,6 +1705,7 @@ void static BitcoinMiner() for (z=31; z>=0; z--) fprintf(stderr,"%02x",((uint8_t *)&h)[z]); fprintf(stderr," Invalid block mined, try again\n"); + gotinvalid = 1; return(false); } KOMODO_CHOSEN_ONE = 1; diff --git a/src/net.h b/src/net.h index 3f30175c3..3e06e9831 100644 --- a/src/net.h +++ b/src/net.h @@ -33,6 +33,7 @@ #include "sync.h" #include "uint256.h" #include "utilstrencodings.h" +#include "util.h" #include #include @@ -63,7 +64,7 @@ static const unsigned int MAX_INV_SZ = 50000; /** The maximum number of new addresses to accumulate before announcing. */ static const unsigned int MAX_ADDR_TO_SEND = 1000; /** Maximum length of incoming protocol messages (no message over 2 MiB is currently acceptable). */ -static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1024 * 1024; // depends on MAX_BLOCK_SIZE +static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = (_MAX_BLOCK_SIZE + 24); // 24 is msgheader size /** Maximum length of strSubVer in `version` message */ static const unsigned int MAX_SUBVERSION_LENGTH = 256; /** -listen default */ diff --git a/src/pow.cpp b/src/pow.cpp index b69c4fce6..cc2b0b7c2 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -37,9 +37,8 @@ #endif // ENABLE_RUST uint32_t komodo_chainactive_timestamp(); -extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_EQUIHASH, ASSETCHAINS_STAKED; -extern char ASSETCHAINS_SYMBOL[65]; -extern int32_t ASSETCHAINS_LWMAPOS,VERUS_BLOCK_POSUNITS, VERUS_CONSECUTIVE_POS_THRESHOLD, VERUS_NOPOS_THRESHHOLD; +#include "komodo_defs.h" + unsigned int lwmaGetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params); unsigned int lwmaCalculateNextWorkRequired(const CBlockIndex* pindexLast, const Consensus::Params& params); diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 00fc8cee9..bb3314760 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -24,8 +24,8 @@ #include "tinyformat.h" #include "utilstrencodings.h" #include "crypto/common.h" +#include "komodo_defs.h" -extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH; // default hash algorithm for block uint256 (CBlockHeader::*CBlockHeader::hashFunction)() const = &CBlockHeader::GetSHA256DHash; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index a0643528d..ff9b1b4c4 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -48,10 +48,8 @@ using namespace std; -extern int32_t ASSETCHAINS_ALGO, ASSETCHAINS_EQUIHASH, ASSETCHAINS_LWMAPOS; -extern uint64_t ASSETCHAINS_STAKED; -extern int32_t KOMODO_MININGTHREADS; -extern bool VERUS_MINTBLOCKS; +#include "komodo_defs.h" + arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc); /** diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 0ebfa7b25..e6d8025b1 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -77,7 +77,8 @@ int32_t notarizedtxid_height(char *dest,char *txidstr,int32_t *kmdnotarized_heig extern uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT; extern uint32_t ASSETCHAINS_CC; extern uint32_t ASSETCHAINS_MAGIC; -extern uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_STAKED,ASSETCHAINS_SUPPLY,ASSETCHAINS_LASTERA; +extern uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_STAKED,ASSETCHAINS_SUPPLY; +extern uint32_t ASSETCHAINS_LASTERA; extern int32_t ASSETCHAINS_LWMAPOS,ASSETCHAINS_SAPLING; extern uint64_t ASSETCHAINS_ENDSUBSIDY[],ASSETCHAINS_REWARD[],ASSETCHAINS_HALVING[],ASSETCHAINS_DECAY[]; extern std::string NOTARY_PUBKEY; extern uint8_t NOTARY_PUBKEY33[]; @@ -224,7 +225,7 @@ UniValue getinfo(const UniValue& params, bool fHelp) } } if (ASSETCHAINS_LASTERA > 0) - obj.push_back(Pair("eras", ASSETCHAINS_LASTERA + 1)); + obj.push_back(Pair("eras", (int64_t)(ASSETCHAINS_LASTERA + 1))); obj.push_back(Pair("reward", acReward)); obj.push_back(Pair("halving", acHalving)); obj.push_back(Pair("decay", acDecay)); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 90f8fd6db..82a400e71 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -463,6 +463,10 @@ static const CRPCCommand vRPCCommands[] = // Payments { "payments", "paymentsaddress", &paymentsaddress, true }, + { "CClib", "cclibaddress", &cclibaddress, true }, + { "CClib", "cclibinfo", &cclibinfo, true }, + { "CClib", "cclib", &cclib, true }, + // Gateways { "gateways", "gatewaysaddress", &gatewaysaddress, true }, { "gateways", "gatewayslist", &gatewayslist, true }, @@ -533,6 +537,9 @@ static const CRPCCommand vRPCCommands[] = { "util", "reconsiderblock", &reconsiderblock, true }, /* Not shown in help */ { "hidden", "setmocktime", &setmocktime, true }, + { "hidden", "test_ac", &test_ac, true }, + { "hidden", "test_heirmarker", &test_heirmarker, true }, + #ifdef ENABLE_WALLET /* Wallet */ { "wallet", "resendwallettransactions", &resendwallettransactions, true}, diff --git a/src/rpc/server.h b/src/rpc/server.h index d5ebb56ab..3edc85ebf 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -287,6 +287,9 @@ extern UniValue marmara_creditloop(const UniValue& params, bool fHelp); extern UniValue marmara_settlement(const UniValue& params, bool fHelp); extern UniValue marmara_lock(const UniValue& params, bool fHelp); extern UniValue paymentsaddress(const UniValue& params, bool fHelp); +extern UniValue cclibaddress(const UniValue& params, bool fHelp); +extern UniValue cclibinfo(const UniValue& params, bool fHelp); +extern UniValue cclib(const UniValue& params, bool fHelp); extern UniValue gatewaysaddress(const UniValue& params, bool fHelp); extern UniValue gatewayslist(const UniValue& params, bool fHelp); extern UniValue gatewaysinfo(const UniValue& params, bool fHelp); @@ -468,4 +471,8 @@ extern UniValue paxprices(const UniValue& params, bool fHelp); extern UniValue paxdeposit(const UniValue& params, bool fHelp); extern UniValue paxwithdraw(const UniValue& params, bool fHelp); +// test rpc: +extern UniValue test_ac(const UniValue& params, bool fHelp); +extern UniValue test_heirmarker(const UniValue& params, bool fHelp); + #endif // BITCOIN_RPCSERVER_H diff --git a/src/script/sign.cpp b/src/script/sign.cpp index b90ceafb5..685018828 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -45,14 +45,14 @@ bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, key = *pprivKey; else if (!keystore || !keystore->GetKey(address, key)) return false; - + uint256 hash; try { hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, consensusBranchId); } catch (logic_error ex) { return false; } - + if (scriptCode.IsPayToCryptoCondition()) { CC *cc = (CC *)extraData; @@ -75,9 +75,9 @@ bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, return false; } } - + vchSig.push_back((unsigned char)nHashType); - + return true; } @@ -128,7 +128,7 @@ std::vector &GetCryptoConditions() static bool initialized = false; static std::vector vCC = std::vector(); CCcontract_info C; - + if (!initialized) { // this should initialize any desired auto-signed crypto-conditions @@ -140,7 +140,7 @@ bool GetCCByUnspendableAddress(struct CCcontract_info *cp, char *addrstr) { std::vector &vCC = GetCryptoConditions(); bool found = false; - + for (int i = 0; i < vCC.size(); i++) { if (strcmp(addrstr, vCC[i].unspendableCCaddr) == 0) @@ -157,7 +157,7 @@ bool CCinitLite(struct CCcontract_info *cp, uint8_t evalcode) { std::vector &vCC = GetCryptoConditions(); bool found = false; - + for (int i = 0; i < vCC.size(); i++) { if (vCC[i].evalcode == evalcode) @@ -172,7 +172,7 @@ bool CCinitLite(struct CCcontract_info *cp, uint8_t evalcode) bool _Getscriptaddress(char *destaddr, const CScript &scriptPubKey) { - CTxDestination address; + CTxDestination address; txnouttype whichType; std::vector> vvch = std::vector>(); if (Solver(scriptPubKey, whichType, vvch) && vvch[0].size() == 20) @@ -199,10 +199,10 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip vector vPK; vector vParams = vector(); COptCCParams p; - + // get information to sign with CCcontract_info C; - + scriptPubKey.IsPayToCryptoCondition(&subScript, vParams); if (vParams.empty()) { @@ -219,12 +219,12 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip { p = COptCCParams(vParams[0]); } - + if (p.IsValid() && p.vKeys.size() >= p.n) { bool is1of2 = (p.m == 1 && p.n == 2); CKey privKey; - + // must be a valid cc eval code if (CCinitLite(&C, p.evalCode)) { @@ -232,7 +232,7 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip if (!is1of2) { bool havePriv = creator.KeyStore().GetKey(p.vKeys[0].GetID(), privKey); - + // if we don't have the private key, it must be the unspendable address if (!havePriv && (p.vKeys[0] == CPubKey(ParseHex(C.CChexstr)))) { @@ -240,9 +240,9 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip std::vector vch(&(C.CCpriv[0]), C.CCpriv + sizeof(C.CCpriv)); privKey.Set(vch.begin(), vch.end(), false); } - + CC *cc = CCcond1(p.evalCode, p.vKeys[0]); - + if (cc) { vector vch; @@ -254,7 +254,7 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip { fprintf(stderr,"vin has 1of1 CC signing error with address.(%s)\n", p.vKeys[0].GetID().ToString().c_str()); } - + cc_free(cc); return ret.size() != 0; } @@ -266,7 +266,7 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip { if (creator.IsKeystoreValid() && creator.KeyStore().GetKey(pk.GetID(), privKey) && privKey.IsValid()) break; - + if (pk == CPubKey(ParseHex(C.CChexstr))) { privKey = CKey(); @@ -275,12 +275,12 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip break; } } - + if (!privKey.IsValid()) return false; - + CC *cc = CCcond1of2(p.evalCode, p.vKeys[0], p.vKeys[1]); - + if (cc) { vector vch; @@ -292,7 +292,7 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip { fprintf(stderr,"vin has 1of2 CC signing error with addresses.(%s)\n(%s)\n", p.vKeys[0].GetID().ToString().c_str(), p.vKeys[1].GetID().ToString().c_str()); } - + cc_free(cc); return ret.size() != 0; } @@ -314,9 +314,9 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP CScript scriptRet; uint160 h160; ret.clear(); - + vector vSolutions; - + if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) { // if this is a CLTV script, solve for the destination after CLTV @@ -324,10 +324,10 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP { uint8_t pushOp = scriptPubKey[0]; uint32_t scriptStart = pushOp + 3; - + // check post CLTV script CScript postfix = CScript(scriptPubKey.size() > scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end()); - + // check again with only postfix subscript if (!Solver(postfix, whichTypeRet, vSolutions)) return false; @@ -335,44 +335,44 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP else return false; } - + CKeyID keyID; - + switch (whichTypeRet) { - case TX_NONSTANDARD: - case TX_NULL_DATA: - return false; - case TX_PUBKEY: - keyID = CPubKey(vSolutions[0]).GetID(); - return Sign1(keyID, creator, scriptPubKey, ret, consensusBranchId); - case TX_PUBKEYHASH: - keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, creator, scriptPubKey, ret, consensusBranchId)) + case TX_NONSTANDARD: + case TX_NULL_DATA: return false; - else - { - CPubKey vch; - creator.KeyStore().GetPubKey(keyID, vch); - ret.push_back(ToByteVector(vch)); - } - return true; - case TX_SCRIPTHASH: - if (creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptRet)) { - ret.push_back(std::vector(scriptRet.begin(), scriptRet.end())); + case TX_PUBKEY: + keyID = CPubKey(vSolutions[0]).GetID(); + return Sign1(keyID, creator, scriptPubKey, ret, consensusBranchId); + case TX_PUBKEYHASH: + keyID = CKeyID(uint160(vSolutions[0])); + if (!Sign1(keyID, creator, scriptPubKey, ret, consensusBranchId)) + return false; + else + { + CPubKey vch; + creator.KeyStore().GetPubKey(keyID, vch); + ret.push_back(ToByteVector(vch)); + } return true; - } - return false; - - case TX_CRYPTOCONDITION: - return SignStepCC(creator, scriptPubKey, vSolutions, ret, consensusBranchId); - - case TX_MULTISIG: - ret.push_back(valtype()); // workaround CHECKMULTISIG bug - return (SignN(vSolutions, creator, scriptPubKey, ret, consensusBranchId)); - - default: - return false; + case TX_SCRIPTHASH: + if (creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptRet)) { + ret.push_back(std::vector(scriptRet.begin(), scriptRet.end())); + return true; + } + return false; + + case TX_CRYPTOCONDITION: + return SignStepCC(creator, scriptPubKey, vSolutions, ret, consensusBranchId); + + case TX_MULTISIG: + ret.push_back(valtype()); // workaround CHECKMULTISIG bug + return (SignN(vSolutions, creator, scriptPubKey, ret, consensusBranchId)); + + default: + return false; } } @@ -399,7 +399,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu txnouttype whichType; solved = SignStep(creator, script, result, whichType, consensusBranchId); CScript subscript; - + if (solved && whichType == TX_SCRIPTHASH) { // Solver returns the subscript that needs to be evaluated; @@ -409,9 +409,9 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu solved = solved && SignStep(creator, script, result, whichType, consensusBranchId) && whichType != TX_SCRIPTHASH; result.push_back(std::vector(subscript.begin(), subscript.end())); } - + sigdata.scriptSig = PushAll(result); - + // Test solution return solved && VerifyScript(sigdata.scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker(), consensusBranchId); } @@ -431,19 +431,19 @@ void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const Signatur } bool SignSignature( - const CKeyStore &keystore, - const CScript& fromPubKey, - CMutableTransaction& txTo, - unsigned int nIn, - const CAmount& amount, - int nHashType, - uint32_t consensusBranchId) + const CKeyStore &keystore, + const CScript& fromPubKey, + CMutableTransaction& txTo, + unsigned int nIn, + const CAmount& amount, + int nHashType, + uint32_t consensusBranchId) { assert(nIn < txTo.vin.size()); - + CTransaction txToConst(txTo); TransactionSignatureCreator creator(&keystore, &txToConst, nIn, amount, nHashType); - + SignatureData sigdata; bool ret = ProduceSignature(creator, fromPubKey, sigdata, consensusBranchId); UpdateTransaction(txTo, nIn, sigdata); @@ -451,24 +451,24 @@ bool SignSignature( } bool SignSignature( - const CKeyStore &keystore, - const CTransaction& txFrom, - CMutableTransaction& txTo, - unsigned int nIn, - int nHashType, - uint32_t consensusBranchId) + const CKeyStore &keystore, + const CTransaction& txFrom, + CMutableTransaction& txTo, + unsigned int nIn, + int nHashType, + uint32_t consensusBranchId) { assert(nIn < txTo.vin.size()); CTxIn& txin = txTo.vin[nIn]; assert(txin.prevout.n < txFrom.vout.size()); const CTxOut& txout = txFrom.vout[txin.prevout.n]; - + return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType, consensusBranchId); } static vector CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const vector& vSolutions, - const vector& sigs1, const vector& sigs2, uint32_t consensusBranchId) + const vector& vSolutions, + const vector& sigs1, const vector& sigs2, uint32_t consensusBranchId) { // Combine all the signatures we've got: set allsigs; @@ -482,7 +482,7 @@ static vector CombineMultisig(const CScript& scriptPubKey, const BaseSi if (!v.empty()) allsigs.insert(v); } - + // Build a map of pubkey -> signature by matching sigs to pubkeys: assert(vSolutions.size() > 1); unsigned int nSigsRequired = vSolutions.front()[0]; @@ -495,7 +495,7 @@ static vector CombineMultisig(const CScript& scriptPubKey, const BaseSi const valtype& pubkey = vSolutions[i+1]; if (sigs.count(pubkey)) continue; // Already got a sig for this pubkey - + if (checker.CheckSig(sig, pubkey, scriptPubKey, consensusBranchId)) { sigs[pubkey] = sig; @@ -517,108 +517,108 @@ static vector CombineMultisig(const CScript& scriptPubKey, const BaseSi // Fill any missing with OP_0: for (unsigned int i = nSigsHave; i < nSigsRequired; i++) result.push_back(valtype()); - + return result; } namespace { -struct Stacks -{ - std::vector script; - - Stacks() {} - explicit Stacks(const std::vector& scriptSigStack_) : script(scriptSigStack_) {} - explicit Stacks(const SignatureData& data, uint32_t consensusBranchId) { - EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), consensusBranchId); - } - - SignatureData Output() const { - SignatureData result; - result.scriptSig = PushAll(script); - return result; - } -}; + struct Stacks + { + std::vector script; + + Stacks() {} + explicit Stacks(const std::vector& scriptSigStack_) : script(scriptSigStack_) {} + explicit Stacks(const SignatureData& data, uint32_t consensusBranchId) { + EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), consensusBranchId); + } + + SignatureData Output() const { + SignatureData result; + result.scriptSig = PushAll(script); + return result; + } + }; } static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const txnouttype txType, const vector& vSolutions, - Stacks sigs1, Stacks sigs2, uint32_t consensusBranchId) + const txnouttype txType, const vector& vSolutions, + Stacks sigs1, Stacks sigs2, uint32_t consensusBranchId) { switch (txType) { - case TX_NONSTANDARD: - case TX_NULL_DATA: - // Don't know anything about this, assume bigger one is correct: - if (sigs1.script.size() >= sigs2.script.size()) - return sigs1; - return sigs2; - case TX_PUBKEY: - case TX_PUBKEYHASH: - case TX_CRYPTOCONDITION: - // Signatures are bigger than placeholders or empty scripts: - if (sigs1.script.empty() || sigs1.script[0].empty()) + case TX_NONSTANDARD: + case TX_NULL_DATA: + // Don't know anything about this, assume bigger one is correct: + if (sigs1.script.size() >= sigs2.script.size()) + return sigs1; return sigs2; - return sigs1; - case TX_SCRIPTHASH: - if (sigs1.script.empty() || sigs1.script.back().empty()) - return sigs2; - else if (sigs2.script.empty() || sigs2.script.back().empty()) + case TX_PUBKEY: + case TX_PUBKEYHASH: + case TX_CRYPTOCONDITION: + // Signatures are bigger than placeholders or empty scripts: + if (sigs1.script.empty() || sigs1.script[0].empty()) + return sigs2; return sigs1; - else - { - // Recur to combine: - valtype spk = sigs1.script.back(); - CScript pubKey2(spk.begin(), spk.end()); - - txnouttype txType2; - vector > vSolutions2; - Solver(pubKey2, txType2, vSolutions2); - sigs1.script.pop_back(); - sigs2.script.pop_back(); - Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, consensusBranchId); - result.script.push_back(spk); - return result; - } - case TX_MULTISIG: - return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script, consensusBranchId)); - default: - return Stacks(); + case TX_SCRIPTHASH: + if (sigs1.script.empty() || sigs1.script.back().empty()) + return sigs2; + else if (sigs2.script.empty() || sigs2.script.back().empty()) + return sigs1; + else + { + // Recur to combine: + valtype spk = sigs1.script.back(); + CScript pubKey2(spk.begin(), spk.end()); + + txnouttype txType2; + vector > vSolutions2; + Solver(pubKey2, txType2, vSolutions2); + sigs1.script.pop_back(); + sigs2.script.pop_back(); + Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, consensusBranchId); + result.script.push_back(spk); + return result; + } + case TX_MULTISIG: + return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script, consensusBranchId)); + default: + return Stacks(); } } SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const SignatureData& scriptSig1, const SignatureData& scriptSig2, - uint32_t consensusBranchId) + const SignatureData& scriptSig1, const SignatureData& scriptSig2, + uint32_t consensusBranchId) { txnouttype txType; vector > vSolutions; Solver(scriptPubKey, txType, vSolutions); - + return CombineSignatures( - scriptPubKey, checker, txType, vSolutions, - Stacks(scriptSig1, consensusBranchId), - Stacks(scriptSig2, consensusBranchId), - consensusBranchId).Output(); + scriptPubKey, checker, txType, vSolutions, + Stacks(scriptSig1, consensusBranchId), + Stacks(scriptSig2, consensusBranchId), + consensusBranchId).Output(); } namespace { -/** Dummy signature checker which accepts all signatures. */ -class DummySignatureChecker : public BaseSignatureChecker -{ -public: - DummySignatureChecker() {} - - bool CheckSig( - const std::vector& scriptSig, - const std::vector& vchPubKey, - const CScript& scriptCode, - uint32_t consensusBranchId) const + /** Dummy signature checker which accepts all signatures. */ + class DummySignatureChecker : public BaseSignatureChecker { - return true; - } -}; -const DummySignatureChecker dummyChecker; + public: + DummySignatureChecker() {} + + bool CheckSig( + const std::vector& scriptSig, + const std::vector& vchPubKey, + const CScript& scriptCode, + uint32_t consensusBranchId) const + { + return true; + } + }; + const DummySignatureChecker dummyChecker; } const BaseSignatureChecker& DummySignatureCreator::Checker() const @@ -627,12 +627,12 @@ const BaseSignatureChecker& DummySignatureCreator::Checker() const } bool DummySignatureCreator::CreateSig( - std::vector& vchSig, - const CKeyID& keyid, - const CScript& scriptCode, - uint32_t consensusBranchId, - CKey *key, - void *extraData) const + std::vector& vchSig, + const CKeyID& keyid, + const CScript& scriptCode, + uint32_t consensusBranchId, + CKey *key, + void *extraData) const { // Create a dummy signature that is a valid DER-encoding vchSig.assign(72, '\000'); @@ -647,3 +647,4 @@ bool DummySignatureCreator::CreateSig( vchSig[6 + 33 + 32] = SIGHASH_ALL; return true; } + diff --git a/src/sendalert.cpp b/src/sendalert.cpp index 700146338..6525cb356 100644 --- a/src/sendalert.cpp +++ b/src/sendalert.cpp @@ -103,7 +103,7 @@ void ThreadSendAlert() // 4000 or higher will put the RPC into safe mode alert.nPriority = 4000; alert.strComment = ""; - alert.strStatusBar = "Your client version has degraded networking behavior. Please update to the most recent version of Verus (0.3.2 or later)."; + alert.strStatusBar = "Your client version has degraded networking behavior. Please update to the most recent version of Komodo (0.3.3 or later)."; alert.strRPCError = alert.strStatusBar; // Set specific client version/versions here. If setSubVer is empty, no filtering on subver is done: diff --git a/src/txdb.cpp b/src/txdb.cpp index 164167c29..c86ee9bfa 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -686,7 +686,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->nTx = diskindex.nTx; pindexNew->nSproutValue = diskindex.nSproutValue; pindexNew->nSaplingValue = diskindex.nSaplingValue; - +//fprintf(stderr,"loadguts ht.%d\n",pindexNew->GetHeight()); // Consistency checks auto header = pindexNew->GetBlockHeader(); if (header.GetHash() != pindexNew->GetBlockHash()) diff --git a/src/util.h b/src/util.h index bd1a2542a..dc421092a 100644 --- a/src/util.h +++ b/src/util.h @@ -3,6 +3,21 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +/****************************************************************************** + * 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. * + * * + ******************************************************************************/ + /** * Server/client environment: argument handling, config file parsing, * logging, thread wrappers @@ -29,6 +44,8 @@ #include #include +#define _MAX_BLOCK_SIZE (4096 * 1024) // changing just _MAX_BLOCK_SIZE will hardfork to that size + static const bool DEFAULT_LOGTIMEMICROS = false; static const bool DEFAULT_LOGIPS = false; static const bool DEFAULT_LOGTIMESTAMPS = true; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 02cf85ee6..14620aa0b 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -57,6 +57,7 @@ #include +#include "komodo_defs.h" using namespace std; @@ -67,8 +68,6 @@ const std::string ADDR_TYPE_SPROUT = "sprout"; const std::string ADDR_TYPE_SAPLING = "sapling"; extern UniValue TxJoinSplitToJSON(const CTransaction& tx); -extern uint8_t ASSETCHAINS_PRIVATE; -extern int32_t USE_EXTERNAL_PUBKEY; uint32_t komodo_segid32(char *coinaddr); int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); int32_t komodo_isnotaryvout(char *coinaddr); // from ac_private chains only @@ -4887,10 +4886,11 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) if (useAnySprout || useAnySapling || zaddrs.size() > 0) { // Get available notes - std::vector sproutEntries,skipsprout; + std::vector sproutEntries; + //std::vector saplingEntries; + //pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs); std::vector saplingEntries,skipsapling; pwalletMain->GetFilteredNotes(sproutEntries, useAnySprout == 0 ? saplingEntries : skipsapling, zaddrs); - // If Sapling is not active, do not allow sending from a sapling addresses. if (!saplingActive && saplingEntries.size() > 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); @@ -5172,7 +5172,6 @@ int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits int32_t ensure_CCrequirements() { - extern uint8_t NOTARY_PUBKEY33[]; CCerror = ""; if ( NOTARY_PUBKEY33[0] == 0 ) return(-1); @@ -5262,8 +5261,6 @@ UniValue setpubkey(const UniValue& params, bool fHelp) char Raddress[18]; uint8_t pubkey33[33]; - extern uint8_t NOTARY_PUBKEY33[]; - extern std::string NOTARY_PUBKEY; if ( NOTARY_PUBKEY33[0] == 0 ) { if (strlen(params[0].get_str().c_str()) == 66) { decode_hex(pubkey33,33,(char *)params[0].get_str().c_str()); @@ -5327,6 +5324,43 @@ UniValue channelsaddress(const UniValue& params, bool fHelp) return(result); } +UniValue cclibaddress(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; std::vector pubkey; + cp = CCinit(&C,EVAL_FIRSTUSER); + if ( fHelp || params.size() > 1 ) + throw runtime_error("cclibaddress [pubkey]\n"); + if ( ensure_CCrequirements() < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + if ( params.size() == 1 ) + pubkey = ParseHex(params[0].get_str().c_str()); + return(CCaddress(cp,(char *)"CClib",pubkey)); +} + +UniValue cclibinfo(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; + cp = CCinit(&C,EVAL_FIRSTUSER); + if ( fHelp || params.size() > 0 ) + throw runtime_error("cclibinfo\n"); + if ( ensure_CCrequirements() < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + return(CClib_info(cp)); +} + +UniValue cclib(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; char *method; cJSON *jsonparams; + cp = CCinit(&C,EVAL_FIRSTUSER); + if ( fHelp || params.size() > 2 ) + throw runtime_error("cclib method [JSON params]\n"); + if ( ensure_CCrequirements() < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + method = (char *)params[0].get_str().c_str(); + jsonparams = cJSON_Parse(params[1].get_str().c_str()); + return(CClib(cp,method,jsonparams)); +} + UniValue oraclesaddress(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; std::vector pubkey; @@ -7329,7 +7363,7 @@ UniValue heirfund(const UniValue& params, bool fHelp) { UniValue result(UniValue::VOBJ); uint256 tokenid = zeroid; - uint64_t txfee; + int64_t txfee; int64_t amount; int64_t inactivitytime; std::string hex; @@ -7340,21 +7374,34 @@ UniValue heirfund(const UniValue& params, bool fHelp) return NullUniValue; if (fHelp || params.size() != 5 && params.size() != 6) - throw runtime_error("heirfundtokens fee funds heirname heirpubkey inactivitytime [tokenid]\n"); + throw runtime_error("heirfund txfee funds heirname heirpubkey inactivitytime [tokenid]\n"); if (ensure_CCrequirements() < 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); - txfee = atoll((char*)params[0].get_str().c_str()); - amount = atoll((char*)params[1].get_str().c_str()); + txfee = atoll(params[0].get_str().c_str()); + if (txfee < 0) + throw runtime_error("incorrect txfee param\n"); + + if(params.size() == 6) // tokens in satoshis: + amount = atoll(params[1].get_str().c_str()); + else // coins: + amount = atof(params[1].get_str().c_str()) * COIN; + + if( amount <= 0 ) + throw runtime_error("incorrect amount\n"); + name = params[2].get_str(); pubkey = ParseHex(params[3].get_str().c_str()); if( !pubkey2pk(pubkey).IsValid() ) throw runtime_error("incorrect pubkey\n"); - inactivitytime = atof((char*)params[4].get_str().c_str()); + inactivitytime = atoll(params[4].get_str().c_str()); + if (inactivitytime <= 0) + throw runtime_error("incorrect inactivity time param\n"); + if (params.size() == 6) { tokenid = Parseuint256((char*)params[5].get_str().c_str()); if(tokenid == zeroid) @@ -7373,7 +7420,7 @@ UniValue heiradd(const UniValue& params, bool fHelp) { UniValue result; uint256 fundingtxid; - uint64_t txfee; + int64_t txfee; int64_t amount; int64_t inactivitytime; std::string hex; @@ -7384,18 +7431,20 @@ UniValue heiradd(const UniValue& params, bool fHelp) return NullUniValue; if (fHelp || params.size() != 3) - throw runtime_error("heiraddtokens fee funds fundingtxid\n"); + throw runtime_error("heiradd txfee funds fundingtxid\n"); if (ensure_CCrequirements() < 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); - txfee = atoll((char*)params[0].get_str().c_str()); - amount = atoll((char*)params[1].get_str().c_str()); + txfee = atoll(params[0].get_str().c_str()); + if (txfee < 0) + throw runtime_error("incorrect txfee param\n"); + fundingtxid = Parseuint256((char*)params[2].get_str().c_str()); - result = HeirAddCaller(fundingtxid, txfee, amount); + result = HeirAddCaller(fundingtxid, txfee, params[1].get_str()); return result; } @@ -7404,7 +7453,6 @@ UniValue heirclaim(const UniValue& params, bool fHelp) UniValue result; // result(UniValue::VOBJ); uint256 fundingtxid; int64_t txfee; - int64_t amount; int64_t inactivitytime; std::string hex; std::vector pubkey; @@ -7415,18 +7463,20 @@ UniValue heirclaim(const UniValue& params, bool fHelp) return NullUniValue; if (fHelp || params.size() != 3) - throw runtime_error("heirclaimtokens fee funds fundingtxid\n"); + throw runtime_error("heirclaim txfee funds fundingtxid\n"); if (ensure_CCrequirements() < 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); - txfee = atoll((char*)params[0].get_str().c_str()); - amount = atoll((char*)params[1].get_str().c_str()); + txfee = atoll(params[0].get_str().c_str()); + if (txfee < 0) + throw runtime_error("incorrect txfee param\n"); + fundingtxid = Parseuint256((char*)params[2].get_str().c_str()); - result = HeirClaimCaller(fundingtxid, txfee, amount); + result = HeirClaimCaller(fundingtxid, txfee, params[1].get_str()); return result; } @@ -7548,3 +7598,79 @@ void RegisterWalletRPCCommands(CRPCTable &tableRPC) for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); } + +UniValue test_ac(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 4)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements() < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + std::vector pubkey1; + std::vector pubkey2; + + pubkey1 = ParseHex(params[0].get_str().c_str()); + pubkey2 = ParseHex(params[1].get_str().c_str()); + + CPubKey pk1 = pubkey2pk(pubkey1); + CPubKey pk2 = pubkey2pk(pubkey2); + + if(!pk1.IsValid() || !pk2.IsValid()) + throw runtime_error("invalid pubkey\n"); + + int64_t txfee = 10000; + int64_t amount = atoll(params[2].get_str().c_str()) * COIN; + uint256 fundingtxid = Parseuint256((char *)params[3].get_str().c_str()); + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, txfee + amount, 60); + + if( normalInputs < txfee + amount) + throw runtime_error("not enough normals\n"); + + mtx.vout.push_back(MakeCC1of2vout(EVAL_HEIR, amount, pk1, pk2)); + + CScript opret; + fundingtxid = revuint256(fundingtxid); + + opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'A' << fundingtxid << (uint8_t)0); + + cp = CCinit(&C, EVAL_HEIR); + return(FinalizeCCTx(0, cp, mtx, myPubkey, txfee, opret)); +} + +UniValue test_heirmarker(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 1)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements() < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint256 fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); + if (normalInputs < 10000) + throw runtime_error("not enough normals\n"); + + mtx.vin.push_back(CTxIn(fundingtxid, 1)); + mtx.vout.push_back(MakeCC1vout(EVAL_HEIR, 10000, myPubkey)); + + CScript opret; + fundingtxid = revuint256(fundingtxid); + + opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'C' << fundingtxid << (uint8_t)0); + + cp = CCinit(&C, EVAL_HEIR); + return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret)); +} \ No newline at end of file diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0000f8d42..d171812c4 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -59,13 +59,7 @@ bool fSendFreeTransactions = false; bool fPayAtLeastCustomFee = true; #include "komodo_defs.h" -extern int32_t USE_EXTERNAL_PUBKEY; -extern std::string NOTARY_PUBKEY; -extern int32_t KOMODO_EXCHANGEWALLET; -extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; -extern int32_t VERUS_MIN_STAKEAGE; CBlockIndex *komodo_chainactive(int32_t height); -extern std::string DONATION_PUBKEY; /** * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index c8f9bcbb6..663e60367 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -77,7 +77,6 @@ static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000; // unless there is some exceptional network disruption. extern unsigned int WITNESS_CACHE_SIZE; - //! Size of HD seed in bytes static const size_t HD_WALLET_SEED_LENGTH = 32;